4 * Apply a WIM image directly to an NTFS volume using libntfs-3g. Restore as
5 * much information as possible, including security data, file attributes, DOS
6 * names, and alternate data streams.
8 * Note: because NTFS-3g offers inode-based interfaces, we actually don't need
9 * to deal with paths at all! (Other than for error messages.)
13 * Copyright (C) 2012, 2013, 2014, 2015 Eric Biggers
15 * This file is free software; you can redistribute it and/or modify it under
16 * the terms of the GNU Lesser General Public License as published by the Free
17 * Software Foundation; either version 3 of the License, or (at your option) any
20 * This file is distributed in the hope that it will be useful, but WITHOUT
21 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
22 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
25 * You should have received a copy of the GNU Lesser General Public License
26 * along with this file; if not, see http://www.gnu.org/licenses/.
36 #include <ntfs-3g/attrib.h>
37 #include <ntfs-3g/reparse.h>
38 #include <ntfs-3g/security.h>
40 #include "wimlib/assert.h"
41 #include "wimlib/apply.h"
42 #include "wimlib/blob_table.h"
43 #include "wimlib/dentry.h"
44 #include "wimlib/encoding.h"
45 #include "wimlib/error.h"
46 #include "wimlib/metadata.h"
47 #include "wimlib/ntfs_3g.h"
48 #include "wimlib/reparse.h"
49 #include "wimlib/security.h"
50 #include "wimlib/security_descriptor.h"
53 ntfs_3g_get_supported_features(const char *target,
54 struct wim_features *supported_features)
56 supported_features->archive_files = 1;
57 supported_features->hidden_files = 1;
58 supported_features->system_files = 1;
59 supported_features->compressed_files = 1;
60 supported_features->not_context_indexed_files = 1;
61 supported_features->named_data_streams = 1;
62 supported_features->hard_links = 1;
63 supported_features->reparse_points = 1;
64 supported_features->security_descriptors = 1;
65 supported_features->short_names = 1;
66 supported_features->timestamps = 1;
67 supported_features->case_sensitive_filenames = 1;
71 struct ntfs_3g_apply_ctx {
72 /* Extract flags, the pointer to the WIMStruct, etc. */
73 struct apply_ctx common;
75 /* Pointer to the open NTFS volume */
78 ntfs_attr *open_attrs[MAX_OPEN_FILES];
79 unsigned num_open_attrs;
80 ntfs_inode *open_inodes[MAX_OPEN_FILES];
81 unsigned num_open_inodes;
83 struct reparse_buffer_disk rpbuf;
86 /* Offset in the blob currently being read */
89 unsigned num_reparse_inodes;
90 ntfs_inode *ntfs_reparse_inodes[MAX_OPEN_FILES];
91 struct wim_inode *wim_reparse_inodes[MAX_OPEN_FILES];
95 sid_size(const wimlib_SID *sid)
97 return offsetof(wimlib_SID, sub_authority) +
98 sizeof(le32) * sid->sub_authority_count;
102 * sd_fixup - Fix up a Windows NT security descriptor for libntfs-3g.
104 * libntfs-3g validates security descriptors before setting them, but old
105 * versions contain bugs causing it to reject unusual but valid security
108 * - Versions before 2013.1.13 reject security descriptors ending with an empty
109 * SACL (System Access Control List). This bug can be worked around either by
110 * moving the empty SACL earlier in the security descriptor or by removing the
111 * SACL entirely. The latter work-around is valid because an empty SACL is
112 * equivalent to a "null", or non-existent, SACL.
113 * - Versions before 2014.2.15 reject security descriptors ending with an empty
114 * DACL (Discretionary Access Control List). This is very similar to the SACL
115 * bug. However, removing the DACL is not a valid workaround because this
116 * changes the meaning of the security descriptor--- an empty DACL allows no
117 * access, whereas a "null" DACL allows all access.
119 * If the security descriptor was fixed, this function returns an allocated
120 * buffer containing the fixed security descriptor, and its size is updated.
121 * Otherwise (or if no memory is available) NULL is returned.
124 sd_fixup(const void *_desc, size_t *size_p)
126 u32 owner_offset, group_offset, dacl_offset;
127 #if !defined(HAVE_NTFS_MNT_RDONLY)
130 bool owner_valid, group_valid;
131 size_t size = *size_p;
132 const wimlib_SECURITY_DESCRIPTOR_RELATIVE *desc = _desc;
133 wimlib_SECURITY_DESCRIPTOR_RELATIVE *desc_new;
134 const wimlib_SID *owner, *group, *sid;
136 /* Don't attempt to fix clearly invalid security descriptors. */
137 if (size < sizeof(wimlib_SECURITY_DESCRIPTOR_RELATIVE))
140 if (le16_to_cpu(desc->control) & wimlib_SE_DACL_PRESENT)
141 dacl_offset = le32_to_cpu(desc->dacl_offset);
145 #if !defined(HAVE_NTFS_MNT_RDONLY)
146 if (le16_to_cpu(desc->control) & wimlib_SE_SACL_PRESENT)
147 sacl_offset = le32_to_cpu(desc->sacl_offset);
152 /* Check if the security descriptor will be affected by one of the bugs.
153 * If not, do nothing and return.
155 * Note: HAVE_NTFS_MNT_RDONLY is defined if libntfs-3g is
156 * version 2013.1.13 or later. */
158 #if !defined(HAVE_NTFS_MNT_RDONLY)
159 (sacl_offset != 0 && sacl_offset == size - sizeof(wimlib_ACL)) ||
161 (dacl_offset != 0 && dacl_offset == size - sizeof(wimlib_ACL))))
164 owner_offset = le32_to_cpu(desc->owner_offset);
165 group_offset = le32_to_cpu(desc->group_offset);
166 owner = (const wimlib_SID*)((const u8*)desc + owner_offset);
167 group = (const wimlib_SID*)((const u8*)desc + group_offset);
169 /* We'll try to move the owner or group SID to the end of the security
170 * descriptor to avoid the bug. This is only possible if at least one
172 owner_valid = (owner_offset != 0) &&
173 (owner_offset % 4 == 0) &&
174 (owner_offset <= size - sizeof(SID)) &&
175 (owner_offset + sid_size(owner) <= size) &&
176 (owner_offset >= sizeof(wimlib_SECURITY_DESCRIPTOR_RELATIVE));
177 group_valid = (group_offset != 0) &&
178 (group_offset % 4 == 0) &&
179 (group_offset <= size - sizeof(SID)) &&
180 (group_offset + sid_size(group) <= size) &&
181 (group_offset >= sizeof(wimlib_SECURITY_DESCRIPTOR_RELATIVE));
184 } else if (group_valid) {
190 desc_new = MALLOC(size + sid_size(sid));
194 memcpy(desc_new, desc, size);
196 desc_new->owner_offset = cpu_to_le32(size);
197 else if (group_valid)
198 desc_new->group_offset = cpu_to_le32(size);
199 memcpy((u8*)desc_new + size, sid, sid_size(sid));
200 *size_p = size + sid_size(sid);
204 /* Set the security descriptor @desc of size @desc_size on the NTFS inode @ni.
207 ntfs_3g_set_security_descriptor(ntfs_inode *ni, const void *desc, size_t desc_size)
209 struct SECURITY_CONTEXT sec_ctx;
210 void *desc_fixed = NULL;
213 memset(&sec_ctx, 0, sizeof(sec_ctx));
214 sec_ctx.vol = ni->vol;
217 if (ntfs_set_ntfs_acl(&sec_ctx, ni, desc, desc_size, 0)) {
218 if (desc_fixed == NULL) {
219 desc_fixed = sd_fixup(desc, &desc_size);
220 if (desc_fixed != NULL) {
225 ret = WIMLIB_ERR_SET_SECURITY;
233 ntfs_3g_set_timestamps(ntfs_inode *ni, const struct wim_inode *inode)
236 inode->i_creation_time,
237 inode->i_last_write_time,
238 inode->i_last_access_time,
241 if (ntfs_inode_set_times(ni, (const char *)times, sizeof(times), 0))
242 return WIMLIB_ERR_SET_TIMESTAMPS;
246 /* Restore the timestamps on the NTFS inode corresponding to @inode. */
248 ntfs_3g_restore_timestamps(ntfs_volume *vol, const struct wim_inode *inode)
253 ni = ntfs_inode_open(vol, inode->i_mft_no);
257 res = ntfs_3g_set_timestamps(ni, inode);
259 if (ntfs_inode_close(ni) || res)
265 ERROR_WITH_ERRNO("Failed to update timestamps of \"%s\" in NTFS volume",
266 dentry_full_path(inode_first_extraction_dentry(inode)));
267 return WIMLIB_ERR_SET_TIMESTAMPS;
270 /* Restore the DOS name of the @dentry.
271 * This closes both @ni and @dir_ni.
272 * If either is NULL, then they are opened temporarily. */
274 ntfs_3g_restore_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni,
275 struct wim_dentry *dentry, ntfs_volume *vol)
278 const char *dos_name;
279 size_t dos_name_nbytes;
281 /* Note: ntfs_set_ntfs_dos_name() closes both inodes (even if it fails).
282 * And it takes in a multibyte string, even though it translates it to
283 * UTF-16LE internally... which is annoying because we currently have
284 * the UTF-16LE string but not the multibyte string. */
286 ret = utf16le_get_tstr(dentry->d_short_name, dentry->d_short_name_nbytes,
287 &dos_name, &dos_name_nbytes);
292 dir_ni = ntfs_inode_open(vol, dentry->d_parent->d_inode->i_mft_no);
294 ni = ntfs_inode_open(vol, dentry->d_inode->i_mft_no);
296 ret = ntfs_set_ntfs_dos_name(ni, dir_ni,
297 dos_name, dos_name_nbytes, 0);
303 utf16le_put_tstr(dos_name);
305 ERROR_WITH_ERRNO("Failed to set DOS name of \"%s\" in NTFS "
306 "volume", dentry_full_path(dentry));
307 ret = WIMLIB_ERR_SET_SHORT_NAME;
311 /* Unlike most other NTFS-3g functions, ntfs_set_ntfs_dos_name()
312 * changes the directory's last modification timestamp...
314 return ntfs_3g_restore_timestamps(vol, dentry->d_parent->d_inode);
317 /* ntfs_inode_close() can take a NULL argument, but it's probably best
318 * not to rely on this behavior. */
320 ntfs_inode_close(ni);
322 ntfs_inode_close(dir_ni);
327 ntfs_3g_restore_reparse_point(ntfs_inode *ni, const struct wim_inode *inode,
328 unsigned blob_size, struct ntfs_3g_apply_ctx *ctx)
330 complete_reparse_point(&ctx->rpbuf, inode, blob_size);
332 if (ntfs_set_ntfs_reparse_data(ni, (const char *)&ctx->rpbuf,
333 REPARSE_DATA_OFFSET + blob_size, 0))
335 ERROR_WITH_ERRNO("Failed to set reparse data on \"%s\"",
337 inode_first_extraction_dentry(inode)));
338 return WIMLIB_ERR_SET_REPARSE_DATA;
346 * Create empty attributes (named data streams and potentially a reparse point)
347 * for the specified file, if there are any.
349 * Since these won't have blob descriptors, they won't show up in the call to
350 * extract_blob_list(). Hence the need for the special case.
353 ntfs_3g_create_empty_attributes(ntfs_inode *ni,
354 const struct wim_inode *inode,
355 struct ntfs_3g_apply_ctx *ctx)
358 for (unsigned i = 0; i < inode->i_num_streams; i++) {
360 const struct wim_inode_stream *strm = &inode->i_streams[i];
363 if (stream_blob_resolved(strm) != NULL)
366 if (strm->stream_type == STREAM_TYPE_REPARSE_POINT) {
367 ret = ntfs_3g_restore_reparse_point(ni, inode, 0, ctx);
370 } else if (stream_is_named_data_stream(strm)) {
371 if (ntfs_attr_add(ni, AT_DATA, strm->stream_name,
372 utf16le_len_chars(strm->stream_name),
375 ERROR_WITH_ERRNO("Failed to create named data "
378 inode_first_extraction_dentry(inode)));
379 return WIMLIB_ERR_NTFS_3G;
386 /* Set attributes, security descriptor, and timestamps on the NTFS inode @ni.
389 ntfs_3g_set_metadata(ntfs_inode *ni, const struct wim_inode *inode,
390 const struct ntfs_3g_apply_ctx *ctx)
393 const struct wim_security_data *sd;
394 struct wim_dentry *one_dentry;
397 extract_flags = ctx->common.extract_flags;
398 sd = wim_get_current_security_data(ctx->common.wim);
399 one_dentry = inode_first_extraction_dentry(inode);
402 if (!(extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES)) {
403 u32 attrib = inode->i_attributes;
405 attrib &= ~(FILE_ATTRIBUTE_SPARSE_FILE |
406 FILE_ATTRIBUTE_ENCRYPTED);
408 if (ntfs_set_ntfs_attrib(ni, (const char *)&attrib,
411 ERROR_WITH_ERRNO("Failed to set attributes on \"%s\" "
413 dentry_full_path(one_dentry));
414 return WIMLIB_ERR_SET_ATTRIBUTES;
418 /* Security descriptor */
419 if (inode_has_security_descriptor(inode)
420 && !(extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS))
425 desc = sd->descriptors[inode->i_security_id];
426 desc_size = sd->sizes[inode->i_security_id];
428 ret = ntfs_3g_set_security_descriptor(ni, desc, desc_size);
430 if (wimlib_print_errors) {
431 ERROR_WITH_ERRNO("Failed to set security descriptor "
432 "on \"%s\" in NTFS volume",
433 dentry_full_path(one_dentry));
434 fprintf(wimlib_error_file,
435 "The security descriptor is: ");
436 print_byte_field(desc, desc_size, wimlib_error_file);
437 fprintf(wimlib_error_file, "\n");
444 ret = ntfs_3g_set_timestamps(ni, inode);
446 ERROR_WITH_ERRNO("Failed to set timestamps on \"%s\" "
448 dentry_full_path(one_dentry));
454 /* Recursively creates all the subdirectories of @dir, which has been created as
455 * the NTFS inode @dir_ni. */
457 ntfs_3g_create_dirs_recursive(ntfs_inode *dir_ni, struct wim_dentry *dir,
458 struct ntfs_3g_apply_ctx *ctx)
460 struct wim_dentry *child;
462 for_dentry_child(child, dir) {
466 if (!(child->d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY))
468 if (!will_extract_dentry(child))
471 ni = ntfs_create(dir_ni, 0, child->d_extraction_name,
472 child->d_extraction_name_nchars, S_IFDIR);
474 ERROR_WITH_ERRNO("Error creating \"%s\" in NTFS volume",
475 dentry_full_path(child));
476 return WIMLIB_ERR_NTFS_3G;
479 child->d_inode->i_mft_no = ni->mft_no;
481 ret = report_file_created(&ctx->common);
483 ret = ntfs_3g_set_metadata(ni, child->d_inode, ctx);
485 ret = ntfs_3g_create_empty_attributes(ni, child->d_inode, ctx);
487 ret = ntfs_3g_create_dirs_recursive(ni, child, ctx);
489 if (ntfs_inode_close_in_dir(ni, dir_ni) && !ret) {
490 ERROR_WITH_ERRNO("Error closing \"%s\" in NTFS volume",
491 dentry_full_path(child));
492 ret = WIMLIB_ERR_NTFS_3G;
500 /* For each WIM dentry in the @root tree that represents a directory, create the
501 * corresponding directory in the NTFS volume @ctx->vol. */
503 ntfs_3g_create_directories(struct wim_dentry *root,
504 struct list_head *dentry_list,
505 struct ntfs_3g_apply_ctx *ctx)
509 struct wim_dentry *dentry;
511 /* Create the directories using POSIX names. */
513 root_ni = ntfs_inode_open(ctx->vol, FILE_root);
515 ERROR_WITH_ERRNO("Can't open root of NTFS volume");
516 return WIMLIB_ERR_NTFS_3G;
519 root->d_inode->i_mft_no = FILE_root;
521 ret = ntfs_3g_create_dirs_recursive(root_ni, root, ctx);
523 if (ntfs_inode_close(root_ni) && !ret) {
524 ERROR_WITH_ERRNO("Error closing root of NTFS volume");
525 ret = WIMLIB_ERR_NTFS_3G;
530 /* Set the DOS name of any directory that has one. */
531 list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
532 if (!(dentry->d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY))
534 if (!dentry_has_short_name(dentry))
536 ret = ntfs_3g_restore_dos_name(NULL, NULL, dentry, ctx->vol);
539 ret = report_file_created(&ctx->common);
546 /* When creating an inode that will have a short (DOS) name, we create it using
547 * the long name associated with the short name. This ensures that the short
548 * name gets associated with the correct long name. */
549 static struct wim_dentry *
550 ntfs_3g_first_extraction_alias(struct wim_inode *inode)
552 struct wim_dentry *dentry;
554 inode_for_each_extraction_alias(dentry, inode)
555 if (dentry_has_short_name(dentry))
557 return inode_first_extraction_dentry(inode);
561 * Add a hard link for the NTFS inode @ni at the location corresponding to the
562 * WIM dentry @dentry.
564 * The parent directory must have already been created on the NTFS volume.
566 * Returns 0 on success; returns WIMLIB_ERR_NTFS_3G and sets errno on failure.
569 ntfs_3g_add_link(ntfs_inode *ni, struct wim_dentry *dentry)
574 /* Open the inode of the parent directory. */
575 dir_ni = ntfs_inode_open(ni->vol, dentry->d_parent->d_inode->i_mft_no);
579 /* Create the link. */
580 res = ntfs_link(ni, dir_ni, dentry->d_extraction_name,
581 dentry->d_extraction_name_nchars);
583 /* Close the parent directory. */
584 if (ntfs_inode_close(dir_ni) || res)
590 ERROR_WITH_ERRNO("Can't create link \"%s\" in NTFS volume",
591 dentry_full_path(dentry));
592 return WIMLIB_ERR_NTFS_3G;
596 ntfs_3g_create_nondirectory(struct wim_inode *inode,
597 struct ntfs_3g_apply_ctx *ctx)
599 struct wim_dentry *first_dentry;
602 struct wim_dentry *dentry;
605 first_dentry = ntfs_3g_first_extraction_alias(inode);
607 /* Create first link. */
609 dir_ni = ntfs_inode_open(ctx->vol, first_dentry->d_parent->d_inode->i_mft_no);
611 ERROR_WITH_ERRNO("Can't open \"%s\" in NTFS volume",
612 dentry_full_path(first_dentry->d_parent));
613 return WIMLIB_ERR_NTFS_3G;
616 ni = ntfs_create(dir_ni, 0, first_dentry->d_extraction_name,
617 first_dentry->d_extraction_name_nchars, S_IFREG);
620 ERROR_WITH_ERRNO("Can't create \"%s\" in NTFS volume",
621 dentry_full_path(first_dentry));
622 ntfs_inode_close(dir_ni);
623 return WIMLIB_ERR_NTFS_3G;
626 inode->i_mft_no = ni->mft_no;
628 /* Set short name if present. */
629 if (dentry_has_short_name(first_dentry)) {
631 ret = ntfs_3g_restore_dos_name(ni, dir_ni, first_dentry, ctx->vol);
633 /* ntfs_3g_restore_dos_name() closed both 'ni' and 'dir_ni'. */
638 /* Reopen the inode. */
639 ni = ntfs_inode_open(ctx->vol, inode->i_mft_no);
641 ERROR_WITH_ERRNO("Failed to reopen \"%s\" "
643 dentry_full_path(first_dentry));
644 return WIMLIB_ERR_NTFS_3G;
647 /* Close the directory in which the first link was created. */
648 if (ntfs_inode_close(dir_ni)) {
649 ERROR_WITH_ERRNO("Failed to close \"%s\" in NTFS volume",
650 dentry_full_path(first_dentry->d_parent));
651 ret = WIMLIB_ERR_NTFS_3G;
656 /* Create additional links if present. */
657 inode_for_each_extraction_alias(dentry, inode) {
658 if (dentry != first_dentry) {
659 ret = ntfs_3g_add_link(ni, dentry);
666 ret = ntfs_3g_set_metadata(ni, inode, ctx);
670 ret = ntfs_3g_create_empty_attributes(ni, inode, ctx);
673 /* Close the inode. */
674 if (ntfs_inode_close(ni) && !ret) {
675 ERROR_WITH_ERRNO("Error closing \"%s\" in NTFS volume",
676 dentry_full_path(first_dentry));
677 ret = WIMLIB_ERR_NTFS_3G;
682 /* For each WIM dentry in the @dentry_list that represents a nondirectory file,
683 * create the corresponding nondirectory file in the NTFS volume.
685 * Directories must have already been created. */
687 ntfs_3g_create_nondirectories(struct list_head *dentry_list,
688 struct ntfs_3g_apply_ctx *ctx)
690 struct wim_dentry *dentry;
691 struct wim_inode *inode;
694 list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
695 inode = dentry->d_inode;
696 if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
698 if (dentry == inode_first_extraction_dentry(inode)) {
699 ret = ntfs_3g_create_nondirectory(inode, ctx);
703 ret = report_file_created(&ctx->common);
711 ntfs_3g_begin_extract_blob_instance(struct blob_descriptor *blob,
713 struct wim_inode *inode,
714 const struct wim_inode_stream *strm,
715 struct ntfs_3g_apply_ctx *ctx)
717 struct wim_dentry *one_dentry = inode_first_extraction_dentry(inode);
718 size_t stream_name_nchars;
721 if (unlikely(strm->stream_type == STREAM_TYPE_REPARSE_POINT)) {
723 if (blob->size > REPARSE_DATA_MAX_SIZE) {
724 ERROR("Reparse data of \"%s\" has size "
725 "%"PRIu64" bytes (exceeds %u bytes)",
726 dentry_full_path(one_dentry),
727 blob->size, REPARSE_DATA_MAX_SIZE);
728 return WIMLIB_ERR_INVALID_REPARSE_DATA;
730 ctx->reparse_ptr = ctx->rpbuf.rpdata;
731 ctx->ntfs_reparse_inodes[ctx->num_reparse_inodes] = ni;
732 ctx->wim_reparse_inodes[ctx->num_reparse_inodes] = inode;
733 ctx->num_reparse_inodes++;
737 /* It's a data stream (may be unnamed or named). */
738 wimlib_assert(strm->stream_type == STREAM_TYPE_DATA);
740 stream_name_nchars = utf16le_len_chars(strm->stream_name);
742 if (stream_name_nchars &&
743 (ntfs_attr_add(ni, AT_DATA, strm->stream_name,
744 stream_name_nchars, NULL, 0)))
746 ERROR_WITH_ERRNO("Failed to create named data stream of \"%s\"",
747 dentry_full_path(one_dentry));
748 return WIMLIB_ERR_NTFS_3G;
751 /* This should be ensured by extract_blob_list() */
752 wimlib_assert(ctx->num_open_attrs < MAX_OPEN_FILES);
754 attr = ntfs_attr_open(ni, AT_DATA, strm->stream_name,
757 ERROR_WITH_ERRNO("Failed to open data stream of \"%s\"",
758 dentry_full_path(one_dentry));
759 return WIMLIB_ERR_NTFS_3G;
761 ctx->open_attrs[ctx->num_open_attrs++] = attr;
762 ntfs_attr_truncate_solid(attr, blob->size);
767 ntfs_3g_cleanup_blob_extract(struct ntfs_3g_apply_ctx *ctx)
771 for (unsigned i = 0; i < ctx->num_open_attrs; i++) {
772 if (ntfs_attr_pclose(ctx->open_attrs[i]))
774 ntfs_attr_close(ctx->open_attrs[i]);
777 ctx->num_open_attrs = 0;
779 for (unsigned i = 0; i < ctx->num_open_inodes; i++) {
780 if (ntfs_inode_close(ctx->open_inodes[i]))
783 ctx->num_open_inodes = 0;
786 ctx->reparse_ptr = NULL;
787 ctx->num_reparse_inodes = 0;
792 ntfs_3g_open_inode(struct wim_inode *inode, struct ntfs_3g_apply_ctx *ctx)
796 /* If the same blob is being extracted to multiple streams of the same
797 * inode, then we must only open the inode once. */
798 if (unlikely(inode->i_num_streams > 1)) {
799 for (unsigned i = 0; i < ctx->num_open_inodes; i++) {
800 if (ctx->open_inodes[i]->mft_no == inode->i_mft_no) {
801 return ctx->open_inodes[i];
806 ni = ntfs_inode_open(ctx->vol, inode->i_mft_no);
808 ERROR_WITH_ERRNO("Can't open \"%s\" in NTFS volume",
810 inode_first_extraction_dentry(inode)));
814 ctx->open_inodes[ctx->num_open_inodes++] = ni;
819 ntfs_3g_begin_extract_blob(struct blob_descriptor *blob, void *_ctx)
821 struct ntfs_3g_apply_ctx *ctx = _ctx;
822 const struct blob_extraction_target *targets = blob_extraction_targets(blob);
826 for (u32 i = 0; i < blob->out_refcnt; i++) {
827 ret = WIMLIB_ERR_NTFS_3G;
828 ni = ntfs_3g_open_inode(targets[i].inode, ctx);
832 ret = ntfs_3g_begin_extract_blob_instance(blob, ni,
834 targets[i].stream, ctx);
842 ntfs_3g_cleanup_blob_extract(ctx);
848 ntfs_3g_extract_chunk(const void *chunk, size_t size, void *_ctx)
850 struct ntfs_3g_apply_ctx *ctx = _ctx;
853 for (unsigned i = 0; i < ctx->num_open_attrs; i++) {
854 res = ntfs_attr_pwrite(ctx->open_attrs[i],
855 ctx->offset, size, chunk);
857 ERROR_WITH_ERRNO("Error writing data to NTFS volume");
858 return WIMLIB_ERR_NTFS_3G;
861 if (ctx->reparse_ptr)
862 ctx->reparse_ptr = mempcpy(ctx->reparse_ptr, chunk, size);
868 ntfs_3g_end_extract_blob(struct blob_descriptor *blob, int status, void *_ctx)
870 struct ntfs_3g_apply_ctx *ctx = _ctx;
878 for (u32 i = 0; i < ctx->num_reparse_inodes; i++) {
879 ret = ntfs_3g_restore_reparse_point(ctx->ntfs_reparse_inodes[i],
880 ctx->wim_reparse_inodes[i],
887 if (ntfs_3g_cleanup_blob_extract(ctx) && !ret) {
888 ERROR_WITH_ERRNO("Error writing data to NTFS volume");
889 ret = WIMLIB_ERR_NTFS_3G;
895 ntfs_3g_count_dentries(const struct list_head *dentry_list)
897 const struct wim_dentry *dentry;
900 list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
902 if ((dentry->d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) &&
903 dentry_has_short_name(dentry))
913 ntfs_3g_extract(struct list_head *dentry_list, struct apply_ctx *_ctx)
915 struct ntfs_3g_apply_ctx *ctx = (struct ntfs_3g_apply_ctx *)_ctx;
917 struct wim_dentry *root;
920 /* For NTFS-3g extraction mode we require that the dentries to extract
921 * form a single tree. */
922 root = list_first_entry(dentry_list, struct wim_dentry,
923 d_extraction_list_node);
925 /* Mount the NTFS volume. */
926 vol = ntfs_mount(ctx->common.target, 0);
928 ERROR_WITH_ERRNO("Failed to mount \"%s\" with NTFS-3g",
930 return WIMLIB_ERR_NTFS_3G;
934 /* Create all inodes and aliases, including short names, and set
935 * metadata (attributes, security descriptors, and timestamps). */
937 ret = start_file_structure_phase(&ctx->common,
938 ntfs_3g_count_dentries(dentry_list));
942 ret = ntfs_3g_create_directories(root, dentry_list, ctx);
946 ret = ntfs_3g_create_nondirectories(dentry_list, ctx);
950 ret = end_file_structure_phase(&ctx->common);
955 struct read_blob_callbacks cbs = {
956 .begin_blob = ntfs_3g_begin_extract_blob,
957 .consume_chunk = ntfs_3g_extract_chunk,
958 .end_blob = ntfs_3g_end_extract_blob,
961 ret = extract_blob_list(&ctx->common, &cbs);
963 /* We do not need a final pass to set timestamps because libntfs-3g does
964 * not update timestamps automatically (exception:
965 * ntfs_set_ntfs_dos_name() does, but we handle this elsewhere). */
968 if (ntfs_umount(ctx->vol, FALSE) && !ret) {
969 ERROR_WITH_ERRNO("Failed to unmount \"%s\" with NTFS-3g",
971 ret = WIMLIB_ERR_NTFS_3G;
976 const struct apply_operations ntfs_3g_apply_ops = {
978 .get_supported_features = ntfs_3g_get_supported_features,
979 .extract = ntfs_3g_extract,
980 .context_size = sizeof(struct ntfs_3g_apply_ctx),
981 .single_tree_only = true,
985 libntfs3g_global_init(void)
987 ntfs_set_char_encoding(setlocale(LC_ALL, ""));