]> wimlib.net Git - wimlib/blobdiff - src/extract.c
extract.c: Pass orig stream to callbacks when reading tmpfile
[wimlib] / src / extract.c
index efbae3d5a901d67691896a54feed9990f461e54d..3cc9959d27df0ebf57bd98853adcff152e1a52fd 100644 (file)
@@ -53,6 +53,7 @@
 #include "wimlib/reparse.h"
 #include "wimlib/resource.h"
 #include "wimlib/security.h"
+#include "wimlib/unix_data.h"
 #ifdef __WIN32__
 #  include "wimlib/win32.h" /* for realpath() equivalent */
 #endif
 #include <sys/stat.h>
 #include <unistd.h>
 
-#define WIMLIB_EXTRACT_FLAG_MULTI_IMAGE 0x80000000
-#define WIMLIB_EXTRACT_FLAG_FROM_PIPE   0x40000000
-#define WIMLIB_EXTRACT_FLAG_IMAGEMODE   0x20000000
+#define WIMLIB_EXTRACT_FLAG_FROM_PIPE   0x80000000
+#define WIMLIB_EXTRACT_FLAG_IMAGEMODE   0x40000000
 
 /* Keep in sync with wimlib.h  */
 #define WIMLIB_EXTRACT_MASK_PUBLIC                             \
        (WIMLIB_EXTRACT_FLAG_NTFS                       |       \
+        WIMLIB_EXTRACT_FLAG_UNIX_DATA                  |       \
         WIMLIB_EXTRACT_FLAG_NO_ACLS                    |       \
         WIMLIB_EXTRACT_FLAG_STRICT_ACLS                |       \
         WIMLIB_EXTRACT_FLAG_RPFIX                      |       \
@@ -112,7 +113,6 @@ dentry_is_supported(struct wim_dentry *dentry,
 
 
 #define PWM_ALLOW_WIM_HDR 0x00001
-#define PWM_SILENT_EOF   0x00002
 
 /* Read the header from a stream in a pipable WIM.  */
 static int
@@ -131,7 +131,9 @@ read_pwm_stream_header(WIMStruct *pwm, struct wim_lookup_table_entry *lte,
        if (ret)
                goto read_error;
 
-       if ((flags & PWM_ALLOW_WIM_HDR) && buf.stream_hdr.magic == PWM_MAGIC) {
+       if ((flags & PWM_ALLOW_WIM_HDR) &&
+           le64_to_cpu(buf.stream_hdr.magic) == PWM_MAGIC)
+       {
                BUILD_BUG_ON(sizeof(buf.pwm_hdr) < sizeof(buf.stream_hdr));
                ret = full_read(&pwm->in_fd, &buf.stream_hdr + 1,
                                sizeof(buf.pwm_hdr) - sizeof(buf.stream_hdr));
@@ -162,8 +164,7 @@ read_pwm_stream_header(WIMStruct *pwm, struct wim_lookup_table_entry *lte,
        return 0;
 
 read_error:
-       if (ret != WIMLIB_ERR_UNEXPECTED_END_OF_FILE || !(flags & PWM_SILENT_EOF))
-               ERROR_WITH_ERRNO("Error reading pipable WIM from pipe");
+       ERROR_WITH_ERRNO("Error reading pipable WIM from pipe");
        return ret;
 }
 
@@ -189,10 +190,10 @@ load_streams_from_pipe(struct apply_ctx *ctx,
        memcpy(ctx->progress.extract.guid, ctx->wim->hdr.guid, WIM_GUID_LEN);
        ctx->progress.extract.part_number = ctx->wim->hdr.part_number;
        ctx->progress.extract.total_parts = ctx->wim->hdr.total_parts;
-       if (ctx->progress_func) {
-               ctx->progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN,
-                                  &ctx->progress);
-       }
+       ret = extract_progress(ctx, WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN);
+       if (ret)
+               goto out;
+
        while (ctx->num_streams_remaining) {
                struct wim_header_disk pwm_hdr;
                struct wim_lookup_table_entry *needed_lte;
@@ -250,11 +251,10 @@ load_streams_from_pipe(struct apply_ctx *ctx,
                                ctx->progress.extract.total_parts = total_parts;
                                memcpy(ctx->progress.extract.guid,
                                       pwm_hdr.guid, WIM_GUID_LEN);
-                               if (ctx->progress_func) {
-                                       ctx->progress_func(
-                                               WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN,
-                                                          &ctx->progress);
-                               }
+                               ret = extract_progress(ctx,
+                                                      WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN);
+                               if (ret)
+                                       goto out;
                        }
                }
        }
@@ -266,41 +266,199 @@ out:
        return ret;
 }
 
+/* Creates a temporary file opened for writing.  The open file descriptor is
+ * returned in @fd_ret and its name is returned in @name_ret (dynamically
+ * allocated).  */
+static int
+create_temporary_file(struct filedes *fd_ret, tchar **name_ret)
+{
+       tchar *name;
+       int open_flags;
+       int raw_fd;
+
+retry:
+       name = ttempnam(NULL, T("wimlib"));
+       if (!name) {
+               ERROR_WITH_ERRNO("Failed to create temporary filename");
+               return WIMLIB_ERR_NOMEM;
+       }
+
+       open_flags = O_WRONLY | O_CREAT | O_EXCL | O_BINARY;
+#ifdef __WIN32__
+       open_flags |= _O_SHORT_LIVED;
+#endif
+       raw_fd = topen(name, open_flags, 0600);
+
+       if (raw_fd < 0) {
+               if (errno == EEXIST) {
+                       FREE(name);
+                       goto retry;
+               }
+               ERROR_WITH_ERRNO("Failed to create temporary file "
+                                "\"%"TS"\"", name);
+               FREE(name);
+               return WIMLIB_ERR_OPEN;
+       }
+
+       filedes_init(fd_ret, raw_fd);
+       *name_ret = name;
+       return 0;
+}
+
 static int
-begin_extract_stream_with_progress(struct wim_lookup_table_entry *lte,
-                                  u32 flags, void *_ctx)
+begin_extract_stream_wrapper(struct wim_lookup_table_entry *lte,
+                            u32 flags, void *_ctx)
 {
        struct apply_ctx *ctx = _ctx;
 
        ctx->cur_stream = lte;
 
-       return (*ctx->saved_cbs->begin_stream)(lte, flags,
-                                              ctx->saved_cbs->begin_stream_ctx);
+       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);
 }
 
 static int
-consume_chunk_with_progress(const void *chunk, size_t size, void *_ctx)
+extract_chunk_wrapper(const void *chunk, size_t size, void *_ctx)
 {
        struct apply_ctx *ctx = _ctx;
-       wimlib_progress_func_t progress_func = ctx->progress_func;
        union wimlib_progress_info *progress = &ctx->progress;
-       u32 num_copies = ctx->cur_stream->out_refcnt;
+       int ret;
 
-       progress->extract.completed_bytes += size * num_copies;
+       if (likely(ctx->supported_features.hard_links)) {
+               progress->extract.completed_bytes +=
+                       (u64)size * 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++) {
+                       const struct wim_inode *inode = owners[i].inode;
+                       const struct wim_dentry *dentry;
+
+                       list_for_each_entry(dentry,
+                                           &inode->i_extraction_aliases,
+                                           d_extraction_alias_node)
+                       {
+                               progress->extract.completed_bytes += size;
+                       }
+               }
+       }
        if (progress->extract.completed_bytes >= ctx->next_progress) {
-               progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS, progress);
+
+               ret = extract_progress(ctx, WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS);
+               if (ret)
+                       return ret;
+
                if (progress->extract.completed_bytes >=
                    progress->extract.total_bytes)
                {
                        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;
                }
        }
-       return (*ctx->saved_cbs->consume_chunk)(chunk, size,
-                                               ctx->saved_cbs->consume_chunk_ctx);
+
+       if (unlikely(filedes_valid(&ctx->tmpfile_fd))) {
+               /* Just extracting to temporary file for now.  */
+               ret = full_write(&ctx->tmpfile_fd, chunk, size);
+               if (ret) {
+                       ERROR_WITH_ERRNO("Error writing data to "
+                                        "temporary file \"%"TS"\"",
+                                        ctx->tmpfile_name);
+               }
+               return ret;
+       } else {
+               return (*ctx->saved_cbs->consume_chunk)(chunk, size,
+                                                       ctx->saved_cbs->consume_chunk_ctx);
+       }
+}
+
+static int
+extract_from_tmpfile(const tchar *tmpfile_name, struct apply_ctx *ctx)
+{
+       struct wim_lookup_table_entry tmpfile_lte;
+       struct wim_lookup_table_entry *orig_lte = ctx->cur_stream;
+       const struct stream_owner *owners = stream_owners(orig_lte);
+       const struct read_stream_list_callbacks *cbs = ctx->saved_cbs;
+       int ret;
+
+       /* Copy the stream's data from the temporary file to each of its
+        * destinations.
+        *
+        * This is executed only in the very uncommon case that a
+        * single-instance stream is being extracted to more than
+        * MAX_OPEN_STREAMS locations!  */
+
+       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;
+
+       for (u32 i = 0; i < orig_lte->out_refcnt; i++) {
+               tmpfile_lte.inline_stream_owners[0] = owners[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.  */
+
+               ret = (*cbs->begin_stream)(orig_lte, 0,
+                                          cbs->begin_stream_ctx);
+               if (ret)
+                       return ret;
+
+               /* 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);
+
+               return (*cbs->end_stream)(orig_lte, ret,
+                                         cbs->end_stream_ctx);
+       }
+       return 0;
+}
+
+static int
+end_extract_stream_wrapper(struct wim_lookup_table_entry *stream,
+                          int status, void *_ctx)
+{
+       struct apply_ctx *ctx = _ctx;
+
+       if (unlikely(filedes_valid(&ctx->tmpfile_fd))) {
+               filedes_close(&ctx->tmpfile_fd);
+               if (!status)
+                       status = extract_from_tmpfile(ctx->tmpfile_name, ctx);
+               filedes_invalidate(&ctx->tmpfile_fd);
+               tunlink(ctx->tmpfile_name);
+               FREE(ctx->tmpfile_name);
+               return status;
+       } else {
+               return (*ctx->saved_cbs->end_stream)(stream, status,
+                                                    ctx->saved_cbs->end_stream_ctx);
+       }
 }
 
 /*
@@ -313,30 +471,34 @@ consume_chunk_with_progress(const void *chunk, size_t size, void *_ctx)
  *
  * This also works if the WIM is being read from a pipe, whereas attempting to
  * read streams directly (e.g. with read_full_stream_into_buf()) will not.
+ *
+ * This also will split up streams that will need to be extracted to more than
+ * MAX_OPEN_STREAMS locations, as measured by the 'out_refcnt' of each stream.
+ * Therefore, the apply_operations implementation need not worry about running
+ * out of file descriptors, unless it might open more than one file descriptor
+ * per nominal destination (e.g. Win32 currently might because the destination
+ * file system might not support hard links).
  */
 int
 extract_stream_list(struct apply_ctx *ctx,
                    const struct read_stream_list_callbacks *cbs)
 {
        struct read_stream_list_callbacks wrapper_cbs = {
-               .begin_stream      = begin_extract_stream_with_progress,
+               .begin_stream      = begin_extract_stream_wrapper,
                .begin_stream_ctx  = ctx,
-               .consume_chunk     = consume_chunk_with_progress,
+               .consume_chunk     = extract_chunk_wrapper,
                .consume_chunk_ctx = ctx,
-               .end_stream        = cbs->end_stream,
-               .end_stream_ctx    = cbs->end_stream_ctx,
+               .end_stream        = end_extract_stream_wrapper,
+               .end_stream_ctx    = ctx,
        };
-       if (ctx->progress_func) {
-               ctx->saved_cbs = cbs;
-               cbs = &wrapper_cbs;
-       }
+       ctx->saved_cbs = cbs;
        if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_FROM_PIPE) {
-               return load_streams_from_pipe(ctx, cbs);
+               return load_streams_from_pipe(ctx, &wrapper_cbs);
        } else {
                return read_stream_list(&ctx->stream_list,
                                        offsetof(struct wim_lookup_table_entry,
                                                 extraction_list),
-                                       cbs, VERIFY_STREAM_HASHES);
+                                       &wrapper_cbs, VERIFY_STREAM_HASHES);
        }
 }
 
@@ -422,7 +584,7 @@ remove_contained_trees(struct wim_dentry **trees, size_t num_trees)
        for (i = 0; i < num_trees; i++) {
                struct wim_dentry *d = trees[i];
                while (!dentry_is_root(d)) {
-                       d = d->parent;
+                       d = d->d_parent;
                        if (d->tmp_flag)
                                goto tree_contained;
                }
@@ -489,7 +651,7 @@ build_dentry_list(struct list_head *dentry_list, struct wim_dentry **trees,
                        place_after = dentry_list;
                        ancestor = dentry;
                        do {
-                               ancestor = ancestor->parent;
+                               ancestor = ancestor->d_parent;
                                if (will_extract_dentry(ancestor)) {
                                        place_after = &ancestor->d_extraction_list_node;
                                        break;
@@ -498,7 +660,7 @@ build_dentry_list(struct list_head *dentry_list, struct wim_dentry **trees,
 
                        ancestor = dentry;
                        do {
-                               ancestor = ancestor->parent;
+                               ancestor = ancestor->d_parent;
                                if (will_extract_dentry(ancestor))
                                        break;
                                list_add(&ancestor->d_extraction_list_node, place_after);
@@ -593,12 +755,14 @@ dentry_calculate_extraction_name(struct wim_dentry *dentry,
        if (dentry_is_root(dentry))
                return 0;
 
+#ifdef WITH_NTFS_3G
        if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
                dentry->d_extraction_name = dentry->file_name;
                dentry->d_extraction_name_nchars = dentry->file_name_nbytes /
                                                   sizeof(utf16lechar);
                return 0;
        }
+#endif
 
        if (!ctx->supported_features.case_sensitive_filenames) {
                struct wim_dentry *other;
@@ -1114,6 +1278,13 @@ do_feature_check(const struct wim_features *required_features,
                return WIMLIB_ERR_UNSUPPORTED;
        }
 
+       if (required_features->unix_data &&
+           !(extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA))
+       {
+               WARNING("Ignoring UNIX metadata of %lu files",
+                       required_features->unix_data);
+       }
+
        /* DOS Names.  */
        if (required_features->short_names &&
            !supported_features->short_names)
@@ -1153,8 +1324,7 @@ select_apply_operations(int extract_flags)
 
 static int
 extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees,
-             const tchar *target, int extract_flags,
-             wimlib_progress_func_t progress_func)
+             const tchar *target, int extract_flags)
 {
        const struct apply_operations *ops;
        struct apply_ctx *ctx;
@@ -1190,8 +1360,9 @@ extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees,
        ctx->target = target;
        ctx->target_nchars = tstrlen(target);
        ctx->extract_flags = extract_flags;
-       if (progress_func) {
-               ctx->progress_func = progress_func;
+       if (ctx->wim->progfunc) {
+               ctx->progfunc = ctx->wim->progfunc;
+               ctx->progctx = ctx->wim->progctx;
                ctx->progress.extract.image = wim->current_image;
                ctx->progress.extract.extract_flags = (extract_flags &
                                                       WIMLIB_EXTRACT_MASK_PUBLIC);
@@ -1201,6 +1372,7 @@ extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees,
                ctx->progress.extract.target = target;
        }
        INIT_LIST_HEAD(&ctx->stream_list);
+       filedes_invalidate(&ctx->tmpfile_fd);
 
        ret = (*ops->get_supported_features)(target, &ctx->supported_features);
        if (ret)
@@ -1252,38 +1424,31 @@ extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees,
                }
        }
 
-       if (ctx->progress_func) {
-               int msg;
-               if (extract_flags & WIMLIB_EXTRACT_FLAG_IMAGEMODE)
-                       msg = WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN;
-               else
-                       msg = WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN;
-               (*ctx->progress_func)(msg, &ctx->progress);
-       }
+       ret = extract_progress(ctx,
+                              ((extract_flags & WIMLIB_EXTRACT_FLAG_IMAGEMODE) ?
+                                      WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN :
+                                      WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN));
+       if (ret)
+               goto out_cleanup;
 
        ret = (*ops->extract)(&dentry_list, ctx);
        if (ret)
                goto out_cleanup;
 
-       if (ctx->progress_func &&
-           ctx->progress.extract.completed_bytes <
-               ctx->progress.extract.total_bytes)
+       if (ctx->progress.extract.completed_bytes <
+           ctx->progress.extract.total_bytes)
        {
                ctx->progress.extract.completed_bytes =
                        ctx->progress.extract.total_bytes;
-               (*ctx->progress_func)(WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS,
-                                     &ctx->progress);
+               ret = extract_progress(ctx, WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS);
+               if (ret)
+                       goto out_cleanup;
        }
 
-       if (ctx->progress_func) {
-               int msg;
-               if (extract_flags & WIMLIB_EXTRACT_FLAG_IMAGEMODE)
-                       msg = WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END;
-               else
-                       msg = WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END;
-               (*ctx->progress_func)(msg, &ctx->progress);
-       }
-       ret = 0;
+       ret = extract_progress(ctx,
+                              ((extract_flags & WIMLIB_EXTRACT_FLAG_IMAGEMODE) ?
+                                      WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END :
+                                      WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END));
 out_cleanup:
        destroy_stream_list(&ctx->stream_list);
        destroy_dentry_list(&dentry_list);
@@ -1411,7 +1576,7 @@ append_dentry_cb(struct wim_dentry *dentry, void *_ctx)
 static int
 do_wimlib_extract_paths(WIMStruct *wim, int image, const tchar *target,
                        const tchar * const *paths, size_t num_paths,
-                       int extract_flags, wimlib_progress_func_t progress_func)
+                       int extract_flags)
 {
        int ret;
        struct wim_dentry **trees;
@@ -1503,8 +1668,7 @@ do_wimlib_extract_paths(WIMStruct *wim, int image, const tchar *target,
                goto out_free_trees;
        }
 
-       ret = extract_trees(wim, trees, num_trees,
-                           target, extract_flags, progress_func);
+       ret = extract_trees(wim, trees, num_trees, target, extract_flags);
 out_free_trees:
        FREE(trees);
        return ret;
@@ -1512,13 +1676,11 @@ out_free_trees:
 
 static int
 extract_single_image(WIMStruct *wim, int image,
-                    const tchar *target, int extract_flags,
-                    wimlib_progress_func_t progress_func)
+                    const tchar *target, int extract_flags)
 {
        const tchar *path = WIMLIB_WIM_ROOT_PATH;
        extract_flags |= WIMLIB_EXTRACT_FLAG_IMAGEMODE;
-       return do_wimlib_extract_paths(wim, image, target, &path, 1,
-                                      extract_flags, progress_func);
+       return do_wimlib_extract_paths(wim, image, target, &path, 1, extract_flags);
 }
 
 static const tchar * const filename_forbidden_chars =
@@ -1544,10 +1706,7 @@ image_name_ok_as_dir(const tchar *image_name)
 /* Extracts all images from the WIM to the directory @target, with the images
  * placed in subdirectories named by their image names. */
 static int
-extract_all_images(WIMStruct *wim,
-                  const tchar *target,
-                  int extract_flags,
-                  wimlib_progress_func_t progress_func)
+extract_all_images(WIMStruct *wim, const tchar *target, int extract_flags)
 {
        size_t image_name_max_len = max(xml_get_max_image_name_len(wim), 20);
        size_t output_path_len = tstrlen(target);
@@ -1556,8 +1715,6 @@ extract_all_images(WIMStruct *wim,
        int image;
        const tchar *image_name;
 
-       extract_flags |= WIMLIB_EXTRACT_FLAG_MULTI_IMAGE;
-
        if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
                ERROR("Cannot extract multiple images in NTFS extraction mode.");
                return WIMLIB_ERR_INVALID_PARAM;
@@ -1577,8 +1734,7 @@ extract_all_images(WIMStruct *wim,
                         * Use image number instead. */
                        tsprintf(buf + output_path_len + 1, T("%d"), image);
                }
-               ret = extract_single_image(wim, image, buf, extract_flags,
-                                          progress_func);
+               ret = extract_single_image(wim, image, buf, extract_flags);
                if (ret)
                        return ret;
        }
@@ -1586,11 +1742,8 @@ extract_all_images(WIMStruct *wim,
 }
 
 static int
-do_wimlib_extract_image(WIMStruct *wim,
-                       int image,
-                       const tchar *target,
-                       int extract_flags,
-                       wimlib_progress_func_t progress_func)
+do_wimlib_extract_image(WIMStruct *wim, int image, const tchar *target,
+                       int extract_flags)
 {
        if (extract_flags & (WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE |
                             WIMLIB_EXTRACT_FLAG_TO_STDOUT |
@@ -1598,11 +1751,9 @@ do_wimlib_extract_image(WIMStruct *wim,
                return WIMLIB_ERR_INVALID_PARAM;
 
        if (image == WIMLIB_ALL_IMAGES)
-               return extract_all_images(wim, target, extract_flags,
-                                         progress_func);
+               return extract_all_images(wim, target, extract_flags);
        else
-               return extract_single_image(wim, image, target, extract_flags,
-                                           progress_func);
+               return extract_single_image(wim, image, target, extract_flags);
 }
 
 
@@ -1613,19 +1764,18 @@ do_wimlib_extract_image(WIMStruct *wim,
 WIMLIBAPI int
 wimlib_extract_paths(WIMStruct *wim, int image, const tchar *target,
                     const tchar * const *paths, size_t num_paths,
-                    int extract_flags, wimlib_progress_func_t progress_func)
+                    int extract_flags)
 {
        if (extract_flags & ~WIMLIB_EXTRACT_MASK_PUBLIC)
                return WIMLIB_ERR_INVALID_PARAM;
 
        return do_wimlib_extract_paths(wim, image, target, paths, num_paths,
-                                      extract_flags, progress_func);
+                                      extract_flags);
 }
 
 WIMLIBAPI int
 wimlib_extract_pathlist(WIMStruct *wim, int image, const tchar *target,
-                       const tchar *path_list_file, int extract_flags,
-                       wimlib_progress_func_t progress_func)
+                       const tchar *path_list_file, int extract_flags)
 {
        int ret;
        tchar **paths;
@@ -1641,16 +1791,19 @@ wimlib_extract_pathlist(WIMStruct *wim, int image, const tchar *target,
 
        ret = wimlib_extract_paths(wim, image, target,
                                   (const tchar * const *)paths, num_paths,
-                                  extract_flags, progress_func);
+                                  extract_flags);
        FREE(paths);
        FREE(mem);
        return ret;
 }
 
 WIMLIBAPI int
-wimlib_extract_image_from_pipe(int pipe_fd, const tchar *image_num_or_name,
-                              const tchar *target, int extract_flags,
-                              wimlib_progress_func_t progress_func)
+wimlib_extract_image_from_pipe_with_progress(int pipe_fd,
+                                            const tchar *image_num_or_name,
+                                            const tchar *target,
+                                            int extract_flags,
+                                            wimlib_progress_func_t progfunc,
+                                            void *progctx)
 {
        int ret;
        WIMStruct *pwm;
@@ -1665,9 +1818,8 @@ wimlib_extract_image_from_pipe(int pipe_fd, const tchar *image_num_or_name,
         * the pipable WIM.  Caveats:  Unlike getting a WIMStruct with
         * wimlib_open_wim(), getting a WIMStruct in this way will result in
         * an empty lookup table, no XML data read, and no filename set.  */
-       ret = open_wim_as_WIMStruct(&pipe_fd,
-                                   WIMLIB_OPEN_FLAG_FROM_PIPE,
-                                   &pwm, progress_func);
+       ret = open_wim_as_WIMStruct(&pipe_fd, WIMLIB_OPEN_FLAG_FROM_PIPE, &pwm,
+                                   progfunc, progctx);
        if (ret)
                return ret;
 
@@ -1798,20 +1950,31 @@ wimlib_extract_image_from_pipe(int pipe_fd, const tchar *image_num_or_name,
        }
        /* Extract the image.  */
        extract_flags |= WIMLIB_EXTRACT_FLAG_FROM_PIPE;
-       ret = do_wimlib_extract_image(pwm, image, target,
-                                     extract_flags, progress_func);
+       ret = do_wimlib_extract_image(pwm, image, target, extract_flags);
        /* Clean up and return.  */
 out_wimlib_free:
        wimlib_free(pwm);
        return ret;
 }
 
+
+WIMLIBAPI int
+wimlib_extract_image_from_pipe(int pipe_fd, const tchar *image_num_or_name,
+                              const tchar *target, int extract_flags)
+{
+       return wimlib_extract_image_from_pipe_with_progress(pipe_fd,
+                                                           image_num_or_name,
+                                                           target,
+                                                           extract_flags,
+                                                           NULL,
+                                                           NULL);
+}
+
 WIMLIBAPI int
 wimlib_extract_image(WIMStruct *wim, int image, const tchar *target,
-                    int extract_flags, wimlib_progress_func_t progress_func)
+                    int extract_flags)
 {
        if (extract_flags & ~WIMLIB_EXTRACT_MASK_PUBLIC)
                return WIMLIB_ERR_INVALID_PARAM;
-       return do_wimlib_extract_image(wim, image, target, extract_flags,
-                                      progress_func);
+       return do_wimlib_extract_image(wim, image, target, extract_flags);
 }