X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fwin32_apply.c;h=a1301d646290f4d8671428119698e3a41871e619;hp=f971ca6f8bdeaf12c0f479832d36706edd1bd83c;hb=01ce2d43d6ba9721bf46c3e132c4be394ef3f0f9;hpb=bb3005f9b9fff1333900d8635ad9aeda1021eb94 diff --git a/src/win32_apply.c b/src/win32_apply.c index f971ca6f..a1301d64 100644 --- a/src/win32_apply.c +++ b/src/win32_apply.c @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2013-2016 Eric Biggers + * Copyright (C) 2013-2021 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free @@ -40,9 +40,10 @@ #include "wimlib/reparse.h" #include "wimlib/scan.h" /* for mangle_pat() and match_pattern_list() */ #include "wimlib/textfile.h" -#include "wimlib/xml.h" #include "wimlib/wimboot.h" #include "wimlib/wof.h" +#include "wimlib/xattr.h" +#include "wimlib/xml.h" struct win32_apply_ctx { @@ -169,6 +170,9 @@ struct win32_apply_ctx { /* Number of files for which we couldn't set the object ID. */ unsigned long num_object_id_failures; + /* Number of files for which we couldn't set extended attributes. */ + unsigned long num_xattr_failures; + /* The Windows build number of the image being applied, or 0 if unknown. */ u64 windows_build_number; @@ -225,9 +229,14 @@ get_vol_flags(const wchar_t *target, DWORD *vol_flags_ret, } if (wcsstr(filesystem_name, L"NTFS")) { - /* FILE_SUPPORTS_HARD_LINKS is only supported on Windows 7 and - * later. Force it on anyway if filesystem is NTFS. */ + /* + * FILE_SUPPORTS_HARD_LINKS and + * FILE_SUPPORTS_EXTENDED_ATTRIBUTES are only supported on + * Windows 7 and later. Force them on anyway if the filesystem + * is NTFS. + */ *vol_flags_ret |= FILE_SUPPORTS_HARD_LINKS; + *vol_flags_ret |= FILE_SUPPORTS_EXTENDED_ATTRIBUTES; /* There's no volume flag for short names, but according to the * MS documentation they are only user-settable on NTFS. */ @@ -235,14 +244,6 @@ 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); @@ -339,6 +340,9 @@ win32_get_supported_features(const wchar_t *target, supported_features->case_sensitive_filenames = 1; } + if (vol_flags & FILE_SUPPORTS_EXTENDED_ATTRIBUTES) + supported_features->xattrs = 1; + return 0; } @@ -403,10 +407,10 @@ load_prepopulate_pats(struct win32_apply_ctx *ctx) sec.name = T("PrepopulateList"); sec.strings = strings; - ret = do_load_text_file(path, buf, blob->size, &mem, &sec, 1, - LOAD_TEXT_FILE_REMOVE_QUOTES | - LOAD_TEXT_FILE_NO_WARNINGS, - mangle_pat); + ret = load_text_file(path, buf, blob->size, &mem, &sec, 1, + LOAD_TEXT_FILE_REMOVE_QUOTES | + LOAD_TEXT_FILE_NO_WARNINGS, + mangle_pat); STATIC_ASSERT(OS_PREFERRED_PATH_SEPARATOR == WIM_PATH_SEPARATOR); FREE(buf); if (ret) { @@ -425,7 +429,8 @@ can_externally_back_path(const wchar_t *path, const struct win32_apply_ctx *ctx) { /* Does the path match a pattern given in the [PrepopulateList] section * of WimBootCompress.ini? */ - if (ctx->prepopulate_pats && match_pattern_list(path, ctx->prepopulate_pats)) + if (ctx->prepopulate_pats && match_pattern_list(path, ctx->prepopulate_pats, + MATCH_RECURSIVELY)) return false; /* Since we attempt to modify the SYSTEM registry after it's extracted @@ -437,7 +442,7 @@ can_externally_back_path(const wchar_t *path, const struct win32_apply_ctx *ctx) * However, a WIM that wasn't specifically captured in "WIMBoot mode" * may contain SYSTEM.* files. So to make things "just work", hard-code * the pattern. */ - if (match_path(path, L"\\Windows\\System32\\config\\SYSTEM*", false)) + if (match_path(path, L"\\Windows\\System32\\config\\SYSTEM*", 0)) return false; return true; @@ -807,7 +812,7 @@ end_wimboot_extraction(struct win32_apply_ctx *ctx) build_win32_extraction_path(dentry, ctx); - randomize_char_array_with_alnum(subkeyname, 20); + get_random_alnum_chars(subkeyname, 20); subkeyname[20] = L'\0'; res = RegLoadKey(HKEY_LOCAL_MACHINE, subkeyname, ctx->pathbuf.Buffer); @@ -1081,12 +1086,18 @@ prepare_target(struct list_head *dentry_list, struct win32_apply_ctx *ctx) path_max = compute_path_max(dentry_list); /* Add some extra for building Win32 paths for the file encryption APIs, - * and ensure we have at least enough to potentially use a 8.3 name for + * and ensure we have at least enough to potentially use an 8.3 name for * the last component. */ path_max += max(2 + (ctx->target_ntpath.Length / sizeof(wchar_t)), 8 + 1 + 3); ctx->pathbuf.MaximumLength = path_max * sizeof(wchar_t); + if (ctx->pathbuf.MaximumLength != path_max * sizeof(wchar_t)) { + /* Paths are too long for a UNICODE_STRING! */ + ERROR("Some paths are too long to extract (> 32768 characters)!"); + return WIMLIB_ERR_UNSUPPORTED; + } + ctx->pathbuf.Buffer = MALLOC(ctx->pathbuf.MaximumLength); if (!ctx->pathbuf.Buffer) return WIMLIB_ERR_NOMEM; @@ -1268,18 +1279,11 @@ retry: FileShortNameInformation); if (status == STATUS_INVALID_PARAMETER && !retried) { - /* Microsoft forgot to make it possible to remove short names * until Windows 7. Oops. Use a random short name instead. */ - + get_random_alnum_chars(info->FileName, 8); + wcscpy(&info->FileName[8], L".WLB"); info->FileNameLength = 12 * sizeof(wchar_t); - for (int i = 0; i < 8; i++) - info->FileName[i] = 'A' + (rand() % 26); - info->FileName[8] = L'.'; - info->FileName[9] = L'W'; - info->FileName[10] = L'L'; - info->FileName[11] = L'B'; - info->FileName[12] = L'\0'; retried = true; goto retry; } @@ -2420,15 +2424,15 @@ static int get_system_compression_format(int extract_flags) { if (extract_flags & WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K) - return FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS4K; + return FILE_PROVIDER_COMPRESSION_XPRESS4K; if (extract_flags & WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K) - return FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS8K; + return FILE_PROVIDER_COMPRESSION_XPRESS8K; if (extract_flags & WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K) - return FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS16K; + return FILE_PROVIDER_COMPRESSION_XPRESS16K; - return FILE_PROVIDER_COMPRESSION_FORMAT_LZX; + return FILE_PROVIDER_COMPRESSION_LZX; } @@ -2436,11 +2440,11 @@ static const wchar_t * get_system_compression_format_string(int format) { switch (format) { - case FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS4K: + case FILE_PROVIDER_COMPRESSION_XPRESS4K: return L"XPRESS4K"; - case FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS8K: + case FILE_PROVIDER_COMPRESSION_XPRESS8K: return L"XPRESS8K"; - case FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS16K: + case FILE_PROVIDER_COMPRESSION_XPRESS16K: return L"XPRESS16K"; default: return L"LZX"; @@ -2452,16 +2456,16 @@ set_system_compression(HANDLE h, int format) { NTSTATUS status; struct { - struct wof_external_info wof_info; - struct file_provider_external_info file_info; + WOF_EXTERNAL_INFO wof_info; + FILE_PROVIDER_EXTERNAL_INFO_V1 file_info; } in = { .wof_info = { - .version = WOF_CURRENT_VERSION, - .provider = WOF_PROVIDER_FILE, + .Version = WOF_CURRENT_VERSION, + .Provider = WOF_PROVIDER_FILE, }, .file_info = { - .version = FILE_PROVIDER_CURRENT_VERSION, - .compression_format = format, + .Version = FILE_PROVIDER_CURRENT_VERSION, + .Algorithm = format, }, }; @@ -2518,6 +2522,22 @@ static const struct string_list bootloader_patterns = { .num_strings = ARRAY_LEN(bootloader_pattern_strings), }; +/* Returns true if the specified system compression format is supported by the + * bootloader of the image being applied. */ +static bool +bootloader_supports_compression_format(struct win32_apply_ctx *ctx, int format) +{ + /* Windows 10 and later support XPRESS4K */ + if (format == FILE_PROVIDER_COMPRESSION_XPRESS4K) + return ctx->windows_build_number >= 10240; + + /* + * Windows 10 version 1903 and later support the other formats; + * see https://wimlib.net/forums/viewtopic.php?f=1&t=444 + */ + return ctx->windows_build_number >= 18362; +} + static NTSTATUS set_system_compression_on_inode(struct wim_inode *inode, int format, struct win32_apply_ctx *ctx) @@ -2527,12 +2547,8 @@ set_system_compression_on_inode(struct wim_inode *inode, int format, 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) - { + * force this file to XPRESS4K or uncompressed format. */ + if (!bootloader_supports_compression_format(ctx, format)) { /* We need to check the patterns against every name of the * inode, in case any of them match. */ struct wim_dentry *dentry; @@ -2546,7 +2562,8 @@ set_system_compression_on_inode(struct wim_inode *inode, int format, } incompatible = match_pattern_list(dentry->d_full_path, - &bootloader_patterns); + &bootloader_patterns, + MATCH_RECURSIVELY); FREE(dentry->d_full_path); dentry->d_full_path = NULL; @@ -2555,7 +2572,9 @@ set_system_compression_on_inode(struct wim_inode *inode, int format, warned = (ctx->num_system_compression_exclusions++ > 0); - if (is_image_windows_10_or_later(ctx)) { + if (bootloader_supports_compression_format(ctx, + FILE_PROVIDER_COMPRESSION_XPRESS4K)) + { /* Force to XPRESS4K */ if (!warned) { WARNING("For compatibility with the " @@ -2567,7 +2586,7 @@ set_system_compression_on_inode(struct wim_inode *inode, int format, " you requested.", get_system_compression_format_string(format)); } - format = FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS4K; + format = FILE_PROVIDER_COMPRESSION_XPRESS4K; break; } else { /* Force to uncompressed */ @@ -2806,6 +2825,105 @@ set_object_id(HANDLE h, const struct wim_inode *inode, } } +static int +set_xattrs(HANDLE h, const struct wim_inode *inode, struct win32_apply_ctx *ctx) +{ + const void *entries, *entries_end; + u32 len; + const struct wim_xattr_entry *entry; + size_t bufsize = 0; + u8 _buf[1024] _aligned_attribute(4); + u8 *buf = _buf; + FILE_FULL_EA_INFORMATION *ea, *ea_prev; + NTSTATUS status; + int ret; + + if (!ctx->common.supported_features.xattrs) + return 0; + + entries = inode_get_xattrs(inode, &len); + if (likely(entries == NULL || len == 0)) /* No extended attributes? */ + return 0; + entries_end = entries + len; + + entry = entries; + for (entry = entries; (void *)entry < entries_end; + entry = xattr_entry_next(entry)) { + if (!valid_xattr_entry(entry, entries_end - (void *)entry)) { + ERROR("\"%"TS"\": extended attribute is corrupt or unsupported", + inode_any_full_path(inode)); + return WIMLIB_ERR_INVALID_XATTR; + } + + bufsize += ALIGN(offsetof(FILE_FULL_EA_INFORMATION, EaName) + + entry->name_len + 1 + + le16_to_cpu(entry->value_len), 4); + } + + if (unlikely(bufsize != (u32)bufsize)) { + ERROR("\"%"TS"\": too many extended attributes to extract!", + inode_any_full_path(inode)); + return WIMLIB_ERR_INVALID_XATTR; + } + + if (unlikely(bufsize > sizeof(_buf))) { + buf = MALLOC(bufsize); + if (!buf) + return WIMLIB_ERR_NOMEM; + } + + ea_prev = NULL; + ea = (FILE_FULL_EA_INFORMATION *)buf; + for (entry = entries; (void *)entry < entries_end; + entry = xattr_entry_next(entry)) { + u8 *p; + + if (ea_prev) + ea_prev->NextEntryOffset = (u8 *)ea - (u8 *)ea_prev; + ea->Flags = entry->flags; + ea->EaNameLength = entry->name_len; + ea->EaValueLength = le16_to_cpu(entry->value_len); + p = mempcpy(ea->EaName, entry->name, + ea->EaNameLength + 1 + ea->EaValueLength); + while ((uintptr_t)p & 3) + *p++ = 0; + ea_prev = ea; + ea = (FILE_FULL_EA_INFORMATION *)p; + } + ea_prev->NextEntryOffset = 0; + wimlib_assert((u8 *)ea - buf == bufsize); + + status = NtSetEaFile(h, &ctx->iosb, buf, bufsize); + if (unlikely(!NT_SUCCESS(status))) { + if (status == STATUS_EAS_NOT_SUPPORTED) { + /* This happens with Samba. */ + WARNING("Filesystem advertised extended attribute (EA) support, but it doesn't\n" + " work. EAs will not be extracted."); + ctx->common.supported_features.xattrs = 0; + } else if (status == STATUS_INVALID_EA_NAME) { + ctx->num_xattr_failures++; + if (ctx->num_xattr_failures < 5) { + winnt_warning(status, + L"Can't set extended attributes on \"%ls\"", + current_path(ctx)); + } else if (ctx->num_xattr_failures == 5) { + WARNING("Suppressing further warnings about " + "failure to set extended attributes."); + } + } else { + winnt_error(status, L"Can't set extended attributes on \"%ls\"", + current_path(ctx)); + ret = WIMLIB_ERR_SET_XATTR; + goto out; + } + } + ret = 0; +out: + if (buf != _buf) + FREE(buf); + return ret; +} + /* Set the security descriptor @desc, of @desc_size bytes, on the file with open * handle @h. */ static NTSTATUS @@ -2949,11 +3067,18 @@ do_apply_metadata_to_file(HANDLE h, const struct wim_inode *inode, { FILE_BASIC_INFORMATION info; NTSTATUS status; + int ret; /* Set the file's object ID if present and object IDs are supported by * the filesystem. */ set_object_id(h, inode, ctx); + /* Set the file's extended attributes (EAs) if present and EAs are + * supported by the filesystem. */ + ret = set_xattrs(h, inode, ctx); + if (ret) + return ret; + /* Set the file's security descriptor if present and we're not in * NO_ACLS mode */ if (inode_has_security_descriptor(inode) && @@ -3018,7 +3143,7 @@ apply_metadata_to_file(const struct wim_dentry *dentry, NTSTATUS status; int ret; - perms = FILE_WRITE_ATTRIBUTES | WRITE_DAC | + perms = FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | WRITE_DAC | WRITE_OWNER | ACCESS_SYSTEM_SECURITY; build_extraction_path(dentry, ctx);