win32_apply.c: Match PrepopulateList patterns against all inode aliases
authorEric Biggers <ebiggers3@gmail.com>
Tue, 14 Oct 2014 03:18:35 +0000 (22:18 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Tue, 14 Oct 2014 04:15:48 +0000 (23:15 -0500)
include/wimlib/inode.h
include/wimlib/wimboot.h
src/extract.c
src/wimboot.c
src/win32_apply.c

index 879375c..733d384 100644 (file)
@@ -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;
index a103901..c10d0dc 100644 (file)
@@ -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],
index 5404b67..b0df25d 100644 (file)
@@ -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
index 3562890..8d3421c 100644 (file)
@@ -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__ */
index 5669e44..30da6b9 100644 (file)
@@ -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) {