X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fwin32_apply.c;h=33c1a5dde166a0b5bf3feb60e60b7ad23b3d872b;hp=fbed6b9c685d479cca4ba69559d98bd6de8a7ee2;hb=9c0f42ccc39f3e1375f47c6710bb19fe991bb1b7;hpb=ef03f83cc940d87f75140b7a8bc1199f4ff22aab diff --git a/src/win32_apply.c b/src/win32_apply.c index fbed6b9c..33c1a5dd 100644 --- a/src/win32_apply.c +++ b/src/win32_apply.c @@ -30,14 +30,15 @@ #include "wimlib/apply.h" #include "wimlib/assert.h" #include "wimlib/blob_table.h" -#include "wimlib/capture.h" /* for mangle_pat() and match_pattern_list() */ #include "wimlib/dentry.h" #include "wimlib/encoding.h" #include "wimlib/error.h" #include "wimlib/metadata.h" +#include "wimlib/object_id.h" #include "wimlib/paths.h" #include "wimlib/pattern.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" @@ -68,7 +69,7 @@ struct win32_apply_ctx { } wimboot; /* External backing information */ - struct string_set *prepopulate_pats; + struct string_list *prepopulate_pats; void *mem_prepopulate_pats; bool tried_to_load_prepopulate_list; @@ -158,6 +159,9 @@ struct win32_apply_ctx { * [PrepopulateList]. */ unsigned long num_system_compression_exclusions; + /* Number of files for which we couldn't set the object ID. */ + unsigned long num_object_id_failures; + /* The Windows build number of the image being applied, or 0 if unknown. */ u64 windows_build_number; @@ -299,6 +303,9 @@ win32_get_supported_features(const wchar_t *target, if (short_names_supported) supported_features->short_names = 1; + if (vol_flags & FILE_SUPPORTS_OBJECT_IDS) + supported_features->object_ids = 1; + supported_features->timestamps = 1; /* Note: Windows does not support case sensitive filenames! At least @@ -331,7 +338,7 @@ load_prepopulate_pats(struct win32_apply_ctx *ctx) const struct blob_descriptor *blob; int ret; void *buf; - struct string_set *s; + struct string_list *strings; void *mem; struct text_file_section sec; @@ -359,14 +366,14 @@ load_prepopulate_pats(struct win32_apply_ctx *ctx) if (ret) return ret; - s = CALLOC(1, sizeof(struct string_set)); - if (!s) { + strings = CALLOC(1, sizeof(struct string_list)); + if (!strings) { FREE(buf); return WIMLIB_ERR_NOMEM; } sec.name = T("PrepopulateList"); - sec.strings = s; + sec.strings = strings; ret = do_load_text_file(path, buf, blob->size, &mem, &sec, 1, LOAD_TEXT_FILE_REMOVE_QUOTES | @@ -375,10 +382,10 @@ load_prepopulate_pats(struct win32_apply_ctx *ctx) STATIC_ASSERT(OS_PREFERRED_PATH_SEPARATOR == WIM_PATH_SEPARATOR); FREE(buf); if (ret) { - FREE(s); + FREE(strings); return ret; } - ctx->prepopulate_pats = s; + ctx->prepopulate_pats = strings; ctx->mem_prepopulate_pats = mem; return 0; } @@ -1003,8 +1010,7 @@ open_target_directory(struct win32_apply_ctx *ctx) 0, FILE_SHARE_VALID_FLAGS, FILE_OPEN_IF, - FILE_DIRECTORY_FILE | - FILE_OPEN_FOR_BACKUP_INTENT, + FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT, NULL, 0); if (!NT_SUCCESS(status)) { @@ -1123,7 +1129,7 @@ adjust_compression_attribute(HANDLE h, const struct wim_dentry *dentry, status = winnt_fsctl(h, FSCTL_SET_COMPRESSION, &compression_state, sizeof(USHORT), NULL, 0, NULL); if (NT_SUCCESS(status)) - return status; + return 0; winnt_error(status, L"Can't %s compression attribute on \"%ls\"", (compressed ? "set" : "clear"), current_path(ctx)); @@ -1465,10 +1471,11 @@ retry: /* * Create a nondirectory file or named data stream at the current path, * superseding any that already exists at that path. If successful, return an - * open handle to the file or named data stream. + * open handle to the file or named data stream with the requested permissions. */ static int -supersede_file_or_stream(struct win32_apply_ctx *ctx, HANDLE *h_ret) +supersede_file_or_stream(struct win32_apply_ctx *ctx, DWORD perms, + HANDLE *h_ret) { NTSTATUS status; bool retried = false; @@ -1477,7 +1484,7 @@ supersede_file_or_stream(struct win32_apply_ctx *ctx, HANDLE *h_ret) * FILE_ATTRIBUTE_ENCRYPTED doesn't get set before we want it to be. */ retry: status = do_create_file(h_ret, - GENERIC_READ | GENERIC_WRITE | DELETE, + perms, NULL, FILE_ATTRIBUTE_SYSTEM, FILE_CREATE, @@ -1589,7 +1596,13 @@ create_empty_streams(const struct wim_dentry *dentry, build_extraction_path_with_ads(dentry, ctx, strm->stream_name, utf16le_len_chars(strm->stream_name)); - ret = supersede_file_or_stream(ctx, &h); + /* + * Note: do not request any permissions on the handle. + * Otherwise, we may encounter a Windows bug where the + * parent directory DACL denies read access to the new + * named data stream, even when using backup semantics! + */ + ret = supersede_file_or_stream(ctx, 0, &h); build_extraction_path(dentry, ctx); @@ -1618,28 +1631,16 @@ create_directory(const struct wim_dentry *dentry, struct win32_apply_ctx *ctx) int ret; /* DELETE is needed for set_short_name(); GENERIC_READ and GENERIC_WRITE - * are needed for adjust_compression_attribute(); WRITE_DAC is needed to - * remove the directory's DACL if the directory already existed */ - perms = GENERIC_READ | GENERIC_WRITE | WRITE_DAC; + * are needed for adjust_compression_attribute(). */ + perms = GENERIC_READ | GENERIC_WRITE; if (!dentry_is_root(dentry)) perms |= DELETE; /* FILE_ATTRIBUTE_SYSTEM is needed to ensure that * FILE_ATTRIBUTE_ENCRYPTED doesn't get set before we want it to be. */ -retry: status = create_file(&h, perms, NULL, FILE_ATTRIBUTE_SYSTEM, FILE_OPEN_IF, FILE_DIRECTORY_FILE, dentry, ctx); if (unlikely(!NT_SUCCESS(status))) { - if (status == STATUS_ACCESS_DENIED) { - if (perms & WRITE_DAC) { - perms &= ~WRITE_DAC; - goto retry; - } - if (perms & DELETE) { - perms &= ~DELETE; - goto retry; - } - } const wchar_t *path = current_path(ctx); winnt_error(status, L"Can't create directory \"%ls\"", path); @@ -1677,25 +1678,6 @@ retry: sizeof(basic_info), FileBasicInformation); } - - /* Also try to remove the directory's DACL. This isn't supposed - * to be necessary because we *always* use backup semantics. - * However, there is a case where NtCreateFile() fails with - * STATUS_ACCESS_DENIED when creating a named data stream that - * was just deleted, using a directory-relative open. I have no - * idea why Windows is broken in this case. */ - if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS)) { - static const SECURITY_DESCRIPTOR_RELATIVE desc = { - .Revision = SECURITY_DESCRIPTOR_REVISION1, - .Control = SE_SELF_RELATIVE | SE_DACL_PRESENT, - .Owner = 0, - .Group = 0, - .Sacl = 0, - .Dacl = 0, - }; - NtSetSecurityObject(h, DACL_SECURITY_INFORMATION, - (void *)&desc); - } } if (!dentry_is_root(dentry)) { @@ -1765,7 +1747,9 @@ create_nondirectory_inode(HANDLE *h_ret, const struct wim_dentry *dentry, build_extraction_path(dentry, ctx); - ret = supersede_file_or_stream(ctx, &h); + ret = supersede_file_or_stream(ctx, + GENERIC_READ | GENERIC_WRITE | DELETE, + &h); if (ret) goto out; @@ -2430,7 +2414,7 @@ static wchar_t *bootloader_pattern_strings[] = { L"\\Windows\\System32\\CodeIntegrity\\driver.stl", }; -static const struct string_set bootloader_patterns = { +static const struct string_list bootloader_patterns = { .strings = bootloader_pattern_strings, .num_strings = ARRAY_LEN(bootloader_pattern_strings), }; @@ -2660,6 +2644,46 @@ end_extract_blob(struct blob_descriptor *blob, int status, void *_ctx) FILE_ATTRIBUTE_SPARSE_FILE | \ FILE_ATTRIBUTE_COMPRESSED) +static void +set_object_id(HANDLE h, const struct wim_inode *inode, + struct win32_apply_ctx *ctx) +{ + const void *object_id; + u32 len; + NTSTATUS status; + + if (!ctx->common.supported_features.object_ids) + return; + + object_id = inode_get_object_id(inode, &len); + if (likely(object_id == NULL)) /* No object ID? */ + return; + + status = winnt_fsctl(h, FSCTL_SET_OBJECT_ID, + object_id, len, NULL, 0, NULL); + if (NT_SUCCESS(status)) + return; + + /* Object IDs must be unique within the filesystem. A duplicate might + * occur if an image containing object IDs is applied twice to the same + * filesystem. Arguably, the user should be warned in this case; but + * the reality seems to be that nothing important cares about object IDs + * except the Distributed Link Tracking Service... so for now these + * failures are just ignored. */ + if (status == STATUS_DUPLICATE_NAME || + status == STATUS_OBJECT_NAME_COLLISION) + return; + + ctx->num_object_id_failures++; + if (ctx->num_object_id_failures < 10) { + winnt_warning(status, L"Can't set object ID on \"%ls\"", + current_path(ctx)); + } else if (ctx->num_object_id_failures == 10) { + WARNING("Suppressing further warnings about failure to set " + "object IDs."); + } +} + /* Set the security descriptor @desc, of @desc_size bytes, on the file with open * handle @h. */ static NTSTATUS @@ -2804,7 +2828,12 @@ do_apply_metadata_to_file(HANDLE h, const struct wim_inode *inode, FILE_BASIC_INFORMATION info; NTSTATUS status; - /* Set security descriptor if present and not in NO_ACLS mode */ + /* 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 security descriptor if present and we're not in + * NO_ACLS mode */ if (inode_has_security_descriptor(inode) && !(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS)) {