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_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_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_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;
644 * extract_tree - Extract a file or directory tree from the currently selected
647 * @wim: WIMStruct for the WIM file, with the desired image selected
648 * (as wim->current_image).
650 * "Canonical" (i.e. no leading or trailing slashes, path
651 * separators forwald slashes) path inside the WIM image to
652 * extract. An empty string means the full image.
654 * Filesystem path to extract the file or directory tree to.
657 * WIMLIB_EXTRACT_FLAG_*. Also, the private flag
658 * WIMLIB_EXTRACT_FLAG_MULTI_IMAGE will be set if this is being
659 * called through wimlib_extract_image() with WIMLIB_ALL_IMAGES as
663 * If non-NULL, progress function for the extraction. The messages
664 * we may in this function are:
666 * WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN or
667 * WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN;
668 * WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN;
669 * WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END;
670 * WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY;
671 * WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS;
672 * WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS;
673 * WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END or
674 * WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END.
676 * Returns 0 on success; nonzero on failure.
679 extract_tree(WIMStruct *wim, const tchar *wim_source_path, const tchar *target,
680 int extract_flags, wimlib_progress_func_t progress_func)
683 struct list_head stream_list;
684 struct apply_args args;
685 const struct apply_operations *ops;
686 struct wim_dentry *root;
688 memset(&args, 0, sizeof(args));
692 args.target = target;
693 args.target_nchars = tstrlen(target);
694 args.extract_flags = extract_flags;
695 args.progress_func = progress_func;
698 /* Work around defective behavior in Windows where paths longer than 260
699 * characters are not supported by default; instead they need to be
700 * turned into absolute paths and prefixed with "\\?\". */
701 args.target_lowlevel_path = MALLOC(32768 * sizeof(wchar_t));
702 if (!args.target_lowlevel_path)
704 ret = WIMLIB_ERR_NOMEM;
707 args.target_lowlevel_path[0] = L'\\';
708 args.target_lowlevel_path[1] = L'\\';
709 args.target_lowlevel_path[2] = L'?';
710 args.target_lowlevel_path[3] = L'\\';
711 args.target_lowlevel_path_nchars =
712 GetFullPathName(args.target, 32768 - 4,
713 &args.target_lowlevel_path[4], NULL);
715 if (args.target_lowlevel_path_nchars == 0 ||
716 args.target_lowlevel_path_nchars >= 32768 - 4)
718 WARNING("Can't get full path name for \"%ls\"", args.target);
719 FREE(args.target_lowlevel_path);
720 args.target_lowlevel_path = NULL;
722 args.target_lowlevel_path_nchars += 4;
727 args.progress.extract.wimfile_name = wim->filename;
728 args.progress.extract.image = wim->current_image;
729 args.progress.extract.extract_flags = (extract_flags &
730 WIMLIB_EXTRACT_MASK_PUBLIC);
731 args.progress.extract.image_name = wimlib_get_image_name(wim,
733 args.progress.extract.extract_root_wim_source_path = wim_source_path;
734 args.progress.extract.target = target;
738 if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
739 args.vol = ntfs_mount(target, 0);
741 ERROR_WITH_ERRNO("Failed to mount NTFS volume `%"TS"'",
743 ret = WIMLIB_ERR_NTFS_3G;
744 goto out_free_target_lowlevel_path;
746 ops = &ntfs_apply_operations;
749 ops = &normal_apply_operations;
751 root = get_dentry(wim, wim_source_path);
753 ERROR("Path \"%"TS"\" does not exist in WIM image %d",
754 wim_source_path, wim->current_image);
755 ret = WIMLIB_ERR_PATH_DOES_NOT_EXIST;
756 goto out_ntfs_umount;
758 args.extract_root = root;
760 /* Calculate the actual filename component of each extracted dentry, and
761 * in the process set the dentry->needs_extraction flag on dentries that
762 * will be extracted. */
763 ret = for_dentry_in_tree(root, dentry_calculate_extraction_path, &args);
765 goto out_dentry_reset_needs_extraction;
767 /* Build a list of the streams that need to be extracted */
768 find_streams_for_extraction(root,
770 wim->lookup_table, extract_flags);
772 /* Calculate the number of bytes of data that will be extracted */
773 calculate_bytes_to_extract(&stream_list, extract_flags,
776 if (extract_flags & WIMLIB_EXTRACT_FLAG_TO_STDOUT) {
777 ret = extract_dentry_to_stdout(root);
778 goto out_dentry_reset_needs_extraction;
782 progress_func(*wim_source_path ? WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN :
783 WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN,
787 /* If a sequential extraction was specified, sort the streams to be
788 * extracted by their position in the WIM file, so that the WIM file can
789 * be read sequentially. */
790 if (extract_flags & WIMLIB_EXTRACT_FLAG_SEQUENTIAL) {
791 ret = sort_stream_list_by_wim_position(&stream_list);
793 WARNING("Falling back to non-sequential extraction");
794 extract_flags &= ~WIMLIB_EXTRACT_FLAG_SEQUENTIAL;
799 progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN,
803 /* Make the directory structure and extract empty files */
804 args.extract_flags |= WIMLIB_EXTRACT_FLAG_NO_STREAMS;
805 args.apply_dentry = ops->apply_dentry;
806 ret = for_dentry_in_tree(root, maybe_apply_dentry, &args);
807 args.extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_STREAMS;
809 goto out_dentry_reset_needs_extraction;
812 progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END,
816 if (extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX) {
817 args.target_realpath = realpath(target, NULL);
818 if (!args.target_realpath) {
819 ret = WIMLIB_ERR_NOMEM;
820 goto out_dentry_reset_needs_extraction;
822 args.target_realpath_len = tstrlen(args.target_realpath);
825 /* Extract non-empty files */
826 ret = apply_stream_list(&stream_list, &args, ops, progress_func);
828 goto out_free_target_realpath;
831 progress_func(WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS,
835 /* Apply timestamps */
836 ret = for_dentry_in_tree_depth(root,
837 ops->apply_dentry_timestamps, &args);
839 goto out_free_target_realpath;
842 progress_func(*wim_source_path ? WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END :
843 WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END,
846 out_free_target_realpath:
847 FREE(args.target_realpath);
848 out_dentry_reset_needs_extraction:
849 for_dentry_in_tree(root, dentry_reset_needs_extraction, NULL);
852 /* Unmount the NTFS volume */
853 if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
854 if (ntfs_umount(args.vol, FALSE) != 0) {
855 ERROR_WITH_ERRNO("Failed to unmount NTFS volume `%"TS"'",
858 ret = WIMLIB_ERR_NTFS_3G;
862 out_free_target_lowlevel_path:
864 FREE(args.target_lowlevel_path);
870 /* Validates a single wimlib_extract_command, mostly checking to make sure the
871 * extract flags make sense. */
873 check_extract_command(struct wimlib_extract_command *cmd, int wim_header_flags)
876 bool is_entire_image = (cmd->wim_source_path[0] == T('\0'));
878 /* Empty destination path? */
879 if (cmd->fs_dest_path[0] == T('\0'))
880 return WIMLIB_ERR_INVALID_PARAM;
882 extract_flags = cmd->extract_flags;
884 /* Specified both symlink and hardlink modes? */
886 (WIMLIB_EXTRACT_FLAG_SYMLINK |
887 WIMLIB_EXTRACT_FLAG_HARDLINK)) == (WIMLIB_EXTRACT_FLAG_SYMLINK |
888 WIMLIB_EXTRACT_FLAG_HARDLINK))
889 return WIMLIB_ERR_INVALID_PARAM;
892 /* Wanted UNIX data on Windows? */
893 if (extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) {
894 ERROR("Extracting UNIX data is not supported on Windows");
895 return WIMLIB_ERR_INVALID_PARAM;
897 /* Wanted linked extraction on Windows? (XXX This is possible, just not
898 * implemented yet.) */
899 if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
900 WIMLIB_EXTRACT_FLAG_HARDLINK))
902 ERROR("Linked extraction modes are not supported on Windows");
903 return WIMLIB_ERR_INVALID_PARAM;
907 if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
908 /* NTFS-3g extraction mode requested */
910 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
911 WIMLIB_EXTRACT_FLAG_HARDLINK))) {
912 ERROR("Cannot specify symlink or hardlink flags when applying\n"
913 " directly to a NTFS volume");
914 return WIMLIB_ERR_INVALID_PARAM;
916 if (!is_entire_image &&
917 (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS))
919 ERROR("When applying directly to a NTFS volume you can "
920 "only extract a full image, not part of one");
921 return WIMLIB_ERR_INVALID_PARAM;
923 if (extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) {
924 ERROR("Cannot restore UNIX-specific data in "
925 "the NTFS extraction mode");
926 return WIMLIB_ERR_INVALID_PARAM;
929 ERROR("wimlib was compiled without support for NTFS-3g, so");
930 ERROR("we cannot apply a WIM image directly to a NTFS volume");
931 return WIMLIB_ERR_UNSUPPORTED;
935 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_RPFIX |
936 WIMLIB_EXTRACT_FLAG_NORPFIX)) ==
937 (WIMLIB_EXTRACT_FLAG_RPFIX | WIMLIB_EXTRACT_FLAG_NORPFIX))
939 ERROR("Cannot specify RPFIX and NORPFIX flags at the same time!");
940 return WIMLIB_ERR_INVALID_PARAM;
943 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_RPFIX |
944 WIMLIB_EXTRACT_FLAG_NORPFIX)) == 0)
946 /* Do reparse point fixups by default if the WIM header says
947 * they are enabled and we are extracting a full image. */
948 if ((wim_header_flags & WIM_HDR_FLAG_RP_FIX) && is_entire_image)
949 extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
952 if (!is_entire_image && (extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX)) {
953 ERROR("Cannot specify --rpfix when not extracting entire image");
954 return WIMLIB_ERR_INVALID_PARAM;
957 cmd->extract_flags = extract_flags;
962 /* Internal function to execute extraction commands for a WIM image. */
964 do_wimlib_extract_files(WIMStruct *wim,
966 struct wimlib_extract_command *cmds,
968 wimlib_progress_func_t progress_func)
971 bool found_link_cmd = false;
972 bool found_nolink_cmd = false;
974 /* Select the image from which we are extracting files */
975 ret = select_wim_image(wim, image);
979 /* Make sure there are no streams in the WIM that have not been
980 * checksummed yet. */
981 ret = wim_checksum_unhashed_streams(wim);
985 /* Check for problems with the extraction commands */
986 for (size_t i = 0; i < num_cmds; i++) {
987 ret = check_extract_command(&cmds[i], wim->hdr.flags);
990 if (cmds[i].extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
991 WIMLIB_EXTRACT_FLAG_HARDLINK)) {
992 found_link_cmd = true;
994 found_nolink_cmd = true;
996 if (found_link_cmd && found_nolink_cmd) {
997 ERROR("Symlink or hardlink extraction mode must "
998 "be set on all extraction commands");
999 return WIMLIB_ERR_INVALID_PARAM;
1003 /* Execute the extraction commands */
1004 for (size_t i = 0; i < num_cmds; i++) {
1005 ret = extract_tree(wim,
1006 cmds[i].wim_source_path,
1007 cmds[i].fs_dest_path,
1008 cmds[i].extract_flags,
1016 /* Extract files or directories from a WIM image. */
1018 wimlib_extract_files(WIMStruct *wim,
1020 const struct wimlib_extract_command *cmds,
1022 int default_extract_flags,
1023 WIMStruct **additional_swms,
1024 unsigned num_additional_swms,
1025 wimlib_progress_func_t progress_func)
1028 struct wimlib_extract_command *cmds_copy;
1031 default_extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC;
1033 ret = verify_swm_set(wim, additional_swms, num_additional_swms);
1040 if (num_additional_swms)
1041 merge_lookup_tables(wim, additional_swms, num_additional_swms);
1043 cmds_copy = CALLOC(num_cmds, sizeof(cmds[0]));
1045 ret = WIMLIB_ERR_NOMEM;
1046 goto out_restore_lookup_table;
1049 for (size_t i = 0; i < num_cmds; i++) {
1050 cmds_copy[i].extract_flags = (default_extract_flags |
1051 cmds[i].extract_flags)
1052 & WIMLIB_EXTRACT_MASK_PUBLIC;
1053 all_flags |= cmds_copy[i].extract_flags;
1055 cmds_copy[i].wim_source_path = canonicalize_wim_path(cmds[i].wim_source_path);
1056 if (!cmds_copy[i].wim_source_path) {
1057 ret = WIMLIB_ERR_NOMEM;
1058 goto out_free_cmds_copy;
1061 cmds_copy[i].fs_dest_path = canonicalize_fs_path(cmds[i].fs_dest_path);
1062 if (!cmds_copy[i].fs_dest_path) {
1063 ret = WIMLIB_ERR_NOMEM;
1064 goto out_free_cmds_copy;
1068 ret = do_wimlib_extract_files(wim, image,
1069 cmds_copy, num_cmds,
1072 if (all_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
1073 WIMLIB_EXTRACT_FLAG_HARDLINK))
1075 for_lookup_table_entry(wim->lookup_table,
1076 lte_free_extracted_file, NULL);
1079 for (size_t i = 0; i < num_cmds; i++) {
1080 FREE(cmds_copy[i].wim_source_path);
1081 FREE(cmds_copy[i].fs_dest_path);
1084 out_restore_lookup_table:
1085 if (num_additional_swms)
1086 unmerge_lookup_table(wim);
1092 * Extracts an image from a WIM file.
1094 * @wim: WIMStruct for the WIM file.
1096 * @image: Number of the single image to extract.
1098 * @target: Directory or NTFS volume to extract the image to.
1100 * @extract_flags: Bitwise or of WIMLIB_EXTRACT_FLAG_*.
1102 * @progress_func: If non-NULL, a progress function to be called
1105 * Returns 0 on success; nonzero on failure.
1108 extract_single_image(WIMStruct *wim, int image,
1109 const tchar *target, int extract_flags,
1110 wimlib_progress_func_t progress_func)
1113 tchar *target_copy = canonicalize_fs_path(target);
1115 return WIMLIB_ERR_NOMEM;
1116 struct wimlib_extract_command cmd = {
1117 .wim_source_path = T(""),
1118 .fs_dest_path = target_copy,
1119 .extract_flags = extract_flags,
1121 ret = do_wimlib_extract_files(wim, image, &cmd, 1, progress_func);
1126 static const tchar * const filename_forbidden_chars =
1135 /* This function checks if it is okay to use a WIM image's name as a directory
1138 image_name_ok_as_dir(const tchar *image_name)
1140 return image_name && *image_name &&
1141 !tstrpbrk(image_name, filename_forbidden_chars) &&
1142 tstrcmp(image_name, T(".")) &&
1143 tstrcmp(image_name, T(".."));
1146 /* Extracts all images from the WIM to the directory @target, with the images
1147 * placed in subdirectories named by their image names. */
1149 extract_all_images(WIMStruct *wim,
1150 const tchar *target,
1152 wimlib_progress_func_t progress_func)
1154 size_t image_name_max_len = max(xml_get_max_image_name_len(wim), 20);
1155 size_t output_path_len = tstrlen(target);
1156 tchar buf[output_path_len + 1 + image_name_max_len + 1];
1159 const tchar *image_name;
1162 if (tstat(target, &stbuf)) {
1163 if (errno == ENOENT)
1165 if (tmkdir(target, S_IRWXU | S_IRGRP | S_IXGRP |
1168 ERROR_WITH_ERRNO("Failed to create directory \"%"TS"\"", target);
1169 return WIMLIB_ERR_MKDIR;
1172 ERROR_WITH_ERRNO("Failed to stat \"%"TS"\"", target);
1173 return WIMLIB_ERR_STAT;
1175 } else if (!S_ISDIR(stbuf.st_mode)) {
1176 ERROR("\"%"TS"\" is not a directory", target);
1177 return WIMLIB_ERR_NOTDIR;
1180 tmemcpy(buf, target, output_path_len);
1181 buf[output_path_len] = T('/');
1182 for (image = 1; image <= wim->hdr.image_count; image++) {
1183 image_name = wimlib_get_image_name(wim, image);
1184 if (image_name_ok_as_dir(image_name)) {
1185 tstrcpy(buf + output_path_len + 1, image_name);
1187 /* Image name is empty or contains forbidden characters.
1188 * Use image number instead. */
1189 tsprintf(buf + output_path_len + 1, T("%d"), image);
1191 ret = extract_single_image(wim, image, buf, extract_flags,
1199 /* Extracts a single image or all images from a WIM file to a directory or NTFS
1202 wimlib_extract_image(WIMStruct *wim,
1204 const tchar *target,
1206 WIMStruct **additional_swms,
1207 unsigned num_additional_swms,
1208 wimlib_progress_func_t progress_func)
1212 extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC;
1214 ret = verify_swm_set(wim, additional_swms, num_additional_swms);
1218 if (num_additional_swms)
1219 merge_lookup_tables(wim, additional_swms, num_additional_swms);
1221 if (image == WIMLIB_ALL_IMAGES) {
1222 ret = extract_all_images(wim, target,
1223 extract_flags | WIMLIB_EXTRACT_FLAG_MULTI_IMAGE,
1226 ret = extract_single_image(wim, image, target, extract_flags,
1230 if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
1231 WIMLIB_EXTRACT_FLAG_HARDLINK))
1233 for_lookup_table_entry(wim->lookup_table,
1234 lte_free_extracted_file,
1237 if (num_additional_swms)
1238 unmerge_lookup_table(wim);