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_is_regular_file(dentry)) {
443 ERROR("\"%"TS"\" is not a regular file and therefore cannot be "
444 "extracted to standard output", dentry->_full_path);
445 ret = WIMLIB_ERR_NOT_A_REGULAR_FILE;
447 struct wim_lookup_table_entry *lte;
449 lte = inode_unnamed_lte_resolved(dentry->d_inode);
451 ret = extract_wim_resource_to_fd(lte, STDOUT_FILENO,
452 wim_resource_size(lte));
459 static const utf16lechar replacement_char = cpu_to_le16(0xfffd);
461 static const utf16lechar replacement_char = cpu_to_le16('?');
465 file_name_valid(utf16lechar *name, size_t num_chars, bool fix)
471 for (i = 0; i < num_chars; i++) {
474 case cpu_to_le16('\\'):
475 case cpu_to_le16(':'):
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('|'):
483 case cpu_to_le16('/'):
484 case cpu_to_le16('\0'):
486 name[i] = replacement_char;
493 if (name[num_chars - 1] == cpu_to_le16(' ') ||
494 name[num_chars - 1] == cpu_to_le16('.'))
497 name[num_chars - 1] = replacement_char;
506 * dentry_calculate_extraction_path-
508 * Calculate the actual filename component at which a WIM dentry will be
509 * extracted, handling invalid filenames "properly".
511 * dentry->extraction_name usually will be set the same as dentry->file_name (on
512 * UNIX, converted into the platform's multibyte encoding). However, if the
513 * file name contains characters that are not valid on the current platform or
514 * has some other format that is not valid, leave dentry->extraction_name as
515 * NULL and clear dentry->needs_extraction to indicate that this dentry should
516 * not be extracted, unless the appropriate flag
517 * WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES is set in the extract flags, in
518 * which case a substitute filename will be created and set instead.
520 * Conflicts with case-insensitive names on Windows are handled similarly; see
524 dentry_calculate_extraction_path(struct wim_dentry *dentry, void *_args)
526 struct apply_args *args = _args;
529 dentry->needs_extraction = 1;
531 if (dentry == args->extract_root)
534 if (dentry_is_dot_or_dotdot(dentry)) {
535 /* WIM files shouldn't contain . or .. entries. But if they are
536 * there, don't attempt to extract them. */
537 WARNING("Skipping extraction of unexpected . or .. file \"%"TS"\"",
538 dentry_full_path(dentry));
543 struct wim_dentry *other;
544 list_for_each_entry(other, &dentry->case_insensitive_conflict_list,
545 case_insensitive_conflict_list)
547 if (other->needs_extraction) {
548 if (args->extract_flags & WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS)
550 WARNING("\"%"TS"\" has the same case-insensitive "
551 "name as \"%"TS"\"; extracting dummy name instead",
552 dentry_full_path(dentry),
553 dentry_full_path(other));
556 WARNING("Not extracting \"%"TS"\": has same case-insensitive "
558 dentry_full_path(dentry),
559 dentry_full_path(other));
566 if (file_name_valid(dentry->file_name, dentry->file_name_nbytes / 2, false)) {
568 dentry->extraction_name = dentry->file_name;
569 dentry->extraction_name_nchars = dentry->file_name_nbytes / 2;
572 return utf16le_to_tstr(dentry->file_name,
573 dentry->file_name_nbytes,
574 &dentry->extraction_name,
575 &dentry->extraction_name_nchars);
578 if (args->extract_flags & WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES)
580 WARNING("\"%"TS"\" has an invalid filename "
581 "that is not supported on this platform; "
582 "extracting dummy name instead",
583 dentry_full_path(dentry));
586 WARNING("Not extracting \"%"TS"\": has an invalid filename "
587 "that is not supported on this platform",
588 dentry_full_path(dentry));
595 utf16lechar utf16_name_copy[dentry->file_name_nbytes / 2];
597 memcpy(utf16_name_copy, dentry->file_name, dentry->file_name_nbytes);
598 file_name_valid(utf16_name_copy, dentry->file_name_nbytes / 2, true);
603 tchar_name = utf16_name_copy;
604 tchar_nchars = dentry->file_name_nbytes / 2;
606 ret = utf16le_to_tstr(utf16_name_copy,
607 dentry->file_name_nbytes,
608 &tchar_name, &tchar_nchars);
612 size_t fixed_name_num_chars = tchar_nchars;
613 tchar fixed_name[tchar_nchars + 50];
615 tmemcpy(fixed_name, tchar_name, tchar_nchars);
616 fixed_name_num_chars += tsprintf(fixed_name + tchar_nchars,
617 T(" (invalid filename #%lu)"),
618 ++args->invalid_sequence);
622 dentry->extraction_name = memdup(fixed_name, 2 * fixed_name_num_chars + 2);
623 if (!dentry->extraction_name)
624 return WIMLIB_ERR_NOMEM;
625 dentry->extraction_name_nchars = fixed_name_num_chars;
629 dentry->needs_extraction = 0;
630 dentry->not_extracted = 1;
635 dentry_reset_needs_extraction(struct wim_dentry *dentry, void *_ignore)
637 struct wim_inode *inode = dentry->d_inode;
639 dentry->needs_extraction = 0;
640 dentry->not_extracted = 0;
641 inode->i_visited = 0;
642 inode->i_dos_name_extracted = 0;
643 FREE(inode->i_extracted_file);
644 inode->i_extracted_file = NULL;
645 if ((void*)dentry->extraction_name != (void*)dentry->file_name)
646 FREE(dentry->extraction_name);
647 dentry->extraction_name = NULL;
651 #define WINDOWS_NT_MAX_PATH 32768
654 * extract_tree - Extract a file or directory tree from the currently selected
657 * @wim: WIMStruct for the WIM file, with the desired image selected
658 * (as wim->current_image).
660 * "Canonical" (i.e. no leading or trailing slashes, path
661 * separators forwald slashes) path inside the WIM image to
662 * extract. An empty string means the full image.
664 * Filesystem path to extract the file or directory tree to.
667 * WIMLIB_EXTRACT_FLAG_*. Also, the private flag
668 * WIMLIB_EXTRACT_FLAG_MULTI_IMAGE will be set if this is being
669 * called through wimlib_extract_image() with WIMLIB_ALL_IMAGES as
673 * If non-NULL, progress function for the extraction. The messages
674 * we may in this function are:
676 * WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN or
677 * WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN;
678 * WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN;
679 * WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END;
680 * WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY;
681 * WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS;
682 * WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS;
683 * WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END or
684 * WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END.
686 * Returns 0 on success; nonzero on failure.
689 extract_tree(WIMStruct *wim, const tchar *wim_source_path, const tchar *target,
690 int extract_flags, wimlib_progress_func_t progress_func)
693 struct list_head stream_list;
694 struct apply_args args;
695 const struct apply_operations *ops;
696 struct wim_dentry *root;
698 memset(&args, 0, sizeof(args));
702 args.target = target;
703 args.target_nchars = tstrlen(target);
704 args.extract_flags = extract_flags;
705 args.progress_func = progress_func;
708 /* Work around defective behavior in Windows where paths longer than 260
709 * characters are not supported by default; instead they need to be
710 * turned into absolute paths and prefixed with "\\?\". */
711 args.target_lowlevel_path = MALLOC(WINDOWS_NT_MAX_PATH * sizeof(wchar_t));
712 if (!args.target_lowlevel_path)
714 ret = WIMLIB_ERR_NOMEM;
717 args.target_lowlevel_path_nchars =
718 GetFullPathName(args.target, WINDOWS_NT_MAX_PATH - 4,
719 &args.target_lowlevel_path[4], NULL);
721 if (args.target_lowlevel_path_nchars == 0 ||
722 args.target_lowlevel_path_nchars >= WINDOWS_NT_MAX_PATH - 4)
724 WARNING("Can't get full path name for \"%ls\"", args.target);
725 FREE(args.target_lowlevel_path);
726 args.target_lowlevel_path = NULL;
728 wmemcpy(args.target_lowlevel_path, L"\\\\?\\", 4);
729 args.target_lowlevel_path_nchars += 4;
734 args.progress.extract.wimfile_name = wim->filename;
735 args.progress.extract.image = wim->current_image;
736 args.progress.extract.extract_flags = (extract_flags &
737 WIMLIB_EXTRACT_MASK_PUBLIC);
738 args.progress.extract.image_name = wimlib_get_image_name(wim,
740 args.progress.extract.extract_root_wim_source_path = wim_source_path;
741 args.progress.extract.target = target;
745 if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
746 args.vol = ntfs_mount(target, 0);
748 ERROR_WITH_ERRNO("Failed to mount NTFS volume `%"TS"'",
750 ret = WIMLIB_ERR_NTFS_3G;
751 goto out_free_target_lowlevel_path;
753 ops = &ntfs_apply_operations;
756 ops = &normal_apply_operations;
758 root = get_dentry(wim, wim_source_path);
760 ERROR("Path \"%"TS"\" does not exist in WIM image %d",
761 wim_source_path, wim->current_image);
762 ret = WIMLIB_ERR_PATH_DOES_NOT_EXIST;
763 goto out_ntfs_umount;
765 args.extract_root = root;
767 /* Calculate the actual filename component of each extracted dentry, and
768 * in the process set the dentry->needs_extraction flag on dentries that
769 * will be extracted. */
770 ret = for_dentry_in_tree(root, dentry_calculate_extraction_path, &args);
772 goto out_dentry_reset_needs_extraction;
774 /* Build a list of the streams that need to be extracted */
775 ret = find_streams_for_extraction(root,
777 wim->lookup_table, extract_flags);
779 goto out_dentry_reset_needs_extraction;
781 /* Calculate the number of bytes of data that will be extracted */
782 calculate_bytes_to_extract(&stream_list, extract_flags,
785 if (extract_flags & WIMLIB_EXTRACT_FLAG_TO_STDOUT) {
786 ret = extract_dentry_to_stdout(root);
787 goto out_dentry_reset_needs_extraction;
791 progress_func(*wim_source_path ? WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN :
792 WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN,
796 /* If a sequential extraction was specified, sort the streams to be
797 * extracted by their position in the WIM file, so that the WIM file can
798 * be read sequentially. */
799 if (extract_flags & WIMLIB_EXTRACT_FLAG_SEQUENTIAL) {
800 ret = sort_stream_list_by_wim_position(&stream_list);
802 WARNING("Falling back to non-sequential extraction");
803 extract_flags &= ~WIMLIB_EXTRACT_FLAG_SEQUENTIAL;
808 progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN,
812 /* Make the directory structure and extract empty files */
813 args.extract_flags |= WIMLIB_EXTRACT_FLAG_NO_STREAMS;
814 args.apply_dentry = ops->apply_dentry;
815 ret = for_dentry_in_tree(root, maybe_apply_dentry, &args);
816 args.extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_STREAMS;
818 goto out_dentry_reset_needs_extraction;
821 progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END,
825 if (extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX) {
826 args.target_realpath = realpath(target, NULL);
827 if (!args.target_realpath) {
828 ret = WIMLIB_ERR_NOMEM;
829 goto out_dentry_reset_needs_extraction;
831 args.target_realpath_len = tstrlen(args.target_realpath);
834 /* Extract non-empty files */
835 ret = apply_stream_list(&stream_list, &args, ops, progress_func);
837 goto out_free_target_realpath;
840 progress_func(WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS,
844 /* Apply timestamps */
845 ret = for_dentry_in_tree_depth(root,
846 ops->apply_dentry_timestamps, &args);
848 goto out_free_target_realpath;
851 progress_func(*wim_source_path ? WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END :
852 WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END,
855 out_free_target_realpath:
856 FREE(args.target_realpath);
857 out_dentry_reset_needs_extraction:
858 for_dentry_in_tree(root, dentry_reset_needs_extraction, NULL);
861 /* Unmount the NTFS volume */
862 if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
863 if (ntfs_umount(args.vol, FALSE) != 0) {
864 ERROR_WITH_ERRNO("Failed to unmount NTFS volume `%"TS"'",
867 ret = WIMLIB_ERR_NTFS_3G;
871 out_free_target_lowlevel_path:
873 FREE(args.target_lowlevel_path);
879 /* Validates a single wimlib_extract_command, mostly checking to make sure the
880 * extract flags make sense. */
882 check_extract_command(struct wimlib_extract_command *cmd, int wim_header_flags)
885 bool is_entire_image = (cmd->wim_source_path[0] == T('\0'));
887 /* Empty destination path? */
888 if (cmd->fs_dest_path[0] == T('\0'))
889 return WIMLIB_ERR_INVALID_PARAM;
891 extract_flags = cmd->extract_flags;
893 /* Specified both symlink and hardlink modes? */
895 (WIMLIB_EXTRACT_FLAG_SYMLINK |
896 WIMLIB_EXTRACT_FLAG_HARDLINK)) == (WIMLIB_EXTRACT_FLAG_SYMLINK |
897 WIMLIB_EXTRACT_FLAG_HARDLINK))
898 return WIMLIB_ERR_INVALID_PARAM;
901 /* Wanted UNIX data on Windows? */
902 if (extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) {
903 ERROR("Extracting UNIX data is not supported on Windows");
904 return WIMLIB_ERR_INVALID_PARAM;
906 /* Wanted linked extraction on Windows? (XXX This is possible, just not
907 * implemented yet.) */
908 if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
909 WIMLIB_EXTRACT_FLAG_HARDLINK))
911 ERROR("Linked extraction modes are not supported on Windows");
912 return WIMLIB_ERR_INVALID_PARAM;
916 if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
917 /* NTFS-3g extraction mode requested */
919 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
920 WIMLIB_EXTRACT_FLAG_HARDLINK))) {
921 ERROR("Cannot specify symlink or hardlink flags when applying\n"
922 " directly to a NTFS volume");
923 return WIMLIB_ERR_INVALID_PARAM;
925 if (!is_entire_image &&
926 (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS))
928 ERROR("When applying directly to a NTFS volume you can "
929 "only extract a full image, not part of one");
930 return WIMLIB_ERR_INVALID_PARAM;
932 if (extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) {
933 ERROR("Cannot restore UNIX-specific data in "
934 "the NTFS extraction mode");
935 return WIMLIB_ERR_INVALID_PARAM;
938 ERROR("wimlib was compiled without support for NTFS-3g, so");
939 ERROR("we cannot apply a WIM image directly to a NTFS volume");
940 return WIMLIB_ERR_UNSUPPORTED;
944 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_RPFIX |
945 WIMLIB_EXTRACT_FLAG_NORPFIX)) ==
946 (WIMLIB_EXTRACT_FLAG_RPFIX | WIMLIB_EXTRACT_FLAG_NORPFIX))
948 ERROR("Cannot specify RPFIX and NORPFIX flags at the same time!");
949 return WIMLIB_ERR_INVALID_PARAM;
952 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_RPFIX |
953 WIMLIB_EXTRACT_FLAG_NORPFIX)) == 0)
955 /* Do reparse point fixups by default if the WIM header says
956 * they are enabled and we are extracting a full image. */
957 if ((wim_header_flags & WIM_HDR_FLAG_RP_FIX) && is_entire_image)
958 extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
961 if (!is_entire_image && (extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX)) {
962 ERROR("Cannot specify --rpfix when not extracting entire image");
963 return WIMLIB_ERR_INVALID_PARAM;
966 cmd->extract_flags = extract_flags;
971 /* Internal function to execute extraction commands for a WIM image. */
973 do_wimlib_extract_files(WIMStruct *wim,
975 struct wimlib_extract_command *cmds,
977 wimlib_progress_func_t progress_func)
980 bool found_link_cmd = false;
981 bool found_nolink_cmd = false;
983 /* Select the image from which we are extracting files */
984 ret = select_wim_image(wim, image);
988 /* Make sure there are no streams in the WIM that have not been
989 * checksummed yet. */
990 ret = wim_checksum_unhashed_streams(wim);
994 /* Check for problems with the extraction commands */
995 for (size_t i = 0; i < num_cmds; i++) {
996 ret = check_extract_command(&cmds[i], wim->hdr.flags);
999 if (cmds[i].extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
1000 WIMLIB_EXTRACT_FLAG_HARDLINK)) {
1001 found_link_cmd = true;
1003 found_nolink_cmd = true;
1005 if (found_link_cmd && found_nolink_cmd) {
1006 ERROR("Symlink or hardlink extraction mode must "
1007 "be set on all extraction commands");
1008 return WIMLIB_ERR_INVALID_PARAM;
1012 /* Execute the extraction commands */
1013 for (size_t i = 0; i < num_cmds; i++) {
1014 ret = extract_tree(wim,
1015 cmds[i].wim_source_path,
1016 cmds[i].fs_dest_path,
1017 cmds[i].extract_flags,
1025 /* Extract files or directories from a WIM image. */
1027 wimlib_extract_files(WIMStruct *wim,
1029 const struct wimlib_extract_command *cmds,
1031 int default_extract_flags,
1032 WIMStruct **additional_swms,
1033 unsigned num_additional_swms,
1034 wimlib_progress_func_t progress_func)
1037 struct wimlib_extract_command *cmds_copy;
1040 default_extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC;
1042 ret = verify_swm_set(wim, additional_swms, num_additional_swms);
1049 if (num_additional_swms)
1050 merge_lookup_tables(wim, additional_swms, num_additional_swms);
1052 cmds_copy = CALLOC(num_cmds, sizeof(cmds[0]));
1054 ret = WIMLIB_ERR_NOMEM;
1055 goto out_restore_lookup_table;
1058 for (size_t i = 0; i < num_cmds; i++) {
1059 cmds_copy[i].extract_flags = (default_extract_flags |
1060 cmds[i].extract_flags)
1061 & WIMLIB_EXTRACT_MASK_PUBLIC;
1062 all_flags |= cmds_copy[i].extract_flags;
1064 cmds_copy[i].wim_source_path = canonicalize_wim_path(cmds[i].wim_source_path);
1065 if (!cmds_copy[i].wim_source_path) {
1066 ret = WIMLIB_ERR_NOMEM;
1067 goto out_free_cmds_copy;
1070 cmds_copy[i].fs_dest_path = canonicalize_fs_path(cmds[i].fs_dest_path);
1071 if (!cmds_copy[i].fs_dest_path) {
1072 ret = WIMLIB_ERR_NOMEM;
1073 goto out_free_cmds_copy;
1077 ret = do_wimlib_extract_files(wim, image,
1078 cmds_copy, num_cmds,
1081 if (all_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
1082 WIMLIB_EXTRACT_FLAG_HARDLINK))
1084 for_lookup_table_entry(wim->lookup_table,
1085 lte_free_extracted_file, NULL);
1088 for (size_t i = 0; i < num_cmds; i++) {
1089 FREE(cmds_copy[i].wim_source_path);
1090 FREE(cmds_copy[i].fs_dest_path);
1093 out_restore_lookup_table:
1094 if (num_additional_swms)
1095 unmerge_lookup_table(wim);
1101 * Extracts an image from a WIM file.
1103 * @wim: WIMStruct for the WIM file.
1105 * @image: Number of the single image to extract.
1107 * @target: Directory or NTFS volume to extract the image to.
1109 * @extract_flags: Bitwise or of WIMLIB_EXTRACT_FLAG_*.
1111 * @progress_func: If non-NULL, a progress function to be called
1114 * Returns 0 on success; nonzero on failure.
1117 extract_single_image(WIMStruct *wim, int image,
1118 const tchar *target, int extract_flags,
1119 wimlib_progress_func_t progress_func)
1122 tchar *target_copy = canonicalize_fs_path(target);
1124 return WIMLIB_ERR_NOMEM;
1125 struct wimlib_extract_command cmd = {
1126 .wim_source_path = T(""),
1127 .fs_dest_path = target_copy,
1128 .extract_flags = extract_flags,
1130 ret = do_wimlib_extract_files(wim, image, &cmd, 1, progress_func);
1135 static const tchar * const filename_forbidden_chars =
1144 /* This function checks if it is okay to use a WIM image's name as a directory
1147 image_name_ok_as_dir(const tchar *image_name)
1149 return image_name && *image_name &&
1150 !tstrpbrk(image_name, filename_forbidden_chars) &&
1151 tstrcmp(image_name, T(".")) &&
1152 tstrcmp(image_name, T(".."));
1155 /* Extracts all images from the WIM to the directory @target, with the images
1156 * placed in subdirectories named by their image names. */
1158 extract_all_images(WIMStruct *wim,
1159 const tchar *target,
1161 wimlib_progress_func_t progress_func)
1163 size_t image_name_max_len = max(xml_get_max_image_name_len(wim), 20);
1164 size_t output_path_len = tstrlen(target);
1165 tchar buf[output_path_len + 1 + image_name_max_len + 1];
1168 const tchar *image_name;
1171 if (tstat(target, &stbuf)) {
1172 if (errno == ENOENT)
1174 if (tmkdir(target, S_IRWXU | S_IRGRP | S_IXGRP |
1177 ERROR_WITH_ERRNO("Failed to create directory \"%"TS"\"", target);
1178 return WIMLIB_ERR_MKDIR;
1181 ERROR_WITH_ERRNO("Failed to stat \"%"TS"\"", target);
1182 return WIMLIB_ERR_STAT;
1184 } else if (!S_ISDIR(stbuf.st_mode)) {
1185 ERROR("\"%"TS"\" is not a directory", target);
1186 return WIMLIB_ERR_NOTDIR;
1189 tmemcpy(buf, target, output_path_len);
1190 buf[output_path_len] = OS_PREFERRED_PATH_SEPARATOR;
1191 for (image = 1; image <= wim->hdr.image_count; image++) {
1192 image_name = wimlib_get_image_name(wim, image);
1193 if (image_name_ok_as_dir(image_name)) {
1194 tstrcpy(buf + output_path_len + 1, image_name);
1196 /* Image name is empty or contains forbidden characters.
1197 * Use image number instead. */
1198 tsprintf(buf + output_path_len + 1, T("%d"), image);
1200 ret = extract_single_image(wim, image, buf, extract_flags,
1208 /* Extracts a single image or all images from a WIM file to a directory or NTFS
1211 wimlib_extract_image(WIMStruct *wim,
1213 const tchar *target,
1215 WIMStruct **additional_swms,
1216 unsigned num_additional_swms,
1217 wimlib_progress_func_t progress_func)
1221 extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC;
1223 ret = verify_swm_set(wim, additional_swms, num_additional_swms);
1227 if (num_additional_swms)
1228 merge_lookup_tables(wim, additional_swms, num_additional_swms);
1230 if (image == WIMLIB_ALL_IMAGES) {
1231 ret = extract_all_images(wim, target,
1232 extract_flags | WIMLIB_EXTRACT_FLAG_MULTI_IMAGE,
1235 ret = extract_single_image(wim, image, target, extract_flags,
1239 if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
1240 WIMLIB_EXTRACT_FLAG_HARDLINK))
1242 for_lookup_table_entry(wim->lookup_table,
1243 lte_free_extracted_file,
1246 if (num_additional_swms)
1247 unmerge_lookup_table(wim);