/* 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;
LOAD_TEXT_FILE_REMOVE_QUOTES |
LOAD_TEXT_FILE_NO_WARNINGS,
mangle_pat);
- BUILD_BUG_ON(OS_PREFERRED_PATH_SEPARATOR != WIM_PATH_SEPARATOR);
+ STATIC_ASSERT(OS_PREFERRED_PATH_SEPARATOR == WIM_PATH_SEPARATOR);
FREE(buf);
if (ret) {
FREE(s);
return FILE_PROVIDER_COMPRESSION_FORMAT_LZX;
}
-static DWORD
+
+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)
{
- DWORD bytes_returned;
- DWORD err;
+ NTSTATUS status;
+ IO_STATUS_BLOCK iosb;
struct {
struct wof_external_info wof_info;
struct file_provider_external_info file_info;
},
};
- if (DeviceIoControl(h, FSCTL_SET_EXTERNAL_BACKING, &in, sizeof(in),
- NULL, 0, &bytes_returned, NULL))
- return 0;
+ /* We intentionally use NtFsControlFile() rather than DeviceIoControl()
+ * here because the "compressing this object would not save space"
+ * status code does not map to a valid Win32 error code on older
+ * versions of Windows (before Windows 10?). This can be a problem if
+ * the WOFADK driver is being used rather than the regular WOF, since
+ * WOFADK can be used on older versions of Windows. */
+ status = (*func_NtFsControlFile)(h, NULL, NULL, NULL, &iosb,
+ FSCTL_SET_EXTERNAL_BACKING,
+ &in, sizeof(in), NULL, 0);
- err = GetLastError();
+ if (status == 0xC000046F) /* "Compressing this object would not save space." */
+ return STATUS_SUCCESS;
- if (err == 344) /* "Compressing this object would not save space." */
- return 0;
+ 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\\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;
- return err;
+ /* 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;
}
/*
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;
- DWORD err;
if (!stream_is_unnamed_data_stream(strm))
continue;
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)) {
- err = set_system_compression(h, format);
- (*func_NtClose)(h);
- } else {
- err = (*func_RtlNtStatusToDosError)(status);
- }
+ status = set_system_compression_on_inode(inode, format, ctx);
+ if (likely(NT_SUCCESS(status)))
+ continue;
- if (err == ERROR_INVALID_FUNCTION) {
+ if (status == STATUS_INVALID_DEVICE_REQUEST) {
WARNING(
"The request to compress the extracted files using System Compression\n"
" will not be honored because the operating system or target volume\n"
return;
}
- if (err) {
- ctx->num_system_compression_failures++;
- if (ctx->num_system_compression_failures < 10) {
- win32_warning(err, 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.");
}
}
}