4 * Apply a WIM image to a NTFS volume. We restore everything we can, including
5 * security data and alternate data streams.
9 * Copyright (C) 2012 Eric Biggers
11 * This file is part of wimlib, a library for working with WIM files.
13 * wimlib is free software; you can redistribute it and/or modify it under the
14 * terms of the GNU General Public License as published by the Free
15 * Software Foundation; either version 3 of the License, or (at your option)
18 * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
19 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
20 * A PARTICULAR PURPOSE. See the GNU General Public License for more
23 * You should have received a copy of the GNU General Public License
24 * along with wimlib; if not, see http://www.gnu.org/licenses/.
31 #include <ntfs-3g/endians.h>
32 #include <ntfs-3g/types.h>
34 #include "wimlib_internal.h"
36 #include "lookup_table.h"
37 #include "buffer_io.h"
38 #include <ntfs-3g/layout.h>
39 #include <ntfs-3g/acls.h>
40 #include <ntfs-3g/attrib.h>
41 #include <ntfs-3g/security.h> /* security.h before xattrs.h */
42 #include <ntfs-3g/xattrs.h>
43 #include <ntfs-3g/reparse.h>
47 static int extract_wim_chunk_to_ntfs_attr(const u8 *buf, size_t len,
48 u64 offset, void *arg)
51 if (ntfs_attr_pwrite(na, offset, len, buf) == len) {
54 ERROR_WITH_ERRNO("Error extracting WIM resource to NTFS attribute");
55 return WIMLIB_ERR_WRITE;
60 * Extracts a WIM resource to a NTFS attribute.
63 extract_wim_resource_to_ntfs_attr(const struct lookup_table_entry *lte,
66 return extract_wim_resource(lte, wim_resource_size(lte),
67 extract_wim_chunk_to_ntfs_attr, na);
70 /* Writes the data streams to a NTFS file
72 * @ni: The NTFS inode for the file.
73 * @inode: The WIM dentry that has an inode containing the streams.
75 * Returns 0 on success, nonzero on failure.
77 static int write_ntfs_data_streams(ntfs_inode *ni, const struct dentry *dentry,
78 union wimlib_progress_info *progress_info)
81 unsigned stream_idx = 0;
82 ntfschar *stream_name = AT_UNNAMED;
83 u32 stream_name_len = 0;
84 const struct inode *inode = dentry->d_inode;
86 DEBUG("Writing %u NTFS data stream%s for `%s'",
88 (inode->num_ads == 0 ? "" : "s"),
89 dentry->full_path_utf8);
92 struct lookup_table_entry *lte;
94 lte = inode_stream_lte_resolved(inode, stream_idx);
96 if (stream_name_len) {
97 /* Create an empty named stream. */
98 ret = ntfs_attr_add(ni, AT_DATA, stream_name,
99 stream_name_len, NULL, 0);
101 ERROR_WITH_ERRNO("Failed to create name data "
102 "stream for extracted file "
104 dentry->full_path_utf8);
105 ret = WIMLIB_ERR_NTFS_3G;
110 /* If there's no lookup table entry, it's an empty stream.
111 * Otherwise, we must open the attribute and extract the data.
116 na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
118 ERROR_WITH_ERRNO("Failed to open a data stream of "
119 "extracted file `%s'",
120 dentry->full_path_utf8);
121 ret = WIMLIB_ERR_NTFS_3G;
124 ret = ntfs_attr_truncate_solid(na, wim_resource_size(lte));
130 ret = extract_wim_resource_to_ntfs_attr(lte, na);
134 progress_info->extract.completed_bytes += wim_resource_size(lte);
136 if (stream_idx == inode->num_ads)
138 stream_name = (ntfschar*)inode->ads_entries[stream_idx].stream_name;
139 stream_name_len = inode->ads_entries[stream_idx].stream_name_len / 2;
145 /* Open the NTFS inode that corresponds to the parent of a WIM dentry. */
146 static ntfs_inode *dentry_open_parent_ni(const struct dentry *dentry,
150 const char *dir_name;
154 p = dentry->full_path_utf8 + dentry->full_path_utf8_len;
161 dir_name = dentry->full_path_utf8;
162 dir_ni = ntfs_pathname_to_inode(vol, NULL, dir_name);
164 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
172 * Makes a NTFS hard link
174 * It is named @from_dentry->file_name and is located under the directory
175 * specified by @dir_ni, and it is made to point to the previously extracted
176 * file located at @inode->extracted_file.
178 * Return 0 on success, nonzero on failure.
180 static int apply_hardlink_ntfs(const struct dentry *from_dentry,
181 const struct inode *inode,
183 ntfs_inode **to_ni_ret)
188 const char *dir_name;
193 if (ntfs_inode_close(dir_ni) != 0) {
194 ERROR_WITH_ERRNO("Error closing directory");
195 return WIMLIB_ERR_NTFS_3G;
200 DEBUG("Extracting NTFS hard link `%s' => `%s'",
201 from_dentry->full_path_utf8, inode->extracted_file);
203 to_ni = ntfs_pathname_to_inode(vol, NULL, inode->extracted_file);
205 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
206 inode->extracted_file);
207 return WIMLIB_ERR_NTFS_3G;
212 dir_ni = dentry_open_parent_ni(from_dentry, vol);
214 return WIMLIB_ERR_NTFS_3G;
216 ret = ntfs_link(to_ni, dir_ni,
217 (ntfschar*)from_dentry->file_name,
218 from_dentry->file_name_len / 2);
220 ERROR_WITH_ERRNO("Could not create hard link `%s' => `%s'",
221 from_dentry->full_path_utf8,
222 inode->extracted_file);
223 ret = WIMLIB_ERR_NTFS_3G;
229 apply_file_attributes_and_security_data(ntfs_inode *ni,
231 const struct dentry *dentry,
234 DEBUG("Setting NTFS file attributes on `%s' to %#"PRIx32,
235 dentry->full_path_utf8, dentry->d_inode->attributes);
237 struct SECURITY_CONTEXT ctx;
239 attributes_le32 = cpu_to_le32(dentry->d_inode->attributes);
240 memset(&ctx, 0, sizeof(ctx));
242 ret = ntfs_xattr_system_setxattr(&ctx, XATTR_NTFS_ATTRIB,
244 (const char*)&attributes_le32,
247 ERROR("Failed to set NTFS file attributes on `%s'",
248 dentry->full_path_utf8);
249 return WIMLIB_ERR_NTFS_3G;
251 if (dentry->d_inode->security_id != -1) {
252 const struct wim_security_data *sd;
253 const char *descriptor;
255 sd = wim_const_security_data(w);
256 wimlib_assert(dentry->d_inode->security_id < sd->num_entries);
257 descriptor = (const char *)sd->descriptors[dentry->d_inode->security_id];
258 DEBUG("Applying security descriptor %d to `%s'",
259 dentry->d_inode->security_id, dentry->full_path_utf8);
261 ret = ntfs_xattr_system_setxattr(&ctx, XATTR_NTFS_ACL,
262 ni, dir_ni, descriptor,
263 sd->sizes[dentry->d_inode->security_id], 0);
266 ERROR_WITH_ERRNO("Failed to set security data on `%s'",
267 dentry->full_path_utf8);
268 return WIMLIB_ERR_NTFS_3G;
274 static int apply_reparse_data(ntfs_inode *ni, const struct dentry *dentry,
275 union wimlib_progress_info *progress_info)
277 struct lookup_table_entry *lte;
280 lte = inode_unnamed_lte_resolved(dentry->d_inode);
282 DEBUG("Applying reparse data to `%s'", dentry->full_path_utf8);
285 ERROR("Could not find reparse data for `%s'",
286 dentry->full_path_utf8);
287 return WIMLIB_ERR_INVALID_DENTRY;
290 if (wim_resource_size(lte) >= 0xffff) {
291 ERROR("Reparse data of `%s' is too long (%"PRIu64" bytes)",
292 dentry->full_path_utf8, wim_resource_size(lte));
293 return WIMLIB_ERR_INVALID_DENTRY;
296 u8 reparse_data_buf[8 + wim_resource_size(lte)];
297 u8 *p = reparse_data_buf;
298 p = put_u32(p, dentry->d_inode->reparse_tag); /* ReparseTag */
299 p = put_u16(p, wim_resource_size(lte)); /* ReparseDataLength */
300 p = put_u16(p, 0); /* Reserved */
302 ret = read_full_wim_resource(lte, p, 0);
306 ret = ntfs_set_ntfs_reparse_data(ni, (char*)reparse_data_buf,
307 wim_resource_size(lte) + 8, 0);
309 ERROR_WITH_ERRNO("Failed to set NTFS reparse data on `%s'",
310 dentry->full_path_utf8);
311 return WIMLIB_ERR_NTFS_3G;
313 progress_info->extract.completed_bytes += wim_resource_size(lte);
317 static int do_apply_dentry_ntfs(struct dentry *dentry, ntfs_inode *dir_ni,
318 struct apply_args *args);
321 * If @dentry is part of a hard link group, search for hard-linked dentries in
322 * the same directory that have a nonempty DOS (short) filename. There should
323 * be exactly 0 or 1 such dentries. If there is 1, extract that dentry first,
324 * so that the DOS name is correctly associated with the corresponding long name
325 * in the Win32 namespace, and not any of the additional names in the POSIX
326 * namespace created from hard links.
328 static int preapply_dentry_with_dos_name(struct dentry *dentry,
329 ntfs_inode **dir_ni_p,
330 struct apply_args *args)
332 struct dentry *other;
333 struct dentry *dentry_with_dos_name;
335 dentry_with_dos_name = NULL;
336 inode_for_each_dentry(other, dentry->d_inode) {
337 if (other != dentry && (dentry->parent == other->parent)
338 && other->short_name_len)
340 if (dentry_with_dos_name) {
341 ERROR("Found multiple DOS names for file `%s' "
342 "in the same directory",
343 dentry_with_dos_name->full_path_utf8);
344 return WIMLIB_ERR_INVALID_DENTRY;
346 dentry_with_dos_name = other;
349 /* If there's a dentry with a DOS name, extract it first */
350 if (dentry_with_dos_name && !dentry_with_dos_name->is_extracted) {
352 const char *dir_name;
355 ntfs_volume *vol = (*dir_ni_p)->vol;
357 DEBUG("pre-applying DOS name `%s'",
358 dentry_with_dos_name->full_path_utf8);
359 ret = do_apply_dentry_ntfs(dentry_with_dos_name,
364 *dir_ni_p = dentry_open_parent_ni(dentry, vol);
366 return WIMLIB_ERR_NTFS_3G;
372 * Applies a WIM dentry to a NTFS filesystem.
374 * @dentry: The WIM dentry to apply
375 * @dir_ni: The NTFS inode for the parent directory
377 * @return: 0 on success; nonzero on failure.
379 static int do_apply_dentry_ntfs(struct dentry *dentry, ntfs_inode *dir_ni,
380 struct apply_args *args)
384 ntfs_inode *ni = NULL;
385 bool is_hardlink = false;
386 ntfs_volume *vol = dir_ni->vol;
387 struct inode *inode = dentry->d_inode;
388 dentry->is_extracted = 1;
390 if (inode->attributes & FILE_ATTRIBUTE_DIRECTORY) {
393 /* If this dentry is hard-linked to any other dentries in the
394 * same directory, make sure to apply the one (if any) with a
395 * DOS name first. Otherwise, NTFS-3g might not assign the file
396 * names correctly. */
397 if (dentry->short_name_len == 0) {
398 ret = preapply_dentry_with_dos_name(dentry,
406 if (inode->link_count > 1) {
407 /* Already extracted another dentry in the hard link
408 * group. We can make a hard link instead of extracting
410 if (inode->extracted_file) {
411 ret = apply_hardlink_ntfs(dentry, inode,
415 goto out_close_dir_ni;
417 goto out_set_dos_name;
419 /* Can't make a hard link; extract the file itself */
420 FREE(inode->extracted_file);
421 inode->extracted_file = STRDUP(dentry->full_path_utf8);
422 if (!inode->extracted_file) {
423 ret = WIMLIB_ERR_NOMEM;
424 goto out_close_dir_ni;
430 * Create a directory or file.
432 * Note: For symbolic links that are not directory junctions, pass
433 * S_IFREG here, since we manually set the reparse data later.
435 ni = ntfs_create(dir_ni, 0, (ntfschar*)dentry->file_name,
436 dentry->file_name_len / 2, type);
439 ERROR_WITH_ERRNO("Could not create NTFS object for `%s'",
440 dentry->full_path_utf8);
441 ret = WIMLIB_ERR_NTFS_3G;
442 goto out_close_dir_ni;
445 /* Write the data streams, unless this is a directory or reparse point
447 if (!(inode->attributes & (FILE_ATTRIBUTE_REPARSE_POINT |
448 FILE_ATTRIBUTE_DIRECTORY))) {
449 ret = write_ntfs_data_streams(ni, dentry, &args->progress);
451 goto out_close_dir_ni;
455 ret = apply_file_attributes_and_security_data(ni, dir_ni,
458 goto out_close_dir_ni;
460 if (inode->attributes & FILE_ATTR_REPARSE_POINT) {
461 ret = apply_reparse_data(ni, dentry, &args->progress);
463 goto out_close_dir_ni;
467 /* Set DOS (short) name if given */
468 if (dentry->short_name_len != 0) {
470 char *short_name_utf8;
471 size_t short_name_utf8_len;
472 ret = utf16_to_utf8(dentry->short_name,
473 dentry->short_name_len,
475 &short_name_utf8_len);
477 goto out_close_dir_ni;
482 const char *dir_name;
484 /* ntfs_set_ntfs_dos_name() closes the inodes in the
485 * wrong order if we have applied a hard link. Close
486 * them ourselves, then re-open then. */
487 if (ntfs_inode_close(dir_ni) != 0) {
489 ret = WIMLIB_ERR_NTFS_3G;
490 ERROR_WITH_ERRNO("Failed to close directory inode");
493 if (ntfs_inode_close(ni) != 0) {
495 ret = WIMLIB_ERR_NTFS_3G;
496 ERROR_WITH_ERRNO("Failed to close hard link target inode");
500 dir_ni = dentry_open_parent_ni(dentry, vol);
502 ret = WIMLIB_ERR_NTFS_3G;
506 ni = ntfs_pathname_to_inode(vol, dir_ni,
507 dentry->file_name_utf8);
509 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
510 dentry->full_path_utf8);
511 ntfs_inode_close(dir_ni);
512 ret = WIMLIB_ERR_NTFS_3G;
513 goto out_close_dir_ni;
517 DEBUG("Setting short (DOS) name of `%s' to %s",
518 dentry->full_path_utf8, short_name_utf8);
520 ret = ntfs_set_ntfs_dos_name(ni, dir_ni, short_name_utf8,
521 short_name_utf8_len, 0);
522 FREE(short_name_utf8);
524 ERROR_WITH_ERRNO("Could not set DOS (short) name for `%s'",
525 dentry->full_path_utf8);
526 ret = WIMLIB_ERR_NTFS_3G;
528 /* inodes have been closed by ntfs_set_ntfs_dos_name(). */
534 if (ntfs_inode_close_in_dir(ni, dir_ni) != 0) {
537 ret = WIMLIB_ERR_NTFS_3G;
538 ERROR_WITH_ERRNO("Failed to close inode for `%s'",
539 dentry->full_path_utf8);
542 if (ntfs_inode_close(dir_ni) != 0) {
544 ret = WIMLIB_ERR_NTFS_3G;
545 ERROR_WITH_ERRNO("Failed to close directory inode");
548 if (ni && ntfs_inode_close(ni) != 0) {
550 ret = WIMLIB_ERR_NTFS_3G;
551 ERROR_WITH_ERRNO("Failed to close inode for `%s'",
552 dentry->full_path_utf8);
558 static int apply_root_dentry_ntfs(const struct dentry *dentry,
559 ntfs_volume *vol, const WIMStruct *w)
564 wimlib_assert(dentry_is_directory(dentry));
565 ni = ntfs_pathname_to_inode(vol, NULL, "/");
567 ERROR_WITH_ERRNO("Could not find root NTFS inode");
568 return WIMLIB_ERR_NTFS_3G;
570 ret = apply_file_attributes_and_security_data(ni, ni, dentry, w);
571 if (ntfs_inode_close(ni) != 0) {
572 ERROR_WITH_ERRNO("Failed to close NTFS inode for root "
574 ret = WIMLIB_ERR_NTFS_3G;
579 /* Applies a WIM dentry to the NTFS volume */
580 int apply_dentry_ntfs(struct dentry *dentry, void *arg)
582 struct apply_args *args = arg;
583 ntfs_volume *vol = args->vol;
584 WIMStruct *w = args->w;
587 if (dentry_is_root(dentry))
588 return apply_root_dentry_ntfs(dentry, vol, w);
590 dir_ni = dentry_open_parent_ni(dentry, vol);
592 return do_apply_dentry_ntfs(dentry, dir_ni, arg);
594 return WIMLIB_ERR_NTFS_3G;
597 int apply_dentry_timestamps_ntfs(struct dentry *dentry, void *arg)
599 struct apply_args *args = arg;
600 ntfs_volume *vol = args->vol;
606 DEBUG("Setting timestamps on `%s'", dentry->full_path_utf8);
608 ni = ntfs_pathname_to_inode(vol, NULL, dentry->full_path_utf8);
610 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
611 dentry->full_path_utf8);
612 return WIMLIB_ERR_NTFS_3G;
616 p = put_u64(p, dentry->d_inode->creation_time);
617 p = put_u64(p, dentry->d_inode->last_write_time);
618 p = put_u64(p, dentry->d_inode->last_access_time);
619 ret = ntfs_inode_set_times(ni, (const char*)buf, 3 * sizeof(u64), 0);
621 ERROR_WITH_ERRNO("Failed to set NTFS timestamps on `%s'",
622 dentry->full_path_utf8);
623 ret = WIMLIB_ERR_NTFS_3G;
626 if (ntfs_inode_close(ni) != 0) {
628 ret = WIMLIB_ERR_NTFS_3G;
629 ERROR_WITH_ERRNO("Failed to close NTFS inode for `%s'",
630 dentry->full_path_utf8);