]> wimlib.net Git - wimlib/blobdiff - src/extract.c
extract.c: Fix dereference of NULL in memory exhausted path
[wimlib] / src / extract.c
index 383aaf710337eba80e051de87202f655b7b67eb7..fb96f13f8723bf26d0e24f9acfd4e9d051bb22c6 100644 (file)
         WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE  |       \
         WIMLIB_EXTRACT_FLAG_WIMBOOT)
 
         WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE  |       \
         WIMLIB_EXTRACT_FLAG_WIMBOOT)
 
+/* Send WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE or
+ * WIMLIB_PROGRESS_MSG_EXTRACT_METADATA.  */
+int
+do_file_extract_progress(struct apply_ctx *ctx, enum wimlib_progress_msg msg)
+{
+       ctx->count_until_file_progress = 512;  /* Arbitrary value to limit calls  */
+       return extract_progress(ctx, msg);
+}
+
 /* Check whether the extraction of a dentry should be skipped completely.  */
 static bool
 dentry_is_supported(struct wim_dentry *dentry,
 /* Check whether the extraction of a dentry should be skipped completely.  */
 static bool
 dentry_is_supported(struct wim_dentry *dentry,
@@ -217,7 +226,7 @@ load_streams_from_pipe(struct apply_ctx *ctx,
                        lte_unbind_wim_resource_spec(found_lte);
                        lte_bind_wim_resource_spec(needed_lte, rspec);
 
                        lte_unbind_wim_resource_spec(found_lte);
                        lte_bind_wim_resource_spec(needed_lte, rspec);
 
-                       ret = (*cbs->begin_stream)(needed_lte, 0,
+                       ret = (*cbs->begin_stream)(needed_lte,
                                                   cbs->begin_stream_ctx);
                        if (ret) {
                                lte_unbind_wim_resource_spec(needed_lte);
                                                   cbs->begin_stream_ctx);
                        if (ret) {
                                lte_unbind_wim_resource_spec(needed_lte);
@@ -260,7 +269,7 @@ load_streams_from_pipe(struct apply_ctx *ctx,
        }
        ret = 0;
 out:
        }
        ret = 0;
 out:
-       if (found_lte->resource_location != RESOURCE_IN_WIM)
+       if (found_lte && found_lte->resource_location != RESOURCE_IN_WIM)
                FREE(rspec);
        free_lookup_table_entry(found_lte);
        return ret;
                FREE(rspec);
        free_lookup_table_entry(found_lte);
        return ret;
@@ -306,18 +315,17 @@ retry:
 }
 
 static int
 }
 
 static int
-begin_extract_stream_wrapper(struct wim_lookup_table_entry *lte,
-                            u32 flags, void *_ctx)
+begin_extract_stream_wrapper(struct wim_lookup_table_entry *lte, void *_ctx)
 {
        struct apply_ctx *ctx = _ctx;
 
        ctx->cur_stream = lte;
 {
        struct apply_ctx *ctx = _ctx;
 
        ctx->cur_stream = lte;
+       ctx->cur_stream_offset = 0;
 
        if (unlikely(lte->out_refcnt > MAX_OPEN_STREAMS))
                return create_temporary_file(&ctx->tmpfile_fd, &ctx->tmpfile_name);
        else
 
        if (unlikely(lte->out_refcnt > MAX_OPEN_STREAMS))
                return create_temporary_file(&ctx->tmpfile_fd, &ctx->tmpfile_name);
        else
-               return (*ctx->saved_cbs->begin_stream)(lte, flags,
-                                                      ctx->saved_cbs->begin_stream_ctx);
+               return (*ctx->saved_cbs->begin_stream)(lte, ctx->saved_cbs->begin_stream_ctx);
 }
 
 static int
 }
 
 static int
@@ -327,9 +335,13 @@ extract_chunk_wrapper(const void *chunk, size_t size, void *_ctx)
        union wimlib_progress_info *progress = &ctx->progress;
        int ret;
 
        union wimlib_progress_info *progress = &ctx->progress;
        int ret;
 
+       ctx->cur_stream_offset += size;
+
        if (likely(ctx->supported_features.hard_links)) {
                progress->extract.completed_bytes +=
                        (u64)size * ctx->cur_stream->out_refcnt;
        if (likely(ctx->supported_features.hard_links)) {
                progress->extract.completed_bytes +=
                        (u64)size * ctx->cur_stream->out_refcnt;
+               if (ctx->cur_stream_offset == ctx->cur_stream->size)
+                       progress->extract.completed_streams += ctx->cur_stream->out_refcnt;
        } else {
                const struct stream_owner *owners = stream_owners(ctx->cur_stream);
                for (u32 i = 0; i < ctx->cur_stream->out_refcnt; i++) {
        } else {
                const struct stream_owner *owners = stream_owners(ctx->cur_stream);
                for (u32 i = 0; i < ctx->cur_stream->out_refcnt; i++) {
@@ -341,6 +353,8 @@ extract_chunk_wrapper(const void *chunk, size_t size, void *_ctx)
                                            d_extraction_alias_node)
                        {
                                progress->extract.completed_bytes += size;
                                            d_extraction_alias_node)
                        {
                                progress->extract.completed_bytes += size;
+                               if (ctx->cur_stream_offset == ctx->cur_stream->size)
+                                       progress->extract.completed_streams++;
                        }
                }
        }
                        }
                }
        }
