X-Git-Url: https://wimlib.net/git/?a=blobdiff_plain;f=src%2Fextract_image.c;h=396753501084c55974d654482426f81e8d0fcd2b;hb=a8c340c8924f62c0dd2e4b712bee084ba4257adf;hp=dfb8e4c04ef692d8457bf0d264275df34c6e79c1;hpb=bb40342796df9c677f6903b596abf4e9e5769845;p=wimlib diff --git a/src/extract_image.c b/src/extract_image.c index dfb8e4c0..39675350 100644 --- a/src/extract_image.c +++ b/src/extract_image.c @@ -25,490 +25,24 @@ #include "config.h" -#include +#include +#include +#include +#include #ifdef __WIN32__ # include "win32.h" -#else -# ifdef HAVE_UTIME_H -# include -# endif -# include "timestamp.h" -# include #endif -#include -#include -#include -#include -#include -#include - +#include "wimlib_internal.h" #include "dentry.h" #include "lookup_table.h" -#include "wimlib_internal.h" #include "xml.h" #ifdef WITH_NTFS_3G # include #endif -#ifdef HAVE_ALLOCA_H -# include -#endif - - -#ifndef __WIN32__ - -/* Returns the number of components of @path. */ -static unsigned -get_num_path_components(const char *path) -{ - unsigned num_components = 0; - while (*path) { - while (*path == '/') - path++; - if (*path) - num_components++; - while (*path && *path != '/') - path++; - } - return num_components; -} - -static const char * -path_next_part(const char *path) -{ - while (*path && *path != '/') - path++; - while (*path && *path == '/') - path++; - return path; -} - -static int -extract_regular_file_linked(struct wim_dentry *dentry, - const char *output_path, - struct apply_args *args, - struct wim_lookup_table_entry *lte) -{ - /* This mode overrides the normal hard-link extraction and - * instead either symlinks or hardlinks *all* identical files in - * the WIM, even if they are in a different image (in the case - * of a multi-image extraction) */ - - if (args->extract_flags & WIMLIB_EXTRACT_FLAG_HARDLINK) { - if (link(lte->extracted_file, output_path) != 0) { - ERROR_WITH_ERRNO("Failed to hard link " - "`%s' to `%s'", - output_path, lte->extracted_file); - return WIMLIB_ERR_LINK; - } - } else { - int num_path_components; - int num_output_dir_path_components; - size_t extracted_file_len; - char *p; - const char *p2; - size_t i; - - num_path_components = get_num_path_components(dentry->_full_path) - 1; - num_output_dir_path_components = get_num_path_components(args->target); - - if (args->extract_flags & WIMLIB_EXTRACT_FLAG_MULTI_IMAGE) { - num_path_components++; - num_output_dir_path_components--; - } - extracted_file_len = strlen(lte->extracted_file); - - char buf[extracted_file_len + 3 * num_path_components + 1]; - p = &buf[0]; - - for (i = 0; i < num_path_components; i++) { - *p++ = '.'; - *p++ = '.'; - *p++ = '/'; - } - p2 = lte->extracted_file; - while (*p2 == '/') - p2++; - while (num_output_dir_path_components > 0) { - p2 = path_next_part(p2); - num_output_dir_path_components--; - } - strcpy(p, p2); - if (symlink(buf, output_path) != 0) { - ERROR_WITH_ERRNO("Failed to symlink `%s' to `%s'", - buf, lte->extracted_file); - return WIMLIB_ERR_LINK; - } - } - return 0; -} - -static int -symlink_apply_unix_data(const char *link, - const struct wimlib_unix_data *unix_data) -{ - if (lchown(link, unix_data->uid, unix_data->gid)) { - if (errno == EPERM) { - /* Ignore */ - WARNING_WITH_ERRNO("failed to set symlink UNIX " - "owner/group on \"%s\"", link); - } else { - ERROR_WITH_ERRNO("failed to set symlink UNIX " - "owner/group on \"%s\"", link); - return WIMLIB_ERR_INVALID_DENTRY; - } - } - return 0; -} - -static int -fd_apply_unix_data(int fd, const char *path, - const struct wimlib_unix_data *unix_data) -{ - if (fchown(fd, unix_data->uid, unix_data->gid)) { - if (errno == EPERM) { - WARNING_WITH_ERRNO("failed to set file UNIX " - "owner/group on \"%s\"", path); - /* Ignore? */ - } else { - ERROR_WITH_ERRNO("failed to set file UNIX " - "owner/group on \"%s\"", path); - return WIMLIB_ERR_INVALID_DENTRY; - } - } - - if (fchmod(fd, unix_data->mode)) { - if (errno == EPERM) { - WARNING_WITH_ERRNO("failed to set UNIX file mode " - "on \"%s\"", path); - /* Ignore? */ - } else { - ERROR_WITH_ERRNO("failed to set UNIX file mode " - "on \"%s\"", path); - return WIMLIB_ERR_INVALID_DENTRY; - } - } - return 0; -} - -static int -dir_apply_unix_data(const char *dir, const struct wimlib_unix_data *unix_data) -{ - int dfd = open(dir, O_RDONLY); - int ret; - if (dfd >= 0) { - ret = fd_apply_unix_data(dfd, dir, unix_data); - if (close(dfd)) { - ERROR_WITH_ERRNO("can't close directory `%s'", dir); - ret = WIMLIB_ERR_MKDIR; - } - } else { - ERROR_WITH_ERRNO("can't open directory `%s'", dir); - ret = WIMLIB_ERR_MKDIR; - } - return ret; -} - -static int -extract_regular_file_unlinked(struct wim_dentry *dentry, - struct apply_args *args, - const char *output_path, - struct wim_lookup_table_entry *lte) -{ - /* Normal mode of extraction. Regular files and hard links are - * extracted in the way that they appear in the WIM. */ - - int out_fd; - int ret; - struct wim_inode *inode = dentry->d_inode; - - if (!((args->extract_flags & WIMLIB_EXTRACT_FLAG_MULTI_IMAGE) - && (args->extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | - WIMLIB_EXTRACT_FLAG_HARDLINK)))) - { - /* If the dentry is part of a hard link set of at least 2 - * dentries and one of the other dentries has already been - * extracted, make a hard link to the file corresponding to this - * already-extracted directory. Otherwise, extract the file and - * set the inode->i_extracted_file field so that other dentries - * in the hard link group can link to it. */ - if (inode->i_nlink > 1) { - if (inode->i_extracted_file) { - DEBUG("Extracting hard link `%s' => `%s'", - output_path, inode->i_extracted_file); - if (link(inode->i_extracted_file, output_path) != 0) { - ERROR_WITH_ERRNO("Failed to hard link " - "`%s' to `%s'", - output_path, - inode->i_extracted_file); - return WIMLIB_ERR_LINK; - } - return 0; - } - FREE(inode->i_extracted_file); - inode->i_extracted_file = STRDUP(output_path); - if (!inode->i_extracted_file) { - ERROR("Failed to allocate memory for filename"); - return WIMLIB_ERR_NOMEM; - } - } - } - - /* Extract the contents of the file to @output_path. */ - - out_fd = open(output_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); - if (out_fd == -1) { - ERROR_WITH_ERRNO("Failed to open the file `%s' for writing", - output_path); - return WIMLIB_ERR_OPEN; - } - - if (!lte) { - /* Empty file with no lookup table entry */ - DEBUG("Empty file `%s'.", output_path); - ret = 0; - goto out_extract_unix_data; - } - - ret = extract_wim_resource_to_fd(lte, out_fd, wim_resource_size(lte)); - if (ret) { - ERROR("Failed to extract resource to `%s'", output_path); - goto out; - } - -out_extract_unix_data: - if (args->extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) { - struct wimlib_unix_data unix_data; - ret = inode_get_unix_data(inode, &unix_data, NULL); - if (ret > 0) - ; - else if (ret < 0) - ret = 0; - else - ret = fd_apply_unix_data(out_fd, output_path, &unix_data); - if (ret) - goto out; - } - if (lte) - args->progress.extract.completed_bytes += wim_resource_size(lte); -out: - if (close(out_fd) != 0) { - ERROR_WITH_ERRNO("Failed to close file `%s'", output_path); - if (ret == 0) - ret = WIMLIB_ERR_WRITE; - } - return ret; -} - -static int -extract_regular_file(struct wim_dentry *dentry, - struct apply_args *args, - const char *output_path) -{ - struct wim_lookup_table_entry *lte; - const struct wim_inode *inode = dentry->d_inode; - - lte = inode_unnamed_lte_resolved(inode); - - if (lte && (args->extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | - WIMLIB_EXTRACT_FLAG_HARDLINK))) - { - if (lte->extracted_file) { - return extract_regular_file_linked(dentry, output_path, args, lte); - } else { - lte->extracted_file = STRDUP(output_path); - if (!lte->extracted_file) - return WIMLIB_ERR_NOMEM; - } - } - return extract_regular_file_unlinked(dentry, args, output_path, lte); -} - -static int -extract_symlink(struct wim_dentry *dentry, - struct apply_args *args, - const char *output_path) -{ - char target[4096 + args->target_realpath_len]; - char *fixed_target; - const struct wim_inode *inode = dentry->d_inode; - - ssize_t ret = wim_inode_readlink(inode, - target + args->target_realpath_len, - sizeof(target) - args->target_realpath_len - 1); - struct wim_lookup_table_entry *lte; - - if (ret <= 0) { - ERROR("Could not read the symbolic link from dentry `%s'", - dentry->_full_path); - return WIMLIB_ERR_INVALID_DENTRY; - } - target[args->target_realpath_len + ret] = '\0'; - if (target[args->target_realpath_len] == '/' && - args->extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX) - { - /* Fix absolute symbolic link target to point into the actual - * extraction destination */ - memcpy(target, args->target_realpath, - args->target_realpath_len); - fixed_target = target; - } else { - /* Keep same link target */ - fixed_target = target + args->target_realpath_len; - } - ret = symlink(fixed_target, output_path); - if (ret) { - ERROR_WITH_ERRNO("Failed to symlink `%s' to `%s'", - output_path, fixed_target); - return WIMLIB_ERR_LINK; - } - if (args->extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) { - struct wimlib_unix_data unix_data; - ret = inode_get_unix_data(inode, &unix_data, NULL); - if (ret > 0) - ; - else if (ret < 0) - ret = 0; - else - ret = symlink_apply_unix_data(output_path, &unix_data); - if (ret) - return ret; - } - lte = inode_unnamed_lte_resolved(inode); - wimlib_assert(lte != NULL); - args->progress.extract.completed_bytes += wim_resource_size(lte); - return 0; -} - -#endif /* !__WIN32__ */ - -static int -extract_directory(struct wim_dentry *dentry, - const tchar *output_path, bool is_root) -{ - int ret; - struct stat stbuf; - - ret = tstat(output_path, &stbuf); - if (ret == 0) { - if (S_ISDIR(stbuf.st_mode)) { - /*if (!is_root)*/ - /*WARNING("`%s' already exists", output_path);*/ - goto dir_exists; - } else { - ERROR("`%"TS"' is not a directory", output_path); - return WIMLIB_ERR_MKDIR; - } - } else { - if (errno != ENOENT) { - ERROR_WITH_ERRNO("Failed to stat `%"TS"'", output_path); - return WIMLIB_ERR_STAT; - } - } - - if (tmkdir(output_path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) - { - ERROR_WITH_ERRNO("Cannot create directory `%"TS"'", output_path); - return WIMLIB_ERR_MKDIR; - } -dir_exists: - ret = 0; -#ifndef __WIN32__ - if (dentry) { - struct wimlib_unix_data unix_data; - ret = inode_get_unix_data(dentry->d_inode, &unix_data, NULL); - if (ret > 0) - ; - else if (ret < 0) - ret = 0; - else - ret = dir_apply_unix_data(output_path, &unix_data); - } -#endif - return ret; -} - -#ifndef __WIN32__ -static int -unix_do_apply_dentry(const char *output_path, size_t output_path_len, - struct wim_dentry *dentry, struct apply_args *args) -{ - const struct wim_inode *inode = dentry->d_inode; - - if (inode_is_symlink(inode)) - return extract_symlink(dentry, args, output_path); - else if (inode_is_directory(inode)) - return extract_directory((args->extract_flags & - WIMLIB_EXTRACT_FLAG_UNIX_DATA) ? dentry : NULL, - output_path, false); - else - return extract_regular_file(dentry, args, output_path); -} - -static int -unix_do_apply_dentry_timestamps(const char *output_path, - size_t output_path_len, - struct wim_dentry *dentry, - struct apply_args *args) -{ - int ret; - const struct wim_inode *inode = dentry->d_inode; - -#ifdef HAVE_UTIMENSAT - /* Convert the WIM timestamps, which are accurate to 100 nanoseconds, - * into `struct timespec's for passing to utimensat(), which is accurate - * to 1 nanosecond. */ - - struct timespec ts[2]; - ts[0] = wim_timestamp_to_timespec(inode->i_last_access_time); - ts[1] = wim_timestamp_to_timespec(inode->i_last_write_time); - ret = utimensat(AT_FDCWD, output_path, ts, AT_SYMLINK_NOFOLLOW); - if (ret) - ret = errno; -#else - ret = ENOSYS; -#endif - - if (ret == ENOSYS) { - /* utimensat() not implemented or not available */ - #ifdef HAVE_LUTIMES - /* Convert the WIM timestamps, which are accurate to 100 - * nanoseconds, into `struct timeval's for passing to lutimes(), - * which is accurate to 1 microsecond. */ - struct timeval tv[2]; - tv[0] = wim_timestamp_to_timeval(inode->i_last_access_time); - tv[1] = wim_timestamp_to_timeval(inode->i_last_write_time); - ret = lutimes(output_path, tv); - if (ret) - ret = errno; - #endif - } - - if (ret == ENOSYS) { - /* utimensat() and lutimes() both not implemented or not - * available */ - #ifdef HAVE_UTIME - /* Convert the WIM timestamps, which are accurate to 100 - * nanoseconds, into a `struct utimbuf's for passing to - * utime(), which is accurate to 1 second. */ - struct utimbuf buf; - buf.actime = wim_timestamp_to_unix(inode->i_last_access_time); - buf.modtime = wim_timestamp_to_unix(inode->i_last_write_time); - ret = utime(output_path, &buf); - #endif - } - if (ret && args->num_utime_warnings < 10) { - WARNING_WITH_ERRNO("Failed to set timestamp on file `%s'", - output_path); - args->num_utime_warnings++; - } - return 0; -} -#endif /* !__WIN32__ */ - static int do_apply_op(struct wim_dentry *dentry, struct apply_args *args, int (*apply_dentry_func)(const tchar *, size_t, @@ -567,19 +101,6 @@ apply_dentry_timestamps_normal(struct wim_dentry *dentry, void *arg) #endif } -static bool -dentry_is_descendent(const struct wim_dentry *dentry, - const struct wim_dentry *ancestor) -{ - for (;;) { - if (dentry == ancestor) - return true; - if (dentry_is_root(dentry)) - return false; - dentry = dentry->parent; - } -} - /* Extract a dentry if it hasn't already been extracted and either * WIMLIB_EXTRACT_FLAG_NO_STREAMS is not specified, or the dentry is a directory * and/or has no unnamed stream. */ @@ -589,10 +110,7 @@ maybe_apply_dentry(struct wim_dentry *dentry, void *arg) struct apply_args *args = arg; int ret; - if (dentry->is_extracted) - return 0; - - if (!dentry_is_descendent(dentry, args->extract_root)) + if (!dentry->needs_extraction) return 0; if (args->extract_flags & WIMLIB_EXTRACT_FLAG_NO_STREAMS && @@ -608,7 +126,7 @@ maybe_apply_dentry(struct wim_dentry *dentry, void *arg) } ret = args->apply_dentry(dentry, args); if (ret == 0) - dentry->is_extracted = 1; + dentry->needs_extraction = 0; return ret; } @@ -649,24 +167,33 @@ maybe_add_stream_for_extraction(struct wim_lookup_table_entry *lte, struct list_head *stream_list) { if (++lte->out_refcnt == 1) { - INIT_LIST_HEAD(<e->inode_list); + INIT_LIST_HEAD(<e->lte_dentry_list); list_add_tail(<e->extraction_list, stream_list); } } -static void -inode_find_streams_for_extraction(struct wim_inode *inode, - struct list_head *stream_list, - int extract_flags) +struct find_streams_ctx { + struct list_head stream_list; + int extract_flags; +}; + +static int +dentry_find_streams_to_extract(struct wim_dentry *dentry, void *_ctx) { + struct find_streams_ctx *ctx = _ctx; + struct wim_inode *inode = dentry->d_inode; struct wim_lookup_table_entry *lte; - bool inode_added = false; + bool dentry_added = false; + struct list_head *stream_list = &ctx->stream_list; + int extract_flags = ctx->extract_flags; + + dentry->needs_extraction = 1; lte = inode_unnamed_lte_resolved(inode); if (lte) { maybe_add_stream_for_extraction(lte, stream_list); - list_add_tail(&inode->i_lte_inode_list, <e->inode_list); - inode_added = true; + list_add_tail(&dentry->tmp_list, <e->lte_dentry_list); + dentry_added = true; } /* Determine whether to include alternate data stream entries or not. @@ -689,34 +216,15 @@ inode_find_streams_for_extraction(struct wim_inode *inode, if (lte) { maybe_add_stream_for_extraction(lte, stream_list); - if (!inode_added) { - list_add_tail(&inode->i_lte_inode_list, - <e->inode_list); - inode_added = true; + if (!dentry_added) { + list_add_tail(&dentry->tmp_list, + <e->lte_dentry_list); + dentry_added = true; } } } } } -} - -struct find_streams_ctx { - struct list_head stream_list; - int extract_flags; -}; - -static int -dentry_find_streams_to_extract(struct wim_dentry *dentry, void *_ctx) -{ - struct find_streams_ctx *ctx = _ctx; - struct wim_inode *inode = dentry->d_inode; - - dentry->is_extracted = 0; - if (!inode->i_visited) { - inode_find_streams_for_extraction(inode, &ctx->stream_list, - ctx->extract_flags); - inode->i_visited = 1; - } return 0; } @@ -752,9 +260,9 @@ find_streams_for_extraction(struct wim_dentry *root, } static int -dentry_mark_inode_unvisited(struct wim_dentry *dentry, void *_ignore) +dentry_reset_needs_extraction(struct wim_dentry *dentry, void *_ignore) { - dentry->d_inode->i_visited = 0; + dentry->needs_extraction = 0; return 0; } @@ -784,7 +292,6 @@ apply_stream_list(struct list_head *stream_list, uint64_t bytes_per_progress = args->progress.extract.total_bytes / 100; uint64_t next_progress = bytes_per_progress; struct wim_lookup_table_entry *lte; - struct wim_inode *inode; struct wim_dentry *dentry; int ret; @@ -798,30 +305,28 @@ apply_stream_list(struct list_head *stream_list, /* For each distinct stream to be extracted */ list_for_each_entry(lte, stream_list, extraction_list) { - /* For each inode that contains the stream */ - list_for_each_entry(inode, <e->inode_list, i_lte_inode_list) { - /* For each dentry that points to the inode */ - inode_for_each_dentry(dentry, inode) { - /* Extract the dentry if it was not already - * extracted */ - ret = maybe_apply_dentry(dentry, args); - if (ret) - return ret; - if (progress_func && - args->progress.extract.completed_bytes >= next_progress) + /* For each dentry to be extracted that is a name for an inode + * containing the stream */ + list_for_each_entry(dentry, <e->lte_dentry_list, tmp_list) { + /* Extract the dentry if it was not already + * extracted */ + ret = maybe_apply_dentry(dentry, args); + if (ret) + return ret; + if (progress_func && + args->progress.extract.completed_bytes >= next_progress) + { + progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS, + &args->progress); + if (args->progress.extract.completed_bytes >= + args->progress.extract.total_bytes) { - progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS, - &args->progress); - if (args->progress.extract.completed_bytes >= - args->progress.extract.total_bytes) - { - next_progress = ~0ULL; - } else { - next_progress = - min (args->progress.extract.completed_bytes + - bytes_per_progress, - args->progress.extract.total_bytes); - } + next_progress = ~0ULL; + } else { + next_progress = + min (args->progress.extract.completed_bytes + + bytes_per_progress, + args->progress.extract.total_bytes); } } } @@ -982,7 +487,6 @@ extract_tree(WIMStruct *wim, const tchar *wim_source_path, const tchar *target, if (ret) goto out_ntfs_umount; - /* Build a list of the streams that need to be extracted */ find_streams_for_extraction(root, &stream_list, @@ -994,7 +498,7 @@ extract_tree(WIMStruct *wim, const tchar *wim_source_path, const tchar *target, if (extract_flags & WIMLIB_EXTRACT_FLAG_TO_STDOUT) { ret = extract_dentry_to_stdout(root); - goto out_mark_inodes_unvisited; + goto out_dentry_reset_needs_extraction; } if (progress_func) { @@ -1025,7 +529,7 @@ extract_tree(WIMStruct *wim, const tchar *wim_source_path, const tchar *target, ret = for_dentry_in_tree(root, maybe_apply_dentry, &args); args.extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_STREAMS; if (ret) - goto out_mark_inodes_unvisited; + goto out_dentry_reset_needs_extraction; if (progress_func) { progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END, @@ -1036,7 +540,7 @@ extract_tree(WIMStruct *wim, const tchar *wim_source_path, const tchar *target, args.target_realpath = realpath(target, NULL); if (!args.target_realpath) { ret = WIMLIB_ERR_NOMEM; - goto out_mark_inodes_unvisited; + goto out_dentry_reset_needs_extraction; } args.target_realpath_len = tstrlen(args.target_realpath); } @@ -1064,8 +568,8 @@ extract_tree(WIMStruct *wim, const tchar *wim_source_path, const tchar *target, } out_free_target_realpath: FREE(args.target_realpath); -out_mark_inodes_unvisited: - for_dentry_in_tree(root, dentry_mark_inode_unvisited, NULL); +out_dentry_reset_needs_extraction: + for_dentry_in_tree(root, dentry_reset_needs_extraction, NULL); out_ntfs_umount: #ifdef WITH_NTFS_3G /* Unmount the NTFS volume */ @@ -1104,7 +608,7 @@ check_extract_command(struct wimlib_extract_command *cmd, int wim_header_flags) return WIMLIB_ERR_INVALID_PARAM; #ifdef __WIN32__ - /* Wanted UNIX data on Win32? */ + /* Wanted UNIX data on Windows? */ if (extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) { ERROR("Extracting UNIX data is not supported on Windows"); return WIMLIB_ERR_INVALID_PARAM; @@ -1232,9 +736,9 @@ do_wimlib_extract_files(WIMStruct *wim, WIMLIBAPI int wimlib_extract_files(WIMStruct *wim, int image, - int default_extract_flags, const struct wimlib_extract_command *cmds, size_t num_cmds, + int default_extract_flags, WIMStruct **additional_swms, unsigned num_additional_swms, wimlib_progress_func_t progress_func) @@ -1363,7 +867,9 @@ static bool image_name_ok_as_dir(const tchar *image_name) { return image_name && *image_name && - !tstrpbrk(image_name, filename_forbidden_chars); + !tstrpbrk(image_name, filename_forbidden_chars) && + tstrcmp(image_name, T(".")) && + tstrcmp(image_name, T("..")); } /* Extracts all images from the WIM to the directory @target, with the images @@ -1380,10 +886,25 @@ extract_all_images(WIMStruct *wim, int ret; int image; const tchar *image_name; + struct stat stbuf; - ret = extract_directory(NULL, target, true); - if (ret) - return ret; + if (tstat(target, &stbuf)) { + if (errno == ENOENT) + { + if (tmkdir(target, S_IRWXU | S_IRGRP | S_IXGRP | + S_IROTH | S_IXOTH)) + { + ERROR_WITH_ERRNO("Failed to create directory \"%"TS"\"", target); + return WIMLIB_ERR_MKDIR; + } + } else { + ERROR_WITH_ERRNO("Failed to stat \"%"TS"\"", target); + return WIMLIB_ERR_STAT; + } + } else if (!S_ISDIR(stbuf.st_mode)) { + ERROR("\"%"TS"\" is not a directory", target); + return WIMLIB_ERR_NOTDIR; + } tmemcpy(buf, target, output_path_len); buf[output_path_len] = T('/'); @@ -1434,11 +955,10 @@ wimlib_extract_image(WIMStruct *wim, } if (image == WIMLIB_ALL_IMAGES) { - extract_flags |= WIMLIB_EXTRACT_FLAG_MULTI_IMAGE; - ret = extract_all_images(wim, target, extract_flags, + ret = extract_all_images(wim, target, + extract_flags | WIMLIB_EXTRACT_FLAG_MULTI_IMAGE, progress_func); } else { - extract_flags &= ~WIMLIB_EXTRACT_FLAG_MULTI_IMAGE; ret = extract_single_image(wim, image, target, extract_flags, progress_func); }