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;
298 inode_resolve_ltes(inode, lookup_table);
299 for (unsigned i = 0; i <= inode->i_num_ads; i++) {
300 lte = inode_stream_lte_resolved(inode, i);
308 find_streams_for_extraction(struct wim_dentry *root,
309 struct list_head *stream_list,
310 struct wim_lookup_table *lookup_table,
313 struct find_streams_ctx ctx;
315 INIT_LIST_HEAD(&ctx.stream_list);
316 ctx.extract_flags = extract_flags;
317 for_dentry_in_tree(root, dentry_resolve_and_zero_lte_refcnt, lookup_table);
318 for_dentry_in_tree(root, dentry_find_streams_to_extract, &ctx);
319 list_transfer(&ctx.stream_list, stream_list);
322 struct apply_operations {
323 int (*apply_dentry)(struct wim_dentry *dentry, void *arg);
324 int (*apply_dentry_timestamps)(struct wim_dentry *dentry, void *arg);
327 static const struct apply_operations normal_apply_operations = {
328 .apply_dentry = apply_dentry_normal,
329 .apply_dentry_timestamps = apply_dentry_timestamps_normal,
333 static const struct apply_operations ntfs_apply_operations = {
334 .apply_dentry = apply_dentry_ntfs,
335 .apply_dentry_timestamps = apply_dentry_timestamps_ntfs,
340 apply_stream_list(struct list_head *stream_list,
341 struct apply_args *args,
342 const struct apply_operations *ops,
343 wimlib_progress_func_t progress_func)
345 uint64_t bytes_per_progress = args->progress.extract.total_bytes / 100;
346 uint64_t next_progress = bytes_per_progress;
347 struct wim_lookup_table_entry *lte;
348 struct wim_dentry *dentry;
351 /* This complicated loop is essentially looping through the dentries,
352 * although dentries may be visited more than once (if a dentry contains
353 * two different nonempty streams) or not at all (if a dentry contains
354 * no non-empty streams).
356 * The outer loop is over the distinct streams to be extracted so that
357 * sequential reading of the WIM can be implemented. */
359 /* For each distinct stream to be extracted */
360 list_for_each_entry(lte, stream_list, extraction_list) {
361 /* For each dentry to be extracted that is a name for an inode
362 * containing the stream */
363 list_for_each_entry(dentry, <e->lte_dentry_list, extraction_stream_list) {
364 /* Extract the dentry if it was not already
366 ret = maybe_apply_dentry(dentry, args);
370 args->progress.extract.completed_bytes >= next_progress)
372 progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS,
374 if (args->progress.extract.completed_bytes >=
375 args->progress.extract.total_bytes)
377 next_progress = ~0ULL;
380 min (args->progress.extract.completed_bytes +
382 args->progress.extract.total_bytes);
391 sort_stream_list_by_wim_position(struct list_head *stream_list)
393 struct list_head *cur;
395 struct wim_lookup_table_entry **array;
400 list_for_each(cur, stream_list)
402 array_size = num_streams * sizeof(array[0]);
403 array = MALLOC(array_size);
405 ERROR("Failed to allocate %zu bytes to sort stream entries",
407 return WIMLIB_ERR_NOMEM;
409 cur = stream_list->next;
410 for (i = 0; i < num_streams; i++) {
411 array[i] = container_of(cur, struct wim_lookup_table_entry, extraction_list);
415 qsort(array, num_streams, sizeof(array[0]), cmp_streams_by_wim_position);
417 INIT_LIST_HEAD(stream_list);
418 for (i = 0; i < num_streams; i++)
419 list_add_tail(&array[i]->extraction_list, stream_list);
425 * Extract a dentry to standard output.
427 * This obviously doesn't make sense in all cases. We return an error if the
428 * dentry does not correspond to a regular file. Otherwise we extract the
429 * unnamed data stream only.
432 extract_dentry_to_stdout(struct wim_dentry *dentry)
435 if (!dentry_is_regular_file(dentry)) {
436 ERROR("\"%"TS"\" is not a regular file and therefore cannot be "
437 "extracted to standard output", dentry->_full_path);
438 ret = WIMLIB_ERR_NOT_A_REGULAR_FILE;
440 struct wim_lookup_table_entry *lte;
442 lte = inode_unnamed_lte_resolved(dentry->d_inode);
444 ret = extract_wim_resource_to_fd(lte, STDOUT_FILENO,
445 wim_resource_size(lte));
452 static const utf16lechar replacement_char = cpu_to_le16(0xfffd);
454 static const utf16lechar replacement_char = cpu_to_le16('?');
458 file_name_valid(utf16lechar *name, size_t num_chars, bool fix)
464 for (i = 0; i < num_chars; i++) {
467 case cpu_to_le16('\\'):
468 case cpu_to_le16(':'):
469 case cpu_to_le16('*'):
470 case cpu_to_le16('?'):
471 case cpu_to_le16('"'):
472 case cpu_to_le16('<'):
473 case cpu_to_le16('>'):
474 case cpu_to_le16('|'):
476 case cpu_to_le16('/'):
477 case cpu_to_le16('\0'):
479 name[i] = replacement_char;
486 if (name[num_chars - 1] == cpu_to_le16(' ') ||
487 name[num_chars - 1] == cpu_to_le16('.'))
490 name[num_chars - 1] = replacement_char;
499 * dentry_calculate_extraction_path-
501 * Calculate the actual filename component at which a WIM dentry will be
502 * extracted, handling invalid filenames "properly".
504 * dentry->extraction_name usually will be set the same as dentry->file_name (on
505 * UNIX, converted into the platform's multibyte encoding). However, if the
506 * file name contains characters that are not valid on the current platform or
507 * has some other format that is not valid, leave dentry->extraction_name as
508 * NULL and clear dentry->needs_extraction to indicate that this dentry should
509 * not be extracted, unless the appropriate flag
510 * WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES is set in the extract flags, in
511 * which case a substitute filename will be created and set instead.
513 * Conflicts with case-insensitive names on Windows are handled similarly; see
517 dentry_calculate_extraction_path(struct wim_dentry *dentry, void *_args)
519 struct apply_args *args = _args;
522 dentry->needs_extraction = 1;
524 if (dentry == args->extract_root)
527 if (dentry_is_dot_or_dotdot(dentry)) {
528 /* WIM files shouldn't contain . or .. entries. But if they are
529 * there, don't attempt to extract them. */
530 WARNING("Skipping extraction of unexpected . or .. file \"%"TS"\"",
531 dentry_full_path(dentry));
536 struct wim_dentry *other;
537 list_for_each_entry(other, &dentry->case_insensitive_conflict_list,
538 case_insensitive_conflict_list)
540 if (other->needs_extraction) {
541 if (args->extract_flags & WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS)
543 WARNING("\"%"TS"\" has the same case-insensitive "
544 "name as \"%"TS"\"; extracting dummy name instead",
545 dentry_full_path(dentry),
546 dentry_full_path(other));
549 WARNING("Not extracting \"%"TS"\": has same case-insensitive "
551 dentry_full_path(dentry),
552 dentry_full_path(other));
559 if (file_name_valid(dentry->file_name, dentry->file_name_nbytes / 2, false)) {
561 dentry->extraction_name = dentry->file_name;
562 dentry->extraction_name_nchars = dentry->file_name_nbytes / 2;
565 return utf16le_to_tstr(dentry->file_name,
566 dentry->file_name_nbytes,
567 &dentry->extraction_name,
568 &dentry->extraction_name_nchars);
571 if (args->extract_flags & WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES)
573 WARNING("\"%"TS"\" has an invalid filename "
574 "that is not supported on this platform; "
575 "extracting dummy name instead",
576 dentry_full_path(dentry));
579 WARNING("Not extracting \"%"TS"\": has an invalid filename "
580 "that is not supported on this platform",
581 dentry_full_path(dentry));
588 utf16lechar utf16_name_copy[dentry->file_name_nbytes / 2];
590 memcpy(utf16_name_copy, dentry->file_name, dentry->file_name_nbytes);
591 file_name_valid(utf16_name_copy, dentry->file_name_nbytes / 2, true);
596 tchar_name = utf16_name_copy;
597 tchar_nchars = dentry->file_name_nbytes / 2;
599 ret = utf16le_to_tstr(utf16_name_copy,
600 dentry->file_name_nbytes,
601 &tchar_name, &tchar_nchars);
605 size_t fixed_name_num_chars = tchar_nchars;
606 tchar fixed_name[tchar_nchars + 50];
608 tmemcpy(fixed_name, tchar_name, tchar_nchars);
609 fixed_name_num_chars += tsprintf(fixed_name + tchar_nchars,
610 T(" (invalid filename #%lu)"),
611 ++args->invalid_sequence);
615 dentry->extraction_name = memdup(fixed_name, 2 * fixed_name_num_chars + 2);
616 if (!dentry->extraction_name)
617 return WIMLIB_ERR_NOMEM;
618 dentry->extraction_name_nchars = fixed_name_num_chars;
622 dentry->needs_extraction = 0;
623 dentry->not_extracted = 1;
628 dentry_reset_needs_extraction(struct wim_dentry *dentry, void *_ignore)
630 dentry->needs_extraction = 0;
631 dentry->not_extracted = 0;
632 dentry->is_win32_name = 0;
633 dentry->d_inode->i_visited = 0;
634 dentry->d_inode->i_dos_name_extracted = 0;
635 FREE(dentry->d_inode->i_extracted_file);
636 dentry->d_inode->i_extracted_file = NULL;
637 if ((void*)dentry->extraction_name != (void*)dentry->file_name)
638 FREE(dentry->extraction_name);
639 dentry->extraction_name = NULL;
643 #define WINDOWS_NT_MAX_PATH 32768
646 * extract_tree - Extract a file or directory tree from the currently selected
649 * @wim: WIMStruct for the WIM file, with the desired image selected
650 * (as wim->current_image).
652 * "Canonical" (i.e. no leading or trailing slashes, path
653 * separators forwald slashes) path inside the WIM image to
654 * extract. An empty string means the full image.
656 * Filesystem path to extract the file or directory tree to.
659 * WIMLIB_EXTRACT_FLAG_*. Also, the private flag
660 * WIMLIB_EXTRACT_FLAG_MULTI_IMAGE will be set if this is being
661 * called through wimlib_extract_image() with WIMLIB_ALL_IMAGES as
665 * If non-NULL, progress function for the extraction. The messages
666 * we may in this function are:
668 * WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN or
669 * WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN;
670 * WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN;
671 * WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END;
672 * WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY;
673 * WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS;
674 * WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS;
675 * WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END or
676 * WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END.
678 * Returns 0 on success; nonzero on failure.
681 extract_tree(WIMStruct *wim, const tchar *wim_source_path, const tchar *target,
682 int extract_flags, wimlib_progress_func_t progress_func)
685 struct list_head stream_list;
686 struct apply_args args;
687 const struct apply_operations *ops;
688 struct wim_dentry *root;
690 memset(&args, 0, sizeof(args));
694 args.target = target;
695 args.target_nchars = tstrlen(target);
696 args.extract_flags = extract_flags;
697 args.progress_func = progress_func;
700 /* Work around defective behavior in Windows where paths longer than 260
701 * characters are not supported by default; instead they need to be
702 * turned into absolute paths and prefixed with "\\?\". */
703 args.target_lowlevel_path = MALLOC(WINDOWS_NT_MAX_PATH * sizeof(wchar_t));
704 if (!args.target_lowlevel_path)
706 ret = WIMLIB_ERR_NOMEM;
709 args.target_lowlevel_path_nchars =
710 GetFullPathName(args.target, WINDOWS_NT_MAX_PATH - 4,
711 &args.target_lowlevel_path[4], NULL);
713 if (args.target_lowlevel_path_nchars == 0 ||
714 args.target_lowlevel_path_nchars >= WINDOWS_NT_MAX_PATH - 4)
716 WARNING("Can't get full path name for \"%ls\"", args.target);
717 FREE(args.target_lowlevel_path);
718 args.target_lowlevel_path = NULL;
720 wmemcpy(args.target_lowlevel_path, L"\\\\?\\", 4);
721 args.target_lowlevel_path_nchars += 4;
726 args.progress.extract.wimfile_name = wim->filename;
727 args.progress.extract.image = wim->current_image;
728 args.progress.extract.extract_flags = (extract_flags &
729 WIMLIB_EXTRACT_MASK_PUBLIC);
730 args.progress.extract.image_name = wimlib_get_image_name(wim,
732 args.progress.extract.extract_root_wim_source_path = wim_source_path;
733 args.progress.extract.target = target;
737 if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
738 args.vol = ntfs_mount(target, 0);
740 ERROR_WITH_ERRNO("Failed to mount NTFS volume `%"TS"'",
742 ret = WIMLIB_ERR_NTFS_3G;
743 goto out_free_target_lowlevel_path;
745 ops = &ntfs_apply_operations;
748 ops = &normal_apply_operations;
750 root = get_dentry(wim, wim_source_path);
752 ERROR("Path \"%"TS"\" does not exist in WIM image %d",
753 wim_source_path, wim->current_image);
754 ret = WIMLIB_ERR_PATH_DOES_NOT_EXIST;
755 goto out_ntfs_umount;
757 args.extract_root = root;
759 /* Calculate the actual filename component of each extracted dentry, and
760 * in the process set the dentry->needs_extraction flag on dentries that
761 * will be extracted. */
762 ret = for_dentry_in_tree(root, dentry_calculate_extraction_path, &args);
764 goto out_dentry_reset_needs_extraction;
766 /* Build a list of the streams that need to be extracted */
767 find_streams_for_extraction(root,
769 wim->lookup_table, extract_flags);
771 /* Calculate the number of bytes of data that will be extracted */
772 calculate_bytes_to_extract(&stream_list, extract_flags,
775 if (extract_flags & WIMLIB_EXTRACT_FLAG_TO_STDOUT) {
776 ret = extract_dentry_to_stdout(root);
777 goto out_dentry_reset_needs_extraction;
781 progress_func(*wim_source_path ? WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN :
782 WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN,
786 /* If a sequential extraction was specified, sort the streams to be
787 * extracted by their position in the WIM file, so that the WIM file can
788 * be read sequentially. */
789 if (extract_flags & WIMLIB_EXTRACT_FLAG_SEQUENTIAL) {
790 ret = sort_stream_list_by_wim_position(&stream_list);
792 WARNING("Falling back to non-sequential extraction");
793 extract_flags &= ~WIMLIB_EXTRACT_FLAG_SEQUENTIAL;
798 progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN,
802 /* Make the directory structure and extract empty files */
803 args.extract_flags |= WIMLIB_EXTRACT_FLAG_NO_STREAMS;
804 args.apply_dentry = ops->apply_dentry;
805 ret = for_dentry_in_tree(root, maybe_apply_dentry, &args);
806 args.extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_STREAMS;
808 goto out_dentry_reset_needs_extraction;
811 progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END,
815 if (extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX) {
816 args.target_realpath = realpath(target, NULL);
817 if (!args.target_realpath) {
818 ret = WIMLIB_ERR_NOMEM;
819 goto out_dentry_reset_needs_extraction;
821 args.target_realpath_len = tstrlen(args.target_realpath);
824 /* Extract non-empty files */
825 ret = apply_stream_list(&stream_list, &args, ops, progress_func);
827 goto out_free_target_realpath;
830 progress_func(WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS,
834 /* Apply timestamps */
835 ret = for_dentry_in_tree_depth(root,
836 ops->apply_dentry_timestamps, &args);
838 goto out_free_target_realpath;
841 progress_func(*wim_source_path ? WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END :
842 WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END,
845 out_free_target_realpath:
846 FREE(args.target_realpath);
847 out_dentry_reset_needs_extraction:
848 for_dentry_in_tree(root, dentry_reset_needs_extraction, NULL);
851 /* Unmount the NTFS volume */
852 if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
853 if (ntfs_umount(args.vol, FALSE) != 0) {
854 ERROR_WITH_ERRNO("Failed to unmount NTFS volume `%"TS"'",
857 ret = WIMLIB_ERR_NTFS_3G;
861 out_free_target_lowlevel_path:
863 FREE(args.target_lowlevel_path);
869 /* Validates a single wimlib_extract_command, mostly checking to make sure the
870 * extract flags make sense. */
872 check_extract_command(struct wimlib_extract_command *cmd, int wim_header_flags)
875 bool is_entire_image = (cmd->wim_source_path[0] == T('\0'));
877 /* Empty destination path? */
878 if (cmd->fs_dest_path[0] == T('\0'))
879 return WIMLIB_ERR_INVALID_PARAM;
881 extract_flags = cmd->extract_flags;
883 /* Specified both symlink and hardlink modes? */
885 (WIMLIB_EXTRACT_FLAG_SYMLINK |
886 WIMLIB_EXTRACT_FLAG_HARDLINK)) == (WIMLIB_EXTRACT_FLAG_SYMLINK |
887 WIMLIB_EXTRACT_FLAG_HARDLINK))
888 return WIMLIB_ERR_INVALID_PARAM;
891 /* Wanted UNIX data on Windows? */
892 if (extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) {
893 ERROR("Extracting UNIX data is not supported on Windows");
894 return WIMLIB_ERR_INVALID_PARAM;
896 /* Wanted linked extraction on Windows? (XXX This is possible, just not
897 * implemented yet.) */
898 if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
899 WIMLIB_EXTRACT_FLAG_HARDLINK))
901 ERROR("Linked extraction modes are not supported on Windows");
902 return WIMLIB_ERR_INVALID_PARAM;
906 if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
907 /* NTFS-3g extraction mode requested */
909 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
910 WIMLIB_EXTRACT_FLAG_HARDLINK))) {
911 ERROR("Cannot specify symlink or hardlink flags when applying\n"
912 " directly to a NTFS volume");
913 return WIMLIB_ERR_INVALID_PARAM;
915 if (!is_entire_image &&
916 (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS))
918 ERROR("When applying directly to a NTFS volume you can "
919 "only extract a full image, not part of one");
920 return WIMLIB_ERR_INVALID_PARAM;
922 if (extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) {
923 ERROR("Cannot restore UNIX-specific data in "
924 "the NTFS extraction mode");
925 return WIMLIB_ERR_INVALID_PARAM;
928 ERROR("wimlib was compiled without support for NTFS-3g, so");
929 ERROR("we cannot apply a WIM image directly to a NTFS volume");
930 return WIMLIB_ERR_UNSUPPORTED;
934 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_RPFIX |
935 WIMLIB_EXTRACT_FLAG_NORPFIX)) ==
936 (WIMLIB_EXTRACT_FLAG_RPFIX | WIMLIB_EXTRACT_FLAG_NORPFIX))
938 ERROR("Cannot specify RPFIX and NORPFIX flags at the same time!");
939 return WIMLIB_ERR_INVALID_PARAM;
942 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_RPFIX |
943 WIMLIB_EXTRACT_FLAG_NORPFIX)) == 0)
945 /* Do reparse point fixups by default if the WIM header says
946 * they are enabled and we are extracting a full image. */
947 if ((wim_header_flags & WIM_HDR_FLAG_RP_FIX) && is_entire_image)
948 extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
951 if (!is_entire_image && (extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX)) {
952 ERROR("Cannot specify --rpfix when not extracting entire image");
953 return WIMLIB_ERR_INVALID_PARAM;
956 cmd->extract_flags = extract_flags;
961 /* Internal function to execute extraction commands for a WIM image. */
963 do_wimlib_extract_files(WIMStruct *wim,
965 struct wimlib_extract_command *cmds,
967 wimlib_progress_func_t progress_func)
970 bool found_link_cmd = false;
971 bool found_nolink_cmd = false;
973 /* Select the image from which we are extracting files */
974 ret = select_wim_image(wim, image);
978 /* Make sure there are no streams in the WIM that have not been
979 * checksummed yet. */
980 ret = wim_checksum_unhashed_streams(wim);
984 /* Check for problems with the extraction commands */
985 for (size_t i = 0; i < num_cmds; i++) {
986 ret = check_extract_command(&cmds[i], wim->hdr.flags);
989 if (cmds[i].extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
990 WIMLIB_EXTRACT_FLAG_HARDLINK)) {
991 found_link_cmd = true;
993 found_nolink_cmd = true;
995 if (found_link_cmd && found_nolink_cmd) {
996 ERROR("Symlink or hardlink extraction mode must "
997 "be set on all extraction commands");
998 return WIMLIB_ERR_INVALID_PARAM;
1002 /* Execute the extraction commands */
1003 for (size_t i = 0; i < num_cmds; i++) {
1004 ret = extract_tree(wim,
1005 cmds[i].wim_source_path,
1006 cmds[i].fs_dest_path,
1007 cmds[i].extract_flags,
1015 /* Extract files or directories from a WIM image. */
1017 wimlib_extract_files(WIMStruct *wim,
1019 const struct wimlib_extract_command *cmds,
1021 int default_extract_flags,
1022 WIMStruct **additional_swms,
1023 unsigned num_additional_swms,
1024 wimlib_progress_func_t progress_func)
1027 struct wimlib_extract_command *cmds_copy;
1030 default_extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC;
1032 ret = verify_swm_set(wim, additional_swms, num_additional_swms);
1039 if (num_additional_swms)
1040 merge_lookup_tables(wim, additional_swms, num_additional_swms);
1042 cmds_copy = CALLOC(num_cmds, sizeof(cmds[0]));
1044 ret = WIMLIB_ERR_NOMEM;
1045 goto out_restore_lookup_table;
1048 for (size_t i = 0; i < num_cmds; i++) {
1049 cmds_copy[i].extract_flags = (default_extract_flags |
1050 cmds[i].extract_flags)
1051 & WIMLIB_EXTRACT_MASK_PUBLIC;
1052 all_flags |= cmds_copy[i].extract_flags;
1054 cmds_copy[i].wim_source_path = canonicalize_wim_path(cmds[i].wim_source_path);
1055 if (!cmds_copy[i].wim_source_path) {
1056 ret = WIMLIB_ERR_NOMEM;
1057 goto out_free_cmds_copy;
1060 cmds_copy[i].fs_dest_path = canonicalize_fs_path(cmds[i].fs_dest_path);
1061 if (!cmds_copy[i].fs_dest_path) {
1062 ret = WIMLIB_ERR_NOMEM;
1063 goto out_free_cmds_copy;
1067 ret = do_wimlib_extract_files(wim, image,
1068 cmds_copy, num_cmds,
1071 if (all_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
1072 WIMLIB_EXTRACT_FLAG_HARDLINK))
1074 for_lookup_table_entry(wim->lookup_table,
1075 lte_free_extracted_file, NULL);
1078 for (size_t i = 0; i < num_cmds; i++) {
1079 FREE(cmds_copy[i].wim_source_path);
1080 FREE(cmds_copy[i].fs_dest_path);
1083 out_restore_lookup_table:
1084 if (num_additional_swms)
1085 unmerge_lookup_table(wim);
1091 * Extracts an image from a WIM file.
1093 * @wim: WIMStruct for the WIM file.
1095 * @image: Number of the single image to extract.
1097 * @target: Directory or NTFS volume to extract the image to.
1099 * @extract_flags: Bitwise or of WIMLIB_EXTRACT_FLAG_*.
1101 * @progress_func: If non-NULL, a progress function to be called
1104 * Returns 0 on success; nonzero on failure.
1107 extract_single_image(WIMStruct *wim, int image,
1108 const tchar *target, int extract_flags,
1109 wimlib_progress_func_t progress_func)
1112 tchar *target_copy = canonicalize_fs_path(target);
1114 return WIMLIB_ERR_NOMEM;
1115 struct wimlib_extract_command cmd = {
1116 .wim_source_path = T(""),
1117 .fs_dest_path = target_copy,
1118 .extract_flags = extract_flags,
1120 ret = do_wimlib_extract_files(wim, image, &cmd, 1, progress_func);
1125 static const tchar * const filename_forbidden_chars =
1134 /* This function checks if it is okay to use a WIM image's name as a directory
1137 image_name_ok_as_dir(const tchar *image_name)
1139 return image_name && *image_name &&
1140 !tstrpbrk(image_name, filename_forbidden_chars) &&
1141 tstrcmp(image_name, T(".")) &&
1142 tstrcmp(image_name, T(".."));
1145 /* Extracts all images from the WIM to the directory @target, with the images
1146 * placed in subdirectories named by their image names. */
1148 extract_all_images(WIMStruct *wim,
1149 const tchar *target,
1151 wimlib_progress_func_t progress_func)
1153 size_t image_name_max_len = max(xml_get_max_image_name_len(wim), 20);
1154 size_t output_path_len = tstrlen(target);
1155 tchar buf[output_path_len + 1 + image_name_max_len + 1];
1158 const tchar *image_name;
1161 if (tstat(target, &stbuf)) {
1162 if (errno == ENOENT)
1164 if (tmkdir(target, S_IRWXU | S_IRGRP | S_IXGRP |
1167 ERROR_WITH_ERRNO("Failed to create directory \"%"TS"\"", target);
1168 return WIMLIB_ERR_MKDIR;
1171 ERROR_WITH_ERRNO("Failed to stat \"%"TS"\"", target);
1172 return WIMLIB_ERR_STAT;
1174 } else if (!S_ISDIR(stbuf.st_mode)) {
1175 ERROR("\"%"TS"\" is not a directory", target);
1176 return WIMLIB_ERR_NOTDIR;
1179 tmemcpy(buf, target, output_path_len);
1180 buf[output_path_len] = OS_PREFERRED_PATH_SEPARATOR;
1181 for (image = 1; image <= wim->hdr.image_count; image++) {
1182 image_name = wimlib_get_image_name(wim, image);
1183 if (image_name_ok_as_dir(image_name)) {
1184 tstrcpy(buf + output_path_len + 1, image_name);
1186 /* Image name is empty or contains forbidden characters.
1187 * Use image number instead. */
1188 tsprintf(buf + output_path_len + 1, T("%d"), image);
1190 ret = extract_single_image(wim, image, buf, extract_flags,
1198 /* Extracts a single image or all images from a WIM file to a directory or NTFS
1201 wimlib_extract_image(WIMStruct *wim,
1203 const tchar *target,
1205 WIMStruct **additional_swms,
1206 unsigned num_additional_swms,
1207 wimlib_progress_func_t progress_func)
1211 extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC;
1213 ret = verify_swm_set(wim, additional_swms, num_additional_swms);
1217 if (num_additional_swms)
1218 merge_lookup_tables(wim, additional_swms, num_additional_swms);
1220 if (image == WIMLIB_ALL_IMAGES) {
1221 ret = extract_all_images(wim, target,
1222 extract_flags | WIMLIB_EXTRACT_FLAG_MULTI_IMAGE,
1225 ret = extract_single_image(wim, image, target, extract_flags,
1229 if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
1230 WIMLIB_EXTRACT_FLAG_HARDLINK))
1232 for_lookup_table_entry(wim->lookup_table,
1233 lte_free_extracted_file,
1236 if (num_additional_swms)
1237 unmerge_lookup_table(wim);