4 * Support for extracting WIM images, or files or directories contained in a WIM
9 * Copyright (C) 2012, 2013 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/.
32 # include "wimlib/win32_common.h" /* For GetFullPathName() */
35 #include "wimlib/apply.h"
36 #include "wimlib/dentry.h"
37 #include "wimlib/encoding.h"
38 #include "wimlib/endianness.h"
39 #include "wimlib/error.h"
40 #include "wimlib/lookup_table.h"
41 #include "wimlib/paths.h"
42 #include "wimlib/resource.h"
43 #include "wimlib/swm.h"
45 # include "wimlib/win32.h" /* for realpath() equivalent */
47 #include "wimlib/xml.h"
52 # include <ntfs-3g/volume.h> /* for ntfs_mount(), ntfs_umount() */
58 #define MAX_EXTRACT_LONG_PATH_WARNINGS 5
61 do_apply_op(struct wim_dentry *dentry, struct apply_args *args,
62 int (*apply_dentry_func)(const tchar *, size_t,
63 struct wim_dentry *, struct apply_args *))
66 size_t extraction_path_nchars;
68 LIST_HEAD(ancestor_list);
73 if (args->target_lowlevel_path) {
74 target = args->target_lowlevel_path;
75 target_nchars = args->target_lowlevel_path_nchars;
79 target = args->target;
80 target_nchars = args->target_nchars;
83 extraction_path_nchars = target_nchars;
85 for (d = dentry; d != args->extract_root; d = d->parent) {
88 extraction_path_nchars += d->extraction_name_nchars + 1;
89 list_add(&d->tmp_list, &ancestor_list);
92 tchar extraction_path[extraction_path_nchars + 1];
93 p = tmempcpy(extraction_path, target, target_nchars);
96 list_for_each_entry(d, &ancestor_list, tmp_list) {
97 *p++ = OS_PREFERRED_PATH_SEPARATOR;
98 p = tmempcpy(p, d->extraction_name, d->extraction_name_nchars);
103 /* Warn the user if the path exceeds MAX_PATH */
105 /* + 1 for '\0', -4 for \\?\. */
106 if (extraction_path_nchars + 1 - 4 > MAX_PATH) {
107 if (dentry->needs_extraction &&
108 args->num_long_paths < MAX_EXTRACT_LONG_PATH_WARNINGS)
110 WARNING("Path \"%ls\" exceeds MAX_PATH and will not be accessible "
111 "to most Windows software", extraction_path);
112 if (++args->num_long_paths == MAX_EXTRACT_LONG_PATH_WARNINGS)
113 WARNING("Suppressing further warnings about long paths");
117 return (*apply_dentry_func)(extraction_path, extraction_path_nchars,
122 /* Extracts a file, directory, or symbolic link from the WIM archive. */
124 apply_dentry_normal(struct wim_dentry *dentry, void *arg)
127 return do_apply_op(dentry, arg, win32_do_apply_dentry);
129 return do_apply_op(dentry, arg, unix_do_apply_dentry);
134 /* Apply timestamps to an extracted file or directory */
136 apply_dentry_timestamps_normal(struct wim_dentry *dentry, void *arg)
139 return do_apply_op(dentry, arg, win32_do_apply_dentry_timestamps);
141 return do_apply_op(dentry, arg, unix_do_apply_dentry_timestamps);
146 dentry_is_dot_or_dotdot(const struct wim_dentry *dentry)
148 const utf16lechar *file_name = dentry->file_name;
149 return file_name != NULL &&
150 file_name[0] == cpu_to_le16('.') &&
151 (file_name[1] == cpu_to_le16('\0') ||
152 (file_name[1] == cpu_to_le16('.') &&
153 file_name[2] == cpu_to_le16('\0')));
156 /* Extract a dentry if it hasn't already been extracted and either
157 * WIMLIB_EXTRACT_FLAG_NO_STREAMS is not specified, or the dentry is a directory
158 * and/or has no unnamed stream. */
160 maybe_apply_dentry(struct wim_dentry *dentry, void *arg)
162 struct apply_args *args = arg;
165 if (!dentry->needs_extraction)
168 if (args->extract_flags & WIMLIB_EXTRACT_FLAG_NO_STREAMS &&
169 !dentry_is_directory(dentry) &&
170 inode_unnamed_lte_resolved(dentry->d_inode) != NULL)
173 if ((args->extract_flags & WIMLIB_EXTRACT_FLAG_VERBOSE) &&
174 args->progress_func) {
175 ret = calculate_dentry_full_path(dentry);
178 args->progress.extract.cur_path = dentry->_full_path;
179 args->progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY,
182 ret = args->apply_dentry(dentry, args);
184 dentry->needs_extraction = 0;
189 calculate_bytes_to_extract(struct list_head *stream_list,
191 union wimlib_progress_info *progress)
193 struct wim_lookup_table_entry *lte;
197 /* For each stream to be extracted... */
198 list_for_each_entry(lte, stream_list, extraction_list) {
200 (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK))
202 /* In the symlink or hard link extraction mode, each
203 * stream will be extracted one time regardless of how
204 * many dentries share the stream. */
205 wimlib_assert(!(extract_flags & WIMLIB_EXTRACT_FLAG_NTFS));
206 if (!lte->extracted_file) {
208 total_bytes += wim_resource_size(lte);
211 num_streams += lte->out_refcnt;
212 total_bytes += lte->out_refcnt * wim_resource_size(lte);
215 progress->extract.num_streams = num_streams;
216 progress->extract.total_bytes = total_bytes;
217 progress->extract.completed_bytes = 0;
221 maybe_add_stream_for_extraction(struct wim_lookup_table_entry *lte,
222 struct list_head *stream_list)
224 if (++lte->out_refcnt == 1) {
225 INIT_LIST_HEAD(<e->lte_dentry_list);
226 list_add_tail(<e->extraction_list, stream_list);
230 struct find_streams_ctx {
231 struct list_head stream_list;
236 dentry_find_streams_to_extract(struct wim_dentry *dentry, void *_ctx)
238 struct find_streams_ctx *ctx = _ctx;
239 struct wim_inode *inode = dentry->d_inode;
240 struct wim_lookup_table_entry *lte;
241 bool dentry_added = false;
242 struct list_head *stream_list = &ctx->stream_list;
243 int extract_flags = ctx->extract_flags;
245 if (!dentry->needs_extraction)
248 lte = inode_unnamed_lte_resolved(inode);
250 if (!inode->i_visited)
251 maybe_add_stream_for_extraction(lte, stream_list);
252 list_add_tail(&dentry->extraction_stream_list, <e->lte_dentry_list);
256 /* Determine whether to include alternate data stream entries or not.
258 * UNIX: Include them if extracting using NTFS-3g.
260 * Windows: Include them undconditionally, although if the filesystem is
261 * not NTFS we won't actually be able to extract them. */
262 #if defined(WITH_NTFS_3G)
263 if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS)
264 #elif defined(__WIN32__)
270 for (unsigned i = 0; i < inode->i_num_ads; i++) {
271 if (inode->i_ads_entries[i].stream_name_nbytes != 0) {
272 lte = inode->i_ads_entries[i].lte;
274 if (!inode->i_visited) {
275 maybe_add_stream_for_extraction(lte,
279 list_add_tail(&dentry->extraction_stream_list,
280 <e->lte_dentry_list);
287 inode->i_visited = 1;
292 dentry_resolve_and_zero_lte_refcnt(struct wim_dentry *dentry, void *_lookup_table)
294 struct wim_inode *inode = dentry->d_inode;
295 struct wim_lookup_table *lookup_table = _lookup_table;
296 struct wim_lookup_table_entry *lte;
299 ret = inode_resolve_ltes(inode, lookup_table);
302 for (unsigned i = 0; i <= inode->i_num_ads; i++) {
303 lte = inode_stream_lte_resolved(inode, i);
311 find_streams_for_extraction(struct wim_dentry *root,
312 struct list_head *stream_list,
313 struct wim_lookup_table *lookup_table,
316 struct find_streams_ctx ctx;
319 INIT_LIST_HEAD(&ctx.stream_list);
320 ctx.extract_flags = extract_flags;
321 ret = for_dentry_in_tree(root, dentry_resolve_and_zero_lte_refcnt, lookup_table);
324 for_dentry_in_tree(root, dentry_find_streams_to_extract, &ctx);
325 list_transfer(&ctx.stream_list, stream_list);
329 struct apply_operations {
330 int (*apply_dentry)(struct wim_dentry *dentry, void *arg);
331 int (*apply_dentry_timestamps)(struct wim_dentry *dentry, void *arg);
334 static const struct apply_operations normal_apply_operations = {
335 .apply_dentry = apply_dentry_normal,
336 .apply_dentry_timestamps = apply_dentry_timestamps_normal,
340 static const struct apply_operations ntfs_apply_operations = {
341 .apply_dentry = apply_dentry_ntfs,
342 .apply_dentry_timestamps = apply_dentry_timestamps_ntfs,
347 apply_stream_list(struct list_head *stream_list,
348 struct apply_args *args,
349 const struct apply_operations *ops,
350 wimlib_progress_func_t progress_func)
352 uint64_t bytes_per_progress = args->progress.extract.total_bytes / 100;
353 uint64_t next_progress = bytes_per_progress;
354 struct wim_lookup_table_entry *lte;
355 struct wim_dentry *dentry;
358 /* This complicated loop is essentially looping through the dentries,
359 * although dentries may be visited more than once (if a dentry contains
360 * two different nonempty streams) or not at all (if a dentry contains
361 * no non-empty streams).
363 * The outer loop is over the distinct streams to be extracted so that
364 * sequential reading of the WIM can be implemented. */
366 /* For each distinct stream to be extracted */
367 list_for_each_entry(lte, stream_list, extraction_list) {
368 /* For each dentry to be extracted that is a name for an inode
369 * containing the stream */
370 list_for_each_entry(dentry, <e->lte_dentry_list, extraction_stream_list) {
371 /* Extract the dentry if it was not already
373 ret = maybe_apply_dentry(dentry, args);
377 args->progress.extract.completed_bytes >= next_progress)
379 progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS,
381 if (args->progress.extract.completed_bytes >=
382 args->progress.extract.total_bytes)
384 next_progress = ~0ULL;
387 min (args->progress.extract.completed_bytes +
389 args->progress.extract.total_bytes);
398 sort_stream_list_by_wim_position(struct list_head *stream_list)
400 struct list_head *cur;
402 struct wim_lookup_table_entry **array;
407 list_for_each(cur, stream_list)
409 array_size = num_streams * sizeof(array[0]);
410 array = MALLOC(array_size);
412 ERROR("Failed to allocate %zu bytes to sort stream entries",
414 return WIMLIB_ERR_NOMEM;
416 cur = stream_list->next;
417 for (i = 0; i < num_streams; i++) {
418 array[i] = container_of(cur, struct wim_lookup_table_entry, extraction_list);
422 qsort(array, num_streams, sizeof(array[0]), cmp_streams_by_wim_position);
424 INIT_LIST_HEAD(stream_list);
425 for (i = 0; i < num_streams; i++)
426 list_add_tail(&array[i]->extraction_list, stream_list);
432 * Extract a dentry to standard output.
434 * This obviously doesn't make sense in all cases. We return an error if the
435 * dentry does not correspond to a regular file. Otherwise we extract the
436 * unnamed data stream only.
439 extract_dentry_to_stdout(struct wim_dentry *dentry)
442 if (dentry->d_inode->i_attributes & (FILE_ATTRIBUTE_REPARSE_POINT |
443 FILE_ATTRIBUTE_DIRECTORY))
445 ERROR("\"%"TS"\" is not a regular file and therefore cannot be "
446 "extracted to standard output", dentry_full_path(dentry));
447 ret = WIMLIB_ERR_NOT_A_REGULAR_FILE;
449 struct wim_lookup_table_entry *lte;
451 lte = inode_unnamed_lte_resolved(dentry->d_inode);
453 ret = extract_wim_resource_to_fd(lte, STDOUT_FILENO,
454 wim_resource_size(lte));
461 static const utf16lechar replacement_char = cpu_to_le16(0xfffd);
463 static const utf16lechar replacement_char = cpu_to_le16('?');
467 file_name_valid(utf16lechar *name, size_t num_chars, bool fix)
473 for (i = 0; i < num_chars; i++) {
476 case cpu_to_le16('\\'):
477 case cpu_to_le16(':'):
478 case cpu_to_le16('*'):
479 case cpu_to_le16('?'):
480 case cpu_to_le16('"'):
481 case cpu_to_le16('<'):
482 case cpu_to_le16('>'):
483 case cpu_to_le16('|'):
485 case cpu_to_le16('/'):
486 case cpu_to_le16('\0'):
488 name[i] = replacement_char;
495 if (name[num_chars - 1] == cpu_to_le16(' ') ||
496 name[num_chars - 1] == cpu_to_le16('.'))
499 name[num_chars - 1] = replacement_char;
508 * dentry_calculate_extraction_path-
510 * Calculate the actual filename component at which a WIM dentry will be
511 * extracted, handling invalid filenames "properly".
513 * dentry->extraction_name usually will be set the same as dentry->file_name (on
514 * UNIX, converted into the platform's multibyte encoding). However, if the
515 * file name contains characters that are not valid on the current platform or
516 * has some other format that is not valid, leave dentry->extraction_name as
517 * NULL and clear dentry->needs_extraction to indicate that this dentry should
518 * not be extracted, unless the appropriate flag
519 * WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES is set in the extract flags, in
520 * which case a substitute filename will be created and set instead.
522 * Conflicts with case-insensitive names on Windows are handled similarly; see
526 dentry_calculate_extraction_path(struct wim_dentry *dentry, void *_args)
528 struct apply_args *args = _args;
531 dentry->needs_extraction = 1;
533 if (dentry == args->extract_root)
536 if (dentry_is_dot_or_dotdot(dentry)) {
537 /* WIM files shouldn't contain . or .. entries. But if they are
538 * there, don't attempt to extract them. */
539 WARNING("Skipping extraction of unexpected . or .. file \"%"TS"\"",
540 dentry_full_path(dentry));
545 struct wim_dentry *other;
546 list_for_each_entry(other, &dentry->case_insensitive_conflict_list,
547 case_insensitive_conflict_list)
549 if (other->needs_extraction) {
550 if (args->extract_flags & WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS)
552 WARNING("\"%"TS"\" has the same case-insensitive "
553 "name as \"%"TS"\"; extracting dummy name instead",
554 dentry_full_path(dentry),
555 dentry_full_path(other));
558 WARNING("Not extracting \"%"TS"\": has same case-insensitive "
560 dentry_full_path(dentry),
561 dentry_full_path(other));
568 if (file_name_valid(dentry->file_name, dentry->file_name_nbytes / 2, false)) {
570 dentry->extraction_name = dentry->file_name;
571 dentry->extraction_name_nchars = dentry->file_name_nbytes / 2;
574 return utf16le_to_tstr(dentry->file_name,
575 dentry->file_name_nbytes,
576 &dentry->extraction_name,
577 &dentry->extraction_name_nchars);
580 if (args->extract_flags & WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES)
582 WARNING("\"%"TS"\" has an invalid filename "
583 "that is not supported on this platform; "
584 "extracting dummy name instead",
585 dentry_full_path(dentry));
588 WARNING("Not extracting \"%"TS"\": has an invalid filename "
589 "that is not supported on this platform",
590 dentry_full_path(dentry));
597 utf16lechar utf16_name_copy[dentry->file_name_nbytes / 2];
599 memcpy(utf16_name_copy, dentry->file_name, dentry->file_name_nbytes);
600 file_name_valid(utf16_name_copy, dentry->file_name_nbytes / 2, true);
605 tchar_name = utf16_name_copy;
606 tchar_nchars = dentry->file_name_nbytes / 2;
608 ret = utf16le_to_tstr(utf16_name_copy,
609 dentry->file_name_nbytes,
610 &tchar_name, &tchar_nchars);
614 size_t fixed_name_num_chars = tchar_nchars;
615 tchar fixed_name[tchar_nchars + 50];
617 tmemcpy(fixed_name, tchar_name, tchar_nchars);
618 fixed_name_num_chars += tsprintf(fixed_name + tchar_nchars,
619 T(" (invalid filename #%lu)"),
620 ++args->invalid_sequence);
624 dentry->extraction_name = memdup(fixed_name, 2 * fixed_name_num_chars + 2);
625 if (!dentry->extraction_name)
626 return WIMLIB_ERR_NOMEM;
627 dentry->extraction_name_nchars = fixed_name_num_chars;
631 dentry->needs_extraction = 0;
632 dentry->not_extracted = 1;
637 dentry_reset_needs_extraction(struct wim_dentry *dentry, void *_ignore)
639 struct wim_inode *inode = dentry->d_inode;
641 dentry->needs_extraction = 0;
642 dentry->not_extracted = 0;
643 inode->i_visited = 0;
644 inode->i_dos_name_extracted = 0;
645 FREE(inode->i_extracted_file);
646 inode->i_extracted_file = NULL;
647 if ((void*)dentry->extraction_name != (void*)dentry->file_name)
648 FREE(dentry->extraction_name);
649 dentry->extraction_name = NULL;
653 #define WINDOWS_NT_MAX_PATH 32768
656 * extract_tree - Extract a file or directory tree from the currently selected
659 * @wim: WIMStruct for the WIM file, with the desired image selected
660 * (as wim->current_image).
662 * "Canonical" (i.e. no leading or trailing slashes, path
663 * separators forwald slashes) path inside the WIM image to
664 * extract. An empty string means the full image.
666 * Filesystem path to extract the file or directory tree to.
669 * WIMLIB_EXTRACT_FLAG_*. Also, the private flag
670 * WIMLIB_EXTRACT_FLAG_MULTI_IMAGE will be set if this is being
671 * called through wimlib_extract_image() with WIMLIB_ALL_IMAGES as
675 * If non-NULL, progress function for the extraction. The messages
676 * we may in this function are:
678 * WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN or
679 * WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN;
680 * WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN;
681 * WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END;
682 * WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY;
683 * WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS;
684 * WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS;
685 * WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END or
686 * WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END.
688 * Returns 0 on success; nonzero on failure.
691 extract_tree(WIMStruct *wim, const tchar *wim_source_path, const tchar *target,
692 int extract_flags, wimlib_progress_func_t progress_func)
695 struct list_head stream_list;
696 struct apply_args args;
697 const struct apply_operations *ops;
698 struct wim_dentry *root;
700 memset(&args, 0, sizeof(args));
704 args.target = target;
705 args.target_nchars = tstrlen(target);
706 args.extract_flags = extract_flags;
707 args.progress_func = progress_func;
710 /* Work around defective behavior in Windows where paths longer than 260
711 * characters are not supported by default; instead they need to be
712 * turned into absolute paths and prefixed with "\\?\". */
713 args.target_lowlevel_path = MALLOC(WINDOWS_NT_MAX_PATH * sizeof(wchar_t));
714 if (!args.target_lowlevel_path)
716 ret = WIMLIB_ERR_NOMEM;
719 args.target_lowlevel_path_nchars =
720 GetFullPathName(args.target, WINDOWS_NT_MAX_PATH - 4,
721 &args.target_lowlevel_path[4], NULL);
723 if (args.target_lowlevel_path_nchars == 0 ||
724 args.target_lowlevel_path_nchars >= WINDOWS_NT_MAX_PATH - 4)
726 WARNING("Can't get full path name for \"%ls\"", args.target);
727 FREE(args.target_lowlevel_path);
728 args.target_lowlevel_path = NULL;
730 wmemcpy(args.target_lowlevel_path, L"\\\\?\\", 4);
731 args.target_lowlevel_path_nchars += 4;
736 args.progress.extract.wimfile_name = wim->filename;
737 args.progress.extract.image = wim->current_image;
738 args.progress.extract.extract_flags = (extract_flags &
739 WIMLIB_EXTRACT_MASK_PUBLIC);
740 args.progress.extract.image_name = wimlib_get_image_name(wim,
742 args.progress.extract.extract_root_wim_source_path = wim_source_path;
743 args.progress.extract.target = target;
747 if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
748 args.vol = ntfs_mount(target, 0);
750 ERROR_WITH_ERRNO("Failed to mount NTFS volume `%"TS"'",
752 ret = WIMLIB_ERR_NTFS_3G;
753 goto out_free_target_lowlevel_path;
755 ops = &ntfs_apply_operations;
758 ops = &normal_apply_operations;
760 root = get_dentry(wim, wim_source_path);
762 ERROR("Path \"%"TS"\" does not exist in WIM image %d",
763 wim_source_path, wim->current_image);
764 ret = WIMLIB_ERR_PATH_DOES_NOT_EXIST;
765 goto out_ntfs_umount;
767 args.extract_root = root;
769 /* Calculate the actual filename component of each extracted dentry, and
770 * in the process set the dentry->needs_extraction flag on dentries that
771 * will be extracted. */
772 ret = for_dentry_in_tree(root, dentry_calculate_extraction_path, &args);
774 goto out_dentry_reset_needs_extraction;
776 /* Build a list of the streams that need to be extracted */
777 ret = find_streams_for_extraction(root,
779 wim->lookup_table, extract_flags);
781 goto out_dentry_reset_needs_extraction;
783 /* Calculate the number of bytes of data that will be extracted */
784 calculate_bytes_to_extract(&stream_list, extract_flags,
787 if (extract_flags & WIMLIB_EXTRACT_FLAG_TO_STDOUT) {
788 ret = extract_dentry_to_stdout(root);
789 goto out_dentry_reset_needs_extraction;
793 progress_func(*wim_source_path ? WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN :
794 WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN,
798 /* If a sequential extraction was specified, sort the streams to be
799 * extracted by their position in the WIM file, so that the WIM file can
800 * be read sequentially. */
801 if (extract_flags & WIMLIB_EXTRACT_FLAG_SEQUENTIAL) {
802 ret = sort_stream_list_by_wim_position(&stream_list);
804 WARNING("Falling back to non-sequential extraction");
805 extract_flags &= ~WIMLIB_EXTRACT_FLAG_SEQUENTIAL;
810 progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN,
814 /* Make the directory structure and extract empty files */
815 args.extract_flags |= WIMLIB_EXTRACT_FLAG_NO_STREAMS;
816 args.apply_dentry = ops->apply_dentry;
817 ret = for_dentry_in_tree(root, maybe_apply_dentry, &args);
818 args.extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_STREAMS;
820 goto out_dentry_reset_needs_extraction;
823 progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END,
827 if (extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX) {
828 args.target_realpath = realpath(target, NULL);
829 if (!args.target_realpath) {
830 ret = WIMLIB_ERR_NOMEM;
831 goto out_dentry_reset_needs_extraction;
833 args.target_realpath_len = tstrlen(args.target_realpath);
836 /* Extract non-empty files */
837 ret = apply_stream_list(&stream_list, &args, ops, progress_func);
839 goto out_free_target_realpath;
842 progress_func(WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS,
846 /* Apply timestamps */
847 ret = for_dentry_in_tree_depth(root,
848 ops->apply_dentry_timestamps, &args);
850 goto out_free_target_realpath;
853 progress_func(*wim_source_path ? WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END :
854 WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END,
857 out_free_target_realpath:
858 FREE(args.target_realpath);
859 out_dentry_reset_needs_extraction:
860 for_dentry_in_tree(root, dentry_reset_needs_extraction, NULL);
863 /* Unmount the NTFS volume */
864 if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
865 if (ntfs_umount(args.vol, FALSE) != 0) {
866 ERROR_WITH_ERRNO("Failed to unmount NTFS volume `%"TS"'",
869 ret = WIMLIB_ERR_NTFS_3G;
873 out_free_target_lowlevel_path:
875 FREE(args.target_lowlevel_path);
881 /* Validates a single wimlib_extract_command, mostly checking to make sure the
882 * extract flags make sense. */
884 check_extract_command(struct wimlib_extract_command *cmd, int wim_header_flags)
887 bool is_entire_image = (cmd->wim_source_path[0] == T('\0'));
889 /* Empty destination path? */
890 if (cmd->fs_dest_path[0] == T('\0'))
891 return WIMLIB_ERR_INVALID_PARAM;
893 extract_flags = cmd->extract_flags;
895 /* Specified both symlink and hardlink modes? */
897 (WIMLIB_EXTRACT_FLAG_SYMLINK |
898 WIMLIB_EXTRACT_FLAG_HARDLINK)) == (WIMLIB_EXTRACT_FLAG_SYMLINK |
899 WIMLIB_EXTRACT_FLAG_HARDLINK))
900 return WIMLIB_ERR_INVALID_PARAM;
903 /* Wanted UNIX data on Windows? */
904 if (extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) {
905 ERROR("Extracting UNIX data is not supported on Windows");
906 return WIMLIB_ERR_INVALID_PARAM;
908 /* Wanted linked extraction on Windows? (XXX This is possible, just not
909 * implemented yet.) */
910 if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
911 WIMLIB_EXTRACT_FLAG_HARDLINK))
913 ERROR("Linked extraction modes are not supported on Windows");
914 return WIMLIB_ERR_INVALID_PARAM;
918 if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
919 /* NTFS-3g extraction mode requested */
921 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
922 WIMLIB_EXTRACT_FLAG_HARDLINK))) {
923 ERROR("Cannot specify symlink or hardlink flags when applying\n"
924 " directly to a NTFS volume");
925 return WIMLIB_ERR_INVALID_PARAM;
927 if (!is_entire_image &&
928 (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS))
930 ERROR("When applying directly to a NTFS volume you can "
931 "only extract a full image, not part of one");
932 return WIMLIB_ERR_INVALID_PARAM;
934 if (extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) {
935 ERROR("Cannot restore UNIX-specific data in "
936 "the NTFS extraction mode");
937 return WIMLIB_ERR_INVALID_PARAM;
940 ERROR("wimlib was compiled without support for NTFS-3g, so");
941 ERROR("we cannot apply a WIM image directly to a NTFS volume");
942 return WIMLIB_ERR_UNSUPPORTED;
946 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_RPFIX |
947 WIMLIB_EXTRACT_FLAG_NORPFIX)) ==
948 (WIMLIB_EXTRACT_FLAG_RPFIX | WIMLIB_EXTRACT_FLAG_NORPFIX))
950 ERROR("Cannot specify RPFIX and NORPFIX flags at the same time!");
951 return WIMLIB_ERR_INVALID_PARAM;
954 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_RPFIX |
955 WIMLIB_EXTRACT_FLAG_NORPFIX)) == 0)
957 /* Do reparse point fixups by default if the WIM header says
958 * they are enabled and we are extracting a full image. */
959 if ((wim_header_flags & WIM_HDR_FLAG_RP_FIX) && is_entire_image)
960 extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
963 if (!is_entire_image && (extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX)) {
964 ERROR("Cannot specify --rpfix when not extracting entire image");
965 return WIMLIB_ERR_INVALID_PARAM;
968 cmd->extract_flags = extract_flags;
973 /* Internal function to execute extraction commands for a WIM image. */
975 do_wimlib_extract_files(WIMStruct *wim,
977 struct wimlib_extract_command *cmds,
979 wimlib_progress_func_t progress_func)
982 bool found_link_cmd = false;
983 bool found_nolink_cmd = false;
985 /* Select the image from which we are extracting files */
986 ret = select_wim_image(wim, image);
990 /* Make sure there are no streams in the WIM that have not been
991 * checksummed yet. */
992 ret = wim_checksum_unhashed_streams(wim);
996 /* Check for problems with the extraction commands */
997 for (size_t i = 0; i < num_cmds; i++) {
998 ret = check_extract_command(&cmds[i], wim->hdr.flags);
1001 if (cmds[i].extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
1002 WIMLIB_EXTRACT_FLAG_HARDLINK)) {
1003 found_link_cmd = true;
1005 found_nolink_cmd = true;
1007 if (found_link_cmd && found_nolink_cmd) {
1008 ERROR("Symlink or hardlink extraction mode must "
1009 "be set on all extraction commands");
1010 return WIMLIB_ERR_INVALID_PARAM;
1014 /* Execute the extraction commands */
1015 for (size_t i = 0; i < num_cmds; i++) {
1016 ret = extract_tree(wim,
1017 cmds[i].wim_source_path,
1018 cmds[i].fs_dest_path,
1019 cmds[i].extract_flags,
1027 /* Extract files or directories from a WIM image. */
1029 wimlib_extract_files(WIMStruct *wim,
1031 const struct wimlib_extract_command *cmds,
1033 int default_extract_flags,
1034 WIMStruct **additional_swms,
1035 unsigned num_additional_swms,
1036 wimlib_progress_func_t progress_func)
1039 struct wimlib_extract_command *cmds_copy;
1042 default_extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC;
1044 ret = verify_swm_set(wim, additional_swms, num_additional_swms);
1051 if (num_additional_swms)
1052 merge_lookup_tables(wim, additional_swms, num_additional_swms);
1054 cmds_copy = CALLOC(num_cmds, sizeof(cmds[0]));
1056 ret = WIMLIB_ERR_NOMEM;
1057 goto out_restore_lookup_table;
1060 for (size_t i = 0; i < num_cmds; i++) {
1061 cmds_copy[i].extract_flags = (default_extract_flags |
1062 cmds[i].extract_flags)
1063 & WIMLIB_EXTRACT_MASK_PUBLIC;
1064 all_flags |= cmds_copy[i].extract_flags;
1066 cmds_copy[i].wim_source_path = canonicalize_wim_path(cmds[i].wim_source_path);
1067 if (!cmds_copy[i].wim_source_path) {
1068 ret = WIMLIB_ERR_NOMEM;
1069 goto out_free_cmds_copy;
1072 cmds_copy[i].fs_dest_path = canonicalize_fs_path(cmds[i].fs_dest_path);
1073 if (!cmds_copy[i].fs_dest_path) {
1074 ret = WIMLIB_ERR_NOMEM;
1075 goto out_free_cmds_copy;
1079 ret = do_wimlib_extract_files(wim, image,
1080 cmds_copy, num_cmds,
1083 if (all_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
1084 WIMLIB_EXTRACT_FLAG_HARDLINK))
1086 for_lookup_table_entry(wim->lookup_table,
1087 lte_free_extracted_file, NULL);
1090 for (size_t i = 0; i < num_cmds; i++) {
1091 FREE(cmds_copy[i].wim_source_path);
1092 FREE(cmds_copy[i].fs_dest_path);
1095 out_restore_lookup_table:
1096 if (num_additional_swms)
1097 unmerge_lookup_table(wim);
1103 * Extracts an image from a WIM file.
1105 * @wim: WIMStruct for the WIM file.
1107 * @image: Number of the single image to extract.
1109 * @target: Directory or NTFS volume to extract the image to.
1111 * @extract_flags: Bitwise or of WIMLIB_EXTRACT_FLAG_*.
1113 * @progress_func: If non-NULL, a progress function to be called
1116 * Returns 0 on success; nonzero on failure.
1119 extract_single_image(WIMStruct *wim, int image,
1120 const tchar *target, int extract_flags,
1121 wimlib_progress_func_t progress_func)
1124 tchar *target_copy = canonicalize_fs_path(target);
1126 return WIMLIB_ERR_NOMEM;
1127 struct wimlib_extract_command cmd = {
1128 .wim_source_path = T(""),
1129 .fs_dest_path = target_copy,
1130 .extract_flags = extract_flags,
1132 ret = do_wimlib_extract_files(wim, image, &cmd, 1, progress_func);
1137 static const tchar * const filename_forbidden_chars =
1146 /* This function checks if it is okay to use a WIM image's name as a directory
1149 image_name_ok_as_dir(const tchar *image_name)
1151 return image_name && *image_name &&
1152 !tstrpbrk(image_name, filename_forbidden_chars) &&
1153 tstrcmp(image_name, T(".")) &&
1154 tstrcmp(image_name, T(".."));
1157 /* Extracts all images from the WIM to the directory @target, with the images
1158 * placed in subdirectories named by their image names. */
1160 extract_all_images(WIMStruct *wim,
1161 const tchar *target,
1163 wimlib_progress_func_t progress_func)
1165 size_t image_name_max_len = max(xml_get_max_image_name_len(wim), 20);
1166 size_t output_path_len = tstrlen(target);
1167 tchar buf[output_path_len + 1 + image_name_max_len + 1];
1170 const tchar *image_name;
1173 if (tstat(target, &stbuf)) {
1174 if (errno == ENOENT)
1176 if (tmkdir(target, S_IRWXU | S_IRGRP | S_IXGRP |
1179 ERROR_WITH_ERRNO("Failed to create directory \"%"TS"\"", target);
1180 return WIMLIB_ERR_MKDIR;
1183 ERROR_WITH_ERRNO("Failed to stat \"%"TS"\"", target);
1184 return WIMLIB_ERR_STAT;
1186 } else if (!S_ISDIR(stbuf.st_mode)) {
1187 ERROR("\"%"TS"\" is not a directory", target);
1188 return WIMLIB_ERR_NOTDIR;
1191 tmemcpy(buf, target, output_path_len);
1192 buf[output_path_len] = OS_PREFERRED_PATH_SEPARATOR;
1193 for (image = 1; image <= wim->hdr.image_count; image++) {
1194 image_name = wimlib_get_image_name(wim, image);
1195 if (image_name_ok_as_dir(image_name)) {
1196 tstrcpy(buf + output_path_len + 1, image_name);
1198 /* Image name is empty or contains forbidden characters.
1199 * Use image number instead. */
1200 tsprintf(buf + output_path_len + 1, T("%d"), image);
1202 ret = extract_single_image(wim, image, buf, extract_flags,
1210 /* Extracts a single image or all images from a WIM file to a directory or NTFS
1213 wimlib_extract_image(WIMStruct *wim,
1215 const tchar *target,
1217 WIMStruct **additional_swms,
1218 unsigned num_additional_swms,
1219 wimlib_progress_func_t progress_func)
1223 extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC;
1225 ret = verify_swm_set(wim, additional_swms, num_additional_swms);
1229 if (num_additional_swms)
1230 merge_lookup_tables(wim, additional_swms, num_additional_swms);
1232 if (image == WIMLIB_ALL_IMAGES) {
1233 ret = extract_all_images(wim, target,
1234 extract_flags | WIMLIB_EXTRACT_FLAG_MULTI_IMAGE,
1237 ret = extract_single_image(wim, image, target, extract_flags,
1241 if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
1242 WIMLIB_EXTRACT_FLAG_HARDLINK))
1244 for_lookup_table_entry(wim->lookup_table,
1245 lte_free_extracted_file,
1248 if (num_additional_swms)
1249 unmerge_lookup_table(wim);