]> wimlib.net Git - wimlib/blobdiff - src/win32_apply.c
system compression: add several more exclusion patterns
[wimlib] / src / win32_apply.c
index 7a93c4198114b9d1db0f3790a6679fc87f7f62c6..9a03aa1188349d98aaa635f9f44600f27b5fb773 100644 (file)
@@ -151,6 +151,11 @@ struct win32_apply_ctx {
        /* Number of files on which we couldn't set System Compression.  */
        unsigned long num_system_compression_failures;
 
+       /* The number of files on which we used XPRESS4K System Compression
+        * rather than a stronger variant, to be compatible with the Windows
+        * bootloader.  */
+       unsigned long num_xpress4k_forced_files;
+
        /* Have we tried to enable short name support on the target volume yet?
         */
        bool tried_to_enable_short_names;
@@ -2264,6 +2269,22 @@ get_system_compression_format(int extract_flags)
        return FILE_PROVIDER_COMPRESSION_FORMAT_LZX;
 }
 
+
+static const wchar_t *
+get_system_compression_format_string(int format)
+{
+       switch (format) {
+       case FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS4K:
+               return L"XPRESS4K";
+       case FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS8K:
+               return L"XPRESS8K";
+       case FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS16K:
+               return L"XPRESS16K";
+       default:
+               return L"LZX";
+       }
+}
+
 static NTSTATUS
 set_system_compression(HANDLE h, int format)
 {
@@ -2299,6 +2320,113 @@ set_system_compression(HANDLE h, int format)
        return status;
 }
 
