*/
/*
- * Copyright (C) 2013-2016 Eric Biggers
+ * Copyright 2013-2023 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
* details.
*
* You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
*/
-#ifdef __WIN32__
+#ifdef _WIN32
#ifdef HAVE_CONFIG_H
# include "config.h"
#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 {
/* 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;
}
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. */
}
}
-/* 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);
supported_features->case_sensitive_filenames = 1;
}
+ if (vol_flags & FILE_SUPPORTS_EXTENDED_ATTRIBUTES)
+ supported_features->xattrs = 1;
+
return 0;
}
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) {
{
/* 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
* 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;
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);
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;
HANDLE h;
size_t bufsize = offsetof(FILE_NAME_INFORMATION, FileName) +
(13 * sizeof(wchar_t));
- u8 buf[bufsize] _aligned_attribute(8);
+ u8 buf[bufsize] __attribute__((aligned(8)));
bool retried = false;
FILE_NAME_INFORMATION *info = (FILE_NAME_INFORMATION *)buf;
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;
}
size_t bufsize = offsetof(FILE_NAME_INFORMATION, FileName) +
max(dentry->d_short_name_nbytes, sizeof(wchar_t)) +
sizeof(wchar_t);
- u8 buf[bufsize] _aligned_attribute(8);
+ u8 buf[bufsize] __attribute__((aligned(8)));
FILE_NAME_INFORMATION *info = (FILE_NAME_INFORMATION *)buf;
NTSTATUS status;
bool tried_to_remove_existing = false;
if (unlikely(!NT_SUCCESS(status))) {
winnt_error(status, L"Can't open \"%ls\" for deletion "
"(perms=%x, flags=%x)",
- current_path(ctx), perms, flags);
+ current_path(ctx), (u32)perms, (u32)flags);
return WIMLIB_ERR_OPEN;
}
if (strm->stream_type == STREAM_TYPE_REPARSE_POINT &&
ctx->common.supported_features.reparse_points)
{
- u8 buf[REPARSE_DATA_OFFSET] _aligned_attribute(8);
+ u8 buf[REPARSE_DATA_OFFSET] __attribute__((aligned(8)));
struct reparse_buffer_disk *rpbuf =
(struct reparse_buffer_disk *)buf;
complete_reparse_point(rpbuf, inode, 0);
size_t bufsize = offsetof(FILE_LINK_INFORMATION, FileName) +
ctx->pathbuf.Length + sizeof(wchar_t);
- u8 buf[bufsize] _aligned_attribute(8);
+ u8 buf[bufsize] __attribute__((aligned(8)));
FILE_LINK_INFORMATION *info = (FILE_LINK_INFORMATION *)buf;
NTSTATUS status;
info->FileNameLength = ctx->pathbuf.Length;
memcpy(info->FileName, ctx->pathbuf.Buffer, ctx->pathbuf.Length);
info->FileName[info->FileNameLength / 2] = L'\0';
+ /*
+ * Note: the null terminator isn't actually necessary, but if
+ * you don't add the extra character, you get
+ * STATUS_INFO_LENGTH_MISMATCH when FileNameLength is 2.
+ */
- /* Note: the null terminator isn't actually necessary,
- * but if you don't add the extra character, you get
- * STATUS_INFO_LENGTH_MISMATCH when FileNameLength
- * happens to be 2 */
-
- status = NtSetInformationFile(h, &ctx->iosb, info, bufsize,
- FileLinkInformation);
- if (NT_SUCCESS(status))
- return 0;
+ /*
+ * When fuzzing with wlfuzz.exe, creating a hard link sometimes
+ * fails with STATUS_ACCESS_DENIED. However, it eventually
+ * succeeds when re-attempted...
+ */
+ int i = 0;
+ do {
+ status = NtSetInformationFile(h, &ctx->iosb, info,
+ bufsize,
+ FileLinkInformation);
+ if (NT_SUCCESS(status))
+ return 0;
+ } while (++i < 32);
winnt_error(status, L"Failed to create link \"%ls\"",
current_path(ctx));
return WIMLIB_ERR_LINK;
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;
}
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";
{
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,
},
};
.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)
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;
}
incompatible = match_pattern_list(dentry->d_full_path,
- &bootloader_patterns);
+ &bootloader_patterns,
+ MATCH_RECURSIVELY);
FREE(dentry->d_full_path);
dentry->d_full_path = NULL;
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 "
" you requested.",
get_system_compression_format_string(format));
}
- format = FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS4K;
+ format = FILE_PROVIDER_COMPRESSION_XPRESS4K;
break;
} else {
/* Force to uncompressed */
}
}
+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] __attribute__((aligned(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
{
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) &&
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);
.context_size = sizeof(struct win32_apply_ctx),
};
-#endif /* __WIN32__ */
+#endif /* _WIN32 */