From 29ac4319aa9c75811cd5629cd3471a681fbeb552 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 13 Oct 2014 22:18:35 -0500 Subject: [PATCH] win32_apply.c: Match PrepopulateList patterns against all inode aliases --- include/wimlib/inode.h | 3 + include/wimlib/wimboot.h | 3 +- src/extract.c | 5 +- src/wimboot.c | 26 +++---- src/win32_apply.c | 143 +++++++++++++++++++++++++-------------- 5 files changed, 107 insertions(+), 73 deletions(-) diff --git a/include/wimlib/inode.h b/include/wimlib/inode.h index 879375cc..733d3847 100644 --- a/include/wimlib/inode.h +++ b/include/wimlib/inode.h @@ -102,6 +102,9 @@ struct wim_inode { * has no ADS entries */ u8 i_canonical_streams : 1; + /* Cached value */ + u8 i_can_externally_back : 1; + /* Pointer to a malloc()ed array of i_num_ads alternate data stream * entries for this inode. */ struct wim_ads_entry *i_ads_entries; diff --git a/include/wimlib/wimboot.h b/include/wimlib/wimboot.h index a1039017..c10d0dcc 100644 --- a/include/wimlib/wimboot.h +++ b/include/wimlib/wimboot.h @@ -14,9 +14,8 @@ wimboot_alloc_data_source_id(const wchar_t *wim_path, const wchar_t *target, u64 *data_source_id_ret, bool *wof_running_ret); -extern int +extern bool wimboot_set_pointer(HANDLE h, - const wchar_t *printable_path, const struct wim_lookup_table_entry *lte, u64 data_source_id, const u8 lookup_table_hash[SHA1_HASH_SIZE], diff --git a/src/extract.c b/src/extract.c index 5404b676..b0df25d0 100644 --- a/src/extract.c +++ b/src/extract.c @@ -737,6 +737,7 @@ destroy_dentry_list(struct list_head *dentry_list) inode = dentry->d_inode; dentry_reset_extraction_list_node(dentry); inode->i_visited = 0; + inode->i_can_externally_back = 0; if ((void *)dentry->d_extraction_name != (void *)dentry->file_name) FREE(dentry->d_extraction_name); dentry->d_extraction_name = NULL; @@ -1476,12 +1477,12 @@ extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees, if (ret) goto out_cleanup; + dentry_list_build_inode_alias_lists(&dentry_list); + ret = dentry_list_ref_streams(&dentry_list, ctx); if (ret) goto out_cleanup; - dentry_list_build_inode_alias_lists(&dentry_list); - if (extract_flags & WIMLIB_EXTRACT_FLAG_FROM_PIPE) { /* When extracting from a pipe, the number of bytes of data to * extract can't be determined in the normal way (examining the diff --git a/src/wimboot.c b/src/wimboot.c index 3562890d..8d3421c4 100644 --- a/src/wimboot.c +++ b/src/wimboot.c @@ -1059,8 +1059,6 @@ out: * * @h * Open handle to the file, with GENERIC_WRITE access. - * @printable_name - * Printable representation of the path to the file. * @lte * Unnamed data stream of the file. * @data_source_id @@ -1071,11 +1069,10 @@ out: * %true if the WOF driver appears to be available and working; %false if * not. * - * Returns 0 on success, or a positive error code on failure. + * Returns %true on success, or %false on failure with GetLastError() set. */ -int +bool wimboot_set_pointer(HANDLE h, - const wchar_t *printable_name, const struct wim_lookup_table_entry *lte, u64 data_source_id, const u8 lookup_table_hash[SHA1_HASH_SIZE], @@ -1106,7 +1103,7 @@ wimboot_set_pointer(HANDLE h, if (!DeviceIoControl(h, FSCTL_SET_EXTERNAL_BACKING, &in, sizeof(in), NULL, 0, &bytes_returned, NULL)) - goto fail; + return false; } else { /* The WOF driver is running. We need to create the reparse @@ -1144,7 +1141,7 @@ wimboot_set_pointer(HANDLE h, if (!DeviceIoControl(h, FSCTL_SET_REPARSE_POINT, &in, sizeof(in), NULL, 0, &bytes_returned, NULL)) - goto fail; + return false; /* We also need to create an unnamed data stream of the correct * size. Otherwise the file shows up as zero length. It can be @@ -1152,25 +1149,18 @@ wimboot_set_pointer(HANDLE h, * are unimportant. */ if (!DeviceIoControl(h, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &bytes_returned, NULL)) - goto fail; + return false; if (!SetFilePointerEx(h, (LARGE_INTEGER){ .QuadPart = lte->size}, NULL, FILE_BEGIN)) - goto fail; + return false; if (!SetEndOfFile(h)) - goto fail; + return false; } - return 0; - -fail: - err = GetLastError(); - set_errno_from_win32_error(err); - ERROR_WITH_ERRNO("\"%ls\": Couldn't set WIMBoot pointer data " - "(err=%"PRIu32")", printable_name, (u32)err); - return WIMLIB_ERR_WIMBOOT; + return true; } #endif /* __WIN32__ */ diff --git a/src/win32_apply.c b/src/win32_apply.c index 5669e441..30da6b98 100644 --- a/src/win32_apply.c +++ b/src/win32_apply.c @@ -192,6 +192,13 @@ get_vol_flags(const wchar_t *target, DWORD *vol_flags_ret, } } +static const wchar_t * +current_path(struct win32_apply_ctx *ctx); + +static void +build_extraction_path(const struct wim_dentry *dentry, + struct win32_apply_ctx *ctx); + static int win32_get_supported_features(const wchar_t *target, struct wim_features *supported_features) @@ -337,43 +344,33 @@ can_externally_back_path(const wchar_t *path, size_t path_nchars, return true; } -static const wchar_t * -current_path(struct win32_apply_ctx *ctx); - -static void -build_extraction_path(const struct wim_dentry *dentry, - struct win32_apply_ctx *ctx); - #define WIM_BACKING_NOT_ENABLED -1 #define WIM_BACKING_NOT_POSSIBLE -2 #define WIM_BACKING_EXCLUDED -3 -/* - * Determines if the unnamed data stream of a file will be created as an - * external backing, as opposed to a standard extraction. - */ static int -win32_will_externally_back(struct wim_dentry *dentry, struct apply_ctx *_ctx) +will_externally_back_inode(struct wim_inode *inode, struct win32_apply_ctx *ctx, + const struct wim_dentry **excluded_dentry_ret) { - struct win32_apply_ctx *ctx = (struct win32_apply_ctx *)_ctx; + struct list_head *next; + struct wim_dentry *dentry; struct wim_lookup_table_entry *stream; int ret; - if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT)) - return WIM_BACKING_NOT_ENABLED; + if (inode->i_can_externally_back) + return 0; - if (!ctx->wimboot.tried_to_load_prepopulate_list) { - ret = load_prepopulate_pats(ctx); - if (ret == WIMLIB_ERR_NOMEM) - return ret; - } + /* This may do redundant checks because the cached value + * i_can_externally_back is 2-state (as opposed to 3-state: + * unknown/no/yes). But most files can be externally backed, so this + * way is fine. */ - if (dentry->d_inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY | - FILE_ATTRIBUTE_REPARSE_POINT | - FILE_ATTRIBUTE_ENCRYPTED)) + if (inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY | + FILE_ATTRIBUTE_REPARSE_POINT | + FILE_ATTRIBUTE_ENCRYPTED)) return WIM_BACKING_NOT_POSSIBLE; - stream = inode_unnamed_lte_resolved(dentry->d_inode); + stream = inode_unnamed_lte_resolved(inode); if (!stream || stream->resource_location != RESOURCE_IN_WIM || @@ -381,36 +378,72 @@ win32_will_externally_back(struct wim_dentry *dentry, struct apply_ctx *_ctx) stream->size != stream->rspec->uncompressed_size) return WIM_BACKING_NOT_POSSIBLE; - ret = calculate_dentry_full_path(dentry); - if (ret) - return ret; + /* + * We need to check the patterns in [PrepopulateList] against every name + * of the inode, in case any of them match. + */ + next = inode->i_extraction_aliases.next; + do { + dentry = list_entry(next, struct wim_dentry, + d_extraction_alias_node); - if (!can_externally_back_path(dentry->_full_path, - wcslen(dentry->_full_path), ctx)) - return WIM_BACKING_EXCLUDED; + ret = calculate_dentry_full_path(dentry); + if (ret) + return ret; + if (!can_externally_back_path(dentry->_full_path, + wcslen(dentry->_full_path), ctx)) + { + if (excluded_dentry_ret) + *excluded_dentry_ret = dentry; + return WIM_BACKING_EXCLUDED; + } + next = next->next; + } while (next != &inode->i_extraction_aliases); + + inode->i_can_externally_back = 1; return 0; } +/* + * Determines if the unnamed data stream of a file will be created as an + * external backing, as opposed to a standard extraction. + */ +static int +win32_will_externally_back(struct wim_dentry *dentry, struct apply_ctx *_ctx) +{ + struct win32_apply_ctx *ctx = (struct win32_apply_ctx *)_ctx; + + if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT)) + return WIM_BACKING_NOT_ENABLED; + + if (!ctx->wimboot.tried_to_load_prepopulate_list) + if (load_prepopulate_pats(ctx) == WIMLIB_ERR_NOMEM) + return WIMLIB_ERR_NOMEM; + + return will_externally_back_inode(dentry->d_inode, ctx, NULL); +} + static int -set_external_backing(HANDLE h, struct wim_dentry *dentry, struct win32_apply_ctx *ctx) +set_external_backing(HANDLE h, struct wim_inode *inode, struct win32_apply_ctx *ctx) { int ret; + const struct wim_dentry *excluded_dentry; - ret = win32_will_externally_back(dentry, &ctx->common); + ret = will_externally_back_inode(inode, ctx, &excluded_dentry); if (ret > 0) /* Error. */ return ret; if (ret < 0 && ret != WIM_BACKING_EXCLUDED) return 0; /* Not externally backing, other than due to exclusion. */ - build_extraction_path(dentry, ctx); - - if (ret == WIM_BACKING_EXCLUDED) { + if (unlikely(ret == WIM_BACKING_EXCLUDED)) { /* Not externally backing due to exclusion. */ union wimlib_progress_info info; - info.wimboot_exclude.path_in_wim = dentry->_full_path; + build_extraction_path(excluded_dentry, ctx); + + info.wimboot_exclude.path_in_wim = excluded_dentry->_full_path; info.wimboot_exclude.extraction_path = current_path(ctx); return call_progress(ctx->common.progfunc, @@ -418,12 +451,22 @@ set_external_backing(HANDLE h, struct wim_dentry *dentry, struct win32_apply_ctx &info, ctx->common.progctx); } else { /* Externally backing. */ - return wimboot_set_pointer(h, - current_path(ctx), - inode_unnamed_lte_resolved(dentry->d_inode), - ctx->wimboot.data_source_id, - ctx->wimboot.wim_lookup_table_hash, - ctx->wimboot.wof_running); + if (unlikely(!wimboot_set_pointer(h, + inode_unnamed_lte_resolved(inode), + ctx->wimboot.data_source_id, + ctx->wimboot.wim_lookup_table_hash, + ctx->wimboot.wof_running))) + { + const DWORD err = GetLastError(); + + build_extraction_path(inode_first_extraction_dentry(inode), ctx); + set_errno_from_win32_error(err); + ERROR_WITH_ERRNO("\"%ls\": Couldn't set WIMBoot " + "pointer data (err=%"PRIu32")", + current_path(ctx), (u32)err); + return WIMLIB_ERR_WIMBOOT; + } + return 0; } } @@ -443,11 +486,9 @@ start_wimboot_extraction(struct win32_apply_ctx *ctx) int ret; WIMStruct *wim = ctx->common.wim; - if (!ctx->wimboot.tried_to_load_prepopulate_list) { - ret = load_prepopulate_pats(ctx); - if (ret == WIMLIB_ERR_NOMEM) - return ret; - } + if (!ctx->wimboot.tried_to_load_prepopulate_list) + if (load_prepopulate_pats(ctx) == WIMLIB_ERR_NOMEM) + return WIMLIB_ERR_NOMEM; if (!wim_info_get_wimboot(wim->wim_info, wim->current_image)) WARNING("Image is not marked as WIMBoot compatible!"); @@ -1439,7 +1480,7 @@ create_links(HANDLE h, const struct wim_dentry *first_dentry, /* Create a nondirectory file, including all links. */ static int -create_nondirectory(const struct wim_inode *inode, struct win32_apply_ctx *ctx) +create_nondirectory(struct wim_inode *inode, struct win32_apply_ctx *ctx) { struct wim_dentry *first_dentry; HANDLE h; @@ -1462,7 +1503,7 @@ create_nondirectory(const struct wim_inode *inode, struct win32_apply_ctx *ctx) /* "WIMBoot" extraction: set external backing by the WIM file if needed. */ if (!ret && unlikely(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT)) - ret = set_external_backing(h, first_dentry, ctx); + ret = set_external_backing(h, inode, ctx); (*func_NtClose)(h); return ret; @@ -1473,8 +1514,8 @@ create_nondirectory(const struct wim_inode *inode, struct win32_apply_ctx *ctx) static int create_nondirectories(struct list_head *dentry_list, struct win32_apply_ctx *ctx) { - const struct wim_dentry *dentry; - const struct wim_inode *inode; + struct wim_dentry *dentry; + struct wim_inode *inode; int ret; list_for_each_entry(dentry, dentry_list, d_extraction_list_node) { -- 2.43.0