+/* Hard-coded list of files which the Windows bootloader needs to access before
+ * the WOF driver has been loaded.  Since the Windows bootloader only supports
+ * the XPRESS4K variant of System Compression, such files should not be
+ * compressed using other variants.  */
+static wchar_t *xpress4k_only_pattern_strings[] = {
+       L"*winload.*",
+       L"*winresume.*",
+       L"\\Windows\\AppPatch\\drvmain.sdb",
+       L"\\Windows\\Boot\\DVD\\*",
+       L"\\Windows\\Boot\\EFI\\*",
+       L"\\Windows\\bootstat.dat",
+       L"\\Windows\\Fonts\\vgaoem.fon",
+       L"\\Windows\\Fonts\\vgasys.fon",
+       L"\\Windows\\INF\\errata.inf",
+       L"\\Windows\\System32\\config\\*",
+       L"\\Windows\\System32\\ntkrnlpa.exe",
+       L"\\Windows\\System32\\ntoskrnl.exe",
+       L"\\Windows\\System32\\bootvid.dll",
+       L"\\Windows\\System32\\ci.dll",
+       L"\\Windows\\System32\\hal*.dll",
+       L"\\Windows\\System32\\mcupdate_AuthenticAMD.dll",
+       L"\\Windows\\System32\\mcupdate_GenuineIntel.dll",
+       L"\\Windows\\System32\\pshed.dll",
+       L"\\Windows\\System32\\apisetschema.dll",
+       L"\\Windows\\System32\\api-ms-win*.dll",
+       L"\\Windows\\System32\\ext-ms-win*.dll",
+       L"\\Windows\\System32\\KernelBase.dll",
+       L"\\Windows\\System32\\drivers\\*.sys",
+       L"\\Windows\\System32\\*.nls",
+       L"\\Windows\\System32\\kbd*.dll",
+       L"\\Windows\\System32\\kd*.dll",
+       L"\\Windows\\System32\\clfs.sys",
+       L"\\Windows\\System32\\CodeIntegrity\\driver.stl",
+};
+
+static const struct string_set xpress4k_only_patterns = {
+       .strings = xpress4k_only_pattern_strings,
+       .num_strings = ARRAY_LEN(xpress4k_only_pattern_strings),
+};
+
+static NTSTATUS
+set_system_compression_on_inode(struct wim_inode *inode, int format,
+                               struct win32_apply_ctx *ctx)
+{
+       bool retried = false;
+       NTSTATUS status;
+       HANDLE h;
+
+       /* If needed, force the XPRESS4K format for this file.  */
+       if (format != FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS4K) {
+               /* We need to check the patterns against every name of the
+                * inode, in case any of them match.  */
+               struct wim_dentry *dentry;
+               inode_for_each_extraction_alias(dentry, inode) {
+                       bool incompatible;
+
+                       if (calculate_dentry_full_path(dentry)) {
+                               ERROR("Unable to compute file path!");
+                               return STATUS_NO_MEMORY;
+                       }
+
+                       incompatible = match_pattern_list(dentry->d_full_path,
+                                                         &xpress4k_only_patterns);
+                       FREE(dentry->d_full_path);
+                       dentry->d_full_path = NULL;
+
+                       if (incompatible) {
+                               if (ctx->num_xpress4k_forced_files++ == 0) {
+                                       WARNING("For compatibility with the "
+                                               "Windows bootloader, some "
+                                               "files are being\n"
+                                               "          compacted "
+                                               "using the XPRESS4K format "
+                                               "instead of the %"TS" format\n"
+                                               "          you requested.",
+                                               get_system_compression_format_string(format));
+                               }
+                               format = FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS4K;
+                               break;
+                       }
+               }
+       }
+
+       /* Open the extracted file.  */
+       status = create_file(&h, GENERIC_READ | GENERIC_WRITE, NULL,
+                            0, FILE_OPEN, 0,
+                            inode_first_extraction_dentry(inode), ctx);
+
+       if (!NT_SUCCESS(status))
+               return status;
+retry:
+       /* Compress the file.  If the attempt fails with "invalid device
+        * request", then attach wof.sys (or wofadk.sys) and retry.  */
+       status = set_system_compression(h, format);
+       if (unlikely(status == STATUS_INVALID_DEVICE_REQUEST && !retried)) {
+               wchar_t drive_path[7];
+               if (!win32_get_drive_path(ctx->common.target, drive_path) &&
+                   win32_try_to_attach_wof(drive_path + 4)) {
+                       retried = true;
+                       goto retry;
+               }
+       }
+
+       (*func_NtClose)(h);
+       return status;
+}
+
 /*
  * This function is called when doing a "compact-mode" extraction and we just
  * finished extracting a blob to one or more locations.  For each location that
@@ -2325,7 +2453,6 @@ handle_system_compression(struct blob_descriptor *blob, struct win32_apply_ctx *
        for (u32 i = 0; i < blob->out_refcnt; i++) {
                struct wim_inode *inode = targets[i].inode;
                struct wim_inode_stream *strm = targets[i].stream;
-               HANDLE h;
                NTSTATUS status;
 
                if (!stream_is_unnamed_data_stream(strm))
@@ -2334,14 +2461,9 @@ handle_system_compression(struct blob_descriptor *blob, struct win32_apply_ctx *
                if (will_externally_back_inode(inode, ctx, NULL, false) != 0)
                        continue;
 
-               status = create_file(&h, GENERIC_READ | GENERIC_WRITE, NULL,
-                                    0, FILE_OPEN, 0,
-                                    inode_first_extraction_dentry(inode), ctx);
-
-               if (NT_SUCCESS(status)) {
-                       status = set_system_compression(h, format);
-                       (*func_NtClose)(h);
-               }
+               status = set_system_compression_on_inode(inode, format, ctx);
+               if (likely(NT_SUCCESS(status)))
+                       continue;
 
                if (status == STATUS_INVALID_DEVICE_REQUEST) {
                        WARNING(
@@ -2353,16 +2475,14 @@ handle_system_compression(struct blob_descriptor *blob, struct win32_apply_ctx *
                        return;
                }
 
-               if (!NT_SUCCESS(status)) {
-                       ctx->num_system_compression_failures++;
-                       if (ctx->num_system_compression_failures < 10) {
-                               winnt_warning(status, L"\"%ls\": Failed to compress "
-                                             "extracted file using System Compression",
-                                             current_path(ctx));
-                       } else if (ctx->num_system_compression_failures == 10) {
-                               WARNING("Suppressing further warnings about "
-                                       "System Compression failures.");
-                       }
+               ctx->num_system_compression_failures++;
+               if (ctx->num_system_compression_failures < 10) {
+                       winnt_warning(status, L"\"%ls\": Failed to compress "
+                                     "extracted file using System Compression",
+                                     current_path(ctx));
+               } else if (ctx->num_system_compression_failures == 10) {
+                       WARNING("Suppressing further warnings about "
+                               "System Compression failures.");
                }
        }
 }