@@ -355,8 +369,23 @@ extract_chunk_wrapper(const void *chunk, size_t size, void *_ctx)
                {
                        ctx->next_progress = UINT64_MAX;
                } else {
                {
                        ctx->next_progress = UINT64_MAX;
                } else {
-                       ctx->next_progress += progress->extract.total_bytes / 128;
-                       if (ctx->next_progress > progress->extract.total_bytes)
+                       /* Send new message as soon as another 1/128 of the
+                        * total has been extracted.  (Arbitrary number.)  */
+                       ctx->next_progress =
+                               progress->extract.completed_bytes +
+                                       progress->extract.total_bytes / 128;
+
+                       /* ... Unless that would be more than 5000000 bytes, in
+                        * which case send the next after the next 5000000
+                        * bytes.  (Another arbitrary number.)  */
+                       if (progress->extract.completed_bytes + 5000000 <
+                           ctx->next_progress)
+                               ctx->next_progress =
+                                       progress->extract.completed_bytes + 5000000;
+
+                       /* ... But always send a message as soon as we're
+                        * completely done.  */
+                       if (progress->extract.total_bytes < ctx->next_progress)
                                ctx->next_progress = progress->extract.total_bytes;
                }
        }
                                ctx->next_progress = progress->extract.total_bytes;
                }
        }
@@ -377,13 +406,17 @@ extract_chunk_wrapper(const void *chunk, size_t size, void *_ctx)
 }
 
 static int
 }
 
 static int
