X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fwin32_apply.c;h=4f1379489ac8efc44e856278c55548ebd7eab07d;hp=5bc1e51f9c12b3ba68a8c37b5ac9629a2d986962;hb=56f8ec512d3af16e3c39b528328396338ba98887;hpb=658ee1d652613948b29c412d75b3732801c2a235 diff --git a/src/win32_apply.c b/src/win32_apply.c index 5bc1e51f..4f137948 100644 --- a/src/win32_apply.c +++ b/src/win32_apply.c @@ -151,10 +151,16 @@ 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; + /* 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? */ @@ -218,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); @@ -672,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."); @@ -887,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 */ @@ -975,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, @@ -984,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); @@ -1083,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, @@ -1102,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; } @@ -1387,67 +1400,71 @@ delete_file_or_stream(struct win32_apply_ctx *ctx) { NTSTATUS status; HANDLE h; - FILE_DISPOSITION_INFORMATION disposition_info; - FILE_BASIC_INFORMATION basic_info; - bool retried = false; + ULONG perms = DELETE; + ULONG flags = FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE; + + /* First try opening the file with FILE_DELETE_ON_CLOSE. In most cases, + * all we have to do is that plus close the file handle. */ +retry: + status = do_create_file(&h, perms, NULL, 0, FILE_OPEN, flags, ctx); + + if (unlikely(status == STATUS_CANNOT_DELETE)) { + /* This error occurs for files with FILE_ATTRIBUTE_READONLY set. + * Try an alternate approach: first open the file without + * FILE_DELETE_ON_CLOSE, then reset the file attributes, then + * set the "delete" disposition on the handle. */ + if (flags & FILE_DELETE_ON_CLOSE) { + flags &= ~FILE_DELETE_ON_CLOSE; + perms |= FILE_WRITE_ATTRIBUTES; + goto retry; + } + } - status = do_create_file(&h, - DELETE, - NULL, - 0, - FILE_OPEN, - FILE_NON_DIRECTORY_FILE, - ctx); if (unlikely(!NT_SUCCESS(status))) { - winnt_error(status, L"Can't open \"%ls\" for deletion", - current_path(ctx)); + winnt_error(status, L"Can't open \"%ls\" for deletion " + "(perms=%x, flags=%x)", + current_path(ctx), perms, flags); return WIMLIB_ERR_OPEN; } -retry: - disposition_info.DoDeleteFile = TRUE; - status = (*func_NtSetInformationFile)(h, &ctx->iosb, - &disposition_info, - sizeof(disposition_info), - FileDispositionInformation); - (*func_NtClose)(h); - if (likely(NT_SUCCESS(status))) - return 0; + if (unlikely(!(flags & FILE_DELETE_ON_CLOSE))) { - if (status == STATUS_CANNOT_DELETE && !retried) { - /* Clear file attributes and try again. This is necessary for - * FILE_ATTRIBUTE_READONLY files. */ - status = do_create_file(&h, - FILE_WRITE_ATTRIBUTES | DELETE, - NULL, - 0, - FILE_OPEN, - FILE_NON_DIRECTORY_FILE, - ctx); - if (!NT_SUCCESS(status)) { - winnt_error(status, - L"Can't open \"%ls\" to reset attributes", - current_path(ctx)); - return WIMLIB_ERR_OPEN; - } - memset(&basic_info, 0, sizeof(basic_info)); - basic_info.FileAttributes = FILE_ATTRIBUTE_NORMAL; + FILE_BASIC_INFORMATION basic_info = + { .FileAttributes = FILE_ATTRIBUTE_NORMAL }; status = (*func_NtSetInformationFile)(h, &ctx->iosb, &basic_info, sizeof(basic_info), FileBasicInformation); + if (!NT_SUCCESS(status)) { - winnt_error(status, - L"Can't reset file attributes on \"%ls\"", - current_path(ctx)); + winnt_error(status, L"Can't reset attributes of \"%ls\" " + "to prepare for deletion", current_path(ctx)); + (*func_NtClose)(h); + return WIMLIB_ERR_SET_ATTRIBUTES; + } + + FILE_DISPOSITION_INFORMATION disp_info = + { .DoDeleteFile = TRUE }; + status = (*func_NtSetInformationFile)(h, &ctx->iosb, + &disp_info, + sizeof(disp_info), + FileDispositionInformation); + if (!NT_SUCCESS(status)) { + winnt_error(status, L"Can't set delete-on-close " + "disposition on \"%ls\"", current_path(ctx)); (*func_NtClose)(h); return WIMLIB_ERR_SET_ATTRIBUTES; } - retried = true; - goto retry; } - winnt_error(status, L"Can't delete \"%ls\"", current_path(ctx)); - return WIMLIB_ERR_OPEN; + + status = (*func_NtClose)(h); + if (unlikely(!NT_SUCCESS(status))) { + winnt_error(status, L"Error closing \"%ls\" after setting " + "delete-on-close disposition", current_path(ctx)); + return WIMLIB_ERR_OPEN; + } + + return 0; } /* @@ -2320,14 +2337,15 @@ 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[] = { +/* 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", @@ -2352,9 +2370,9 @@ static wchar_t *xpress4k_only_pattern_strings[] = { 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 const struct string_set bootloader_patterns = { + .strings = bootloader_pattern_strings, + .num_strings = ARRAY_LEN(bootloader_pattern_strings), }; static NTSTATUS @@ -2365,13 +2383,19 @@ set_system_compression_on_inode(struct wim_inode *inode, int format, NTSTATUS status; HANDLE h; - /* If needed, force the XPRESS4K format for this file. */ - if (format != FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS4K) { + /* 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!"); @@ -2379,12 +2403,18 @@ set_system_compression_on_inode(struct wim_inode *inode, int format, } incompatible = match_pattern_list(dentry->d_full_path, - &xpress4k_only_patterns); + &bootloader_patterns); FREE(dentry->d_full_path); dentry->d_full_path = NULL; - if (incompatible) { - if (ctx->num_xpress4k_forced_files++ == 0) { + 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" @@ -2396,7 +2426,19 @@ set_system_compression_on_inode(struct wim_inode *inode, int 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; } + } } @@ -2899,6 +2941,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);