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 dentry->needs_extraction = 0;
638 dentry->not_extracted = 0;
639 dentry->is_win32_name = 0;
640 dentry->d_inode->i_visited = 0;
641 dentry->d_inode->i_dos_name_extracted = 0;
642 FREE(dentry->d_inode->i_extracted_file);
643 dentry->d_inode->i_extracted_file = NULL;
644 if ((void*)dentry->extraction_name != (void*)dentry->file_name)
645 FREE(dentry->extraction_name);
646 dentry->extraction_name = NULL;
650 #define WINDOWS_NT_MAX_PATH 32768
653 * extract_tree - Extract a file or directory tree from the currently selected
656 * @wim: WIMStruct for the WIM file, with the desired image selected
657 * (as wim->current_image).
659 * "Canonical" (i.e. no leading or trailing slashes, path
660 * separators forwald slashes) path inside the WIM image to
661 * extract. An empty string means the full image.
663 * Filesystem path to extract the file or directory tree to.
666 * WIMLIB_EXTRACT_FLAG_*. Also, the private flag
667 * WIMLIB_EXTRACT_FLAG_MULTI_IMAGE will be set if this is being
668 * called through wimlib_extract_image() with WIMLIB_ALL_IMAGES as
672 * If non-NULL, progress function for the extraction. The messages
673 * we may in this function are:
675 * WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN or
676 * WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN;
677 * WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN;
678 * WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END;
679 * WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY;
680 * WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS;
681 * WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS;
682 * WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END or
683 * WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END.
685 * Returns 0 on success; nonzero on failure.
688 extract_tree(WIMStruct *wim, const tchar *wim_source_path, const tchar *target,
689 int extract_flags, wimlib_progress_func_t progress_func)
692 struct list_head stream_list;
693 struct apply_args args;
694 const struct apply_operations *ops;
695 struct wim_dentry *root;
697 memset(&args, 0, sizeof(args));
701 args.target = target;
702 args.target_nchars = tstrlen(target);
703 args.extract_flags = extract_flags;
704 args.progress_func = progress_func;
707 /* Work around defective behavior in Windows where paths longer than 260
708 * characters are not supported by default; instead they need to be
709 * turned into absolute paths and prefixed with "\\?\". */
710 args.target_lowlevel_path = MALLOC(WINDOWS_NT_MAX_PATH * sizeof(wchar_t));
711 if (!args.target_lowlevel_path)
713 ret = WIMLIB_ERR_NOMEM;
716 args.target_lowlevel_path_nchars =
717 GetFullPathName(args.target, WINDOWS_NT_MAX_PATH - 4,
718 &args.target_lowlevel_path[4], NULL);
720 if (args.target_lowlevel_path_nchars == 0 ||
721 args.target_lowlevel_path_nchars >= WINDOWS_NT_MAX_PATH - 4)
723 WARNING("Can't get full path name for \"%ls\"", args.target);
724 FREE(args.target_lowlevel_path);
725 args.target_lowlevel_path = NULL;
727 wmemcpy(args.target_lowlevel_path, L"\\\\?\\", 4);
728 args.target_lowlevel_path_nchars += 4;
733 args.progress.extract.wimfile_name = wim->filename;
734 args.progress.extract.image = wim->current_image;
735 args.progress.extract.extract_flags = (extract_flags &
736 WIMLIB_EXTRACT_MASK_PUBLIC);
737 args.progress.extract.image_name = wimlib_get_image_name(wim,
739 args.progress.extract.extract_root_wim_source_path = wim_source_path;
740 args.progress.extract.target = target;
744 if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
745 args.vol = ntfs_mount(target, 0);
747 ERROR_WITH_ERRNO("Failed to mount NTFS volume `%"TS"'",
749 ret = WIMLIB_ERR_NTFS_3G;
750 goto out_free_target_lowlevel_path;
752 ops = &ntfs_apply_operations;
755 ops = &normal_apply_operations;
757 root = get_dentry(wim, wim_source_path);
759 ERROR("Path \"%"TS"\" does not exist in WIM image %d",
760 wim_source_path, wim->current_image);
761 ret = WIMLIB_ERR_PATH_DOES_NOT_EXIST;
762 goto out_ntfs_umount;
764 args.extract_root = root;
766 /* Calculate the actual filename component of each extracted dentry, and
767 * in the process set the dentry->needs_extraction flag on dentries that
768 * will be extracted. */
769 ret = for_dentry_in_tree(root, dentry_calculate_extraction_path, &args);
771 goto out_dentry_reset_needs_extraction;
773 /* Build a list of the streams that need to be extracted */
774 ret = find_streams_for_extraction(root,
776 wim->lookup_table, extract_flags);
778 goto out_dentry_reset_needs_extraction;
780 /* Calculate the number of bytes of data that will be extracted */
781 calculate_bytes_to_extract(&stream_list, extract_flags,
784 if (extract_flags & WIMLIB_EXTRACT_FLAG_TO_STDOUT) {
785 ret = extract_dentry_to_stdout(root);
786 goto out_dentry_reset_needs_extraction;
790 progress_func(*wim_source_path ? WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN :
791 WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN,
795 /* If a sequential extraction was specified, sort the streams to be
796 * extracted by their position in the WIM file, so that the WIM file can
797 * be read sequentially. */
798 if (extract_flags & WIMLIB_EXTRACT_FLAG_SEQUENTIAL) {
799 ret = sort_stream_list_by_wim_position(&stream_list);
801 WARNING("Falling back to non-sequential extraction");
802 extract_flags &= ~WIMLIB_EXTRACT_FLAG_SEQUENTIAL;
807 progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN,
811 /* Make the directory structure and extract empty files */
812 args.extract_flags |= WIMLIB_EXTRACT_FLAG_NO_STREAMS;
813 args.apply_dentry = ops->apply_dentry;
814 ret = for_dentry_in_tree(root, maybe_apply_dentry, &args);
815 args.extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_STREAMS;
817 goto out_dentry_reset_needs_extraction;
820 progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END,
824 if (extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX) {
825 args.target_realpath = realpath(target, NULL);
826 if (!args.target_realpath) {
827 ret = WIMLIB_ERR_NOMEM;
828 goto out_dentry_reset_needs_extraction;
830 args.target_realpath_len = tstrlen(args.target_realpath);
833 /* Extract non-empty files */
834 ret = apply_stream_list(&stream_list, &args, ops, progress_func);
836 goto out_free_target_realpath;
839 progress_func(WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS,
843 /* Apply timestamps */
844 ret = for_dentry_in_tree_depth(root,
845 ops->apply_dentry_timestamps, &args);
847 goto out_free_target_realpath;
850 progress_func(*wim_source_path ? WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END :
851 WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END,
854 out_free_target_realpath:
855 FREE(args.target_realpath);
856 out_dentry_reset_needs_extraction:
857 for_dentry_in_tree(root, dentry_reset_needs_extraction, NULL);
860 /* Unmount the NTFS volume */
861 if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
862 if (ntfs_umount(args.vol, FALSE) != 0) {
863 ERROR_WITH_ERRNO("Failed to unmount NTFS volume `%"TS"'",
866 ret = WIMLIB_ERR_NTFS_3G;
870 out_free_target_lowlevel_path:
872 FREE(args.target_lowlevel_path);
878 /* Validates a single wimlib_extract_command, mostly checking to make sure the
879 * extract flags make sense. */
881 check_extract_command(struct wimlib_extract_command *cmd, int wim_header_flags)
884 bool is_entire_image = (cmd->wim_source_path[0] == T('\0'));
886 /* Empty destination path? */
887 if (cmd->fs_dest_path[0] == T('\0'))
888 return WIMLIB_ERR_INVALID_PARAM;
890 extract_flags = cmd->extract_flags;
892 /* Specified both symlink and hardlink modes? */
894 (WIMLIB_EXTRACT_FLAG_SYMLINK |
895 WIMLIB_EXTRACT_FLAG_HARDLINK)) == (WIMLIB_EXTRACT_FLAG_SYMLINK |
896 WIMLIB_EXTRACT_FLAG_HARDLINK))
897 return WIMLIB_ERR_INVALID_PARAM;
900 /* Wanted UNIX data on Windows? */
901 if (extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) {
902 ERROR("Extracting UNIX data is not supported on Windows");
903 return WIMLIB_ERR_INVALID_PARAM;
905 /* Wanted linked extraction on Windows? (XXX This is possible, just not
906 * implemented yet.) */
907 if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
908 WIMLIB_EXTRACT_FLAG_HARDLINK))
910 ERROR("Linked extraction modes are not supported on Windows");
911 return WIMLIB_ERR_INVALID_PARAM;
915 if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
916 /* NTFS-3g extraction mode requested */
918 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
919 WIMLIB_EXTRACT_FLAG_HARDLINK))) {
920 ERROR("Cannot specify symlink or hardlink flags when applying\n"
921 " directly to a NTFS volume");
922 return WIMLIB_ERR_INVALID_PARAM;
924 if (!is_entire_image &&
925 (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS))
927 ERROR("When applying directly to a NTFS volume you can "
928 "only extract a full image, not part of one");
929 return WIMLIB_ERR_INVALID_PARAM;
931 if (extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) {
932 ERROR("Cannot restore UNIX-specific data in "
933 "the NTFS extraction mode");
934 return WIMLIB_ERR_INVALID_PARAM;
937 ERROR("wimlib was compiled without support for NTFS-3g, so");
938 ERROR("we cannot apply a WIM image directly to a NTFS volume");
939 return WIMLIB_ERR_UNSUPPORTED;
943 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_RPFIX |
944 WIMLIB_EXTRACT_FLAG_NORPFIX)) ==
945 (WIMLIB_EXTRACT_FLAG_RPFIX | WIMLIB_EXTRACT_FLAG_NORPFIX))
947 ERROR("Cannot specify RPFIX and NORPFIX flags at the same time!");
948 return WIMLIB_ERR_INVALID_PARAM;
951 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_RPFIX |
952 WIMLIB_EXTRACT_FLAG_NORPFIX)) == 0)
954 /* Do reparse point fixups by default if the WIM header says
955 * they are enabled and we are extracting a full image. */
956 if ((wim_header_flags & WIM_HDR_FLAG_RP_FIX) && is_entire_image)
957 extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
960 if (!is_entire_image && (extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX)) {
961 ERROR("Cannot specify --rpfix when not extracting entire image");
962 return WIMLIB_ERR_INVALID_PARAM;
965 cmd->extract_flags = extract_flags;
970 /* Internal function to execute extraction commands for a WIM image. */
972 do_wimlib_extract_files(WIMStruct *wim,
974 struct wimlib_extract_command *cmds,
976 wimlib_progress_func_t progress_func)
979 bool found_link_cmd = false;
980 bool found_nolink_cmd = false;
982 /* Select the image from which we are extracting files */
983 ret = select_wim_image(wim, image);
987 /* Make sure there are no streams in the WIM that have not been
988 * checksummed yet. */
989 ret = wim_checksum_unhashed_streams(wim);
993 /* Check for problems with the extraction commands */
994 for (size_t i = 0; i < num_cmds; i++) {
995 ret = check_extract_command(&cmds[i], wim->hdr.flags);
998 if (cmds[i].extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
999 WIMLIB_EXTRACT_FLAG_HARDLINK)) {
1000 found_link_cmd = true;
1002 found_nolink_cmd = true;
1004 if (found_link_cmd && found_nolink_cmd) {
1005 ERROR("Symlink or hardlink extraction mode must "
1006 "be set on all extraction commands");
1007 return WIMLIB_ERR_INVALID_PARAM;
1011 /* Execute the extraction commands */
1012 for (size_t i = 0; i < num_cmds; i++) {
1013 ret = extract_tree(wim,
1014 cmds[i].wim_source_path,
1015 cmds[i].fs_dest_path,
1016 cmds[i].extract_flags,
1024 /* Extract files or directories from a WIM image. */
1026 wimlib_extract_files(WIMStruct *wim,
1028 const struct wimlib_extract_command *cmds,
1030 int default_extract_flags,
1031 WIMStruct **additional_swms,
1032 unsigned num_additional_swms,
1033 wimlib_progress_func_t progress_func)
1036 struct wimlib_extract_command *cmds_copy;
1039 default_extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC;
1041 ret = verify_swm_set(wim, additional_swms, num_additional_swms);
1048 if (num_additional_swms)
1049 merge_lookup_tables(wim, additional_swms, num_additional_swms);
1051 cmds_copy = CALLOC(num_cmds, sizeof(cmds[0]));
1053 ret = WIMLIB_ERR_NOMEM;
1054 goto out_restore_lookup_table;
1057 for (size_t i = 0; i < num_cmds; i++) {
1058 cmds_copy[i].extract_flags = (default_extract_flags |
1059 cmds[i].extract_flags)
1060 & WIMLIB_EXTRACT_MASK_PUBLIC;
1061 all_flags |= cmds_copy[i].extract_flags;
1063 cmds_copy[i].wim_source_path = canonicalize_wim_path(cmds[i].wim_source_path);
1064 if (!cmds_copy[i].wim_source_path) {
1065 ret = WIMLIB_ERR_NOMEM;
1066 goto out_free_cmds_copy;
1069 cmds_copy[i].fs_dest_path = canonicalize_fs_path(cmds[i].fs_dest_path);
1070 if (!cmds_copy[i].fs_dest_path) {
1071 ret = WIMLIB_ERR_NOMEM;
1072 goto out_free_cmds_copy;
1076 ret = do_wimlib_extract_files(wim, image,
1077 cmds_copy, num_cmds,
1080 if (all_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
1081 WIMLIB_EXTRACT_FLAG_HARDLINK))
1083 for_lookup_table_entry(wim->lookup_table,
1084 lte_free_extracted_file, NULL);
1087 for (size_t i = 0; i < num_cmds; i++) {
1088 FREE(cmds_copy[i].wim_source_path);
1089 FREE(cmds_copy[i].fs_dest_path);
1092 out_restore_lookup_table:
1093 if (num_additional_swms)
1094 unmerge_lookup_table(wim);
1100 * Extracts an image from a WIM file.
1102 * @wim: WIMStruct for the WIM file.
1104 * @image: Number of the single image to extract.
1106 * @target: Directory or NTFS volume to extract the image to.
1108 * @extract_flags: Bitwise or of WIMLIB_EXTRACT_FLAG_*.
1110 * @progress_func: If non-NULL, a progress function to be called
1113 * Returns 0 on success; nonzero on failure.
1116 extract_single_image(WIMStruct *wim, int image,
1117 const tchar *target, int extract_flags,
1118 wimlib_progress_func_t progress_func)
1121 tchar *target_copy = canonicalize_fs_path(target);
1123 return WIMLIB_ERR_NOMEM;
1124 struct wimlib_extract_command cmd = {
1125 .wim_source_path = T(""),
1126 .fs_dest_path = target_copy,
1127 .extract_flags = extract_flags,
1129 ret = do_wimlib_extract_files(wim, image, &cmd, 1, progress_func);
1134 static const tchar * const filename_forbidden_chars =
1143 /* This function checks if it is okay to use a WIM image's name as a directory
1146 image_name_ok_as_dir(const tchar *image_name)
1148 return image_name && *image_name &&
1149 !tstrpbrk(image_name, filename_forbidden_chars) &&
1150 tstrcmp(image_name, T(".")) &&
1151 tstrcmp(image_name, T(".."));
1154 /* Extracts all images from the WIM to the directory @target, with the images
1155 * placed in subdirectories named by their image names. */
1157 extract_all_images(WIMStruct *wim,
1158 const tchar *target,
1160 wimlib_progress_func_t progress_func)
1162 size_t image_name_max_len = max(xml_get_max_image_name_len(wim), 20);
1163 size_t output_path_len = tstrlen(target);
1164 tchar buf[output_path_len + 1 + image_name_max_len + 1];
1167 const tchar *image_name;
1170 if (tstat(target, &stbuf)) {
1171 if (errno == ENOENT)
1173 if (tmkdir(target, S_IRWXU | S_IRGRP | S_IXGRP |
1176 ERROR_WITH_ERRNO("Failed to create directory \"%"TS"\"", target);
1177 return WIMLIB_ERR_MKDIR;
1180 ERROR_WITH_ERRNO("Failed to stat \"%"TS"\"", target);
1181 return WIMLIB_ERR_STAT;
1183 } else if (!S_ISDIR(stbuf.st_mode)) {
1184 ERROR("\"%"TS"\" is not a directory", target);
1185 return WIMLIB_ERR_NOTDIR;
1188 tmemcpy(buf, target, output_path_len);
1189 buf[output_path_len] = OS_PREFERRED_PATH_SEPARATOR;
1190 for (image = 1; image <= wim->hdr.image_count; image++) {
1191 image_name = wimlib_get_image_name(wim, image);
1192 if (image_name_ok_as_dir(image_name)) {
1193 tstrcpy(buf + output_path_len + 1, image_name);
1195 /* Image name is empty or contains forbidden characters.
1196 * Use image number instead. */
1197 tsprintf(buf + output_path_len + 1, T("%d"), image);
1199 ret = extract_single_image(wim, image, buf, extract_flags,
1207 /* Extracts a single image or all images from a WIM file to a directory or NTFS
1210 wimlib_extract_image(WIMStruct *wim,
1212 const tchar *target,
1214 WIMStruct **additional_swms,
1215 unsigned num_additional_swms,
1216 wimlib_progress_func_t progress_func)
1220 extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC;
1222 ret = verify_swm_set(wim, additional_swms, num_additional_swms);
1226 if (num_additional_swms)
1227 merge_lookup_tables(wim, additional_swms, num_additional_swms);
1229 if (image == WIMLIB_ALL_IMAGES) {
1230 ret = extract_all_images(wim, target,
1231 extract_flags | WIMLIB_EXTRACT_FLAG_MULTI_IMAGE,
1234 ret = extract_single_image(wim, image, target, extract_flags,
1238 if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
1239 WIMLIB_EXTRACT_FLAG_HARDLINK))
1241 for_lookup_table_entry(wim->lookup_table,
1242 lte_free_extracted_file,
1245 if (num_additional_swms)
1246 unmerge_lookup_table(wim);