-extract_from_tmpfile(const tchar *tmpfile_name,
-                    struct wim_lookup_table_entry *orig_lte,
-                    struct apply_ctx *ctx)
+extract_from_tmpfile(const tchar *tmpfile_name, struct apply_ctx *ctx)
 {
        struct wim_lookup_table_entry tmpfile_lte;
 {
        struct wim_lookup_table_entry tmpfile_lte;
-       const struct stream_owner *owners = stream_owners(orig_lte);
+       struct wim_lookup_table_entry *orig_lte = ctx->cur_stream;
+       const struct read_stream_list_callbacks *cbs = ctx->saved_cbs;
        int ret;
        int ret;
+       const u32 orig_refcnt = orig_lte->out_refcnt;
+
+       BUILD_BUG_ON(MAX_OPEN_STREAMS < ARRAY_LEN(orig_lte->inline_stream_owners));
+
+       struct stream_owner *owners = orig_lte->stream_owners;
 
        /* Copy the stream's data from the temporary file to each of its
         * destinations.
 
        /* Copy the stream's data from the temporary file to each of its
         * destinations.
@@ -395,15 +428,38 @@ extract_from_tmpfile(const tchar *tmpfile_name,
        memcpy(&tmpfile_lte, orig_lte, sizeof(struct wim_lookup_table_entry));
        tmpfile_lte.resource_location = RESOURCE_IN_FILE_ON_DISK;
        tmpfile_lte.file_on_disk = ctx->tmpfile_name;
        memcpy(&tmpfile_lte, orig_lte, sizeof(struct wim_lookup_table_entry));
        tmpfile_lte.resource_location = RESOURCE_IN_FILE_ON_DISK;
        tmpfile_lte.file_on_disk = ctx->tmpfile_name;
-       tmpfile_lte.out_refcnt = 1;
+       ret = 0;
+       for (u32 i = 0; i < orig_refcnt; i++) {
+
+               /* Note: it usually doesn't matter whether we pass the original
+                * stream entry to callbacks provided by the extraction backend
+                * as opposed to the tmpfile stream entry, since they shouldn't
+                * actually read data from the stream other than through the
+                * read_stream_prefix() call below.  But for
+                * WIMLIB_EXTRACT_FLAG_WIMBOOT mode on Windows it does matter
+                * because it needs the original stream location in order to
+                * create the external backing reference.  */
 
 
-       for (u32 i = 0; i < orig_lte->out_refcnt; i++) {
-               tmpfile_lte.inline_stream_owners[0] = owners[i];
-               ret = read_full_stream_with_cbs(&tmpfile_lte, ctx->saved_cbs);
+               orig_lte->out_refcnt = 1;
+               orig_lte->inline_stream_owners[0] = owners[i];
+
+               ret = (*cbs->begin_stream)(orig_lte, cbs->begin_stream_ctx);
                if (ret)
                if (ret)
-                       return ret;
+                       break;
+
+               /* Extra SHA-1 isn't necessary here, but it shouldn't hurt as
+                * this case is very rare anyway.  */
+               ret = extract_stream(&tmpfile_lte, tmpfile_lte.size,
+                                    cbs->consume_chunk,
+                                    cbs->consume_chunk_ctx);
+
+               ret = (*cbs->end_stream)(orig_lte, ret, cbs->end_stream_ctx);
+               if (ret)
+                       break;
        }
        }
-       return 0;
+       FREE(owners);
+       orig_lte->out_refcnt = 0;
+       return ret;
 }
 
 static int
 }
 
 static int
@@ -415,8 +471,7 @@ end_extract_stream_wrapper(struct wim_lookup_table_entry *stream,
        if (unlikely(filedes_valid(&ctx->tmpfile_fd))) {
                filedes_close(&ctx->tmpfile_fd);
                if (!status)
        if (unlikely(filedes_valid(&ctx->tmpfile_fd))) {
                filedes_close(&ctx->tmpfile_fd);
                if (!status)
-                       status = extract_from_tmpfile(ctx->tmpfile_name,
-                                                     stream, ctx);
+                       status = extract_from_tmpfile(ctx->tmpfile_name, ctx);
                filedes_invalidate(&ctx->tmpfile_fd);
                tunlink(ctx->tmpfile_name);
                FREE(ctx->tmpfile_name);
                filedes_invalidate(&ctx->tmpfile_fd);
                tunlink(ctx->tmpfile_name);
                FREE(ctx->tmpfile_name);
@@ -930,7 +985,7 @@ ref_stream(struct wim_lookup_table_entry *lte, u32 stream_idx,
                return 0;
 
        ctx->progress.extract.total_bytes += lte->size;
                return 0;
 
        ctx->progress.extract.total_bytes += lte->size;
-       ctx->progress.extract.num_streams++;
+       ctx->progress.extract.total_streams++;
 
        if (inode->i_visited)
                return 0;
 
        if (inode->i_visited)
                return 0;
@@ -1473,12 +1528,16 @@ check_extract_flags(const WIMStruct *wim, int *extract_flags_p)
        }
 #endif
 
        }
 #endif
 
-#ifndef __WIN32__
        if (extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT) {
        if (extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT) {
+#ifdef __WIN32__
+               if (!wim->filename)
+                       return WIMLIB_ERR_NO_FILENAME;
+#else
                ERROR("WIMBoot extraction is only supported on Windows!");
                return WIMLIB_ERR_UNSUPPORTED;
                ERROR("WIMBoot extraction is only supported on Windows!");
                return WIMLIB_ERR_UNSUPPORTED;
-       }
 #endif
 #endif
+       }
+
 
        if ((extract_flags & (WIMLIB_EXTRACT_FLAG_RPFIX |
                              WIMLIB_EXTRACT_FLAG_NORPFIX |
 
        if ((extract_flags & (WIMLIB_EXTRACT_FLAG_RPFIX |
                              WIMLIB_EXTRACT_FLAG_NORPFIX |