X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fwin32_apply.c;h=630a77f183179864cafaa4adace99e4e8ffe7449;hp=9ebc161e504cb85e808f568f5149f7847097d53b;hb=b8ebc57d493d7b6e660a50f7789fcb5451f5d77d;hpb=944ca027b829261f33af8577fd8fe2afdc4d02b7 diff --git a/src/win32_apply.c b/src/win32_apply.c index 9ebc161e..630a77f1 100644 --- a/src/win32_apply.c +++ b/src/win32_apply.c @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2013, 2014 Eric Biggers + * Copyright (C) 2013, 2014, 2015 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 @@ -28,11 +28,13 @@ #include "wimlib/win32_common.h" #include "wimlib/apply.h" +#include "wimlib/assert.h" #include "wimlib/capture.h" /* for mangle_pat() and match_pattern_list() */ #include "wimlib/dentry.h" #include "wimlib/error.h" #include "wimlib/lookup_table.h" #include "wimlib/metadata.h" +#include "wimlib/paths.h" #include "wimlib/reparse.h" #include "wimlib/textfile.h" #include "wimlib/xml.h" @@ -176,11 +178,9 @@ get_vol_flags(const wchar_t *target, DWORD *vol_flags_ret, vol_flags_ret, filesystem_name, ARRAY_LEN(filesystem_name))) { - DWORD err = GetLastError(); - set_errno_from_win32_error(err); - WARNING_WITH_ERRNO("Failed to get volume information for " - "\"%ls\" (err=%"PRIu32")", - target, (u32)err); + win32_warning(GetLastError(), + L"Failed to get volume information for \"%ls\"", + target); return; } @@ -480,10 +480,8 @@ set_external_backing(HANDLE h, struct wim_inode *inode, struct win32_apply_ctx * const DWORD err = GetLastError(); build_extraction_path(inode_first_extraction_dentry(inode), ctx); - set_errno_from_win32_error(err); - ERROR_WITH_ERRNO("\"%ls\": Couldn't set WIMBoot " - "pointer data (err=%"PRIu32")", - current_path(ctx), (u32)err); + win32_error(err, L"\"%ls\": Couldn't set WIMBoot pointer data", + current_path(ctx)); return WIMLIB_ERR_WIMBOOT; } return 0; @@ -591,10 +589,9 @@ out_unload_key: out_check_res: if (res) { /* Warning only. */ - set_errno_from_win32_error(res); - WARNING_WITH_ERRNO("Failed to set \\Setup: dword \"WimBoot\"=1 value " - "in registry hive \"%ls\" (err=%"PRIu32")", - ctx->pathbuf.Buffer, (u32)res); + win32_warning(res, L"Failed to set \\Setup: dword \"WimBoot\"=1 " + "value in registry hive \"%ls\"", + ctx->pathbuf.Buffer); } out: return 0; @@ -761,26 +758,19 @@ current_path(struct win32_apply_ctx *ctx) return ctx->print_buffer; } -/* - * Ensures the target directory exists and opens a handle to it, in preparation - * of using paths relative to it. - */ +/* Open handle to the target directory if it is not already open. If the target + * directory does not exist, this creates it. */ static int -prepare_target(struct list_head *dentry_list, struct win32_apply_ctx *ctx) +open_target_directory(struct win32_apply_ctx *ctx) { - int ret; NTSTATUS status; - size_t path_max; - - /* Open handle to the target directory (possibly creating it). */ - ret = win32_path_to_nt_path(ctx->common.target, &ctx->target_ntpath); - if (ret) - return ret; + if (ctx->h_target) + return 0; ctx->attr.Length = sizeof(ctx->attr); + ctx->attr.RootDirectory = NULL; ctx->attr.ObjectName = &ctx->target_ntpath; - status = (*func_NtCreateFile)(&ctx->h_target, FILE_TRAVERSE, &ctx->attr, @@ -794,17 +784,45 @@ prepare_target(struct list_head *dentry_list, struct win32_apply_ctx *ctx) FILE_OPEN_FOR_BACKUP_INTENT, NULL, 0); - if (!NT_SUCCESS(status)) { - set_errno_from_nt_status(status); - ERROR_WITH_ERRNO("Can't open or create directory \"%ls\" " - "(status=0x%08"PRIx32")", - ctx->common.target, (u32)status); + winnt_error(status, L"Can't open or create directory \"%ls\"", + ctx->common.target); return WIMLIB_ERR_OPENDIR; } + ctx->attr.RootDirectory = ctx->h_target; + ctx->attr.ObjectName = &ctx->pathbuf; + return 0; +} - path_max = compute_path_max(dentry_list); +static void +close_target_directory(struct win32_apply_ctx *ctx) +{ + if (ctx->h_target) { + (*func_NtClose)(ctx->h_target); + ctx->h_target = NULL; + ctx->attr.RootDirectory = NULL; + } +} + +/* + * Ensures the target directory exists and opens a handle to it, in preparation + * of using paths relative to it. + */ +static int +prepare_target(struct list_head *dentry_list, struct win32_apply_ctx *ctx) +{ + int ret; + size_t path_max; + + ret = win32_path_to_nt_path(ctx->common.target, &ctx->target_ntpath); + if (ret) + return ret; + ret = open_target_directory(ctx); + if (ret) + return ret; + + 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 * the last component. */ @@ -816,9 +834,6 @@ prepare_target(struct list_head *dentry_list, struct win32_apply_ctx *ctx) if (!ctx->pathbuf.Buffer) return WIMLIB_ERR_NOMEM; - ctx->attr.RootDirectory = ctx->h_target; - ctx->attr.ObjectName = &ctx->pathbuf; - ctx->print_buffer = MALLOC((ctx->common.target_nchars + 1 + path_max + 1) * sizeof(wchar_t)); if (!ctx->print_buffer) @@ -901,81 +916,11 @@ adjust_compression_attribute(HANDLE h, const struct wim_dentry *dentry, if (NT_SUCCESS(status)) return 0; - set_errno_from_nt_status(status); - ERROR_WITH_ERRNO("Can't %s compression attribute on \"%ls\" " - "(status=0x%08"PRIx32")", - (compressed ? "set" : "clear"), - current_path(ctx), status); + winnt_error(status, L"Can't %s compression attribute on \"%ls\"", + (compressed ? "set" : "clear"), current_path(ctx)); return WIMLIB_ERR_SET_ATTRIBUTES; } -/* - * Clear FILE_ATTRIBUTE_ENCRYPTED if the file or directory is not supposed to be - * encrypted. - * - * You can provide FILE_ATTRIBUTE_ENCRYPTED to NtCreateFile() to set it on the - * created file. However, the file or directory will otherwise default to the - * encryption state of the parent directory. This function works around this - * limitation by using DecryptFile() to remove FILE_ATTRIBUTE_ENCRYPTED on files - * (and directories) that are not supposed to have it set. - * - * Regardless of whether it succeeds or fails, this function may close the - * handle to the file. If it does, it sets it to NULL. - */ -static int -maybe_clear_encryption_attribute(HANDLE *h_ptr, const struct wim_dentry *dentry, - struct win32_apply_ctx *ctx) -{ - if (dentry->d_inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) - return 0; - - if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES) - return 0; - - if (!ctx->common.supported_features.encrypted_files) - return 0; - - FILE_BASIC_INFORMATION info; - NTSTATUS status; - BOOL bret; - - /* Get current attributes */ - status = (*func_NtQueryInformationFile)(*h_ptr, &ctx->iosb, - &info, sizeof(info), - FileBasicInformation); - if (NT_SUCCESS(status) && - !(info.FileAttributes & FILE_ATTRIBUTE_ENCRYPTED)) - { - /* Nothing needs to be done. */ - return 0; - } - - /* Set the new encryption state */ - - /* Due to Windows' crappy file encryption APIs, we need to close the - * handle to the file so we don't get ERROR_SHARING_VIOLATION. We also - * hack together a Win32 path, although we will use the \\?\ prefix so - * it will actually be a NT path in disguise... */ - (*func_NtClose)(*h_ptr); - *h_ptr = NULL; - - build_win32_extraction_path(dentry, ctx); - - bret = DecryptFile(ctx->pathbuf.Buffer, 0); - - /* Restore the NT namespace path */ - build_extraction_path(dentry, ctx); - - if (!bret) { - DWORD err = GetLastError(); - set_errno_from_win32_error(err); - ERROR_WITH_ERRNO("Can't decrypt file \"%ls\" (err=%"PRIu32")", - current_path(ctx), (u32)err); - return WIMLIB_ERR_SET_ATTRIBUTES; - } - return 0; -} - /* Try to enable short name support on the target volume. If successful, return * true. If unsuccessful, issue a warning and return false. */ static bool @@ -1008,8 +953,9 @@ try_to_enable_short_names(const wchar_t *volume) return true; fail: - WARNING("Failed to enable short name support on %ls " - "(err=%"PRIu32")", volume + 4, (u32)GetLastError()); + win32_warning(GetLastError(), + L"Failed to enable short name support on %ls", + volume + 4); return false; } @@ -1041,8 +987,7 @@ remove_conflicting_short_name(const struct wim_dentry *dentry, struct win32_appl FILE_SHARE_VALID_FLAGS, FILE_OPEN_REPARSE_POINT | FILE_OPEN_FOR_BACKUP_INTENT); if (!NT_SUCCESS(status)) { - WARNING("Can't open \"%ls\" (status=0x%08"PRIx32")", - current_path(ctx), (u32)status); + winnt_warning(status, L"Can't open \"%ls\"", current_path(ctx)); goto out; } @@ -1181,13 +1126,7 @@ retry: return 0; } - if (status == STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME) { - ERROR("Can't set short name when short " - "names are not enabled on the volume!"); - } else { - ERROR("Can't set short name on \"%ls\" (status=0x%08"PRIx32")", - current_path(ctx), (u32)status); - } + winnt_error(status, L"Can't set short name on \"%ls\"", current_path(ctx)); return WIMLIB_ERR_SET_SHORT_NAME; } @@ -1244,6 +1183,116 @@ create_file(PHANDLE FileHandle, ctx); } +static int +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; + + 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)); + 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 (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; + 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)); + (*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; +} + +/* + * 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. + */ +static int +supersede_file_or_stream(struct win32_apply_ctx *ctx, HANDLE *h_ret) +{ + NTSTATUS status; + bool retried = false; + + /* FILE_ATTRIBUTE_SYSTEM is needed to ensure that + * 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, + NULL, + FILE_ATTRIBUTE_SYSTEM, + FILE_CREATE, + FILE_NON_DIRECTORY_FILE, + ctx); + if (likely(NT_SUCCESS(status))) + return 0; + + /* STATUS_OBJECT_NAME_COLLISION means that the file or stream already + * exists. Delete the existing file or stream, then try again. + * + * Note: we don't use FILE_OVERWRITE_IF or FILE_SUPERSEDE because of + * problems with certain file attributes, especially + * FILE_ATTRIBUTE_ENCRYPTED. FILE_SUPERSEDE is also broken in the + * Windows PE ramdisk. */ + if (status == STATUS_OBJECT_NAME_COLLISION && !retried) { + int ret = delete_file_or_stream(ctx); + if (ret) + return ret; + retried = true; + goto retry; + } + winnt_error(status, L"Can't create \"%ls\"", current_path(ctx)); + return WIMLIB_ERR_OPEN; +} + /* Create empty named data streams. * * Since these won't have 'struct wim_lookup_table_entry's, they won't show up @@ -1254,7 +1303,6 @@ create_any_empty_ads(const struct wim_dentry *dentry, struct win32_apply_ctx *ctx) { const struct wim_inode *inode = dentry->d_inode; - LARGE_INTEGER allocation_size; bool path_modified = false; int ret = 0; @@ -1263,7 +1311,6 @@ create_any_empty_ads(const struct wim_dentry *dentry, for (u16 i = 0; i < inode->i_num_ads; i++) { const struct wim_ads_entry *entry; - NTSTATUS status; HANDLE h; entry = &inode->i_ads_entries[i]; @@ -1276,25 +1323,14 @@ create_any_empty_ads(const struct wim_dentry *dentry, if (entry->lte) continue; - /* Probably setting the allocation size to 0 has no effect, but - * we might as well try. */ - allocation_size.QuadPart = 0; - build_extraction_path_with_ads(dentry, ctx, entry->stream_name, entry->stream_name_nbytes / sizeof(wchar_t)); path_modified = true; - status = do_create_file(&h, FILE_WRITE_DATA, &allocation_size, - 0, FILE_SUPERSEDE, 0, ctx); - if (!NT_SUCCESS(status)) { - set_errno_from_nt_status(status); - ERROR_WITH_ERRNO("Can't create \"%ls\" " - "(status=0x%08"PRIx32")", - current_path(ctx), (u32)status); - ret = WIMLIB_ERR_OPEN; + ret = supersede_file_or_stream(ctx, &h); + if (ret) break; - } (*func_NtClose)(h); } /* Restore the path to the dentry itself */ @@ -1317,28 +1353,18 @@ create_directory(const struct wim_dentry *dentry, HANDLE h; NTSTATUS status; int ret; - ULONG attrib; - /* Special attributes: + /* DELETE is needed for set_short_name(); GENERIC_READ and GENERIC_WRITE + * are needed for adjust_compression_attribute(). * - * Use FILE_ATTRIBUTE_ENCRYPTED if the directory needs to have it set. - * This doesn't work for FILE_ATTRIBUTE_COMPRESSED (unfortunately). - * - * Don't specify FILE_ATTRIBUTE_DIRECTORY; it gets set anyway as a - * result of the FILE_DIRECTORY_FILE option. */ - attrib = (dentry->d_inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED); - - /* DELETE is needed for set_short_name(). - * GENERIC_READ and GENERIC_WRITE are needed for - * adjust_compression_attribute(). */ + * FILE_ATTRIBUTE_SYSTEM is needed to ensure that + * FILE_ATTRIBUTE_ENCRYPTED doesn't get set before we want it to be. */ status = create_file(&h, GENERIC_READ | GENERIC_WRITE | DELETE, NULL, - attrib, FILE_OPEN_IF, FILE_DIRECTORY_FILE, + FILE_ATTRIBUTE_SYSTEM, FILE_OPEN_IF, FILE_DIRECTORY_FILE, dentry, ctx); if (!NT_SUCCESS(status)) { - set_errno_from_nt_status(status); - ERROR_WITH_ERRNO("Can't create directory \"%ls\" " - "(status=0x%08"PRIx32")", - current_path(ctx), (u32)status); + winnt_error(status, L"Can't create directory \"%ls\"", + current_path(ctx)); return WIMLIB_ERR_MKDIR; } @@ -1347,12 +1373,7 @@ create_directory(const struct wim_dentry *dentry, if (!ret) ret = adjust_compression_attribute(h, dentry, ctx); - if (!ret) - ret = maybe_clear_encryption_attribute(&h, dentry, ctx); - /* May close the handle!!! */ - - if (h) - (*func_NtClose)(h); + (*func_NtClose)(h); return ret; } @@ -1405,116 +1426,36 @@ create_directories(struct list_head *dentry_list, * * On success, returns an open handle to the file in @h_ret, with GENERIC_READ, * GENERIC_WRITE, and DELETE access. Also, the path to the file will be saved - * in ctx->pathbuf. On failure, returns WIMLIB_ERR_OPEN. + * in ctx->pathbuf. On failure, returns an error code. */ static int create_nondirectory_inode(HANDLE *h_ret, const struct wim_dentry *dentry, struct win32_apply_ctx *ctx) { - const struct wim_inode *inode; - ULONG attrib; - NTSTATUS status; - bool retried = false; - - inode = dentry->d_inode; + int ret; + HANDLE h; - /* If the file already exists and has FILE_ATTRIBUTE_SYSTEM and/or - * FILE_ATTRIBUTE_HIDDEN, these must be specified in order to supersede - * the file. - * - * Normally the user shouldn't be trying to overwrite such files anyway, - * but we at least provide FILE_ATTRIBUTE_SYSTEM and - * FILE_ATTRIBUTE_HIDDEN if the WIM inode has those attributes so that - * we catch the case where the user extracts the same files to the same - * location more than one time. - * - * Also specify FILE_ATTRIBUTE_ENCRYPTED if the file needs to be - * encrypted. - * - * In NO_ATTRIBUTES mode just don't specify any attributes at all. - */ - if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES) { - attrib = 0; - } else { - attrib = (inode->i_attributes & (FILE_ATTRIBUTE_SYSTEM | - FILE_ATTRIBUTE_HIDDEN | - FILE_ATTRIBUTE_ENCRYPTED)); - } build_extraction_path(dentry, ctx); -retry: - status = do_create_file(h_ret, GENERIC_READ | GENERIC_WRITE | DELETE, - NULL, attrib, FILE_SUPERSEDE, - FILE_NON_DIRECTORY_FILE, ctx); - if (NT_SUCCESS(status)) { - int ret; - - ret = adjust_compression_attribute(*h_ret, dentry, ctx); - if (ret) { - (*func_NtClose)(*h_ret); - return ret; - } - - ret = maybe_clear_encryption_attribute(h_ret, dentry, ctx); - /* May close the handle!!! */ - - if (ret) { - if (*h_ret) - (*func_NtClose)(*h_ret); - return ret; - } - if (!*h_ret) { - /* Re-open the handle so that we can return it on - * success. */ - status = do_create_file(h_ret, - GENERIC_READ | - GENERIC_WRITE | DELETE, - NULL, 0, FILE_OPEN, - FILE_NON_DIRECTORY_FILE, ctx); - if (!NT_SUCCESS(status)) - goto fail; - } - - ret = create_any_empty_ads(dentry, ctx); - if (ret) { - (*func_NtClose)(*h_ret); - return ret; - } - return 0; - } - - if (status == STATUS_ACCESS_DENIED && !retried) { - /* We also can't supersede an existing file that has - * FILE_ATTRIBUTE_READONLY set; doing so causes NtCreateFile() - * to return STATUS_ACCESS_DENIED . The only workaround seems - * to be to explicitly remove FILE_ATTRIBUTE_READONLY on the - * existing file, then try again. */ + ret = supersede_file_or_stream(ctx, &h); + if (ret) + goto out; - FILE_BASIC_INFORMATION info; - HANDLE h; + ret = adjust_compression_attribute(h, dentry, ctx); + if (ret) + goto out_close; - status = do_create_file(&h, FILE_WRITE_ATTRIBUTES, NULL, 0, - FILE_OPEN, FILE_NON_DIRECTORY_FILE, ctx); - if (!NT_SUCCESS(status)) - goto fail; + ret = create_any_empty_ads(dentry, ctx); + if (ret) + goto out_close; - memset(&info, 0, sizeof(info)); - info.FileAttributes = FILE_ATTRIBUTE_NORMAL; + *h_ret = h; + return 0; - status = (*func_NtSetInformationFile)(h, &ctx->iosb, - &info, sizeof(info), - FileBasicInformation); - (*func_NtClose)(h); - if (!NT_SUCCESS(status)) - goto fail; - retried = true; - goto retry; - } -fail: - set_errno_from_nt_status(status); - ERROR_WITH_ERRNO("Can't create file \"%ls\" (status=0x%08"PRIx32")", - current_path(ctx), (u32)status); - return WIMLIB_ERR_OPEN; +out_close: + (*func_NtClose)(h); +out: + return ret; } /* Creates a hard link at the location named by @dentry to the file represented @@ -1550,8 +1491,8 @@ create_link(HANDLE h, const struct wim_dentry *dentry, FileLinkInformation); if (NT_SUCCESS(status)) return 0; - ERROR("Failed to create link \"%ls\" (status=0x%08"PRIx32")", - current_path(ctx), (u32)status); + winnt_error(status, L"Failed to create link \"%ls\"", + current_path(ctx)); return WIMLIB_ERR_LINK; } else { HANDLE h2; @@ -1707,22 +1648,6 @@ begin_extract_stream_instance(const struct wim_lookup_table_entry *stream, build_extraction_path(dentry, ctx); } - /* Reparse point? */ - if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) - && (stream_name_nchars == 0)) - { - if (!ctx->common.supported_features.reparse_points) - return 0; - - /* We can't write the reparse stream directly; we must set it - * with FSCTL_SET_REPARSE_POINT, which requires that all the - * data be available. So, stage the data in a buffer. */ - - if (!prepare_data_buffer(ctx, stream->size)) - return WIMLIB_ERR_NOMEM; - list_add_tail(&dentry->tmp_list, &ctx->reparse_dentries); - return 0; - } /* Encrypted file? */ if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) @@ -1748,6 +1673,28 @@ begin_extract_stream_instance(const struct wim_lookup_table_entry *stream, return 0; } + /* Reparse point? + * + * Note: FILE_ATTRIBUTE_REPARSE_POINT is tested *after* + * FILE_ATTRIBUTE_ENCRYPTED since the WIM format does not store both EFS + * data and reparse data for the same file, and the EFS data takes + * precedence. */ + if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) + && (stream_name_nchars == 0)) + { + if (!ctx->common.supported_features.reparse_points) + return 0; + + /* We can't write the reparse stream directly; we must set it + * with FSCTL_SET_REPARSE_POINT, which requires that all the + * data be available. So, stage the data in a buffer. */ + + if (!prepare_data_buffer(ctx, stream->size)) + return WIMLIB_ERR_NOMEM; + list_add_tail(&dentry->tmp_list, &ctx->reparse_dentries); + return 0; + } + if (ctx->num_open_handles == MAX_OPEN_STREAMS) { /* XXX: Fix this. But because of the checks in * extract_stream_list(), this can now only happen on a @@ -1764,10 +1711,8 @@ begin_extract_stream_instance(const struct wim_lookup_table_entry *stream, FILE_SYNCHRONOUS_IO_NONALERT, ctx); if (!NT_SUCCESS(status)) { - set_errno_from_nt_status(status); - ERROR_WITH_ERRNO("Can't open \"%ls\" for writing " - "(status=0x%08"PRIx32")", - current_path(ctx), (u32)status); + winnt_error(status, L"Can't open \"%ls\" for writing", + current_path(ctx)); return WIMLIB_ERR_OPEN; } @@ -1824,10 +1769,8 @@ do_set_reparse_data(const struct wim_dentry *dentry, } fail: - set_errno_from_nt_status(status); - ERROR_WITH_ERRNO("Can't set reparse data on \"%ls\" " - "(status=0x%08"PRIx32")", - current_path(ctx), (u32)status); + winnt_error(status, L"Can't set reparse data on \"%ls\"", + current_path(ctx)); return WIMLIB_ERR_SET_REPARSE_DATA; } @@ -1997,31 +1940,49 @@ import_encrypted_data(PBYTE pbData, PVOID pvCallbackContext, PULONG Length) return ERROR_SUCCESS; } -/* Write the raw encrypted data to the already-created file corresponding to - * @dentry. +/* + * Write the raw encrypted data to the already-created file (or directory) + * corresponding to @dentry. * * The raw encrypted data is provided in ctx->data_buffer, and its size is - * ctx->encrypted_size. */ + * ctx->encrypted_size. + * + * This function may close the target directory, in which case the caller needs + * to re-open it if needed. + */ static int extract_encrypted_file(const struct wim_dentry *dentry, struct win32_apply_ctx *ctx) { void *rawctx; DWORD err; + ULONG flags; + bool retried; /* Temporarily build a Win32 path for OpenEncryptedFileRaw() */ build_win32_extraction_path(dentry, ctx); - err = OpenEncryptedFileRaw(ctx->pathbuf.Buffer, - CREATE_FOR_IMPORT, &rawctx); + flags = CREATE_FOR_IMPORT | OVERWRITE_HIDDEN; + if (dentry->d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) + flags |= CREATE_FOR_DIR; + + retried = false; +retry: + err = OpenEncryptedFileRaw(ctx->pathbuf.Buffer, flags, &rawctx); + if (err == ERROR_SHARING_VIOLATION && !retried) { + /* This can be caused by the handle we have open to the target + * directory. Try closing it temporarily. */ + close_target_directory(ctx); + retried = true; + goto retry; + } /* Restore the NT namespace path */ build_extraction_path(dentry, ctx); if (err != ERROR_SUCCESS) { - set_errno_from_win32_error(err); - ERROR_WITH_ERRNO("Can't open \"%ls\" for encrypted import " - "(err=%"PRIu32")", current_path(ctx), (u32)err); + win32_error(err, L"Can't open \"%ls\" for encrypted import", + current_path(ctx)); return WIMLIB_ERR_OPEN; } @@ -2032,9 +1993,8 @@ extract_encrypted_file(const struct wim_dentry *dentry, CloseEncryptedFileRaw(rawctx); if (err != ERROR_SUCCESS) { - set_errno_from_win32_error(err); - ERROR_WITH_ERRNO("Can't import encrypted file \"%ls\" " - "(err=%"PRIu32")", current_path(ctx), (u32)err); + win32_error(err, L"Can't import encrypted file \"%ls\"", + current_path(ctx)); return WIMLIB_ERR_WRITE; } @@ -2116,10 +2076,7 @@ extract_chunk(const void *chunk, size_t size, void *_ctx) &ctx->iosb, bufptr, count, NULL, NULL); if (!NT_SUCCESS(status)) { - set_errno_from_nt_status(status); - ERROR_WITH_ERRNO("Error writing data to target " - "volume (status=0x%08"PRIx32")", - (u32)status); + winnt_error(status, L"Error writing data to target volume"); return WIMLIB_ERR_WRITE; } bufptr += ctx->iosb.Information; @@ -2185,6 +2142,10 @@ end_extract_stream(struct wim_lookup_table_entry *stream, int status, void *_ctx ret = check_apply_error(dentry, ctx, ret); if (ret) return ret; + /* Re-open the target directory if needed. */ + ret = open_target_directory(ctx); + if (ret) + return ret; } } @@ -2359,10 +2320,9 @@ do_apply_metadata_to_file(HANDLE h, const struct wim_inode *inode, if (!NT_SUCCESS(status) && (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS)) { - set_errno_from_nt_status(status); - ERROR_WITH_ERRNO("Can't set security descriptor " - "on \"%ls\" (status=0x%08"PRIx32")", - current_path(ctx), (u32)status); + winnt_error(status, + L"Can't set security descriptor on \"%ls\"", + current_path(ctx)); return WIMLIB_ERR_SET_SECURITY; } } @@ -2372,10 +2332,13 @@ do_apply_metadata_to_file(HANDLE h, const struct wim_inode *inode, info.LastAccessTime.QuadPart = inode->i_last_access_time; info.LastWriteTime.QuadPart = inode->i_last_write_time; info.ChangeTime.QuadPart = 0; - if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES) - info.FileAttributes = 0; - else + if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES) { + info.FileAttributes = FILE_ATTRIBUTE_NORMAL; + } else { info.FileAttributes = inode->i_attributes & ~SPECIAL_ATTRIBUTES; + if (info.FileAttributes == 0) + info.FileAttributes = FILE_ATTRIBUTE_NORMAL; + } status = (*func_NtSetInformationFile)(h, &ctx->iosb, &info, sizeof(info), @@ -2387,10 +2350,8 @@ do_apply_metadata_to_file(HANDLE h, const struct wim_inode *inode, && !(status == STATUS_INVALID_PARAMETER && dentry_is_root(inode_first_extraction_dentry(inode)))) { - set_errno_from_nt_status(status); - ERROR_WITH_ERRNO("Can't set basic metadata on \"%ls\" " - "(status=0x%08"PRIx32")", - current_path(ctx), (u32)status); + winnt_error(status, L"Can't set basic metadata on \"%ls\"", + current_path(ctx)); return WIMLIB_ERR_SET_ATTRIBUTES; } @@ -2432,10 +2393,8 @@ apply_metadata_to_file(const struct wim_dentry *dentry, continue; } } - set_errno_from_nt_status(status); - ERROR_WITH_ERRNO("Can't open \"%ls\" to set metadata " - "(status=0x%08"PRIx32")", - current_path(ctx), (u32)status); + winnt_error(status, L"Can't open \"%ls\" to set metadata", + current_path(ctx)); return WIMLIB_ERR_OPEN; } @@ -2592,8 +2551,7 @@ win32_extract(struct list_head *dentry_list, struct apply_ctx *_ctx) do_warnings(ctx); out: - if (ctx->h_target) - (*func_NtClose)(ctx->h_target); + close_target_directory(ctx); if (ctx->target_ntpath.Buffer) HeapFree(GetProcessHeap(), 0, ctx->target_ntpath.Buffer); FREE(ctx->pathbuf.Buffer);