]> wimlib.net Git - wimlib/blobdiff - src/win32_apply.c
Clean up util.h and util.c
[wimlib] / src / win32_apply.c
index 507bde86e178862e339e2c43bdee4ff27956a677..7580a71875e156c5a1dd2dab7f4ef6b35292e647 100644 (file)
@@ -33,6 +33,7 @@
 #include "wimlib/error.h"
 #include "wimlib/lookup_table.h"
 #include "wimlib/metadata.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"
 #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);
 
 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)
 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;
                const struct wim_ads_entry *entry;
                NTSTATUS status;
                HANDLE h;
+               bool retried;
+               DWORD disposition;
 
                entry = &inode->i_ads_entries[i];
 
 
                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;
                                               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,
                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")",
                        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);
                 * 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);
                        if (ret)
                                return ret;
 
                        ret = create_any_empty_ads(dentry, ctx);
+                       ret = check_apply_error(dentry, ctx, ret);
                        if (ret)
                                return 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;
        ULONG attrib;
        NTSTATUS status;
        bool retried = false;
+       DWORD disposition;
 
        inode = dentry->d_inode;
 
 
        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);
                                                 FILE_ATTRIBUTE_ENCRYPTED));
        }
        build_extraction_path(dentry, ctx);
+       disposition = FILE_SUPERSEDE;
 retry:
        status = do_create_file(h_ret, GENERIC_READ | GENERIC_WRITE | DELETE,
 retry:
        status = do_create_file(h_ret, GENERIC_READ | GENERIC_WRITE | DELETE,
-                               NULL, attrib, FILE_SUPERSEDE,
+                               NULL, attrib, disposition,
                                FILE_NON_DIRECTORY_FILE, ctx);
                                FILE_NON_DIRECTORY_FILE, ctx);
-       if (NT_SUCCESS(status)) {
+       if (likely(NT_SUCCESS(status))) {
                int ret;
 
                ret = adjust_compression_attribute(*h_ret, dentry, ctx);
                int ret;
 
                ret = adjust_compression_attribute(*h_ret, dentry, ctx);
@@ -1464,6 +1502,16 @@ retry:
                return 0;
        }
 
                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()
        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);
                /* 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;
                }
                        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.  */
 
 /* 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)
 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)
                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;
        }
                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;
         * 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
 }
 
 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.  */
 
                 * 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);
                list_add_tail(&dentry->tmp_list, &ctx->reparse_dentries);
-               return prepare_data_buffer(ctx, stream->size);
+               return 0;
        }
 
        /* Encrypted file?  */
        }
 
        /* 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...  */
                 * 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);
                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) {
        }
 
        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);
                        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 {
                        if (ret)
                                goto fail;
                } else {
@@ -2056,6 +2110,7 @@ begin_extract_stream(struct wim_lookup_table_entry *stream, void *_ctx)
                                                                    dentry,
                                                                    stream_name,
                                                                    ctx);
                                                                    dentry,
                                                                    stream_name,
                                                                    ctx);
+                               ret = check_apply_error(dentry, ctx, ret);
                                if (ret)
                                        goto fail;
                                next = next->next;
                                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);
                              "%"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.  */
                }
                /* 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 = set_reparse_data(dentry, &ctx->rpbuf,
                                               stream->size + REPARSE_DATA_OFFSET,
                                               ctx);
+                       ret = check_apply_error(dentry, ctx, ret);
                        if (ret)
                                return 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);
                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;
                }
                        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);
        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);
                if (ret)
                        return ret;
                ret = report_file_metadata_applied(&ctx->common);