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 struct apply_args *args)
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 args->progress.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;
146 * Makes a NTFS hard link
148 * It is named @from_dentry->file_name and is located under the directory
149 * specified by @dir_ni, and it is made to point to the previously extracted
150 * file located at @inode->extracted_file.
152 * Return 0 on success, nonzero on failure.
154 static int apply_hardlink_ntfs(const struct dentry *from_dentry,
155 const struct inode *inode,
157 ntfs_inode **to_ni_ret)
162 const char *dir_name;
167 wimlib_assert(dentry_is_regular_file(from_dentry)
168 && inode_is_regular_file(inode));
170 if (ntfs_inode_close(dir_ni) != 0) {
171 ERROR_WITH_ERRNO("Error closing directory");
172 return WIMLIB_ERR_NTFS_3G;
177 DEBUG("Extracting NTFS hard link `%s' => `%s'",
178 from_dentry->full_path_utf8, inode->extracted_file);
180 to_ni = ntfs_pathname_to_inode(vol, NULL, inode->extracted_file);
182 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
183 inode->extracted_file);
184 return WIMLIB_ERR_NTFS_3G;
186 p = from_dentry->full_path_utf8 + from_dentry->full_path_utf8_len;
193 dir_name = from_dentry->full_path_utf8;
195 dir_ni = ntfs_pathname_to_inode(vol, NULL, dir_name);
197 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
198 from_dentry->full_path_utf8);
200 return WIMLIB_ERR_NTFS_3G;
204 ret = ntfs_link(to_ni, dir_ni,
205 (ntfschar*)from_dentry->file_name,
206 from_dentry->file_name_len / 2);
208 ERROR_WITH_ERRNO("Could not create hard link `%s' => `%s'",
209 from_dentry->full_path_utf8,
210 inode->extracted_file);
211 ret = WIMLIB_ERR_NTFS_3G;
218 apply_file_attributes_and_security_data(ntfs_inode *ni,
220 const struct dentry *dentry,
223 DEBUG("Setting NTFS file attributes on `%s' to %#"PRIx32,
224 dentry->full_path_utf8, dentry->d_inode->attributes);
226 struct SECURITY_CONTEXT ctx;
228 attributes_le32 = cpu_to_le32(dentry->d_inode->attributes);
229 memset(&ctx, 0, sizeof(ctx));
231 ret = ntfs_xattr_system_setxattr(&ctx, XATTR_NTFS_ATTRIB,
233 (const char*)&attributes_le32,
236 ERROR("Failed to set NTFS file attributes on `%s'",
237 dentry->full_path_utf8);
238 return WIMLIB_ERR_NTFS_3G;
240 if (dentry->d_inode->security_id != -1) {
241 const struct wim_security_data *sd;
242 const char *descriptor;
244 sd = wim_const_security_data(w);
245 wimlib_assert(dentry->d_inode->security_id < sd->num_entries);
246 descriptor = (const char *)sd->descriptors[dentry->d_inode->security_id];
247 DEBUG("Applying security descriptor %d to `%s'",
248 dentry->d_inode->security_id, dentry->full_path_utf8);
250 ret = ntfs_xattr_system_setxattr(&ctx, XATTR_NTFS_ACL,
251 ni, dir_ni, descriptor,
252 sd->sizes[dentry->d_inode->security_id], 0);
255 ERROR_WITH_ERRNO("Failed to set security data on `%s'",
256 dentry->full_path_utf8);
257 return WIMLIB_ERR_NTFS_3G;
263 static int apply_reparse_data(ntfs_inode *ni, const struct dentry *dentry,
264 struct apply_args *args)
266 struct lookup_table_entry *lte;
269 lte = inode_unnamed_lte_resolved(dentry->d_inode);
271 DEBUG("Applying reparse data to `%s'", dentry->full_path_utf8);
274 ERROR("Could not find reparse data for `%s'",
275 dentry->full_path_utf8);
276 return WIMLIB_ERR_INVALID_DENTRY;
279 if (wim_resource_size(lte) >= 0xffff) {
280 ERROR("Reparse data of `%s' is too long (%"PRIu64" bytes)",
281 dentry->full_path_utf8, wim_resource_size(lte));
282 return WIMLIB_ERR_INVALID_DENTRY;
285 u8 reparse_data_buf[8 + wim_resource_size(lte)];
286 u8 *p = reparse_data_buf;
287 p = put_u32(p, dentry->d_inode->reparse_tag); /* ReparseTag */
288 p = put_u16(p, wim_resource_size(lte)); /* ReparseDataLength */
289 p = put_u16(p, 0); /* Reserved */
291 ret = read_full_wim_resource(lte, p, 0);
295 ret = ntfs_set_ntfs_reparse_data(ni, (char*)reparse_data_buf,
296 wim_resource_size(lte) + 8, 0);
298 ERROR_WITH_ERRNO("Failed to set NTFS reparse data on `%s'",
299 dentry->full_path_utf8);
300 return WIMLIB_ERR_NTFS_3G;
302 args->progress.extract.completed_bytes += wim_resource_size(lte);
306 static int do_apply_dentry_ntfs(struct dentry *dentry, ntfs_inode *dir_ni,
307 struct apply_args *args);
310 * If @dentry is part of a hard link group, search for hard-linked dentries in
311 * the same directory that have a nonempty DOS (short) filename. There should
312 * be exactly 0 or 1 such dentries. If there is 1, extract that dentry first,
313 * so that the DOS name is correctly associated with the corresponding long name
314 * in the Win32 namespace, and not any of the additional names in the POSIX
315 * namespace created from hard links.
317 static int preapply_dentry_with_dos_name(struct dentry *dentry,
318 ntfs_inode **dir_ni_p,
319 struct apply_args *args)
321 struct dentry *other;
322 struct dentry *dentry_with_dos_name;
324 dentry_with_dos_name = NULL;
325 inode_for_each_dentry(other, dentry->d_inode) {
326 if (other != dentry && (dentry->parent == other->parent)
327 && other->short_name_len)
329 if (dentry_with_dos_name) {
330 ERROR("Found multiple DOS names for file `%s' "
331 "in the same directory",
332 dentry_with_dos_name->full_path_utf8);
333 return WIMLIB_ERR_INVALID_DENTRY;
335 dentry_with_dos_name = other;
338 /* If there's a dentry with a DOS name, extract it first */
339 if (dentry_with_dos_name && !dentry_with_dos_name->is_extracted) {
341 const char *dir_name;
344 ntfs_volume *vol = (*dir_ni_p)->vol;
346 DEBUG("pre-applying DOS name `%s'",
347 dentry_with_dos_name->full_path_utf8);
348 ret = do_apply_dentry_ntfs(dentry_with_dos_name,
352 p = dentry->full_path_utf8 + dentry->full_path_utf8_len;
359 dir_name = dentry->full_path_utf8;
361 *dir_ni_p = ntfs_pathname_to_inode(vol, NULL, dir_name);
364 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
366 return WIMLIB_ERR_NTFS_3G;
373 * Applies a WIM dentry to a NTFS filesystem.
375 * @dentry: The WIM dentry to apply
376 * @dir_ni: The NTFS inode for the parent directory
378 * @return: 0 on success; nonzero on failure.
380 static int do_apply_dentry_ntfs(struct dentry *dentry, ntfs_inode *dir_ni,
381 struct apply_args *args)
385 ntfs_inode *ni = NULL;
386 bool is_hardlink = false;
387 ntfs_volume *vol = dir_ni->vol;
388 struct inode *inode = dentry->d_inode;
389 dentry->is_extracted = true;
391 if (inode->attributes & FILE_ATTRIBUTE_DIRECTORY) {
394 /* Apply hard-linked directory in same directory with DOS name
395 * (if there is one) before this dentry */
396 if (dentry->short_name_len == 0) {
397 ret = preapply_dentry_with_dos_name(dentry,
405 if (inode->link_count > 1) {
406 /* Already extracted another dentry in the hard link
407 * group. We can make a hard link instead of extracting
409 if (inode->extracted_file) {
410 ret = apply_hardlink_ntfs(dentry, inode,
414 goto out_close_dir_ni;
416 goto out_set_dos_name;
418 /* Can't make a hard link; extract the file itself */
419 FREE(inode->extracted_file);
420 inode->extracted_file = STRDUP(dentry->full_path_utf8);
421 if (!inode->extracted_file) {
422 ret = WIMLIB_ERR_NOMEM;
423 goto out_close_dir_ni;
429 * Create a directory or file.
431 * Note: For symbolic links that are not directory junctions, pass
432 * S_IFREG here, since we manually set the reparse data later.
434 ni = ntfs_create(dir_ni, 0, (ntfschar*)dentry->file_name,
435 dentry->file_name_len / 2, type);
438 ERROR_WITH_ERRNO("Could not create NTFS object for `%s'",
439 dentry->full_path_utf8);
440 ret = WIMLIB_ERR_NTFS_3G;
441 goto out_close_dir_ni;
444 /* Write the data streams, unless this is a directory or reparse point
446 if (!(inode->attributes & (FILE_ATTRIBUTE_REPARSE_POINT |
447 FILE_ATTRIBUTE_DIRECTORY))) {
448 ret = write_ntfs_data_streams(ni, dentry, args);
450 goto out_close_dir_ni;
454 ret = apply_file_attributes_and_security_data(ni, dir_ni,
457 goto out_close_dir_ni;
459 if (inode->attributes & FILE_ATTR_REPARSE_POINT) {
460 ret = apply_reparse_data(ni, dentry, args);
462 goto out_close_dir_ni;
466 /* Set DOS (short) name if given */
467 if (dentry->short_name_len != 0) {
469 char *short_name_utf8;
470 size_t short_name_utf8_len;
471 ret = utf16_to_utf8(dentry->short_name,
472 dentry->short_name_len,
474 &short_name_utf8_len);
476 goto out_close_dir_ni;
481 const char *dir_name;
483 /* ntfs_set_ntfs_dos_name() closes the inodes in the
484 * wrong order if we have applied a hard link. Close
485 * them ourselves, then re-open then. */
486 if (ntfs_inode_close(dir_ni) != 0) {
488 ret = WIMLIB_ERR_NTFS_3G;
489 ERROR_WITH_ERRNO("Failed to close directory inode");
491 if (ntfs_inode_close(ni) != 0) {
493 ret = WIMLIB_ERR_NTFS_3G;
494 ERROR_WITH_ERRNO("Failed to close hard link target inode");
496 p = dentry->full_path_utf8 + dentry->full_path_utf8_len;
503 dir_name = dentry->full_path_utf8;
505 dir_ni = ntfs_pathname_to_inode(vol, NULL, dir_name);
508 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
510 return WIMLIB_ERR_NTFS_3G;
512 ni = ntfs_pathname_to_inode(vol, dir_ni,
513 dentry->file_name_utf8);
515 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
516 dentry->full_path_utf8);
517 ntfs_inode_close(dir_ni);
518 return WIMLIB_ERR_NTFS_3G;
522 DEBUG("Setting short (DOS) name of `%s' to %s",
523 dentry->full_path_utf8, short_name_utf8);
525 ret = ntfs_set_ntfs_dos_name(ni, dir_ni, short_name_utf8,
526 short_name_utf8_len, 0);
527 FREE(short_name_utf8);
529 ERROR_WITH_ERRNO("Could not set DOS (short) name for `%s'",
530 dentry->full_path_utf8);
531 ret = WIMLIB_ERR_NTFS_3G;
533 /* inodes have been closed by ntfs_set_ntfs_dos_name(). */
539 if (ntfs_inode_close_in_dir(ni, dir_ni) != 0) {
542 ret = WIMLIB_ERR_NTFS_3G;
543 ERROR_WITH_ERRNO("Failed to close inode for `%s'",
544 dentry->full_path_utf8);
547 if (ntfs_inode_close(dir_ni) != 0) {
549 ret = WIMLIB_ERR_NTFS_3G;
550 ERROR_WITH_ERRNO("Failed to close directory inode");
552 if (ni && ntfs_inode_close(ni) != 0) {
554 ret = WIMLIB_ERR_NTFS_3G;
555 ERROR_WITH_ERRNO("Failed to close inode for `%s'",
556 dentry->full_path_utf8);
561 static int apply_root_dentry_ntfs(const struct dentry *dentry,
562 ntfs_volume *vol, const WIMStruct *w)
567 wimlib_assert(dentry_is_directory(dentry));
568 ni = ntfs_pathname_to_inode(vol, NULL, "/");
570 ERROR_WITH_ERRNO("Could not find root NTFS inode");
571 return WIMLIB_ERR_NTFS_3G;
573 ret = apply_file_attributes_and_security_data(ni, ni, dentry, w);
574 if (ntfs_inode_close(ni) != 0) {
575 ERROR_WITH_ERRNO("Failed to close NTFS inode for root "
577 ret = WIMLIB_ERR_NTFS_3G;
582 /* Applies a WIM dentry to the NTFS volume */
583 int apply_dentry_ntfs(struct dentry *dentry, void *arg)
585 struct apply_args *args = arg;
586 ntfs_volume *vol = args->vol;
587 int extract_flags = args->extract_flags;
588 WIMStruct *w = args->w;
592 const char *dir_name;
594 if (dentry->is_extracted)
597 if (extract_flags & WIMLIB_EXTRACT_FLAG_NO_STREAMS)
598 if (inode_unnamed_lte_resolved(dentry->d_inode))
601 DEBUG("Applying dentry `%s' to NTFS", dentry->full_path_utf8);
603 if ((extract_flags & WIMLIB_EXTRACT_FLAG_VERBOSE) &&
606 args->progress.extract.cur_path = dentry->full_path_utf8;
607 args->progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY,
611 if (dentry_is_root(dentry))
612 return apply_root_dentry_ntfs(dentry, vol, w);
614 p = dentry->full_path_utf8 + dentry->full_path_utf8_len;
621 dir_name = dentry->full_path_utf8;
623 dir_ni = ntfs_pathname_to_inode(vol, NULL, dir_name);
626 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
628 return WIMLIB_ERR_NTFS_3G;
630 return do_apply_dentry_ntfs(dentry, dir_ni, arg);
633 int apply_dentry_timestamps_ntfs(struct dentry *dentry, void *arg)
635 struct apply_args *args = arg;
636 ntfs_volume *vol = args->vol;
642 DEBUG("Setting timestamps on `%s'", dentry->full_path_utf8);
644 ni = ntfs_pathname_to_inode(vol, NULL, dentry->full_path_utf8);
646 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
647 dentry->full_path_utf8);
648 return WIMLIB_ERR_NTFS_3G;
652 p = put_u64(p, dentry->d_inode->creation_time);
653 p = put_u64(p, dentry->d_inode->last_write_time);
654 p = put_u64(p, dentry->d_inode->last_access_time);
655 ret = ntfs_inode_set_times(ni, (const char*)buf, 3 * sizeof(u64), 0);
657 ERROR_WITH_ERRNO("Failed to set NTFS timestamps on `%s'",
658 dentry->full_path_utf8);
659 ret = WIMLIB_ERR_NTFS_3G;
662 if (ntfs_inode_close(ni) != 0) {
664 ret = WIMLIB_ERR_NTFS_3G;
665 ERROR_WITH_ERRNO("Failed to close NTFS inode for `%s'",
666 dentry->full_path_utf8);