]> wimlib.net Git - wimlib/commitdiff
Merge branch 'wimboot_fast'
authorEric Biggers <ebiggers3@gmail.com>
Fri, 29 Aug 2014 00:45:34 +0000 (19:45 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Fri, 29 Aug 2014 00:45:34 +0000 (19:45 -0500)
include/wimlib/apply.h
include/wimlib/wimboot.h
src/extract.c
src/wimboot.c
src/win32_apply.c

index 73ddd871b2200c2e386cab68679427f751bd87a1..d8e6d936f66900302cbdb9f4a9f6f4ffc8f6d23e 100644 (file)
@@ -33,6 +33,8 @@ struct wim_features {
 
 struct wim_lookup_table_entry;
 struct read_stream_list_callbacks;
+struct apply_operations;
+struct wim_dentry;
 
 struct apply_ctx {
        /* The WIMStruct from which files are being extracted from the currently
@@ -62,6 +64,7 @@ struct apply_ctx {
        struct wim_features supported_features;
 
        /* The members below should not be used outside of extract.c  */
+       const struct apply_operations *apply_ops;
        u64 next_progress;
        unsigned long invalid_sequence;
        unsigned long num_streams_remaining;
@@ -134,14 +137,105 @@ extern int
 extract_stream_list(struct apply_ctx *ctx,
                    const struct read_stream_list_callbacks *cbs);
 
+/*
+ * Represents an extraction backend.
+ */
 struct apply_operations {
+
+       /* Name of the extraction backend.  */
        const char *name;
+
+       /*
+        * Query the features supported by the extraction backend.
+        *
+        * @target
+        *      The target string that was provided by the user.  (Often a
+        *      directory, but extraction backends are free to interpret this
+        *      differently.)
+        *
+        * @supported_features
+        *      A structure, each of whose members represents a feature that may
+        *      be supported by the extraction backend.  For each feature that
+        *      the extraction backend supports, this routine must set the
+        *      corresponding member to a nonzero value.
+        *
+        * Return 0 if successful; otherwise a positive wimlib error code.
+        */
        int (*get_supported_features)(const tchar *target,
                                      struct wim_features *supported_features);
 
+       /*
+        * Main extraction routine.
+        *
+        * The extraction backend is provided a list of dentries that have been
+        * prepared for extraction.  It is free to extract them in any way that
+        * it chooses.  Ideally, it should choose a method that maximizes
+        * performance.
+        *
+        * The target string will be provided in ctx->common.target.  This might
+        * be a directory, although extraction backends are free to interpret it
+        * as they wish.  TODO: in some cases, the common extraction code also
+        * interprets the target string.  This should be completely isolated to
+        * extraction backends.
+        *
+        * The extraction flags will be provided in ctx->common.extract_flags.
+        * Extraction backends should examine them and implement the behaviors
+        * for as many flags as possible.  Some flags are already handled by the
+        * common extraction code.  TODO: this needs to be better formalized.
+        *
+        * @dentry_list, the list of dentries, will be ordered such that the
+        * ancestor of any dentry always precedes any descendents.  Unless
+        * @single_tree_only is set, it's possible that the dentries consist of
+        * multiple disconnected trees.
+        *
+        * 'd_extraction_name' and 'd_extraction_name_nchars' of each dentry
+        * will be set to indicate the actual name with which the dentry should
+        * be extracted.  This may or may not be the same as 'file_name'.
+        * TODO: really, the extraction backends should be responsible for
+        * generating 'd_extraction_name'.
+        *
+        * Each dentry will refer to a valid inode in 'd_inode'.
+        * 'd_inode->i_extraction_aliases' will contain a list of just the
+        * dentries of that inode being extracted.  This will be a (possibly
+        * nonproper) subset of the 'd_inode->i_dentry' list.
+        *
+        * The streams required to be extracted will already be prepared in
+        * 'apply_ctx'.  The extraction backend should call
+        * extract_stream_list() to extract them.
+        *
+        * The will_extract_dentry() utility function, given an arbitrary dentry
+        * in the WIM image (which may not be in the extraction list), can be
+        * used to determine if that dentry is in the extraction list.
+        *
+        * Return 0 if successful; otherwise a positive wimlib error code.
+        */
        int (*extract)(struct list_head *dentry_list, struct apply_ctx *ctx);
 
+       /*
+        * Query whether the unnamed data stream of the specified file will be
+        * extracted as "externally backed".  If so, the extraction backend is
+        * assumed to handle this separately, and the common extraction code
+        * will not register a usage of that stream.
+        *
+        * This routine is optional.
+        *
+        * Return:
+        *      < 0 if the file will *not* be externally backed.
+        *      = 0 if the file will be externally backed.
+        *      > 0 (wimlib error code) if another error occurred.
+        */
+       int (*will_externally_back)(struct wim_dentry *dentry, struct apply_ctx *ctx);
+
+       /*
+        * Size of the backend-specific extraction context.  It must contain
+        * 'struct apply_ctx' as its first member.
+        */
        size_t context_size;
+
+       /*
+        * Set this if the extraction backend only supports extracting dentries
+        * that form a single tree, not multiple trees.
+        */
        bool single_tree_only;
 };
 
index 6be4fae204d6c294b8a9a435abe3bec19e023dde..a1039017d942e777be54a395b4a9377ce494b230 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef _WIMBOOT_H_
 #define _WIMBOOT_H_
 
+#include "wimlib/win32_common.h"
 #include "wimlib/sha1.h"
 #include "wimlib/types.h"
 #include "wimlib/header.h"
@@ -14,7 +15,7 @@ wimboot_alloc_data_source_id(const wchar_t *wim_path,
                             bool *wof_running_ret);
 
 extern int
-wimboot_set_pointer(OBJECT_ATTRIBUTES *attr,
+wimboot_set_pointer(HANDLE h,
                    const wchar_t *printable_path,
                    const struct wim_lookup_table_entry *lte,
                    u64 data_source_id,
index 8e35994aa6a48c60079c31198c1c7a25fdeffa24..0029aa88016097b161ac515f61ffbbacf3c27cff 100644 (file)
@@ -1080,23 +1080,43 @@ ref_stream(struct wim_lookup_table_entry *lte, u32 stream_idx,
 }
 
 static int
-dentry_ref_streams(struct wim_dentry *dentry, struct apply_ctx *ctx)
+ref_unnamed_stream(struct wim_dentry *dentry, struct apply_ctx *ctx)
 {
        struct wim_inode *inode = dentry->d_inode;
        int ret;
+       u16 stream_idx;
+       struct wim_lookup_table_entry *stream;
 
-       /* The unnamed data stream will always be extracted, except in an
-        * unlikely case.  */
-       if (!inode_is_encrypted_directory(inode)) {
-               u16 stream_idx;
-               struct wim_lookup_table_entry *stream;
+       if (unlikely(inode_is_encrypted_directory(inode)))
+               return 0;
 
-               stream = inode_unnamed_stream_resolved(inode, &stream_idx);
-               ret = ref_stream(stream, stream_idx, dentry, ctx);
-               if (ret)
-                       return ret;
+       if (unlikely(ctx->apply_ops->will_externally_back)) {
+               ret = (*ctx->apply_ops->will_externally_back)(dentry, ctx);
+               if (ret >= 0) {
+                       if (ret) /* Error */
+                               return ret;
+                       /* Will externally back */
+                       return 0;
+               }
+               /* Won't externally back */
        }
 
+       stream = inode_unnamed_stream_resolved(inode, &stream_idx);
+       return ref_stream(stream, stream_idx, dentry, ctx);
+}
+
+static int
+dentry_ref_streams(struct wim_dentry *dentry, struct apply_ctx *ctx)
+{
+       struct wim_inode *inode = dentry->d_inode;
+       int ret;
+
+       /* The unnamed data stream will almost always be extracted, but there
+        * exist cases in which it won't be.  */
+       ret = ref_unnamed_stream(dentry, ctx);
+       if (ret)
+               return ret;
+
        /* Named data streams will be extracted only if supported in the current
         * extraction mode and volume, and to avoid complications, if not doing
         * a linked extraction.  */
@@ -1433,6 +1453,7 @@ extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees,
        }
        INIT_LIST_HEAD(&ctx->stream_list);
        filedes_invalidate(&ctx->tmpfile_fd);
+       ctx->apply_ops = ops;
 
        ret = (*ops->get_supported_features)(target, &ctx->supported_features);
        if (ret)
index 514ff8c5b53c1122fbe70f48f80b90c04ad3abd9..cdd74d9468f267dece137673db408ec30a838068 100644 (file)
@@ -1024,10 +1024,10 @@ out:
  * This turns it into a reparse point that redirects accesses to it, to the
  * corresponding resource in the WIM archive.
  *
- * @attr
- *     Object attributes that specify the path to the file.
+ * @h
+ *     Open handle to the file, with GENERIC_WRITE access.
  * @printable_name
- *     Printable representation of the path encoded in @attr.
+ *     Printable representation of the path to the file.
  * @lte
  *     Unnamed data stream of the file.
  * @data_source_id
@@ -1041,30 +1041,16 @@ out:
  * Returns 0 on success, or a positive error code on failure.
  */
 int
-wimboot_set_pointer(OBJECT_ATTRIBUTES *attr,
+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],
                    bool wof_running)
 {
-       int ret;
-       HANDLE h = NULL;
-       NTSTATUS status;
-       IO_STATUS_BLOCK iosb;
        DWORD bytes_returned;
        DWORD err;
 
-       status = (*func_NtOpenFile)(&h, GENERIC_WRITE | SYNCHRONIZE, attr,
-                                   &iosb, FILE_SHARE_VALID_FLAGS,
-                                   FILE_OPEN_FOR_BACKUP_INTENT |
-                                       FILE_OPEN_REPARSE_POINT |
-                                       FILE_SYNCHRONOUS_IO_NONALERT);
-       if (!NT_SUCCESS(status)) {
-               SetLastError((*func_RtlNtStatusToDosError)(status));
-               goto fail;
-       }
-
        if (wof_running) {
                /* The WOF driver is running.  We can create the reparse point
                 * using FSCTL_SET_EXTERNAL_BACKING.  */
@@ -1144,20 +1130,14 @@ wimboot_set_pointer(OBJECT_ATTRIBUTES *attr,
                        goto fail;
        }
 
-       ret = 0;
-       goto out;
+       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);
-       ret = WIMLIB_ERR_WIMBOOT;
-out:
-       if (h)
-               (*func_NtClose)(h);
-       return ret;
-
+       return WIMLIB_ERR_WIMBOOT;
 }
 
 #endif /* __WIN32__ */
index 987a4fa855004526f2aafab86e641b3151744255..9fac1561a32a76d8cb01ca1047ded7c2bb6a4def 100644 (file)
@@ -53,6 +53,7 @@ struct win32_apply_ctx {
                void *mem_prepopulate_pats;
                u8 wim_lookup_table_hash[SHA1_HASH_SIZE];
                bool wof_running;
+               bool tried_to_load_prepopulate_list;
        } wimboot;
 
        /* Open handle to the target directory  */
@@ -256,6 +257,8 @@ load_prepopulate_pats(struct win32_apply_ctx *ctx)
        void *mem;
        struct text_file_section sec;
 
+       ctx->wimboot.tried_to_load_prepopulate_list = true;
+
        dentry = get_dentry(ctx->common.wim, path, WIMLIB_CASE_INSENSITIVE);
        if (!dentry ||
            (dentry->d_inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY |
@@ -312,6 +315,95 @@ in_prepopulate_list(struct wim_dentry *dentry,
                                  wcslen(dentry->_full_path), pats);
 }
 
+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)
+{
+       struct win32_apply_ctx *ctx = (struct win32_apply_ctx *)_ctx;
+       struct wim_lookup_table_entry *stream;
+       int ret;
+
+       if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT))
+               return WIM_BACKING_NOT_ENABLED;
+
+       if (!ctx->wimboot.tried_to_load_prepopulate_list) {
+               ret = load_prepopulate_pats(ctx);
+               if (ret == WIMLIB_ERR_NOMEM)
+                       return ret;
+       }
+
+       if (dentry->d_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);
+
+       if (!stream ||
+           stream->resource_location != RESOURCE_IN_WIM ||
+           stream->rspec->wim != ctx->common.wim ||
+           stream->size != stream->rspec->uncompressed_size)
+               return WIM_BACKING_NOT_POSSIBLE;
+
+       ret = calculate_dentry_full_path(dentry);
+       if (ret)
+               return ret;
+
+       if (in_prepopulate_list(dentry, ctx))
+               return WIM_BACKING_EXCLUDED;
+
+       return 0;
+}
+
+static int
+set_external_backing(HANDLE h, struct wim_dentry *dentry, struct win32_apply_ctx *ctx)
+{
+       int ret;
+
+       ret = win32_will_externally_back(dentry, &ctx->common);
+       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) {
+               /* Not externally backing due to exclusion.  */
+               union wimlib_progress_info info;
+
+               info.wimboot_exclude.path_in_wim = dentry->_full_path;
+               info.wimboot_exclude.extraction_path = current_path(ctx);
+
+               return call_progress(ctx->common.progfunc,
+                                    WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE,
+                                    &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);
+       }
+}
+
 /* Calculates the SHA-1 message digest of the WIM's lookup table.  */
 static int
 hash_lookup_table(WIMStruct *wim, u8 hash[SHA1_HASH_SIZE])
@@ -328,9 +420,11 @@ start_wimboot_extraction(struct win32_apply_ctx *ctx)
        int ret;
        WIMStruct *wim = ctx->common.wim;
 
-       ret = load_prepopulate_pats(ctx);
-       if (ret == WIMLIB_ERR_NOMEM)
-               return ret;
+       if (!ctx->wimboot.tried_to_load_prepopulate_list) {
+               ret = load_prepopulate_pats(ctx);
+               if (ret == WIMLIB_ERR_NOMEM)
+                       return ret;
+       }
 
        if (!wim_info_get_wimboot(wim->wim_info, wim->current_image))
                WARNING("Image is not marked as WIMBoot compatible!");
@@ -650,11 +744,11 @@ prepare_target(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
 /* When creating an inode that will have a short (DOS) name, we create it using
  * the long name associated with the short name.  This ensures that the short
  * name gets associated with the correct long name.  */
-static const struct wim_dentry *
+static struct wim_dentry *
 first_extraction_alias(const struct wim_inode *inode)
 {
-       const struct list_head *next = inode->i_extraction_aliases.next;
-       const struct wim_dentry *dentry;
+       struct list_head *next = inode->i_extraction_aliases.next;
+       struct wim_dentry *dentry;
 
        do {
                dentry = list_entry(next, struct wim_dentry,
@@ -1324,7 +1418,7 @@ create_links(HANDLE h, const struct wim_dentry *first_dentry,
 static int
 create_nondirectory(const struct wim_inode *inode, struct win32_apply_ctx *ctx)
 {
-       const struct wim_dentry *first_dentry;
+       struct wim_dentry *first_dentry;
        HANDLE h;
        int ret;
 
@@ -1343,6 +1437,10 @@ create_nondirectory(const struct wim_inode *inode, struct win32_apply_ctx *ctx)
        if (!ret)
                ret = create_links(h, first_dentry, 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);
+
        (*func_NtClose)(h);
        return ret;
 }
@@ -1462,42 +1560,6 @@ begin_extract_stream_instance(const struct wim_lookup_table_entry *stream,
                return prepare_data_buffer(ctx, stream->size);
        }
 
-       /* Extracting unnamed data stream in WIMBoot mode?  */
-       if (unlikely(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT)
-           && (stream_name_nchars == 0)
-           && (stream->resource_location == RESOURCE_IN_WIM)
-           && (stream->rspec->wim == ctx->common.wim)
-           && (stream->size == stream->rspec->uncompressed_size))
-       {
-               int ret = calculate_dentry_full_path(dentry);
-               if (ret)
-                       return ret;
-               if (in_prepopulate_list(dentry, ctx)) {
-                       union wimlib_progress_info info;
-
-                       info.wimboot_exclude.path_in_wim = dentry->_full_path;
-                       info.wimboot_exclude.extraction_path = current_path(ctx);
-
-                       ret = call_progress(ctx->common.progfunc,
-                                           WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE,
-                                           &info, ctx->common.progctx);
-                       FREE(dentry->_full_path);
-                       dentry->_full_path = NULL;
-                       if (ret)
-                               return ret;
-                       /* Go on and open the file for normal extraction.  */
-               } else {
-                       FREE(dentry->_full_path);
-                       dentry->_full_path = NULL;
-                       return wimboot_set_pointer(&ctx->attr,
-                                                  current_path(ctx),
-                                                  stream,
-                                                  ctx->wimboot.data_source_id,
-                                                  ctx->wimboot.wim_lookup_table_hash,
-                                                  ctx->wimboot.wof_running);
-               }
-       }
-
        if (ctx->num_open_handles == MAX_OPEN_STREAMS) {
                /* XXX: Fix this.  But because of the checks in
                 * extract_stream_list(), this can now only happen on a
@@ -2271,6 +2333,7 @@ const struct apply_operations win32_apply_ops = {
        .name                   = "Windows",
        .get_supported_features = win32_get_supported_features,
        .extract                = win32_extract,
+       .will_externally_back   = win32_will_externally_back,
        .context_size           = sizeof(struct win32_apply_ctx),
 };