X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fwin32_apply.c;h=7580a71875e156c5a1dd2dab7f4ef6b35292e647;hp=507bde86e178862e339e2c43bdee4ff27956a677;hb=f1c07e953597e3f6a809d35d7d5160af1ff67ed3;hpb=b5ae4e48d30124e356aaf680e19622dc540b3836 diff --git a/src/win32_apply.c b/src/win32_apply.c index 507bde86..7580a718 100644 --- a/src/win32_apply.c +++ b/src/win32_apply.c @@ -33,6 +33,7 @@ #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" @@ -202,6 +203,23 @@ static void build_extraction_path(const struct wim_dentry *dentry, struct win32_apply_ctx *ctx); +static int +report_dentry_apply_error(const struct wim_dentry *dentry, + struct win32_apply_ctx *ctx, int ret) +{ + build_extraction_path(dentry, ctx); + return report_apply_error(&ctx->common, ret, current_path(ctx)); +} + +static inline int +check_apply_error(const struct wim_dentry *dentry, + struct win32_apply_ctx *ctx, int ret) +{ + if (unlikely(ret)) + ret = report_dentry_apply_error(dentry, ctx, ret); + return ret; +} + static int win32_get_supported_features(const wchar_t *target, struct wim_features *supported_features) @@ -1248,6 +1266,8 @@ create_any_empty_ads(const struct wim_dentry *dentry, const struct wim_ads_entry *entry; NTSTATUS status; HANDLE h; + bool retried; + DWORD disposition; entry = &inode->i_ads_entries[i]; @@ -1268,9 +1288,23 @@ create_any_empty_ads(const struct wim_dentry *dentry, entry->stream_name_nbytes / sizeof(wchar_t)); path_modified = true; + + retried = false; + disposition = FILE_SUPERSEDE; + retry: status = do_create_file(&h, FILE_WRITE_DATA, &allocation_size, - 0, FILE_SUPERSEDE, 0, ctx); - if (!NT_SUCCESS(status)) { + 0, disposition, 0, ctx); + if (unlikely(!NT_SUCCESS(status))) { + if (status == STATUS_OBJECT_NAME_NOT_FOUND && !retried) { + /* Workaround for defect in the Windows PE + * in-memory filesystem implementation: + * FILE_SUPERSEDE does not create the file, as + * expected and documented, when the named file + * does not exist. */ + retried = true; + disposition = FILE_CREATE; + goto retry; + } set_errno_from_nt_status(status); ERROR_WITH_ERRNO("Can't create \"%ls\" " "(status=0x%08"PRIx32")", @@ -1366,10 +1400,12 @@ create_directories(struct list_head *dentry_list, * in prepare_target(). */ if (!dentry_is_root(dentry)) { ret = create_directory(dentry, ctx); + ret = check_apply_error(dentry, ctx, ret); if (ret) return ret; ret = create_any_empty_ads(dentry, ctx); + ret = check_apply_error(dentry, ctx, ret); if (ret) return ret; } @@ -1396,6 +1432,7 @@ create_nondirectory_inode(HANDLE *h_ret, const struct wim_dentry *dentry, ULONG attrib; NTSTATUS status; bool retried = false; + DWORD disposition; inode = dentry->d_inode; @@ -1422,11 +1459,12 @@ create_nondirectory_inode(HANDLE *h_ret, const struct wim_dentry *dentry, FILE_ATTRIBUTE_ENCRYPTED)); } build_extraction_path(dentry, ctx); + disposition = FILE_SUPERSEDE; retry: status = do_create_file(h_ret, GENERIC_READ | GENERIC_WRITE | DELETE, - NULL, attrib, FILE_SUPERSEDE, + NULL, attrib, disposition, FILE_NON_DIRECTORY_FILE, ctx); - if (NT_SUCCESS(status)) { + if (likely(NT_SUCCESS(status))) { int ret; ret = adjust_compression_attribute(*h_ret, dentry, ctx); @@ -1464,6 +1502,16 @@ retry: return 0; } + if (status == STATUS_OBJECT_NAME_NOT_FOUND && !retried) { + /* Workaround for defect in the Windows PE in-memory filesystem + * implementation: FILE_SUPERSEDE does not create the file, as + * expected and documented, when the named file does not exist. + */ + retried = true; + disposition = FILE_CREATE; + goto retry; + } + if (status == STATUS_ACCESS_DENIED && !retried) { /* We also can't supersede an existing file that has * FILE_ATTRIBUTE_READONLY set; doing so causes NtCreateFile() @@ -1625,6 +1673,7 @@ create_nondirectories(struct list_head *dentry_list, struct win32_apply_ctx *ctx /* Call create_nondirectory() only once per inode */ if (dentry == inode_first_extraction_dentry(inode)) { ret = create_nondirectory(inode, ctx); + ret = check_apply_error(dentry, ctx, ret); if (ret) return ret; } @@ -1644,17 +1693,17 @@ close_handles(struct win32_apply_ctx *ctx) /* Prepare to read the next stream, which has size @stream_size, into an * in-memory buffer. */ -static int +static bool prepare_data_buffer(struct win32_apply_ctx *ctx, u64 stream_size) { if (stream_size > ctx->data_buffer_size) { /* Larger buffer needed. */ void *new_buffer; if ((size_t)stream_size != stream_size) - return WIMLIB_ERR_NOMEM; + return false; new_buffer = REALLOC(ctx->data_buffer, stream_size); if (!new_buffer) - return WIMLIB_ERR_NOMEM; + return false; ctx->data_buffer = new_buffer; ctx->data_buffer_size = stream_size; } @@ -1662,7 +1711,7 @@ prepare_data_buffer(struct win32_apply_ctx *ctx, u64 stream_size) * extract_chunk() that the data buffer needs to be filled while reading * the stream data. */ ctx->data_buffer_ptr = ctx->data_buffer; - return 0; + return true; } static int @@ -1698,8 +1747,10 @@ begin_extract_stream_instance(const struct wim_lookup_table_entry *stream, * 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 prepare_data_buffer(ctx, stream->size); + return 0; } /* Encrypted file? */ @@ -1720,8 +1771,10 @@ begin_extract_stream_instance(const struct wim_lookup_table_entry *stream, * TODO: This isn't sufficient for extremely large encrypted * files. Perhaps we should create an extra thread to write * such files... */ + if (!prepare_data_buffer(ctx, stream->size)) + return WIMLIB_ERR_NOMEM; list_add_tail(&dentry->tmp_list, &ctx->encrypted_dentries); - return prepare_data_buffer(ctx, stream->size); + return 0; } if (ctx->num_open_handles == MAX_OPEN_STREAMS) { @@ -2041,6 +2094,7 @@ begin_extract_stream(struct wim_lookup_table_entry *stream, void *_ctx) dentry = inode_first_extraction_dentry(inode); ret = begin_extract_stream_instance(stream, dentry, stream_name, ctx); + ret = check_apply_error(dentry, ctx, ret); if (ret) goto fail; } else { @@ -2056,6 +2110,7 @@ begin_extract_stream(struct wim_lookup_table_entry *stream, void *_ctx) dentry, stream_name, ctx); + ret = check_apply_error(dentry, ctx, ret); if (ret) goto fail; next = next->next; @@ -2133,7 +2188,8 @@ end_extract_stream(struct wim_lookup_table_entry *stream, int status, void *_ctx "%"PRIu64" bytes (exceeds %u bytes)", current_path(ctx), stream->size, REPARSE_DATA_MAX_SIZE); - return WIMLIB_ERR_INVALID_REPARSE_DATA; + ret = WIMLIB_ERR_INVALID_REPARSE_DATA; + return check_apply_error(dentry, ctx, ret); } /* In the WIM format, reparse streams are just the reparse data * and omit the header. But we can reconstruct the header. */ @@ -2145,6 +2201,7 @@ end_extract_stream(struct wim_lookup_table_entry *stream, int status, void *_ctx ret = set_reparse_data(dentry, &ctx->rpbuf, stream->size + REPARSE_DATA_OFFSET, ctx); + ret = check_apply_error(dentry, ctx, ret); if (ret) return ret; } @@ -2154,6 +2211,7 @@ end_extract_stream(struct wim_lookup_table_entry *stream, int status, void *_ctx ctx->encrypted_size = stream->size; list_for_each_entry(dentry, &ctx->encrypted_dentries, tmp_list) { ret = extract_encrypted_file(dentry, ctx); + ret = check_apply_error(dentry, ctx, ret); if (ret) return ret; } @@ -2429,6 +2487,7 @@ apply_metadata(struct list_head *dentry_list, struct win32_apply_ctx *ctx) list_for_each_entry_reverse(dentry, dentry_list, d_extraction_list_node) { ret = apply_metadata_to_file(dentry, ctx); + ret = check_apply_error(dentry, ctx, ret); if (ret) return ret; ret = report_file_metadata_applied(&ctx->common);