X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fextract.c;h=ae591bbef6f33cf41d9b5d407b054de35a871b58;hp=d4efc24e2ff826b4983b7bc3d1359010dddb58b9;hb=7e6ea29df780d210dc718b7ae7bf74faf3826efb;hpb=ca1be480fd209d8c24e19350e440c01832310a2d diff --git a/src/extract.c b/src/extract.c index d4efc24e..ae591bbe 100644 --- a/src/extract.c +++ b/src/extract.c @@ -53,7 +53,6 @@ #include "wimlib/reparse.h" #include "wimlib/resource.h" #include "wimlib/security.h" -#include "wimlib/swm.h" #ifdef __WIN32__ # include "wimlib/win32.h" /* for realpath() equivalent */ #endif @@ -123,13 +122,19 @@ ref_stream_to_extract(struct wim_lookup_table_entry *lte, if (!lte) return 0; - if (likely(!is_linked_extraction(ctx)) || (lte->out_refcnt == 0 && - lte->extracted_file == NULL)) + /* Tally the size only for each extraction of the stream (not hard + * links). */ + if (!(dentry->d_inode->i_visited && + ctx->supported_features.hard_links) && + (!is_linked_extraction(ctx) || (lte->out_refcnt == 0 && + lte->extracted_file == NULL))) { ctx->progress.extract.total_bytes += wim_resource_size(lte); ctx->progress.extract.num_streams++; } + /* Add stream to the extraction_list only one time, even if it's going + * to be extracted to multiple locations. */ if (lte->out_refcnt == 0) { list_add_tail(<e->extraction_list, &ctx->stream_list); ctx->num_streams_remaining++; @@ -183,7 +188,9 @@ ref_stream_to_extract(struct wim_lookup_table_entry *lte, * extracted (ctx->stream_list) if not already done so, and also update the * progress information (ctx->progress) with the stream. Furthermore, if doing * a sequential extraction, build a mapping from each the stream to the dentries - * referencing it. */ + * referencing it. + * + * This uses the i_visited member of the inodes (assumed to be 0 initially). */ static int dentry_add_streams_to_extract(struct wim_dentry *dentry, void *_ctx) { @@ -195,10 +202,6 @@ dentry_add_streams_to_extract(struct wim_dentry *dentry, void *_ctx) if (dentry->extraction_skipped) return 0; - /* Don't process additional hard links. */ - if (inode->i_visited && ctx->supported_features.hard_links) - return 0; - /* The unnamed data stream will always be extracted, except in an * unlikely case. */ if (!inode_is_encrypted_directory(inode)) { @@ -333,13 +336,22 @@ extract_inode(const tchar *path, struct apply_ctx *ctx, struct wim_inode *inode) } else #endif /* !__WIN32__ */ if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) { - ret = ctx->ops->create_directory(path, ctx); + ret = ctx->ops->create_directory(path, ctx, &inode->extract_cookie); if (ret) { ERROR_WITH_ERRNO("Failed to create the directory " "\"%"TS"\"", path); } + } else if ((inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) && + ctx->ops->extract_encrypted_stream_creates_file && + ctx->supported_features.encrypted_files) { + ret = ctx->ops->extract_encrypted_stream( + path, inode_unnamed_lte_resolved(inode), ctx); + if (ret) { + ERROR_WITH_ERRNO("Failed to create and extract " + "encrypted file \"%"TS"\"", path); + } } else { - ret = ctx->ops->create_file(path, ctx); + ret = ctx->ops->create_file(path, ctx, &inode->extract_cookie); if (ret) { ERROR_WITH_ERRNO("Failed to create the file " "\"%"TS"\"", path); @@ -553,6 +565,7 @@ extract_streams(const tchar *path, struct apply_ctx *ctx, { struct wim_inode *inode = dentry->d_inode; struct wim_lookup_table_entry *lte; + file_spec_t file_spec; int ret; if (dentry->was_hardlinked) @@ -571,6 +584,11 @@ extract_streams(const tchar *path, struct apply_ctx *ctx, } #endif + if (ctx->ops->uses_cookies) + file_spec.cookie = inode->extract_cookie; + else + file_spec.path = path; + /* Unnamed data stream. */ lte = inode_unnamed_lte_resolved(inode); if (lte && (!lte_spec || lte == lte_spec)) { @@ -579,13 +597,20 @@ extract_streams(const tchar *path, struct apply_ctx *ctx, if (!(inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))) { - if ((inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) && - ctx->supported_features.encrypted_files) - ret = ctx->ops->extract_encrypted_stream(path, lte, ctx); - else - ret = ctx->ops->extract_unnamed_stream(path, lte, ctx); - if (ret) - goto error; + if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED && + ctx->supported_features.encrypted_files) { + if (!ctx->ops->extract_encrypted_stream_creates_file) { + ret = ctx->ops->extract_encrypted_stream( + path, lte, ctx); + if (ret) + goto error; + } + } else { + ret = ctx->ops->extract_unnamed_stream( + file_spec, lte, ctx); + if (ret) + goto error; + } update_extract_progress(ctx, lte); } else if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) @@ -617,7 +642,7 @@ extract_streams(const tchar *path, struct apply_ctx *ctx, continue; if (lte_spec) lte = lte_override; - ret = ctx->ops->extract_named_stream(path, entry->stream_name, + ret = ctx->ops->extract_named_stream(file_spec, entry->stream_name, entry->stream_name_nbytes / 2, lte, ctx); if (ret) @@ -636,16 +661,29 @@ error: * extraction mode. */ static int extract_file_attributes(const tchar *path, struct apply_ctx *ctx, - struct wim_dentry *dentry) + struct wim_dentry *dentry, unsigned pass) { int ret; - if (ctx->ops->set_file_attributes) { - if (dentry == ctx->extract_root && ctx->root_dentry_is_special) - return 0; - ret = ctx->ops->set_file_attributes(path, - dentry->d_inode->i_attributes, - ctx); + if (ctx->ops->set_file_attributes && + !(dentry == ctx->extract_root && ctx->root_dentry_is_special)) { + u32 attributes = dentry->d_inode->i_attributes; + + /* Clear unsupported attributes. */ + attributes &= ctx->supported_attributes_mask; + + if ((attributes & FILE_ATTRIBUTE_DIRECTORY && + !ctx->supported_features.encrypted_directories) || + (!(attributes & FILE_ATTRIBUTE_DIRECTORY) && + !ctx->supported_features.encrypted_files)) + { + attributes &= ~FILE_ATTRIBUTE_ENCRYPTED; + } + + if (attributes == 0) + attributes = FILE_ATTRIBUTE_NORMAL; + + ret = ctx->ops->set_file_attributes(path, attributes, ctx, pass); if (ret) { ERROR_WITH_ERRNO("Failed to set attributes on " "\"%"TS"\"", path); @@ -747,11 +785,14 @@ extract_security(const tchar *path, struct apply_ctx *ctx, "descriptor on \"%"TS"\"", path); return ret; } else { + #if 0 if (errno != EACCES) { WARNING_WITH_ERRNO("Failed to set " "security descriptor " "on \"%"TS"\"", path); } + #endif + ctx->no_security_descriptors++; } } } @@ -798,12 +839,15 @@ dentry_is_supported(struct wim_dentry *dentry, struct wim_inode *inode = dentry->d_inode; if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) { - if (supported_features->reparse_points) - return true; - if (supported_features->symlink_reparse_points && - inode_is_symlink(inode)) - return true; - return false; + return supported_features->reparse_points || + (inode_is_symlink(inode) && + supported_features->symlink_reparse_points); + } + if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) { + if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) + return supported_features->encrypted_directories != 0; + else + return supported_features->encrypted_files != 0; } return true; } @@ -876,6 +920,11 @@ static unsigned get_num_path_components(const tchar *path, tchar path_separator) { unsigned num_components = 0; +#ifdef __WIN32__ + /* Ignore drive letter. */ + if (path[0] != L'\0' && path[1] == L':') + path += 2; +#endif while (*path) { while (*path == path_separator) @@ -918,7 +967,11 @@ extract_multiimage_symlink(const tchar *oldpath, const tchar *newpath, num_target_path_components--; } - p_old = oldpath; + p_old = oldpath + ctx->ops->path_prefix_nchars; +#ifdef __WIN32__ + if (p_old[0] != L'\0' && p_old[1] == ':') + p_old += 2; +#endif while (*p_old == ctx->ops->path_separator) p_old++; while (--num_target_path_components) { @@ -997,13 +1050,18 @@ do_dentry_extract_skeleton(tchar path[], struct wim_dentry *dentry, /* Create empty named data streams. */ if (can_extract_named_data_streams(ctx)) { for (u16 i = 0; i < inode->i_num_ads; i++) { + file_spec_t file_spec; struct wim_ads_entry *entry = &inode->i_ads_entries[i]; if (!ads_entry_is_named_stream(entry)) continue; if (entry->lte) continue; - ret = ctx->ops->extract_named_stream(path, + if (ctx->ops->uses_cookies) + file_spec.cookie = inode->extract_cookie; + else + file_spec.path = path; + ret = ctx->ops->extract_named_stream(file_spec, entry->stream_name, entry->stream_name_nbytes / 2, entry->lte, ctx); @@ -1017,7 +1075,7 @@ do_dentry_extract_skeleton(tchar path[], struct wim_dentry *dentry, } /* Set file attributes (if supported). */ - ret = extract_file_attributes(path, ctx, dentry); + ret = extract_file_attributes(path, ctx, dentry, 0); if (ret) return ret; @@ -1060,6 +1118,11 @@ hardlink: return 0; } +/* This is a wrapper around do_dentry_extract_skeleton() that handles building + * the path, doing short name reordering. This is also idempotent; dentries + * already processed have skeleton_extracted set and no action is taken. See + * apply_operations.requires_short_name_reordering for more details about short + * name reordering. */ static int dentry_extract_skeleton(struct wim_dentry *dentry, void *_ctx) { @@ -1069,32 +1132,19 @@ dentry_extract_skeleton(struct wim_dentry *dentry, void *_ctx) struct wim_dentry *other_dentry; int ret; - /* Here we may re-order the extraction of multiple names (hard links) - * for the same file in the same directory in order to ensure the short - * (DOS) name is set correctly. A short name is always associated with - * exactly one long name, and at least on NTFS, only one long name for a - * file can have a short name associated with it. (More specifically, - * there can be unlimited names in the POSIX namespace, but only one - * name can be in the Win32+DOS namespace, or one name in the Win32 - * namespace with a corresponding name in the DOS namespace.) To ensure - * the short name of a file is associated with the correct long name in - * a directory, we extract the long name with a corresponding short name - * before any additional names. This can affect NTFS-3g extraction - * (which uses ntfs_set_ntfs_dos_name(), which doesn't allow specifying - * the long name to associate with a short name) and may affect Win32 - * extraction as well (which uses SetFileShortName()). */ - if (dentry->skeleton_extracted) return 0; + orig_dentry = NULL; if (ctx->supported_features.short_names + && ctx->ops->requires_short_name_reordering && !dentry_has_short_name(dentry) && !dentry->d_inode->i_dos_name_extracted) { inode_for_each_dentry(other_dentry, dentry->d_inode) { if (dentry_has_short_name(other_dentry) && !other_dentry->skeleton_extracted - && other_dentry->parent == dentry->parent) + && other_dentry->in_extraction_tree) { DEBUG("Creating %"TS" before %"TS" " "to guarantee correct DOS name extraction", @@ -1124,6 +1174,14 @@ again: return 0; } +static int +dentry_extract_dir_skeleton(struct wim_dentry *dentry, void *_ctx) +{ + if (dentry->d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) + return dentry_extract_skeleton(dentry, _ctx); + return 0; +} + /* Create a file or directory, then immediately extract all streams. This * assumes that WIMLIB_EXTRACT_FLAG_SEQUENTIAL is not specified, since the WIM * may not be read sequentially by this function. */ @@ -1369,7 +1427,7 @@ extract_streams_from_pipe(struct apply_ctx *ctx) if ((found_lte->resource_location != RESOURCE_NONEXISTENT) && !(found_lte->resource_entry.flags & WIM_RESHDR_FLAG_METADATA) - && (needed_lte = __lookup_resource(lookup_table, found_lte->hash)) + && (needed_lte = lookup_resource(lookup_table, found_lte->hash)) && (needed_lte->out_refcnt)) { copy_resource_entry(&needed_lte->resource_entry, @@ -1436,6 +1494,13 @@ dentry_extract_final(struct wim_dentry *dentry, void *_ctx) if (ret) return ret; + if (ctx->ops->requires_final_set_attributes_pass) { + /* Set file attributes (if supported). */ + ret = extract_file_attributes(path, ctx, dentry, 1); + if (ret) + return ret; + } + return extract_timestamps(path, ctx, dentry); } @@ -1559,6 +1624,8 @@ dentry_calculate_extraction_path(struct wim_dentry *dentry, void *_args) struct apply_ctx *ctx = _args; int ret; + dentry->in_extraction_tree = 1; + if (dentry == ctx->extract_root || dentry->extraction_skipped) return 0; @@ -1678,6 +1745,7 @@ dentry_reset_needs_extraction(struct wim_dentry *dentry, void *_ignore) { struct wim_inode *inode = dentry->d_inode; + dentry->in_extraction_tree = 0; dentry->extraction_skipped = 0; dentry->was_hardlinked = 0; dentry->skeleton_extracted = 0; @@ -1706,8 +1774,12 @@ dentry_tally_features(struct wim_dentry *dentry, void *_features) features->system_files++; if (inode->i_attributes & FILE_ATTRIBUTE_COMPRESSED) features->compressed_files++; - if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) - features->encrypted_files++; + if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) { + if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) + features->encrypted_directories++; + else + features->encrypted_files++; + } if (inode->i_attributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) features->not_context_indexed_files++; if (inode->i_attributes & FILE_ATTRIBUTE_SPARSE_FILE) @@ -1733,20 +1805,42 @@ dentry_tally_features(struct wim_dentry *dentry, void *_features) return 0; } -static int -dentry_clear_inode_visited(struct wim_dentry *dentry, void *_ignore) -{ - dentry->d_inode->i_visited = 0; - return 0; -} - /* Tally the features necessary to extract a dentry tree. */ static void dentry_tree_get_features(struct wim_dentry *root, struct wim_features *features) { memset(features, 0, sizeof(struct wim_features)); for_dentry_in_tree(root, dentry_tally_features, features); - for_dentry_in_tree(root, dentry_clear_inode_visited, NULL); + dentry_tree_clear_inode_visited(root); +} + +static u32 +compute_supported_attributes_mask(const struct wim_features *supported_features) +{ + u32 mask = ~(u32)0; + + if (!supported_features->archive_files) + mask &= ~FILE_ATTRIBUTE_ARCHIVE; + + if (!supported_features->hidden_files) + mask &= ~FILE_ATTRIBUTE_HIDDEN; + + if (!supported_features->system_files) + mask &= ~FILE_ATTRIBUTE_SYSTEM; + + if (!supported_features->not_context_indexed_files) + mask &= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED; + + if (!supported_features->compressed_files) + mask &= ~FILE_ATTRIBUTE_COMPRESSED; + + if (!supported_features->sparse_files) + mask &= ~FILE_ATTRIBUTE_SPARSE_FILE; + + if (!supported_features->reparse_points) + mask &= ~FILE_ATTRIBUTE_REPARSE_POINT; + + return mask; } static int @@ -1757,10 +1851,10 @@ do_feature_check(const struct wim_features *required_features, const tchar *wim_source_path) { const tchar *loc; - const tchar *mode = "this extraction mode"; + const tchar *mode = T("this extraction mode"); if (wim_source_path[0] == '\0') - loc = "the WIM image"; + loc = T("the WIM image"); else loc = wim_source_path; @@ -1807,10 +1901,20 @@ do_feature_check(const struct wim_features *required_features, WARNING( "%lu files in %"TS" are marked as being encrypted,\n" " but encryption is not supported in %"TS". These files\n" -" will be extracted as raw encrypted data instead.", +" will not be extracted.", required_features->encrypted_files, loc, mode); } + if (required_features->encrypted_directories && + !supported_features->encrypted_directories) + { + WARNING( + "%lu directories in %"TS" are marked as being encrypted,\n" +" but encryption is not supported in %"TS".\n" +" These directories will be extracted as unencrypted.", + required_features->encrypted_directories, loc, mode); + } + if (required_features->not_context_indexed_files && !supported_features->not_context_indexed_files) { @@ -1934,6 +2038,15 @@ do_feature_check(const struct wim_features *required_features, return WIMLIB_ERR_UNSUPPORTED; } + if ((extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS) && + required_features->symlink_reparse_points && + !(supported_features->symlink_reparse_points || + supported_features->reparse_points)) + { + ERROR("Extracting symbolic links is not supported in %"TS, mode); + return WIMLIB_ERR_UNSUPPORTED; + } + if ((extract_flags & WIMLIB_EXTRACT_FLAG_SYMLINK) && !supported_features->symlink_reparse_points) { @@ -1945,6 +2058,31 @@ do_feature_check(const struct wim_features *required_features, return 0; } +static void +do_extract_warnings(struct apply_ctx *ctx) +{ + if (ctx->partial_security_descriptors == 0 && + ctx->no_security_descriptors == 0) + return; + + WARNING("Extraction of \"%"TS"\" complete, but with one or more warnings:", + ctx->target); + if (ctx->partial_security_descriptors != 0) { + WARNING("- Could only partially set the security descriptor\n" + " on %lu files or directories.", + ctx->partial_security_descriptors); + } + if (ctx->no_security_descriptors != 0) { + WARNING("- Could not set security descriptor at all\n" + " on %lu files or directories.", + ctx->no_security_descriptors); + } +#ifdef __WIN32__ + WARNING("To fully restore all security descriptors, run the program\n" + " with Administrator rights."); +#endif +} + /* * extract_tree - Extract a file or directory tree from the currently selected * WIM image. @@ -2057,6 +2195,9 @@ extract_tree(WIMStruct *wim, const tchar *wim_source_path, const tchar *target, if (ret) goto out_finish_or_abort_extract; + ctx.supported_attributes_mask = + compute_supported_attributes_mask(&ctx.supported_features); + /* Figure out whether the root dentry is being extracted to the root of * a volume and therefore needs to be treated "specially", for example * not being explicitly created and not having attributes set. */ @@ -2176,6 +2317,13 @@ extract_tree(WIMStruct *wim, const tchar *wim_source_path, const tchar *target, ctx.realtarget_nchars = tstrlen(ctx.realtarget); } + if (ctx.ops->requires_short_name_reordering) { + ret = for_dentry_in_tree(root, dentry_extract_dir_skeleton, + &ctx); + if (ret) + goto out_free_realtarget; + } + /* Finally, the important part: extract the tree of files. */ if (extract_flags & (WIMLIB_EXTRACT_FLAG_SEQUENTIAL | WIMLIB_EXTRACT_FLAG_FROM_PIPE)) { @@ -2248,6 +2396,8 @@ extract_tree(WIMStruct *wim, const tchar *wim_source_path, const tchar *target, &ctx.progress); } + do_extract_warnings(&ctx); + ret = 0; out_free_realtarget: FREE(ctx.realtarget); @@ -2414,8 +2564,6 @@ wimlib_extract_files(WIMStruct *wim, 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) { int ret; @@ -2424,21 +2572,12 @@ wimlib_extract_files(WIMStruct *wim, default_extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC; - ret = verify_swm_set(wim, additional_swms, num_additional_swms); - if (ret) - goto out; - if (num_cmds == 0) - goto out; - - if (num_additional_swms) - merge_lookup_tables(wim, additional_swms, num_additional_swms); + return 0; cmds_copy = CALLOC(num_cmds, sizeof(cmds[0])); - if (!cmds_copy) { - ret = WIMLIB_ERR_NOMEM; - goto out_restore_lookup_table; - } + if (!cmds_copy) + return WIMLIB_ERR_NOMEM; for (size_t i = 0; i < num_cmds; i++) { cmds_copy[i].extract_flags = (default_extract_flags | @@ -2475,10 +2614,6 @@ out_free_cmds_copy: FREE(cmds_copy[i].fs_dest_path); } FREE(cmds_copy); -out_restore_lookup_table: - if (num_additional_swms) - unmerge_lookup_table(wim); -out: return ret; } @@ -2599,24 +2734,10 @@ do_wimlib_extract_image(WIMStruct *wim, int image, const tchar *target, int extract_flags, - WIMStruct **additional_swms, - unsigned num_additional_swms, wimlib_progress_func_t progress_func) { int ret; - if (extract_flags & WIMLIB_EXTRACT_FLAG_FROM_PIPE) { - wimlib_assert(wim->hdr.part_number == 1); - wimlib_assert(num_additional_swms == 0); - } else { - ret = verify_swm_set(wim, additional_swms, num_additional_swms); - if (ret) - return ret; - - if (num_additional_swms) - merge_lookup_tables(wim, additional_swms, num_additional_swms); - } - if (image == WIMLIB_ALL_IMAGES) { ret = extract_all_images(wim, target, extract_flags, progress_func); @@ -2632,8 +2753,6 @@ do_wimlib_extract_image(WIMStruct *wim, lte_free_extracted_file, NULL); } - if (num_additional_swms) - unmerge_lookup_table(wim); return ret; } @@ -2661,8 +2780,7 @@ wimlib_extract_image_from_pipe(int pipe_fd, const tchar *image_num_or_name, * wimlib_open_wim(), getting a WIMStruct in this way will result in * an empty lookup table, no XML data read, and no filename set. */ ret = open_wim_as_WIMStruct(&pipe_fd, - WIMLIB_OPEN_FLAG_FROM_PIPE | - WIMLIB_OPEN_FLAG_SPLIT_OK, + WIMLIB_OPEN_FLAG_FROM_PIPE, &pwm, progress_func); if (ret) return ret; @@ -2787,7 +2905,7 @@ wimlib_extract_image_from_pipe(int pipe_fd, const tchar *image_num_or_name, /* Extract the image. */ extract_flags |= WIMLIB_EXTRACT_FLAG_FROM_PIPE; ret = do_wimlib_extract_image(pwm, image, target, - extract_flags, NULL, 0, progress_func); + extract_flags, progress_func); /* Clean up and return. */ out_wimlib_free: wimlib_free(pwm); @@ -2800,12 +2918,9 @@ wimlib_extract_image(WIMStruct *wim, int image, const tchar *target, int extract_flags, - WIMStruct **additional_swms, - unsigned num_additional_swms, wimlib_progress_func_t progress_func) { extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC; return do_wimlib_extract_image(wim, image, target, extract_flags, - additional_swms, num_additional_swms, progress_func); }