]> wimlib.net Git - wimlib/blobdiff - src/win32_apply.c
New helper: wim_reshdr_to_desc_and_blob()
[wimlib] / src / win32_apply.c
index b326e182a5eb788f8827c31ec0ca5f8675052299..9d627dbe274114853bad69faf45512d667f5bed0 100644 (file)
@@ -50,12 +50,24 @@ struct win32_apply_ctx {
        /* WIMBoot information, only filled in if WIMLIB_EXTRACT_FLAG_WIMBOOT
         * was provided  */
        struct {
-               u64 data_source_id;
+               /* This array contains the WIM files registered with WOF on the
+                * target volume for this extraction operation.  All WIMStructs
+                * in this array are distinct and have ->filename != NULL.  */
+               struct wimboot_wim {
+                       WIMStruct *wim;
+                       u64 data_source_id;
+                       u8 blob_table_hash[SHA1_HASH_SIZE];
+               } *wims;
+               size_t num_wims;
                struct string_set *prepopulate_pats;
                void *mem_prepopulate_pats;
-               u8 blob_table_hash[SHA1_HASH_SIZE];
                bool wof_running;
                bool tried_to_load_prepopulate_list;
+
+               bool have_wrong_version_wims;
+               bool have_uncompressed_wims;
+               bool have_unsupported_compressed_resources;
+               bool have_huge_resources;
        } wimboot;
 
        /* Open handle to the target directory  */
@@ -277,7 +289,7 @@ load_prepopulate_pats(struct win32_apply_ctx *ctx)
 {
        const wchar_t *path = L"\\Windows\\System32\\WimBootCompress.ini";
        struct wim_dentry *dentry;
-       struct blob_descriptor *blob;
+       const struct blob_descriptor *blob;
        int ret;
        void *buf;
        struct string_set *s;
@@ -294,11 +306,14 @@ load_prepopulate_pats(struct win32_apply_ctx *ctx)
            !(blob = inode_get_blob_for_unnamed_data_stream(dentry->d_inode,
                                                            ctx->common.wim->blob_table)))
        {
-               WARNING("%ls does not exist in WIM image!", path);
+               WARNING("%ls does not exist in the WIM image.\n"
+                       "          The default configuration will be used instead; it assumes that all\n"
+                       "          files are valid for external backing regardless of path, equivalent\n"
+                       "          to an empty [PrepopulateList] section.", path);
                return WIMLIB_ERR_PATH_DOES_NOT_EXIST;
        }
 
-       ret = read_full_blob_into_alloc_buf(blob, &buf);
+       ret = read_blob_into_alloc_buf(blob, &buf);
        if (ret)
                return ret;
 
@@ -366,6 +381,75 @@ can_externally_back_path(const wchar_t *path, size_t path_nchars,
        return true;
 }
 
+static bool
+is_resource_valid_for_external_backing(const struct wim_resource_descriptor *rdesc,
+                                      struct win32_apply_ctx *ctx)
+{
+       /* Must be the original WIM file format.  This check excludes pipable
+        * resources and solid resources.  It also excludes other resources
+        * contained in such files even if they would be otherwise compatible.
+        */
+       if (rdesc->wim->hdr.magic != WIM_MAGIC ||
+           rdesc->wim->hdr.wim_version != WIM_VERSION_DEFAULT)
+       {
+               ctx->wimboot.have_wrong_version_wims = true;
+               return false;
+       }
+
+       /*
+        * Whitelist of compression types and chunk sizes supported by
+        * Microsoft's WOF driver.
+        *
+        * Notes:
+        *    - Uncompressed WIMs result in BSOD.  However, this only applies to
+        *      the WIM file itself, not to uncompressed resources in a WIM file
+        *      that is otherwise compressed.
+        *    - XPRESS 64K sometimes appears to work, but sometimes it causes
+        *      reads to fail with STATUS_UNSUCCESSFUL.
+        */
+       switch (rdesc->compression_type) {
+       case WIMLIB_COMPRESSION_TYPE_NONE:
+               if (rdesc->wim->compression_type == WIMLIB_COMPRESSION_TYPE_NONE) {
+                       ctx->wimboot.have_uncompressed_wims = true;
+                       return false;
+               }
+               break;
+       case WIMLIB_COMPRESSION_TYPE_XPRESS:
+               switch (rdesc->chunk_size) {
+               case 4096:
+               case 8192:
+               case 16384:
+               case 32768:
+                       break;
+               default:
+                       ctx->wimboot.have_unsupported_compressed_resources = true;
+                       return false;
+               }
+               break;
+       case WIMLIB_COMPRESSION_TYPE_LZX:
+               switch (rdesc->chunk_size) {
+               case 32768:
+                       break;
+               default:
+                       ctx->wimboot.have_unsupported_compressed_resources = true;
+                       return false;
+               }
+               break;
+       default:
+               ctx->wimboot.have_unsupported_compressed_resources = true;
+               return false;
+       }
+
+       /* Microsoft's WoF driver errors out if it tries to satisfy a read with
+        * ending offset >= 4 GiB from an externally backed file.  */
+       if (rdesc->uncompressed_size > 4200000000) {
+               ctx->wimboot.have_huge_resources = true;
+               return false;
+       }
+
+       return true;
+}
+
 #define WIM_BACKING_NOT_ENABLED                -1
 #define WIM_BACKING_NOT_POSSIBLE       -2
 #define WIM_BACKING_EXCLUDED           -3
@@ -394,13 +478,8 @@ will_externally_back_inode(struct wim_inode *inode, struct win32_apply_ctx *ctx,
 
        blob = inode_get_blob_for_unnamed_data_stream_resolved(inode);
 
-       /* Note: Microsoft's WoF driver errors out if it tries to satisfy a
-        * read, with ending offset >= 4 GiB, from an externally backed file. */
-       if (!blob ||
-           blob->blob_location != BLOB_IN_WIM ||
-           blob->rdesc->wim != ctx->common.wim ||
-           blob->size != blob->rdesc->uncompressed_size ||
-           blob->size > 4200000000)
+       if (!blob || blob->blob_location != BLOB_IN_WIM ||
+           !is_resource_valid_for_external_backing(blob->rdesc, ctx))
                return WIM_BACKING_NOT_POSSIBLE;
 
        /*
@@ -449,11 +528,25 @@ win32_will_externally_back(struct wim_dentry *dentry, struct apply_ctx *_ctx)
        return will_externally_back_inode(dentry->d_inode, ctx, NULL);
 }
 
+/* Find the WOF registration information for the specified WIM file.  */
+static struct wimboot_wim *
+find_wimboot_wim(WIMStruct *wim_to_find, struct win32_apply_ctx *ctx)
+{
+       for (size_t i = 0; i < ctx->wimboot.num_wims; i++)
+               if (wim_to_find == ctx->wimboot.wims[i].wim)
+                       return &ctx->wimboot.wims[i];
+
+       wimlib_assert(0);
+       return NULL;
+}
+
 static int
 set_external_backing(HANDLE h, struct wim_inode *inode, struct win32_apply_ctx *ctx)
 {
        int ret;
        const struct wim_dentry *excluded_dentry;
+       const struct blob_descriptor *blob;
+       const struct wimboot_wim *wimboot_wim;
 
        ret = will_externally_back_inode(inode, ctx, &excluded_dentry);
        if (ret > 0) /* Error.  */
@@ -474,23 +567,27 @@ set_external_backing(HANDLE h, struct wim_inode *inode, struct win32_apply_ctx *
                return call_progress(ctx->common.progfunc,
                                     WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE,
                                     &info, ctx->common.progctx);
-       } else {
-               /* Externally backing.  */
-               if (unlikely(!wimboot_set_pointer(h,
-                                                 inode_get_blob_for_unnamed_data_stream_resolved(inode),
-                                                 ctx->wimboot.data_source_id,
-                                                 ctx->wimboot.blob_table_hash,
-                                                 ctx->wimboot.wof_running)))
-               {
-                       const DWORD err = GetLastError();
+       }
 
-                       build_extraction_path(inode_first_extraction_dentry(inode), ctx);
-                       win32_error(err, L"\"%ls\": Couldn't set WIMBoot pointer data",
-                                   current_path(ctx));
-                       return WIMLIB_ERR_WIMBOOT;
-               }
-               return 0;
+       /* Externally backing.  */
+
+       blob = inode_get_blob_for_unnamed_data_stream_resolved(inode);
+       wimboot_wim = find_wimboot_wim(blob->rdesc->wim, ctx);
+
+       if (unlikely(!wimboot_set_pointer(h,
+                                         blob,
+                                         wimboot_wim->data_source_id,
+                                         wimboot_wim->blob_table_hash,
+                                         ctx->wimboot.wof_running)))
+       {
+               const DWORD err = GetLastError();
+
+               build_extraction_path(inode_first_extraction_dentry(inode), ctx);
+               win32_error(err, L"\"%ls\": Couldn't set WIMBoot pointer data",
+                           current_path(ctx));
+               return WIMLIB_ERR_WIMBOOT;
        }
+       return 0;
 }
 
 /* Calculates the SHA-1 message digest of the WIM's blob table.  */
@@ -500,32 +597,108 @@ hash_blob_table(WIMStruct *wim, u8 hash[SHA1_HASH_SIZE])
        return wim_reshdr_to_hash(&wim->hdr.blob_table_reshdr, wim, hash);
 }
 
+static int
+register_wim_with_wof(WIMStruct *wim, struct win32_apply_ctx *ctx)
+{
+       struct wimboot_wim *p;
+       int ret;
+
+       /* Check if already registered  */
+       for (size_t i = 0; i < ctx->wimboot.num_wims; i++)
+               if (wim == ctx->wimboot.wims[i].wim)
+                       return 0;
+
+       /* Not yet registered  */
+
+       p = REALLOC(ctx->wimboot.wims,
+                   (ctx->wimboot.num_wims + 1) * sizeof(ctx->wimboot.wims[0]));
+       if (!p)
+               return WIMLIB_ERR_NOMEM;
+       ctx->wimboot.wims = p;
+
+       ctx->wimboot.wims[ctx->wimboot.num_wims].wim = wim;
+
+       ret = hash_blob_table(wim, ctx->wimboot.wims[ctx->wimboot.num_wims].blob_table_hash);
+       if (ret)
+               return ret;
+
+       ret = wimboot_alloc_data_source_id(wim->filename,
+                                          wim->hdr.guid,
+                                          ctx->common.wim->current_image,
+                                          ctx->common.target,
+                                          &ctx->wimboot.wims[ctx->wimboot.num_wims].data_source_id,
+                                          &ctx->wimboot.wof_running);
+       if (ret)
+               return ret;
+
+       ctx->wimboot.num_wims++;
+       return 0;
+}
+
 /* Prepare for doing a "WIMBoot" extraction by loading patterns from
- * [PrepopulateList] of WimBootCompress.ini and allocating a WOF data source ID
- * on the target volume.  */
+ * [PrepopulateList] of WimBootCompress.ini and registering each source WIM file
+ * with WOF on the target volume.  */
 static int
-start_wimboot_extraction(struct win32_apply_ctx *ctx)
+start_wimboot_extraction(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
 {
        int ret;
-       WIMStruct *wim = ctx->common.wim;
+       struct wim_dentry *dentry;
 
        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!");
+       if (!wim_info_get_wimboot(ctx->common.wim->wim_info,
+                                 ctx->common.wim->current_image))
+               WARNING("The WIM image is not marked as WIMBoot compatible.  This usually\n"
+                       "          means it is not intended to be used to back a Windows operating\n"
+                       "          system.  Proceeding anyway.");
 
-       ret = hash_blob_table(ctx->common.wim, ctx->wimboot.blob_table_hash);
-       if (ret)
-               return ret;
+       list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
+               struct blob_descriptor *blob;
 
-       return wimboot_alloc_data_source_id(wim->filename,
-                                           wim->hdr.guid,
-                                           wim->current_image,
-                                           ctx->common.target,
-                                           &ctx->wimboot.data_source_id,
-                                           &ctx->wimboot.wof_running);
+               ret = win32_will_externally_back(dentry, &ctx->common);
+               if (ret > 0) /* Error */
+                       return ret;
+               if (ret < 0) /* Won't externally back */
+                       continue;
+
+               blob = inode_get_blob_for_unnamed_data_stream_resolved(dentry->d_inode);
+               ret = register_wim_with_wof(blob->rdesc->wim, ctx);
+               if (ret)
+                       return ret;
+       }
+
+       if (ctx->wimboot.have_wrong_version_wims) {
+  WARNING("At least one of the source WIM files uses a version of the WIM\n"
+"          file format that not supported by Microsoft's wof.sys driver.\n"
+"          Files whose data is contained in one of these WIM files will be\n"
+"          extracted as full files rather than externally backed.");
+       }
+
+       if (ctx->wimboot.have_uncompressed_wims) {
+  WARNING("At least one of the source WIM files is uncompressed.  Files whose\n"
+"          data is contained in an uncompressed WIM file will be extracted as\n"
+"          full files rather than externally backed, since uncompressed WIM\n"
+"          files are not supported by Microsoft's wof.sys driver.");
+       }
+
+       if (ctx->wimboot.have_unsupported_compressed_resources) {
+  WARNING("At least one of the source WIM files uses a compression format that\n"
+"          is not supported by Microsoft's wof.sys driver.  Files whose data is\n"
+"          contained in a compressed resource in one of these WIM files will be\n"
+"          extracted as full files rather than externally backed.  (The\n"
+"          compression formats supported by wof.sys are: XPRESS 4K, XPRESS 8K,\n"
+"          XPRESS 16K, XPRESS 32K, and LZX 32K.)");
+       }
+
+       if (ctx->wimboot.have_huge_resources) {
+  WARNING("Some files exceeded 4.2 GB in size.  Such files will be extracted\n"
+"          as full files rather than externally backed, since very large files\n"
+"          are not supported by Microsoft's wof.sys driver.");
+       }
+
+       return 0;
 }
 
 static void
@@ -2465,11 +2638,11 @@ do_warnings(const struct win32_apply_ctx *ctx)
        }
 }
 
