]> wimlib.net Git - wimlib/blobdiff - src/win32_apply.c
Avoid passing NULL as memcpy source (undefined behavior)
[wimlib] / src / win32_apply.c
index ce62d6af98b651b6e6b961b897799bff60a75fde..cb488a9f6ed656013b74b67cacbf74b6c63914af 100644 (file)
@@ -151,6 +151,17 @@ 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 which, for compatibility with the Windows
+        * bootloader, were not compressed using the requested system
+        * compression format.  This includes matches with the hardcoded pattern
+        * list only; it does not include matches with patterns in
+        * [PrepopulateList].  */
+       unsigned long num_system_compression_exclusions;
+
+       /* The Windows build number of the image being applied, or 0 if unknown.
+        */
+       u64 windows_build_number;
+
        /* Have we tried to enable short name support on the target volume yet?
         */
        bool tried_to_enable_short_names;
@@ -213,6 +224,14 @@ get_vol_flags(const wchar_t *target, DWORD *vol_flags_ret,
        }
 }
 
+/* Is the image being extracted an OS image for Windows 10 or later?  */
+static bool
+is_image_windows_10_or_later(struct win32_apply_ctx *ctx)
+{
+       /* Note: if no build number is available, this returns false.  */
+       return ctx->windows_build_number >= 10240;
+}
+
 static const wchar_t *
 current_path(struct win32_apply_ctx *ctx);
 
@@ -667,8 +686,8 @@ start_wimboot_extraction(struct list_head *dentry_list, struct win32_apply_ctx *
        int ret;
        struct wim_dentry *dentry;
 
-       if (!wim_info_get_wimboot(ctx->common.wim->wim_info,
-                                 ctx->common.wim->current_image))
+       if (!xml_get_wimboot(ctx->common.wim->xml_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.");
@@ -882,7 +901,9 @@ build_extraction_path(const struct wim_dentry *dentry,
             d = d->d_parent)
        {
                p -= d->d_extraction_name_nchars;
-               wmemcpy(p, d->d_extraction_name, d->d_extraction_name_nchars);
+               if (d->d_extraction_name_nchars)
+                       wmemcpy(p, d->d_extraction_name,
+                               d->d_extraction_name_nchars);
                *--p = '\\';
        }
        /* No leading slash  */
@@ -970,6 +991,9 @@ open_target_directory(struct win32_apply_ctx *ctx)
        ctx->attr.Length = sizeof(ctx->attr);
        ctx->attr.RootDirectory = NULL;
        ctx->attr.ObjectName = &ctx->target_ntpath;
+
+       /* Don't use FILE_OPEN_REPARSE_POINT here; we want the extraction to
+        * happen at the directory "pointed to" by the reparse point. */
        status = (*func_NtCreateFile)(&ctx->h_target,
                                      FILE_TRAVERSE,
                                      &ctx->attr,
@@ -979,7 +1003,6 @@ open_target_directory(struct win32_apply_ctx *ctx)
                                      FILE_SHARE_VALID_FLAGS,
                                      FILE_OPEN_IF,
                                      FILE_DIRECTORY_FILE |
-                                             FILE_OPEN_REPARSE_POINT |
                                              FILE_OPEN_FOR_BACKUP_INTENT,
                                      NULL,
                                      0);
@@ -1078,6 +1101,7 @@ adjust_compression_attribute(HANDLE h, const struct wim_dentry *dentry,
        FILE_BASIC_INFORMATION info;
        NTSTATUS status;
        USHORT compression_state;
+       DWORD bytes_returned;
 
        /* Get current attributes  */
        status = (*func_NtQueryInformationFile)(h, &ctx->iosb,
@@ -1097,20 +1121,14 @@ adjust_compression_attribute(HANDLE h, const struct wim_dentry *dentry,
        else
                compression_state = COMPRESSION_FORMAT_NONE;
 
-       status = (*func_NtFsControlFile)(h,
-                                        NULL,
-                                        NULL,
-                                        NULL,
-                                        &ctx->iosb,
-                                        FSCTL_SET_COMPRESSION,
-                                        &compression_state,
-                                        sizeof(USHORT),
-                                        NULL,
-                                        0);
-       if (NT_SUCCESS(status))
+       /* Note: don't use NtFsControlFile() here unless prepared to handle
+        * STATUS_PENDING.  */
+       if (DeviceIoControl(h, FSCTL_SET_COMPRESSION,
+                           &compression_state, sizeof(USHORT), NULL, 0,
+                           &bytes_returned, NULL))
                return 0;
 
-       winnt_error(status, L"Can't %s compression attribute on \"%ls\"",
+       win32_error(GetLastError(), L"Can't %s compression attribute on \"%ls\"",
                    (compressed ? "set" : "clear"), current_path(ctx));
        return WIMLIB_ERR_SET_ATTRIBUTES;
 }
@@ -2264,6 +2282,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 +2333,44 @@ set_system_compression(HANDLE h, int format)
        return status;
 }
 
+/* Hard-coded list of files which the Windows bootloader may need to access
+ * before the WOF driver has been loaded.  */
+static wchar_t *bootloader_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 bootloader_patterns = {
+       .strings = bootloader_pattern_strings,
+       .num_strings = ARRAY_LEN(bootloader_pattern_strings),
+};
+
 static NTSTATUS
 set_system_compression_on_inode(struct wim_inode *inode, int format,
                                struct win32_apply_ctx *ctx)
@@ -2307,6 +2379,65 @@ set_system_compression_on_inode(struct wim_inode *inode, int format,
        NTSTATUS status;
        HANDLE h;
 
+       /* If it may be needed for compatibility with the Windows bootloader,
+        * force this file to XPRESS4K or uncompressed format.  The bootloader
+        * of Windows 10 supports XPRESS4K only; older versions don't support
+        * system compression at all.  */
+       if (!is_image_windows_10_or_later(ctx) ||
+           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;
+                       bool warned;
+
+                       if (calculate_dentry_full_path(dentry)) {
+                               ERROR("Unable to compute file path!");
+                               return STATUS_NO_MEMORY;
+                       }
+
+                       incompatible = match_pattern_list(dentry->d_full_path,
+                                                         &bootloader_patterns);
+                       FREE(dentry->d_full_path);
+                       dentry->d_full_path = NULL;
+
+                       if (!incompatible)
+                               continue;
+
+                       warned = (ctx->num_system_compression_exclusions++ > 0);
+
+                       if (is_image_windows_10_or_later(ctx)) {
+                               /* Force to XPRESS4K  */
+                               if (!warned) {
+                                       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;
+                       } else {
+                               /* Force to uncompressed  */
+                               if (!warned) {
+                                       WARNING("For compatibility with the "
+                                               "Windows bootloader, some "
+                                               "files will not\n"
+                                               "          be compressed with"
+                                               " system compression "
+                                               "(\"compacted\").");
+                               }
+                               return STATUS_SUCCESS;
+                       }
+
+               }
+       }
+
        /* Open the extracted file.  */
        status = create_file(&h, GENERIC_READ | GENERIC_WRITE, NULL,
                             0, FILE_OPEN, 0,
@@ -2806,6 +2937,9 @@ win32_extract(struct list_head *dentry_list, struct apply_ctx *_ctx)
                        goto out;
        }
 
+       ctx->windows_build_number = xml_get_windows_build_number(ctx->common.wim->xml_info,
+                                                                ctx->common.wim->current_image);
+
        dentry_count = count_dentries(dentry_list);
 
        ret = start_file_structure_phase(&ctx->common, dentry_count);