X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fwin32_apply.c;h=c35fbec26b7b769099b94ce930d1eef8013d45f5;hp=7a93c4198114b9d1db0f3790a6679fc87f7f62c6;hb=2fed42ae0eee69e6352425c29ca2b12af99f8992;hpb=211f0c729ab69879e28398d1d71d0a6bc01ed2c8 diff --git a/src/win32_apply.c b/src/win32_apply.c index 7a93c419..c35fbec2 100644 --- a/src/win32_apply.c +++ b/src/win32_apply.c @@ -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); @@ -2264,6 +2283,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 +2334,135 @@ 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) +{ + bool retried = false; + 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, + 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 +2489,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 +2497,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 +2511,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."); } } } @@ -2782,6 +2938,9 @@ win32_extract(struct list_head *dentry_list, struct apply_ctx *_ctx) goto out; } + ctx->windows_build_number = wim_info_get_windows_build_number(ctx->common.wim->wim_info, + ctx->common.wim->current_image); + dentry_count = count_dentries(dentry_list); ret = start_file_structure_phase(&ctx->common, dentry_count);