-static uint64_t
+static u64
 count_dentries(const struct list_head *dentry_list)
 {
        const struct list_head *cur;
-       uint64_t count = 0;
+       u64 count = 0;
 
        list_for_each(cur, dentry_list)
                count++;
@@ -2483,14 +2656,14 @@ win32_extract(struct list_head *dentry_list, struct apply_ctx *_ctx)
 {
        int ret;
        struct win32_apply_ctx *ctx = (struct win32_apply_ctx *)_ctx;
-       uint64_t dentry_count;
+       u64 dentry_count;
 
        ret = prepare_target(dentry_list, ctx);
        if (ret)
                goto out;
 
        if (unlikely(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT)) {
-               ret = start_wimboot_extraction(ctx);
+               ret = start_wimboot_extraction(dentry_list, ctx);
                if (ret)
                        goto out;
        }
@@ -2513,13 +2686,11 @@ win32_extract(struct list_head *dentry_list, struct apply_ctx *_ctx)
        if (ret)
                goto out;
 
-       struct read_blob_list_callbacks cbs = {
-               .begin_blob        = begin_extract_blob,
-               .begin_blob_ctx    = ctx,
-               .consume_chunk     = extract_chunk,
-               .consume_chunk_ctx = ctx,
-               .end_blob          = end_extract_blob,
-               .end_blob_ctx      = ctx,
+       struct read_blob_callbacks cbs = {
+               .begin_blob     = begin_extract_blob,
+               .consume_chunk  = extract_chunk,
+               .end_blob       = end_extract_blob,
+               .ctx            = ctx,
        };
        ret = extract_blob_list(&ctx->common, &cbs);
        if (ret)
@@ -2550,6 +2721,7 @@ out:
                HeapFree(GetProcessHeap(), 0, ctx->target_ntpath.Buffer);
        FREE(ctx->pathbuf.Buffer);
        FREE(ctx->print_buffer);
+       FREE(ctx->wimboot.wims);
        if (ctx->wimboot.prepopulate_pats) {
                FREE(ctx->wimboot.prepopulate_pats->strings);
                FREE(ctx->wimboot.prepopulate_pats);