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;
95 lte = inode_stream_lte_resolved(inode, stream_idx);
97 if (stream_name_len) {
98 /* Create an empty named stream. */
99 ret = ntfs_attr_add(ni, AT_DATA, stream_name,
100 stream_name_len, NULL, 0);
102 ERROR_WITH_ERRNO("Failed to create name data "
103 "stream for extracted file "
105 dentry->full_path_utf8);
106 ret = WIMLIB_ERR_NTFS_3G;
111 /* If there's no lookup table entry, it's an empty stream.
112 * Otherwise, we must open the attribute and extract the data.
115 na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
117 ERROR_WITH_ERRNO("Failed to open a data stream of "
118 "extracted file `%s'",
119 dentry->full_path_utf8);
120 ret = WIMLIB_ERR_NTFS_3G;
123 ret = extract_wim_resource_to_ntfs_attr(lte, na);
126 args->progress.extract.completed_bytes += wim_resource_size(lte);
129 if (stream_idx == inode->num_ads)
131 stream_name = (ntfschar*)inode->ads_entries[stream_idx].stream_name;
132 stream_name_len = inode->ads_entries[stream_idx].stream_name_len / 2;
139 * Makes a NTFS hard link
141 * It is named @from_dentry->file_name and is located under the directory
142 * specified by @dir_ni, and it is made to point to the previously extracted
143 * file located at @inode->extracted_file.
145 * Return 0 on success, nonzero on failure.
147 static int apply_hardlink_ntfs(const struct dentry *from_dentry,
148 const struct inode *inode,
150 ntfs_inode **to_ni_ret)
155 const char *dir_name;
160 wimlib_assert(dentry_is_regular_file(from_dentry)
161 && inode_is_regular_file(inode));
163 if (ntfs_inode_close(dir_ni) != 0) {
164 ERROR_WITH_ERRNO("Error closing directory");
165 return WIMLIB_ERR_NTFS_3G;
170 DEBUG("Extracting NTFS hard link `%s' => `%s'",
171 from_dentry->full_path_utf8, inode->extracted_file);
173 to_ni = ntfs_pathname_to_inode(vol, NULL, inode->extracted_file);
175 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
176 inode->extracted_file);
177 return WIMLIB_ERR_NTFS_3G;
179 p = from_dentry->full_path_utf8 + from_dentry->full_path_utf8_len;
186 dir_name = from_dentry->full_path_utf8;
188 dir_ni = ntfs_pathname_to_inode(vol, NULL, dir_name);
190 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
191 from_dentry->full_path_utf8);
193 return WIMLIB_ERR_NTFS_3G;
197 ret = ntfs_link(to_ni, dir_ni,
198 (ntfschar*)from_dentry->file_name,
199 from_dentry->file_name_len / 2);
201 ERROR_WITH_ERRNO("Could not create hard link `%s' => `%s'",
202 from_dentry->full_path_utf8,
203 inode->extracted_file);
204 ret = WIMLIB_ERR_NTFS_3G;
211 apply_file_attributes_and_security_data(ntfs_inode *ni,
213 const struct dentry *dentry,
216 DEBUG("Setting NTFS file attributes on `%s' to %#"PRIx32,
217 dentry->full_path_utf8, dentry->d_inode->attributes);
219 struct SECURITY_CONTEXT ctx;
221 attributes_le32 = cpu_to_le32(dentry->d_inode->attributes);
222 memset(&ctx, 0, sizeof(ctx));
224 ret = ntfs_xattr_system_setxattr(&ctx, XATTR_NTFS_ATTRIB,
226 (const char*)&attributes_le32,
229 ERROR("Failed to set NTFS file attributes on `%s'",
230 dentry->full_path_utf8);
231 return WIMLIB_ERR_NTFS_3G;
233 if (dentry->d_inode->security_id != -1) {
234 const struct wim_security_data *sd;
235 const char *descriptor;
237 sd = wim_const_security_data(w);
238 wimlib_assert(dentry->d_inode->security_id < sd->num_entries);
239 descriptor = (const char *)sd->descriptors[dentry->d_inode->security_id];
240 DEBUG("Applying security descriptor %d to `%s'",
241 dentry->d_inode->security_id, dentry->full_path_utf8);
243 ret = ntfs_xattr_system_setxattr(&ctx, XATTR_NTFS_ACL,
244 ni, dir_ni, descriptor,
245 sd->sizes[dentry->d_inode->security_id], 0);
248 ERROR_WITH_ERRNO("Failed to set security data on `%s'",
249 dentry->full_path_utf8);
250 return WIMLIB_ERR_NTFS_3G;
256 static int apply_reparse_data(ntfs_inode *ni, const struct dentry *dentry,
257 struct apply_args *args)
259 struct lookup_table_entry *lte;
262 lte = inode_unnamed_lte_resolved(dentry->d_inode);
264 DEBUG("Applying reparse data to `%s'", dentry->full_path_utf8);
267 ERROR("Could not find reparse data for `%s'",
268 dentry->full_path_utf8);
269 return WIMLIB_ERR_INVALID_DENTRY;
272 if (wim_resource_size(lte) >= 0xffff) {
273 ERROR("Reparse data of `%s' is too long (%"PRIu64" bytes)",
274 dentry->full_path_utf8, wim_resource_size(lte));
275 return WIMLIB_ERR_INVALID_DENTRY;
278 u8 reparse_data_buf[8 + wim_resource_size(lte)];
279 u8 *p = reparse_data_buf;
280 p = put_u32(p, dentry->d_inode->reparse_tag); /* ReparseTag */
281 p = put_u16(p, wim_resource_size(lte)); /* ReparseDataLength */
282 p = put_u16(p, 0); /* Reserved */
284 ret = read_full_wim_resource(lte, p, 0);
288 ret = ntfs_set_ntfs_reparse_data(ni, (char*)reparse_data_buf,
289 wim_resource_size(lte) + 8, 0);
291 ERROR_WITH_ERRNO("Failed to set NTFS reparse data on `%s'",
292 dentry->full_path_utf8);
293 return WIMLIB_ERR_NTFS_3G;
295 args->progress.extract.completed_bytes += wim_resource_size(lte);
299 static int do_apply_dentry_ntfs(struct dentry *dentry, ntfs_inode *dir_ni,
300 struct apply_args *args);
303 * If @dentry is part of a hard link group, search for hard-linked dentries in
304 * the same directory that have a nonempty DOS (short) filename. There should
305 * be exactly 0 or 1 such dentries. If there is 1, extract that dentry first,
306 * so that the DOS name is correctly associated with the corresponding long name
307 * in the Win32 namespace, and not any of the additional names in the POSIX
308 * namespace created from hard links.
310 static int preapply_dentry_with_dos_name(struct dentry *dentry,
311 ntfs_inode **dir_ni_p,
312 struct apply_args *args)
314 struct dentry *other;
315 struct dentry *dentry_with_dos_name;
317 dentry_with_dos_name = NULL;
318 inode_for_each_dentry(other, dentry->d_inode) {
319 if (other != dentry && (dentry->parent == other->parent)
320 && other->short_name_len)
322 if (dentry_with_dos_name) {
323 ERROR("Found multiple DOS names for file `%s' "
324 "in the same directory",
325 dentry_with_dos_name->full_path_utf8);
326 return WIMLIB_ERR_INVALID_DENTRY;
328 dentry_with_dos_name = other;
331 /* If there's a dentry with a DOS name, extract it first */
332 if (dentry_with_dos_name && !dentry_with_dos_name->is_extracted) {
334 const char *dir_name;
337 ntfs_volume *vol = (*dir_ni_p)->vol;
339 DEBUG("pre-applying DOS name `%s'",
340 dentry_with_dos_name->full_path_utf8);
341 ret = do_apply_dentry_ntfs(dentry_with_dos_name,
345 p = dentry->full_path_utf8 + dentry->full_path_utf8_len;
352 dir_name = dentry->full_path_utf8;
354 *dir_ni_p = ntfs_pathname_to_inode(vol, NULL, dir_name);
357 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
359 return WIMLIB_ERR_NTFS_3G;
366 * Applies a WIM dentry to a NTFS filesystem.
368 * @dentry: The WIM dentry to apply
369 * @dir_ni: The NTFS inode for the parent directory
371 * @return: 0 on success; nonzero on failure.
373 static int do_apply_dentry_ntfs(struct dentry *dentry, ntfs_inode *dir_ni,
374 struct apply_args *args)
378 ntfs_inode *ni = NULL;
379 bool is_hardlink = false;
380 ntfs_volume *vol = dir_ni->vol;
381 struct inode *inode = dentry->d_inode;
382 dentry->is_extracted = true;
384 if (inode->attributes & FILE_ATTRIBUTE_DIRECTORY) {
387 /* Apply hard-linked directory in same directory with DOS name
388 * (if there is one) before this dentry */
389 if (dentry->short_name_len == 0) {
390 ret = preapply_dentry_with_dos_name(dentry,
398 if (inode->link_count > 1) {
399 /* Already extracted another dentry in the hard link
400 * group. We can make a hard link instead of extracting
402 if (inode->extracted_file) {
403 ret = apply_hardlink_ntfs(dentry, inode,
407 goto out_close_dir_ni;
409 goto out_set_dos_name;
411 /* Can't make a hard link; extract the file itself */
412 FREE(inode->extracted_file);
413 inode->extracted_file = STRDUP(dentry->full_path_utf8);
414 if (!inode->extracted_file) {
415 ret = WIMLIB_ERR_NOMEM;
416 goto out_close_dir_ni;
422 * Create a directory or file.
424 * Note: For symbolic links that are not directory junctions, pass
425 * S_IFREG here, since we manually set the reparse data later.
427 ni = ntfs_create(dir_ni, 0, (ntfschar*)dentry->file_name,
428 dentry->file_name_len / 2, type);
431 ERROR_WITH_ERRNO("Could not create NTFS object for `%s'",
432 dentry->full_path_utf8);
433 ret = WIMLIB_ERR_NTFS_3G;
434 goto out_close_dir_ni;
437 /* Write the data streams, unless this is a directory or reparse point
439 if (!(inode->attributes & (FILE_ATTRIBUTE_REPARSE_POINT |
440 FILE_ATTRIBUTE_DIRECTORY))) {
441 ret = write_ntfs_data_streams(ni, dentry, args);
443 goto out_close_dir_ni;
447 ret = apply_file_attributes_and_security_data(ni, dir_ni,
450 goto out_close_dir_ni;
452 if (inode->attributes & FILE_ATTR_REPARSE_POINT) {
453 ret = apply_reparse_data(ni, dentry, args);
455 goto out_close_dir_ni;
459 /* Set DOS (short) name if given */
460 if (dentry->short_name_len != 0) {
462 char *short_name_utf8;
463 size_t short_name_utf8_len;
464 ret = utf16_to_utf8(dentry->short_name,
465 dentry->short_name_len,
467 &short_name_utf8_len);
469 goto out_close_dir_ni;
474 const char *dir_name;
476 /* ntfs_set_ntfs_dos_name() closes the inodes in the
477 * wrong order if we have applied a hard link. Close
478 * them ourselves, then re-open then. */
479 if (ntfs_inode_close(dir_ni) != 0) {
481 ret = WIMLIB_ERR_NTFS_3G;
482 ERROR_WITH_ERRNO("Failed to close directory inode");
484 if (ntfs_inode_close(ni) != 0) {
486 ret = WIMLIB_ERR_NTFS_3G;
487 ERROR_WITH_ERRNO("Failed to close hard link target inode");
489 p = dentry->full_path_utf8 + dentry->full_path_utf8_len;
496 dir_name = dentry->full_path_utf8;
498 dir_ni = ntfs_pathname_to_inode(vol, NULL, dir_name);
501 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
503 return WIMLIB_ERR_NTFS_3G;
505 ni = ntfs_pathname_to_inode(vol, dir_ni,
506 dentry->file_name_utf8);
508 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
510 return WIMLIB_ERR_NTFS_3G;
514 DEBUG("Setting short (DOS) name of `%s' to %s",
515 dentry->full_path_utf8, short_name_utf8);
517 ret = ntfs_set_ntfs_dos_name(ni, dir_ni, short_name_utf8,
518 short_name_utf8_len, 0);
519 FREE(short_name_utf8);
521 ERROR_WITH_ERRNO("Could not set DOS (short) name for `%s'",
522 dentry->full_path_utf8);
523 ret = WIMLIB_ERR_NTFS_3G;
525 /* inodes have been closed by ntfs_set_ntfs_dos_name(). */
530 if (ntfs_inode_close(dir_ni) != 0) {
532 ret = WIMLIB_ERR_NTFS_3G;
533 ERROR_WITH_ERRNO("Failed to close directory inode");
535 if (ni && ntfs_inode_close(ni) != 0) {
537 ret = WIMLIB_ERR_NTFS_3G;
538 ERROR_WITH_ERRNO("Failed to close inode");
543 static int apply_root_dentry_ntfs(const struct dentry *dentry,
544 ntfs_volume *vol, const WIMStruct *w)
549 wimlib_assert(dentry_is_directory(dentry));
550 ni = ntfs_pathname_to_inode(vol, NULL, "/");
552 ERROR_WITH_ERRNO("Could not find root NTFS inode");
553 return WIMLIB_ERR_NTFS_3G;
555 ret = apply_file_attributes_and_security_data(ni, ni, dentry, w);
556 if (ntfs_inode_close(ni) != 0) {
557 ERROR_WITH_ERRNO("Failed to close NTFS inode for root "
559 ret = WIMLIB_ERR_NTFS_3G;
564 /* Applies a WIM dentry to the NTFS volume */
565 int apply_dentry_ntfs(struct dentry *dentry, void *arg)
567 struct apply_args *args = arg;
568 ntfs_volume *vol = args->vol;
569 int extract_flags = args->extract_flags;
570 WIMStruct *w = args->w;
574 const char *dir_name;
576 if (dentry->is_extracted)
579 if (extract_flags & WIMLIB_EXTRACT_FLAG_NO_STREAMS)
580 if (inode_unnamed_lte_resolved(dentry->d_inode))
583 DEBUG("Applying dentry `%s' to NTFS", dentry->full_path_utf8);
585 if ((extract_flags & WIMLIB_EXTRACT_FLAG_VERBOSE) &&
588 args->progress.extract.cur_path = dentry->full_path_utf8;
589 args->progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY,
593 if (dentry_is_root(dentry))
594 return apply_root_dentry_ntfs(dentry, vol, w);
596 p = dentry->full_path_utf8 + dentry->full_path_utf8_len;
603 dir_name = dentry->full_path_utf8;
605 dir_ni = ntfs_pathname_to_inode(vol, NULL, dir_name);
608 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
610 return WIMLIB_ERR_NTFS_3G;
612 return do_apply_dentry_ntfs(dentry, dir_ni, arg);
615 int apply_dentry_timestamps_ntfs(struct dentry *dentry, void *arg)
617 struct apply_args *args = arg;
618 ntfs_volume *vol = args->vol;
624 DEBUG("Setting timestamps on `%s'", dentry->full_path_utf8);
626 ni = ntfs_pathname_to_inode(vol, NULL, dentry->full_path_utf8);
628 ERROR_WITH_ERRNO("Could not find NTFS inode for `%s'",
629 dentry->full_path_utf8);
630 return WIMLIB_ERR_NTFS_3G;
634 p = put_u64(p, dentry->d_inode->creation_time);
635 p = put_u64(p, dentry->d_inode->last_write_time);
636 p = put_u64(p, dentry->d_inode->last_access_time);
637 ret = ntfs_inode_set_times(ni, (const char*)buf, 3 * sizeof(u64), 0);
639 ERROR_WITH_ERRNO("Failed to set NTFS timestamps on `%s'",
640 dentry->full_path_utf8);
641 ret = WIMLIB_ERR_NTFS_3G;
644 if (ntfs_inode_close(ni) != 0) {
646 ret = WIMLIB_ERR_NTFS_3G;
647 ERROR_WITH_ERRNO("Failed to close NTFS inode for `%s'",
648 dentry->full_path_utf8);