X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fwin32_apply.c;h=4c2f97d34d41ff3ad5e9bbdb37618e4a4ef01d4d;hp=c35fbec26b7b769099b94ce930d1eef8013d45f5;hb=f84102ca83ae3ab8e6dffc4a1880d901b64d5442;hpb=2fed42ae0eee69e6352425c29ca2b12af99f8992 diff --git a/src/win32_apply.c b/src/win32_apply.c index c35fbec2..4c2f97d3 100644 --- a/src/win32_apply.c +++ b/src/win32_apply.c @@ -686,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."); @@ -901,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 */ @@ -989,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, @@ -998,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); @@ -1097,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, @@ -1116,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; } @@ -1401,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; } /* @@ -1622,16 +1625,28 @@ 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(). */ - perms = GENERIC_READ | 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; 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 (!NT_SUCCESS(status)) { + 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; + } + } winnt_error(status, L"Can't create directory \"%ls\"", current_path(ctx)); return WIMLIB_ERR_MKDIR; @@ -1646,9 +1661,29 @@ create_directory(const struct wim_dentry *dentry, struct win32_apply_ctx *ctx) * directory, even though this contradicts Microsoft's * documentation for FILE_ATTRIBUTE_READONLY which states it is * not honored for directories! */ - FILE_BASIC_INFORMATION basic_info = { .FileAttributes = FILE_ATTRIBUTE_NORMAL }; - (*func_NtSetInformationFile)(h, &ctx->iosb, &basic_info, - sizeof(basic_info), FileBasicInformation); + if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES)) { + FILE_BASIC_INFORMATION basic_info = + { .FileAttributes = FILE_ATTRIBUTE_NORMAL }; + (*func_NtSetInformationFile)(h, &ctx->iosb, &basic_info, + 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. */ + 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, + }; + (*func_NtSetSecurityObject)(h, DACL_SECURITY_INFORMATION, (void *)&desc); } if (!dentry_is_root(dentry)) { @@ -2938,8 +2973,8 @@ win32_extract(struct list_head *dentry_list, struct apply_ctx *_ctx) goto out; } - ctx->windows_build_number = wim_info_get_windows_build_number(ctx->common.wim->wim_info, - ctx->common.wim->current_image); + 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);