From: Eric Biggers Date: Thu, 16 Jan 2014 07:45:57 +0000 (-0600) Subject: Allow 'wimextract' to use wildcard paths on command line X-Git-Tag: v1.6.1~24 X-Git-Url: https://wimlib.net/git/?p=wimlib;a=commitdiff_plain;h=5961a91bf69aa12da60b8f97ec07fc048202f785 Allow 'wimextract' to use wildcard paths on command line And a couple more changes, described in the NEWS. --- diff --git a/NEWS b/NEWS index 1fba3b55..23c39903 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,20 @@ Version 1.6.1: applying it with v1.6.1, decompress it with v1.6.0 then compress it with v1.6.1. + wimextract now allows wildcard characters in paths specified on the + command line (not just in a list file, as was added in v1.6.0). + + wimextract now allows a command line to contain multiple list files, + possibly interspersed with direct paths. + + The '--strict-wildcards' option to wimextract has been removed and + replaced with the inverse option '--nullglob' --- that is, the default + behavior is now to issue an error when any path (possibly containing + wildcard characters) does not match any paths in the WIM image, and this + can be changed by specifying '--nullglob'. + + Added '--preserve-dir-structure' option to wimextract. + Fixed more permissions problems when extracting files on Windows. Memory usage for LZMS and LZX compression has been decreased. diff --git a/doc/imagex-extract.1.in b/doc/imagex-extract.1.in index 9a9e1592..e059b208 100644 --- a/doc/imagex-extract.1.in +++ b/doc/imagex-extract.1.in @@ -2,9 +2,7 @@ .SH NAME @IMAGEX_PROGNAME@-extract \- Extract files or directories from a WIM image .SH SYNOPSIS -\fB@IMAGEX_PROGNAME@ extract\fR \fIWIMFILE\fR \fIIMAGE\fR [\fIPATH\fR...] [\fIOPTION\fR...] -.br -\fB@IMAGEX_PROGNAME@ extract\fR \fIWIMFILE\fR \fIIMAGE\fR @\fILISTFILE\fR [\fIOPTION\fR...] +\fB@IMAGEX_PROGNAME@ extract\fR \fIWIMFILE\fR \fIIMAGE\fR [(\fIPATH\fR | @\fILISTFILE\fR)...] [\fIOPTION\fR...] .SH DESCRIPTION \fB@IMAGEX_PROGNAME@ extract\fR extracts one or more files or directory trees from the specified \fIIMAGE\fR contained in the Windows Imaging (WIM) file @@ -21,50 +19,63 @@ directory trees to extract. It may be a 1-based index of an image in the WIM or the name of an image in the WIM. Use the \fB@IMAGEX_PROGNAME@ info\fR (1) command to show what images a WIM file contains. .PP -Each \fIPATH\fR specifies a file or directory tree within the WIM image to -extract. Alternatively, a single \fILISTFILE\fR beginning with the '@' -character is taken as a file that itself contains a list of files or directory -trees to extract. See \fBPATH_SPECIFICATIONS\fR. +If no additional arguments are given, the entire WIM image is extracted. +Otherwise, each additional argument is interpreted as a \fIPATH\fR if it does +not begin with the '@' character, or a \fILISTFILE\fR if it does. Each +\fIPATH\fR specifies a file or directory tree within the WIM image to extract, +whereas each \fILISTFILE\fR specifies a file that itself contains a list of +paths to extract. See \fBPATHS AND LISTFILES\fR for more details. .PP By default, files and directories are extracted to the current directory. Use -\fB--dest-dir\fR to choose an alternate target directory. Alternatively, use -\fB--to-stdout\fR to extract a file to standard output to pipe into another +\fB--dest-dir\fR to select a different destination directory. Alternatively, +use \fB--to-stdout\fR to extract a file to standard output to pipe into another program. .PP +A file or directory extracted from a \fIPATH\fR argument is by default extracted +directly into the destination directory, whereas a file or directory extracted +from a \fILISTFILE\fR argument is by default extracted into the destination +directory in such a way that the archive's directory structure is +preserved. Use \fB--preserve-dir-structure\fR to always get the latter +behavior. +.PP \fB@IMAGEX_PROGNAME@ extract\fR supports extracting files and directory trees from stand-alone WIMs as well as split WIMs. See \fBSPLIT WIMS\fR. -.SH PATH SPECIFICATIONS -Except when a single path is specified and prefixd by the '@' character, each -\fIPATH\fR specifies a file or directory tree within the WIM image to extract. -Each such path must be specified as an absolute path starting from the root of -the WIM image, like those output by the \fB@IMAGEX_PROGNAME@ dir\fR (1) command. -However, path separators may be either forward or backward slashes, and the -leading slash is optional; also, on Windows, the paths are treated -case-insensitively, while on UNIX, paths are treated case-sensitively, except -when overridden through the \fBWIMLIB_IMAGEX_IGNORE_CASE\fR environmental -variable, as documented in \fB@IMAGEX_PROGNAME@\fR (1). -.PP -If no \fIPATH\fRs are provided, the default behavior is to extract the full -image, as if the path "/" had been provided. -.PP -If a single \fIPATH\fR is provided and is prefixed with the '@' character, it is -interpreted as the path to a \fILISTFILE\fR which must be a UTF-8 text file that -contains a list of paths (files or directories) to extract, one per line. In -each line, leading and trailing whitespace is ignored, and lines beginning with -the ';' character and otherwise empty lines are ignored. Each path must be -unquoted and must specify a full path in the WIM image, as described above. -However, unless \fB--no-wildcards\fR is specified, each path in the list file -may also contain the wildcard characters '?' and '*'. The '?' character matches -any character other than a path separator, whereas the '*' character matches -zero or more non-path-separator characters. Consequently, a single wildcard -pattern may expand to multiple actual files or directories. -.PP -In the \fILISTFILE\fR mode, by default a wildcard pattern that matches no files -or directories in the WIM image only produces a warning; use -\fB--strict-wildcards\fR if you want an error instead. Also, when using a list -file, files and directories not located at the root of the WIM image will be -extracted to a corresponding subdirectory of the destination directory rather -than directly to the destination directory itself. +.SH PATHS AND LISTFILES +Each path, including those on the command line and those in listfiles, must be +specified as an absolute path starting from the root of the WIM image, like +those output by the \fB@IMAGEX_PROGNAME@ dir\fR (1) command. However, path +separators may be either forward or backward slashes, and the leading slash is +optional. In addition, on Windows, the paths are treated case-insensitively, +whereas on UNIX-like systems, paths are treated case-sensitively, except when +overridden through the \fBWIMLIB_IMAGEX_IGNORE_CASE\fR environmental variable, +as documented in \fB@IMAGEX_PROGNAME@\fR (1). +.PP +By default, each path may contain the wildcard characters '?' and '*'. The '?' +character matches any character other than a path separator, whereas the '*' +character matches zero or more non-path-separator characters. Consequently, a +single wildcard pattern may expand to multiple actual files or directories. +Use the \fB--no-wildcards\fR option to disable wildcard matching and search for +each path literally. +.PP +Each \fILISTFILE\fR must be a UTF-8 text file that contains a list of paths to +extract, one per line. Wildcard characters are allowed by default. +The following demonstrates an example listfile: +.PP +.RS +.nf + +; This is a comment (begins with semicolon) +/Users +/Windows/explorer.exe +/Windows/System32/en-US/* + +; Both forward and backslashes are valid. +; Don't quote paths containing spaces. +\\Program Files\\A* + +; Leading and trailing whitespace is ignored + \\Windows\\notepad* + .SH SPLIT WIMS You may use \fB@IMAGEX_PROGNAME@ extract\fR to extract files or directory trees from a split WIM. This uses the \fB--refs\fR="\fIGLOB\fR" option in the same @@ -82,6 +93,16 @@ See \fBSPLIT_WIMS\fR. Note: \fIGLOB\fR is listed in quotes because it is interpreted by \fB@IMAGEX_PROGNAME@\fR and may need to be quoted to protect against shell expansion. .TP +\fB--dest-dir\fR=\fIDIR\fR +Extract the files and directories to the directory \fIDIR\fR instead of to the +current working directory. +.TP +\fB--to-stdout\fR +Extract the files to standard output instead of to the filesystem. This can +only be provided if all the specified \fIPATH\fRs are to regular files (not +directories or reparse points). If present, alternate data streams are not +extracted. +.TP \fB--unix-data\fR See the documentation for this option in \fB@IMAGEX_PROGNAME@-apply\fR (1). .TP @@ -97,25 +118,19 @@ See the documentation for this option in \fB@IMAGEX_PROGNAME@-apply\fR (1). \fB--include-invalid-names\fR See the documentation for this option in \fB@IMAGEX_PROGNAME@-apply\fR (1). .TP -\fB--to-stdout\fR -Extract the files to standard output instead of to the filesystem. This can -only be provided if all the specified \fIPATH\fRs are to regular files (not -directories or reparse points). If present, alternate data streams are not -extracted. -.TP -\fB--dest-dir\fR=\fIDIR\fR -Extract the files and directories to the directory \fIDIR\fR instead of to the -current working directory. -.TP \fB--no-wildcards\fR -Do not interpret wildcard characters in paths in the \fILISTFILE\fR. +Do not interpret wildcard characters in paths. +.TP +\fB--nullglob\fR +If a wildcard pattern does not match any paths, ignore it instead of failing +with an error. .TP -\fB--strict-wildcards\fR -Fail if any wildcard patterns in \fILISTFILE\fR do not match any files or -directories in the WIM image. The default behavior is to warn only. This -option has no effect if \fB--no-wildcards\fR is also specified or if \fIPATH\fRs -are specified instead of a \fILISTFILE\fR; in those cases, an error is issued if -any file to extract does not exist. +\fB--preserve-dir-structure\fR +When extracting paths, preserve the archive directory structure instead of +extracting the file or directory tree named by each path directly to the +destination directory. Note: \fB--preserve-dir-structure\fR is already the +default behavior for paths in listfiles (but not paths directly specified on the +command line). .SH NOTES See the documentation \fB@IMAGEX_PROGNAME@ apply\fR (1) for documentation about what data and metadata are extracted on UNIX-like systems versus on Windows. diff --git a/include/wimlib.h b/include/wimlib.h index c2c32a8c..8e851aeb 100644 --- a/include/wimlib.h +++ b/include/wimlib.h @@ -1517,6 +1517,12 @@ typedef int (*wimlib_iterate_lookup_table_callback_t)(const struct wimlib_resour /** Do not extract Windows file attributes such as readonly, hidden, etc. */ #define WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES 0x00100000 +/** For wimlib_extract_paths() and wimlib_extract_pathlist() only: Do not + * preserve the directory structure of the archive when extracting --- that is, + * place each extracted files or directory tree directly in the target + * directory. */ +#define WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE 0x00200000 + /** @} */ /** @ingroup G_mounting_wim_images * @{ */ diff --git a/programs/imagex-win32.c b/programs/imagex-win32.c index 0d87bab3..c5c40d5f 100644 --- a/programs/imagex-win32.c +++ b/programs/imagex-win32.c @@ -56,28 +56,6 @@ L" Maybe try converting your text file to UTF-16LE?\n" return NULL; } -static inline bool -is_path_separator(wchar_t c) -{ - return c == L'/' || c == L'\\'; -} - -/* basename() (modifying, trailing-slash stripping version) for wide-character - * strings. Understands both forward and backward slashes. */ -wchar_t * -win32_wbasename(wchar_t *path) -{ - wchar_t *p = wcschr(path, L'\0'); - - p--; - while (p >= path && is_path_separator(*p)) - *p-- = '\0'; - while (p >= path && !is_path_separator(*p)) - p--; - p++; - return p; -} - /* Set a file descriptor to binary mode. */ void set_fd_to_binary_mode(int fd) { diff --git a/programs/imagex.c b/programs/imagex.c index 6044a93c..d0127b62 100644 --- a/programs/imagex.c +++ b/programs/imagex.c @@ -48,15 +48,12 @@ #ifdef __WIN32__ # include "imagex-win32.h" -# define tbasename win32_wbasename # define OS_PREFERRED_PATH_SEPARATOR L'\\' # define OS_PREFERRED_PATH_SEPARATOR_STRING L"\\" # define print_security_descriptor win32_print_security_descriptor #else /* __WIN32__ */ -# include # include # include -# define tbasename basename # define OS_PREFERRED_PATH_SEPARATOR '/' # define OS_PREFERRED_PATH_SEPARATOR_STRING "/" # define print_security_descriptor default_print_security_descriptor @@ -77,6 +74,34 @@ static inline void set_fd_to_binary_mode(int fd) #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0])) +static inline bool +is_any_path_separator(tchar c) +{ + return c == T('/') || c == T('\\'); +} + +/* Like basename(), but handles both forward and backwards slashes. */ +static tchar * +tbasename(tchar *path) +{ + tchar *p = tstrchr(path, T('\0')); + + for (;;) { + if (p == path) + return path; + if (!is_any_path_separator(*--p)) + break; + *p = T('\0'); + } + + for (;;) { + if (p == path) + return path; + if (is_any_path_separator(*--p)) + return ++p; + } +} + #define for_opt(c, opts) while ((c = getopt_long_only(argc, (tchar**)argv, T(""), \ opts, NULL)) != -1) @@ -147,12 +172,14 @@ enum { IMAGEX_NO_ACLS_OPTION, IMAGEX_NO_ATTRIBUTES_OPTION, IMAGEX_NO_WILDCARDS_OPTION, + IMAGEX_NULLGLOB_OPTION, IMAGEX_ONE_FILE_ONLY_OPTION, IMAGEX_NOT_PIPABLE_OPTION, IMAGEX_PACK_CHUNK_SIZE_OPTION, IMAGEX_PACK_STREAMS_OPTION, IMAGEX_PATH_OPTION, IMAGEX_PIPABLE_OPTION, + IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION, IMAGEX_REBUILD_OPTION, IMAGEX_RECOMPRESS_OPTION, IMAGEX_RECURSIVE_OPTION, @@ -164,7 +191,6 @@ enum { IMAGEX_STAGING_DIR_OPTION, IMAGEX_STREAMS_INTERFACE_OPTION, IMAGEX_STRICT_ACLS_OPTION, - IMAGEX_STRICT_WILDCARDS_OPTION, IMAGEX_SYMLINK_OPTION, IMAGEX_THREADS_OPTION, IMAGEX_TO_STDOUT_OPTION, @@ -270,8 +296,9 @@ static const struct option extract_options[] = { {T("dest-dir"), required_argument, NULL, IMAGEX_DEST_DIR_OPTION}, {T("to-stdout"), no_argument, NULL, IMAGEX_TO_STDOUT_OPTION}, {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION}, - {T("strict-wildcards"), no_argument, NULL, IMAGEX_STRICT_WILDCARDS_OPTION}, {T("no-wildcards"), no_argument, NULL, IMAGEX_NO_WILDCARDS_OPTION}, + {T("nullglob"), no_argument, NULL, IMAGEX_NULLGLOB_OPTION}, + {T("preserve-dir-structure"), no_argument, NULL, IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION}, {NULL, 0, NULL, 0}, }; @@ -1250,16 +1277,14 @@ imagex_progress_func(enum wimlib_progress_msg msg, info->extract.target); break; case WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN: - if (info->extract.extract_root_wim_source_path[0] != T('\0')) { - imagex_printf(T("Extracting " - "\""WIMLIB_WIM_PATH_SEPARATOR_STRING"%"TS"\" " - "from image %d (\"%"TS"\") " - "in \"%"TS"\" to \"%"TS"\"\n"), - info->extract.extract_root_wim_source_path, - info->extract.image, - info->extract.image_name, - info->extract.wimfile_name, - info->extract.target); + if (info->extract.extract_root_wim_source_path[0]) { + imagex_printf(T("Extracting \"%"TS"\" from image %d " + "(\"%"TS"\") in \"%"TS"\" to \"%"TS"\"\n"), + info->extract.extract_root_wim_source_path, + info->extract.image, + info->extract.image_name, + info->extract.wimfile_name, + info->extract.target); } break; case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS: @@ -2910,67 +2935,6 @@ out_err: goto out_free_refglobs; } -static bool -is_root_wim_path(const tchar *path) -{ - const tchar *p; - for (p = path; *p; p++) - if (*p != T('\\') && *p != T('/')) - return false; - return true; -} - -static void -free_extract_commands(struct wimlib_extract_command *cmds, size_t num_cmds, - const tchar *dest_dir) -{ - for (size_t i = 0; i < num_cmds; i++) - if (cmds[i].fs_dest_path != dest_dir) - free(cmds[i].fs_dest_path); - free(cmds); -} - -static struct wimlib_extract_command * -prepare_extract_commands(tchar **paths, unsigned num_paths, - int extract_flags, tchar *dest_dir, - size_t *num_cmds_ret) -{ - struct wimlib_extract_command *cmds; - size_t num_cmds; - tchar *emptystr = T(""); - - if (num_paths == 0) { - num_paths = 1; - paths = &emptystr; - } - num_cmds = num_paths; - cmds = calloc(num_cmds, sizeof(cmds[0])); - if (!cmds) { - imagex_error(T("Out of memory!")); - return NULL; - } - - for (size_t i = 0; i < num_cmds; i++) { - cmds[i].extract_flags = extract_flags; - cmds[i].wim_source_path = paths[i]; - if (is_root_wim_path(paths[i])) { - cmds[i].fs_dest_path = dest_dir; - } else { - size_t len = tstrlen(dest_dir) + 1 + tstrlen(paths[i]); - cmds[i].fs_dest_path = malloc((len + 1) * sizeof(tchar)); - if (!cmds[i].fs_dest_path) { - free_extract_commands(cmds, num_cmds, dest_dir); - return NULL; - } - tsprintf(cmds[i].fs_dest_path, - T("%"TS""OS_PREFERRED_PATH_SEPARATOR_STRING"%"TS), - dest_dir, tbasename(paths[i])); - } - } - *num_cmds_ret = num_cmds; - return cmds; -} - /* Extract files or directories from a WIM image */ static int imagex_extract(int argc, tchar **argv, int cmd) @@ -2984,13 +2948,16 @@ imagex_extract(int argc, tchar **argv, int cmd) const tchar *image_num_or_name; const tchar *pathlist; tchar *dest_dir = T("."); - int extract_flags = WIMLIB_EXTRACT_FLAG_NORPFIX; - int listfile_extract_flags = WIMLIB_EXTRACT_FLAG_GLOB_PATHS; + int extract_flags = WIMLIB_EXTRACT_FLAG_NORPFIX | + WIMLIB_EXTRACT_FLAG_GLOB_PATHS | + WIMLIB_EXTRACT_FLAG_STRICT_GLOB; + int notlist_extract_flags = WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE; STRING_SET(refglobs); struct wimlib_extract_command *cmds; size_t num_cmds; + tchar *root_path = T(""); for_opt(c, extract_options) { switch (c) { @@ -3030,10 +2997,13 @@ imagex_extract(int argc, tchar **argv, int cmd) extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS; break; case IMAGEX_NO_WILDCARDS_OPTION: - listfile_extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS; + extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS; + break; + case IMAGEX_NULLGLOB_OPTION: + extract_flags &= ~WIMLIB_EXTRACT_FLAG_STRICT_GLOB; break; - case IMAGEX_STRICT_WILDCARDS_OPTION: - listfile_extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_GLOB; + case IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION: + notlist_extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE; break; default: goto out_usage; @@ -3051,21 +3021,9 @@ imagex_extract(int argc, tchar **argv, int cmd) argc -= 2; argv += 2; - if (argc == 1 && argv[0][0] == T('@')) { - pathlist = argv[0] + 1; - cmds = NULL; - num_cmds = 0; - } else { - cmds = prepare_extract_commands(argv, argc, extract_flags, dest_dir, - &num_cmds); - if (cmds == NULL) - goto out_err; - pathlist = NULL; - } - ret = wimlib_open_wim(wimfile, open_flags, &wim, imagex_progress_func); if (ret) - goto out_free_cmds; + goto out_free_refglobs; image = wimlib_resolve_image(wim, image_num_or_name); ret = verify_image_exists_and_is_single(image, @@ -3080,17 +3038,38 @@ imagex_extract(int argc, tchar **argv, int cmd) goto out_wimlib_free; } - ret = 0; - if (ret == 0 && cmds != NULL) { - ret = wimlib_extract_files(wim, image, cmds, num_cmds, 0, - imagex_progress_func); + if (argc == 0) { + argv = &root_path; + argc = 1; + extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS; } - if (ret == 0 && pathlist != NULL) { - ret = wimlib_extract_pathlist(wim, image, dest_dir, - pathlist, - extract_flags | listfile_extract_flags, - imagex_progress_func); + + while (argc != 0 && ret == 0) { + int num_paths; + + for (num_paths = 0; + num_paths < argc && argv[num_paths][0] != T('@'); + num_paths++) + ; + + if (num_paths) { + ret = wimlib_extract_paths(wim, image, dest_dir, + (const tchar **)argv, + num_paths, + extract_flags | notlist_extract_flags, + imagex_progress_func); + argc -= num_paths; + argv += num_paths; + } else { + ret = wimlib_extract_pathlist(wim, image, dest_dir, + argv[0] + 1, + extract_flags, + imagex_progress_func); + argc--; + argv++; + } } + if (ret == 0) { if (!imagex_be_quiet) imagex_printf(T("Done extracting files.\n")); @@ -3107,8 +3086,6 @@ imagex_extract(int argc, tchar **argv, int cmd) } out_wimlib_free: wimlib_free(wim); -out_free_cmds: - free_extract_commands(cmds, num_cmds, dest_dir); out_free_refglobs: string_set_destroy(&refglobs); return ret; @@ -4083,10 +4060,11 @@ T( ), [CMD_EXTRACT] = T( -" %"TS" WIMFILE (IMAGE_NUM | IMAGE_NAME) ([PATH...] | @LISTFILE)\n" -" [--check] [--ref=\"GLOB\"] [--no-acls] [--strict-acls]\n" -" [--no-attributes] [--to-stdout] [--dest-dir=CMD_DIR]\n" -" [--include-invalid-names]\n" +" %"TS" WIMFILE (IMAGE_NUM | IMAGE_NAME) [(PATH | @LISTFILE)...]\n" +" [--check] [--ref=\"GLOB\"] [--dest-dir=CMD_DIR]\n" +" [--to-stdout] [--no-acls] [--strict-acls]\n" +" [--no-attributes] [--include-invalid-names]\n" +" [--no-wildcards] [--nullglob]\n" ), [CMD_INFO] = T( diff --git a/src/extract.c b/src/extract.c index 23c93054..beb5a542 100644 --- a/src/extract.c +++ b/src/extract.c @@ -69,8 +69,9 @@ #define WIMLIB_EXTRACT_FLAG_MULTI_IMAGE 0x80000000 #define WIMLIB_EXTRACT_FLAG_FROM_PIPE 0x40000000 -#define WIMLIB_EXTRACT_FLAG_PATHMODE 0x20000000 -#define WIMLIB_EXTRACT_MASK_PUBLIC 0x1fffffff +#define WIMLIB_EXTRACT_FLAG_FILEMODE 0x20000000 +#define WIMLIB_EXTRACT_FLAG_IMAGEMODE 0x10000000 +#define WIMLIB_EXTRACT_MASK_PUBLIC 0x0fffffff /* Given a WIM dentry in the tree to be extracted, resolve all streams in the * corresponding inode and set 'out_refcnt' in each to 0. */ @@ -897,6 +898,9 @@ build_extraction_path(tchar path[], struct wim_dentry *dentry, path_nchars += target_prefix_nchars; for (d = dentry; d != ctx->extract_root; d = d->parent) { + if (!d->in_extraction_tree || d->extraction_skipped) + break; + path_nchars += d->extraction_name_nchars + 1; list_add(&d->tmp_list, &ancestor_list); } @@ -1954,17 +1958,11 @@ do_feature_check(const struct wim_features *required_features, const struct wim_features *supported_features, int extract_flags, const struct apply_operations *ops, - const tchar *wim_source_path, bool warn) { - const tchar *loc; + const tchar *loc = T("the extraction operation");; const tchar *mode = T("this extraction mode"); - if (wim_source_path[0] == '\0') - loc = T("the WIM image"); - else - loc = wim_source_path; - if (warn) { /* We're an archive program, so theoretically we can do what we want * with FILE_ATTRIBUTE_ARCHIVE (which is a dumb flag anyway). Don't @@ -2210,20 +2208,14 @@ dentry_set_not_skipped(struct wim_dentry *dentry, void *_ignore) static int extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees, - const tchar *wim_source_path, const tchar *target, - int extract_flags, wimlib_progress_func_t progress_func) + const tchar *target, int extract_flags, + wimlib_progress_func_t progress_func) { struct wim_features required_features; struct apply_ctx ctx; int ret; struct wim_lookup_table_entry *lte; - wimlib_assert(num_trees != 0); - wimlib_assert(target != NULL); - - if (num_trees > 1) - wimlib_assert((extract_flags & WIMLIB_EXTRACT_FLAG_PATHMODE)); - /* Start initializing the apply_ctx. */ memset(&ctx, 0, sizeof(struct apply_ctx)); ctx.wim = wim; @@ -2238,19 +2230,24 @@ extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees, WIMLIB_EXTRACT_MASK_PUBLIC); ctx.progress.extract.image_name = wimlib_get_image_name(wim, wim->current_image); - ctx.progress.extract.extract_root_wim_source_path = wim_source_path; ctx.progress.extract.target = target; } INIT_LIST_HEAD(&ctx.stream_list); - if (extract_flags & WIMLIB_EXTRACT_FLAG_PATHMODE) { - /* Path mode --- there may be multiple trees, and targets are - * set relative to the root of the image. - * - * Consider all dentries to be in the extraction tree, but - * assume all to be skipped unless in one of the subtrees being - * extracted or one of the ancestors of the subtrees up to the - * image root. */ + if (extract_flags & WIMLIB_EXTRACT_FLAG_FILEMODE) { + /* File mode --- target is explicit. */ + wimlib_assert(num_trees == 1); + ret = calculate_dentry_full_path(trees[0]); + if (ret) + return ret; + ctx.progress.extract.extract_root_wim_source_path = trees[0]->_full_path; + ctx.extract_root = trees[0]; + for_dentry_in_tree(ctx.extract_root, dentry_set_not_skipped, NULL); + } else { + /* Targets are to be set relative to the root of the image + * (preserving original directory structure). */ + + ctx.progress.extract.extract_root_wim_source_path = T(""); ctx.extract_root = wim_root_dentry(wim); for_dentry_in_tree(ctx.extract_root, dentry_set_skipped, NULL); @@ -2259,14 +2256,16 @@ extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees, for_dentry_in_tree(trees[i], dentry_set_not_skipped, NULL); d = trees[i]; - while (d != ctx.extract_root) { - d = d->parent; - dentry_set_not_skipped(d, NULL); + + /* Extract directories up to image root if preserving + * directory structure. */ + if (!(extract_flags & WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE)) { + while (d != ctx.extract_root) { + d = d->parent; + dentry_set_not_skipped(d, NULL); + } } } - } else { - ctx.extract_root = trees[0]; - for_dentry_in_tree(ctx.extract_root, dentry_set_not_skipped, NULL); } /* Select the appropriate apply_operations based on the @@ -2298,9 +2297,7 @@ extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees, /* Get and check the features required to extract the dentry tree. */ dentry_tree_get_features(ctx.extract_root, &required_features); ret = do_feature_check(&required_features, &ctx.supported_features, - extract_flags, ctx.ops, - wim_source_path, - !(extract_flags & WIMLIB_EXTRACT_FLAG_PATHMODE)); + extract_flags, ctx.ops, true); if (ret) goto out_finish_or_abort_extract; @@ -2382,10 +2379,10 @@ extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees, if (progress_func) { int msg; - if (*wim_source_path || (extract_flags & WIMLIB_EXTRACT_FLAG_PATHMODE)) - msg = WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN; - else + if (extract_flags & WIMLIB_EXTRACT_FLAG_IMAGEMODE) msg = WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN; + else + msg = WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN; progress_func(msg, &ctx.progress); } @@ -2494,10 +2491,10 @@ extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees, if (progress_func) { int msg; - if (*wim_source_path || (extract_flags & WIMLIB_EXTRACT_FLAG_PATHMODE)) - msg = WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END; - else + if (extract_flags & WIMLIB_EXTRACT_FLAG_IMAGEMODE) msg = WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END; + else + msg = WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END; progress_func(msg, &ctx.progress); } @@ -2526,68 +2523,12 @@ out_dentry_reset_needs_extraction: return ret; } -/* - * extract_tree - Extract a file or directory tree from the currently selected - * WIM image. - * - * @wim: WIMStruct for the WIM file, with the desired image selected - * (as wim->current_image). - * - * @wim_source_path: - * "Canonical" (i.e. no leading or trailing slashes, path - * separators WIM_PATH_SEPARATOR) path inside the WIM image to - * extract. An empty string means the full image. - * - * @target: - * Filesystem path to extract the file or directory tree to. - * (Or, with WIMLIB_EXTRACT_FLAG_NTFS: the name of a NTFS volume.) - * - * @extract_flags: - * WIMLIB_EXTRACT_FLAG_*. Also, the private flag - * WIMLIB_EXTRACT_FLAG_MULTI_IMAGE will be set if this is being - * called through wimlib_extract_image() with WIMLIB_ALL_IMAGES as - * the image. - * - * @progress_func: - * If non-NULL, progress function for the extraction. The messages - * that may be sent in this function are: - * - * WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN or - * WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN; - * WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN; - * WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END; - * WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY; - * WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS; - * WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS; - * WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END or - * WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END. - * - * Returns 0 on success; a positive WIMLIB_ERR_* code on failure. - */ -static int -extract_tree(WIMStruct *wim, const tchar *wim_source_path, - const tchar *target, int extract_flags, - wimlib_progress_func_t progress_func) -{ - struct wim_dentry *root; - - root = get_dentry(wim, wim_source_path, WIMLIB_CASE_PLATFORM_DEFAULT); - - if (root == NULL) { - ERROR("Path \"%"TS"\" does not exist in WIM image %d", - wim_source_path, wim->current_image); - return WIMLIB_ERR_PATH_DOES_NOT_EXIST; - } - return extract_trees(wim, &root, 1, wim_source_path, - target, extract_flags, progress_func); -} - /* Make sure the extraction flags make sense, and update them if needed. */ static int -check_extract_flags(int extract_flags, - const u32 wim_header_flags, - int *updated_extract_flags_ret) +check_extract_flags(const WIMStruct *wim, int *extract_flags_p) { + int extract_flags = *extract_flags_p; + /* Check for invalid flag combinations */ if ((extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | @@ -2625,7 +2566,7 @@ check_extract_flags(int extract_flags, { /* Do reparse point fixups by default if the WIM header says * they are enabled and we are extracting a full image. */ - if (wim_header_flags & WIM_HDR_FLAG_RP_FIX) + if (wim->hdr.flags & WIM_HDR_FLAG_RP_FIX) extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX; } @@ -2649,169 +2590,160 @@ check_extract_flags(int extract_flags, } } - if (updated_extract_flags_ret) - *updated_extract_flags_ret = extract_flags; + *extract_flags_p = extract_flags; return 0; } +static u32 +get_wildcard_flags(int extract_flags) +{ + u32 wildcard_flags = 0; + + if (extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_GLOB) + wildcard_flags |= WILDCARD_FLAG_ERROR_IF_NO_MATCH; + else + wildcard_flags |= WILDCARD_FLAG_WARN_IF_NO_MATCH; + + if (default_ignore_case) + wildcard_flags |= WILDCARD_FLAG_CASE_INSENSITIVE; + + return wildcard_flags; +} + +struct append_dentry_ctx { + struct wim_dentry **dentries; + size_t num_dentries; + size_t num_alloc_dentries; +}; -/* Internal function to execute extraction commands for a WIM image. The paths - * in the extract commands are expected to be already "canonicalized". */ static int -do_wimlib_extract_files(WIMStruct *wim, +append_dentry_cb(struct wim_dentry *dentry, void *_ctx) +{ + struct append_dentry_ctx *ctx = _ctx; + + if (ctx->num_dentries == ctx->num_alloc_dentries) { + struct wim_dentry **new_dentries; + size_t new_length; + + new_length = max(ctx->num_alloc_dentries + 8, + ctx->num_alloc_dentries * 3 / 2); + new_dentries = REALLOC(ctx->dentries, + new_length * sizeof(ctx->dentries[0])); + if (new_dentries == NULL) + return WIMLIB_ERR_NOMEM; + ctx->dentries = new_dentries; + ctx->num_alloc_dentries = new_length; + } + ctx->dentries[ctx->num_dentries++] = dentry; + return 0; +} + +static int +do_wimlib_extract_paths(WIMStruct *wim, int image, - struct wimlib_extract_command *cmds, - size_t num_cmds, + const tchar *target, + const tchar * const *paths, + size_t num_paths, + int extract_flags, wimlib_progress_func_t progress_func) { int ret; - bool found_link_cmd = false; - bool found_nolink_cmd = false; + struct wim_dentry **trees; + size_t num_trees; + + if (wim == NULL || target == NULL || target[0] == T('\0') || + (num_paths != 0 && paths == NULL)) + return WIMLIB_ERR_INVALID_PARAM; + + ret = check_extract_flags(wim, &extract_flags); + if (ret) + return ret; - /* Select the image from which we are extracting files */ ret = select_wim_image(wim, image); if (ret) return ret; - /* Make sure there are no streams in the WIM that have not been - * checksummed yet. */ ret = wim_checksum_unhashed_streams(wim); if (ret) return ret; - /* Check for problems with the extraction commands */ - for (size_t i = 0; i < num_cmds; i++) { + if (extract_flags & WIMLIB_EXTRACT_FLAG_GLOB_PATHS) { - if (cmds[i].fs_dest_path[0] == T('\0')) - return WIMLIB_ERR_INVALID_PARAM; + struct append_dentry_ctx append_dentry_ctx = { + .dentries = NULL, + .num_dentries = 0, + .num_alloc_dentries = 0, + }; - if (cmds[i].extract_flags & WIMLIB_EXTRACT_FLAG_GLOB_PATHS) - return WIMLIB_ERR_INVALID_PARAM; + u32 wildcard_flags = get_wildcard_flags(extract_flags); - ret = check_extract_flags(cmds[i].extract_flags, - wim->hdr.flags, - &cmds[i].extract_flags); - if (ret) - return ret; - if (cmds[i].extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | - WIMLIB_EXTRACT_FLAG_HARDLINK)) { - found_link_cmd = true; - } else { - found_nolink_cmd = true; - } - if (found_link_cmd && found_nolink_cmd) { - ERROR("Symlink or hardlink extraction mode must " - "be set on all extraction commands"); - return WIMLIB_ERR_INVALID_PARAM; + for (size_t i = 0; i < num_paths; i++) { + tchar *path = canonicalize_wim_path(paths[i]); + if (path == NULL) { + ret = WIMLIB_ERR_NOMEM; + trees = append_dentry_ctx.dentries; + goto out_free_trees; + } + ret = expand_wildcard(wim, path, + append_dentry_cb, + &append_dentry_ctx, + wildcard_flags); + FREE(path); + if (ret) { + trees = append_dentry_ctx.dentries; + goto out_free_trees; + } } - } - - /* Execute the extraction commands */ - for (size_t i = 0; i < num_cmds; i++) { - ret = extract_tree(wim, - cmds[i].wim_source_path, - cmds[i].fs_dest_path, - cmds[i].extract_flags, - progress_func); - if (ret) - return ret; - } - return 0; -} - -/* API function documented in wimlib.h */ -WIMLIBAPI int -wimlib_extract_files(WIMStruct *wim, - int image, - const struct wimlib_extract_command *cmds, - size_t num_cmds, - int default_extract_flags, - wimlib_progress_func_t progress_func) -{ - int ret; - struct wimlib_extract_command *cmds_copy; - int all_flags = 0; - - default_extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC; - - if (num_cmds == 0) - return 0; - - cmds_copy = CALLOC(num_cmds, sizeof(cmds[0])); - if (!cmds_copy) - return WIMLIB_ERR_NOMEM; + trees = append_dentry_ctx.dentries; + num_trees = append_dentry_ctx.num_dentries; + } else { + trees = MALLOC(num_paths * sizeof(trees[0])); + if (trees == NULL) + return WIMLIB_ERR_NOMEM; - for (size_t i = 0; i < num_cmds; i++) { - cmds_copy[i].extract_flags = (default_extract_flags | - cmds[i].extract_flags) - & WIMLIB_EXTRACT_MASK_PUBLIC; - all_flags |= cmds_copy[i].extract_flags; + for (size_t i = 0; i < num_paths; i++) { - cmds_copy[i].wim_source_path = canonicalize_wim_path(cmds[i].wim_source_path); - if (!cmds_copy[i].wim_source_path) { - ret = WIMLIB_ERR_NOMEM; - goto out_free_cmds_copy; - } + tchar *path = canonicalize_wim_path(paths[i]); + if (path == NULL) { + ret = WIMLIB_ERR_NOMEM; + goto out_free_trees; + } - cmds_copy[i].fs_dest_path = canonicalize_fs_path(cmds[i].fs_dest_path); - if (!cmds_copy[i].fs_dest_path) { - ret = WIMLIB_ERR_NOMEM; - goto out_free_cmds_copy; + trees[i] = get_dentry(wim, path, + WIMLIB_CASE_PLATFORM_DEFAULT); + FREE(path); + if (trees[i] == NULL) { + ERROR("Path \"%"TS"\" does not exist " + "in WIM image %d", + paths[i], wim->current_image); + ret = WIMLIB_ERR_PATH_DOES_NOT_EXIST; + goto out_free_trees; + } } - + num_trees = num_paths; } - ret = do_wimlib_extract_files(wim, image, - cmds_copy, num_cmds, - progress_func); - if (all_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | - WIMLIB_EXTRACT_FLAG_HARDLINK)) - { - for_lookup_table_entry(wim->lookup_table, - lte_free_extracted_file, NULL); - } -out_free_cmds_copy: - for (size_t i = 0; i < num_cmds; i++) { - FREE(cmds_copy[i].wim_source_path); - FREE(cmds_copy[i].fs_dest_path); + if (num_trees == 0) { + ret = 0; + goto out_free_trees; } - FREE(cmds_copy); + + ret = extract_trees(wim, trees, num_trees, + target, extract_flags, progress_func); +out_free_trees: + FREE(trees); return ret; } -/* - * Extracts an image from a WIM file. - * - * @wim: WIMStruct for the WIM file. - * - * @image: Number of the single image to extract. - * - * @target: Directory or NTFS volume to extract the image to. - * - * @extract_flags: Bitwise or of WIMLIB_EXTRACT_FLAG_*. - * - * @progress_func: If non-NULL, a progress function to be called - * periodically. - * - * Returns 0 on success; nonzero on failure. - */ static int extract_single_image(WIMStruct *wim, int image, const tchar *target, int extract_flags, wimlib_progress_func_t progress_func) { - int ret; - tchar *target_copy = canonicalize_fs_path(target); - if (target_copy == NULL) - return WIMLIB_ERR_NOMEM; - struct wimlib_extract_command cmd = { - .wim_source_path = T(""), - .fs_dest_path = target_copy, - .extract_flags = extract_flags, - }; - ret = do_wimlib_extract_files(wim, image, &cmd, 1, progress_func); - FREE(target_copy); - return ret; + const tchar *path = T(""); + return do_wimlib_extract_paths(wim, image, target, &path, 1, + extract_flags | WIMLIB_EXTRACT_FLAG_IMAGEMODE, + progress_func); } static const tchar * const filename_forbidden_chars = @@ -2891,6 +2823,15 @@ extract_all_images(WIMStruct *wim, return 0; } +static void +clear_lte_extracted_file(WIMStruct *wim, int extract_flags) +{ + if (unlikely(extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | + WIMLIB_EXTRACT_FLAG_HARDLINK))) + for_lookup_table_entry(wim->lookup_table, + lte_free_extracted_file, NULL); +} + static int do_wimlib_extract_image(WIMStruct *wim, int image, @@ -2900,25 +2841,129 @@ do_wimlib_extract_image(WIMStruct *wim, { int ret; - if (image == WIMLIB_ALL_IMAGES) { + if (image == WIMLIB_ALL_IMAGES) ret = extract_all_images(wim, target, extract_flags, progress_func); - } else { + else ret = extract_single_image(wim, image, target, extract_flags, progress_func); + + clear_lte_extracted_file(wim, extract_flags); + return ret; +} + + +/**************************************************************************** + * Extraction API * + ****************************************************************************/ + +/* Note: new code should use wimlib_extract_paths() instead of + * wimlib_extract_files() if possible. */ +WIMLIBAPI int +wimlib_extract_files(WIMStruct *wim, + int image, + const struct wimlib_extract_command *cmds, + size_t num_cmds, + int default_extract_flags, + wimlib_progress_func_t progress_func) +{ + int all_flags = 0; + int link_flags; + int ret; + + if (num_cmds == 0) + return 0; + + default_extract_flags |= WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE; + + for (size_t i = 0; i < num_cmds; i++) { + int cmd_flags = ((cmds[i].extract_flags | + default_extract_flags) & + WIMLIB_EXTRACT_MASK_PUBLIC); + int cmd_link_flags = (cmd_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | + WIMLIB_EXTRACT_FLAG_HARDLINK)); + if (i == 0) { + link_flags = cmd_link_flags; + } else { + if (cmd_link_flags != link_flags) { + ERROR("The same symlink or hardlink extraction mode " + "must be set on all extraction commands!"); + return WIMLIB_ERR_INVALID_PARAM; + } + } + all_flags |= cmd_flags; + } + if (all_flags & WIMLIB_EXTRACT_FLAG_GLOB_PATHS) { + ERROR("Glob paths not supported for wimlib_extract_files(). " + "Use wimlib_extract_paths() instead."); + return WIMLIB_ERR_INVALID_PARAM; } - if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | - WIMLIB_EXTRACT_FLAG_HARDLINK)) - { - for_lookup_table_entry(wim->lookup_table, - lte_free_extracted_file, - NULL); + for (size_t i = 0; i < num_cmds; i++) { + int extract_flags = ((cmds[i].extract_flags | + default_extract_flags) & + WIMLIB_EXTRACT_MASK_PUBLIC); + const tchar *target = cmds[i].fs_dest_path; + const tchar *wim_source_path = cmds[i].wim_source_path; + + ret = do_wimlib_extract_paths(wim, image, target, + &wim_source_path, 1, + extract_flags | WIMLIB_EXTRACT_FLAG_FILEMODE, + progress_func); + if (ret) + break; } + + clear_lte_extracted_file(wim, all_flags); + return ret; +} + +WIMLIBAPI int +wimlib_extract_paths(WIMStruct *wim, + int image, + const tchar *target, + const tchar * const *paths, + size_t num_paths, + int extract_flags, + wimlib_progress_func_t progress_func) +{ + int ret; + + extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC; + + ret = do_wimlib_extract_paths(wim, image, target, paths, num_paths, + extract_flags, progress_func); + clear_lte_extracted_file(wim, extract_flags); + return ret; +} + +WIMLIBAPI int +wimlib_extract_pathlist(WIMStruct *wim, int image, + const tchar *target, + const tchar *path_list_file, + int extract_flags, + wimlib_progress_func_t progress_func) +{ + int ret; + tchar **paths; + size_t num_paths; + void *mem; + + ret = read_path_list_file(path_list_file, &paths, &num_paths, &mem); + if (ret) { + ERROR("Failed to read path list file \"%"TS"\"", + path_list_file); + return ret; + } + + ret = wimlib_extract_paths(wim, image, target, + (const tchar * const *)paths, num_paths, + extract_flags, progress_func); + FREE(paths); + FREE(mem); return ret; } -/* API function documented in wimlib.h */ WIMLIBAPI int wimlib_extract_image_from_pipe(int pipe_fd, const tchar *image_num_or_name, const tchar *target, int extract_flags, @@ -3080,7 +3125,6 @@ out_wimlib_free: return ret; } -/* API function documented in wimlib.h */ WIMLIBAPI int wimlib_extract_image(WIMStruct *wim, int image, @@ -3092,176 +3136,3 @@ wimlib_extract_image(WIMStruct *wim, return do_wimlib_extract_image(wim, image, target, extract_flags, progress_func); } - -/* API function documented in wimlib.h */ -WIMLIBAPI int -wimlib_extract_pathlist(WIMStruct *wim, int image, - const tchar *target, - const tchar *path_list_file, - int extract_flags, - wimlib_progress_func_t progress_func) -{ - int ret; - tchar **paths; - size_t num_paths; - void *mem; - - ret = read_path_list_file(path_list_file, &paths, &num_paths, &mem); - if (ret) { - ERROR("Failed to read path list file \"%"TS"\"", - path_list_file); - return ret; - } - - ret = wimlib_extract_paths(wim, image, target, - (const tchar * const *)paths, num_paths, - extract_flags, progress_func); - FREE(paths); - FREE(mem); - return ret; -} - -struct append_dentry_ctx { - struct wim_dentry **dentries; - size_t num_dentries; - size_t num_alloc_dentries; -}; - -static int -append_dentry_cb(struct wim_dentry *dentry, void *_ctx) -{ - struct append_dentry_ctx *ctx = _ctx; - - if (ctx->num_dentries == ctx->num_alloc_dentries) { - struct wim_dentry **new_dentries; - size_t new_length; - - new_length = max(ctx->num_alloc_dentries + 8, - ctx->num_alloc_dentries * 3 / 2); - new_dentries = REALLOC(ctx->dentries, - new_length * sizeof(ctx->dentries[0])); - if (new_dentries == NULL) - return WIMLIB_ERR_NOMEM; - ctx->dentries = new_dentries; - ctx->num_alloc_dentries = new_length; - } - ctx->dentries[ctx->num_dentries++] = dentry; - return 0; -} - -/* API function documented in wimlib.h */ -WIMLIBAPI int -wimlib_extract_paths(WIMStruct *wim, - int image, - const tchar *target, - const tchar * const *paths, - size_t num_paths, - int extract_flags, - wimlib_progress_func_t progress_func) -{ - int ret; - struct wim_dentry **trees; - size_t num_trees; - - extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC; - - if (wim == NULL || target == NULL || target[0] == T('\0') || - (num_paths != 0 && paths == NULL)) - return WIMLIB_ERR_INVALID_PARAM; - - ret = check_extract_flags(extract_flags, wim->hdr.flags, - &extract_flags); - if (ret) - return ret; - - ret = select_wim_image(wim, image); - if (ret) - return ret; - - if (extract_flags & WIMLIB_EXTRACT_FLAG_GLOB_PATHS) { - - struct append_dentry_ctx append_dentry_ctx = { - .dentries = NULL, - .num_dentries = 0, - .num_alloc_dentries = 0, - }; - - u32 wildcard_flags = 0; - - if (extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_GLOB) - wildcard_flags |= WILDCARD_FLAG_ERROR_IF_NO_MATCH; - else - wildcard_flags |= WILDCARD_FLAG_WARN_IF_NO_MATCH; - - if (default_ignore_case) - wildcard_flags |= WILDCARD_FLAG_CASE_INSENSITIVE; - - for (size_t i = 0; i < num_paths; i++) { - tchar *path = canonicalize_wim_path(paths[i]); - if (path == NULL) { - ret = WIMLIB_ERR_NOMEM; - trees = append_dentry_ctx.dentries; - goto out_free_trees; - } - ret = expand_wildcard(wim, path, - append_dentry_cb, - &append_dentry_ctx, - wildcard_flags); - FREE(path); - if (ret) { - trees = append_dentry_ctx.dentries; - goto out_free_trees; - } - } - trees = append_dentry_ctx.dentries; - num_trees = append_dentry_ctx.num_dentries; - } else { - trees = MALLOC(num_paths * sizeof(trees[0])); - if (trees == NULL) - return WIMLIB_ERR_NOMEM; - - for (size_t i = 0; i < num_paths; i++) { - - tchar *path = canonicalize_wim_path(paths[i]); - if (path == NULL) { - ret = WIMLIB_ERR_NOMEM; - goto out_free_trees; - } - - trees[i] = get_dentry(wim, path, - WIMLIB_CASE_PLATFORM_DEFAULT); - FREE(path); - if (trees[i] == NULL) { - ERROR("Path \"%"TS"\" does not exist " - "in WIM image %d", - paths[i], wim->current_image); - ret = WIMLIB_ERR_PATH_DOES_NOT_EXIST; - goto out_free_trees; - } - } - num_trees = num_paths; - } - - if (num_trees == 0) { - ret = 0; - goto out_free_trees; - } - - ret = extract_trees(wim, trees, num_trees, - T(""), target, - ((extract_flags & - ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS) - | WIMLIB_EXTRACT_FLAG_PATHMODE), - progress_func); - - if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | - WIMLIB_EXTRACT_FLAG_HARDLINK)) - { - for_lookup_table_entry(wim->lookup_table, - lte_free_extracted_file, - NULL); - } -out_free_trees: - FREE(trees); - return ret; -} diff --git a/tests/test-imagex-update_and_extract b/tests/test-imagex-update_and_extract index c5e25c7c..b3c76bd7 100755 --- a/tests/test-imagex-update_and_extract +++ b/tests/test-imagex-update_and_extract @@ -223,12 +223,21 @@ imagex extract test.wim 1 @pathlist --dest-dir=out.dir ../tree-cmp hello2 out.dir/hello2 [ ! -e out.dir/otherfile ] -msg "Testing path list extract (no match w/ strict; error expected)" cat > pathlist << EOF hello* EOF rm -rf out.dir -! imagex extract test.wim 1 @pathlist --dest-dir=out.dir --no-wildcards --strict-wildcards +msg "Testing path list extract (no wildcard, no match; error expected)" +! imagex extract test.wim 1 @pathlist --dest-dir=out.dir --no-wildcards + +cat > pathlist << EOF +foobar* +EOF +rm -rf out.dir +msg "Testing path list extract (wildcard, no match; error expected)" +! imagex extract test.wim 1 @pathlist --dest-dir=out.dir +msg "Testing path list extract (wildcard, no match, nullglob; no error expected)" +imagex extract test.wim 1 @pathlist --dest-dir=out.dir --nullglob msg "Testing path list extract (w/ wildcard)" cat > pathlist << EOF @@ -265,10 +274,10 @@ cat > pathlist << EOF hello1 EOF rm -rf out.dir -! WIMLIB_IMAGEX_IGNORE_CASE=0 imagex extract test.wim 1 @pathlist \ - --dest-dir=out.dir --strict-wildcards -WIMLIB_IMAGEX_IGNORE_CASE=1 imagex extract test.wim 1 @pathlist \ - --dest-dir=out.dir --strict-wildcards +! WIMLIB_IMAGEX_IGNORE_CASE=0 imagex extract test.wim 1 @pathlist --dest-dir=out.dir +! WIMLIB_IMAGEX_IGNORE_CASE=0 imagex extract test.wim 1 @pathlist --dest-dir=out.dir --no-wildcards +WIMLIB_IMAGEX_IGNORE_CASE=1 imagex extract test.wim 1 @pathlist --dest-dir=out.dir +WIMLIB_IMAGEX_IGNORE_CASE=1 imagex extract test.wim 1 @pathlist --dest-dir=out.dir --no-wildcards ../tree-cmp hello1 out.dir/HELLO1 [ ! -e out.dir/topdir/hello1 ]