]> wimlib.net Git - wimlib/blobdiff - src/extract.c
create_temporary_file(): Fix use-after-free
[wimlib] / src / extract.c
index 4d5ba7e0b7cbfa852829d1a4762c015b72137cdd..3438ca73327ba3f29684967a2ba91536027810d8 100644 (file)
@@ -129,7 +129,7 @@ ref_stream_to_extract(struct wim_lookup_table_entry *lte,
            (!is_linked_extraction(ctx) || (lte->out_refcnt == 0 &&
                                            lte->extracted_file == NULL)))
        {
-               ctx->progress.extract.total_bytes += wim_resource_size(lte);
+               ctx->progress.extract.total_bytes += lte->size;
                ctx->progress.extract.num_streams++;
        }
 
@@ -165,7 +165,7 @@ ref_stream_to_extract(struct wim_lookup_table_entry *lte,
                                lte_dentries = REALLOC(prev_lte_dentries,
                                                       alloc_lte_dentries *
                                                        sizeof(lte_dentries[0]));
-                               if (!lte_dentries)
+                               if (lte_dentries == NULL)
                                        return WIMLIB_ERR_NOMEM;
                                if (prev_lte_dentries == NULL) {
                                        memcpy(lte_dentries,
@@ -237,7 +237,7 @@ update_extract_progress(struct apply_ctx *ctx,
        wimlib_progress_func_t progress_func = ctx->progress_func;
        union wimlib_progress_info *progress = &ctx->progress;
 
-       progress->extract.completed_bytes += wim_resource_size(lte);
+       progress->extract.completed_bytes += lte->size;
        if (progress_func &&
            progress->extract.completed_bytes >= ctx->next_progress)
        {
@@ -1202,23 +1202,50 @@ dentry_extract(struct wim_dentry *dentry, void *_ctx)
        return extract_streams(path, ctx, dentry, NULL, NULL);
 }
 
+/* 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 raw_fd;
+
+retry:
+       name = ttempnam(NULL, T("wimlib"));
+       if (name == NULL) {
+               ERROR_WITH_ERRNO("Failed to create temporary filename");
+               return WIMLIB_ERR_NOMEM;
+       }
+
+       raw_fd = topen(name, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0600);
+
+       if (raw_fd < 0) {
+               if (errno == EEXIST) {
+                       FREE(name);
+                       goto retry;
+               }
+               ERROR_WITH_ERRNO("Failed to open temporary file \"%"TS"\"", name);
+               FREE(name);
+               return WIMLIB_ERR_OPEN;
+       }
+
+       filedes_init(fd_ret, raw_fd);
+       *name_ret = name;
+       return 0;
+}
+
 /* Extract all instances of the stream @lte that are being extracted in this
- * call of extract_tree().  @can_seek specifies whether the WIM file descriptor
- * is seekable or not (e.g. is a pipe).  If not and the stream needs to be
- * extracted multiple times, it is extracted to a temporary file first.
- *
- * This is intended for use with sequential extraction of a WIM image
- * (WIMLIB_EXTRACT_FLAG_SEQUENTIAL specified).  */
+ * call of extract_tree(), but actually read the stream data from @lte_override.
+ */
 static int
 extract_stream_instances(struct wim_lookup_table_entry *lte,
-                        struct apply_ctx *ctx, bool can_seek)
+                        struct wim_lookup_table_entry *lte_override,
+                        struct apply_ctx *ctx)
 {
        struct wim_dentry **lte_dentries;
-       struct wim_lookup_table_entry *lte_tmp = NULL;
-       struct wim_lookup_table_entry *lte_override;
-       tchar *stream_tmp_filename = NULL;
        tchar path[ctx->ops->path_max];
-       unsigned i;
+       size_t i;
        int ret;
 
        if (lte->out_refcnt <= ARRAY_LEN(lte->inline_lte_dentries))
@@ -1226,50 +1253,6 @@ extract_stream_instances(struct wim_lookup_table_entry *lte,
        else
                lte_dentries = lte->lte_dentries;
 
-       if (likely(can_seek || lte->out_refcnt < 2)) {
-               lte_override = lte;
-       } else {
-               /* Need to extract stream to temporary file.  */
-               struct filedes fd;
-               int raw_fd;
-
-               stream_tmp_filename = ttempnam(NULL, T("wimlib"));
-               if (!stream_tmp_filename) {
-                       ERROR_WITH_ERRNO("Failed to create temporary filename");
-                       ret = WIMLIB_ERR_OPEN;
-                       goto out;
-               }
-
-               lte_tmp = memdup(lte, sizeof(struct wim_lookup_table_entry));
-               if (!lte_tmp) {
-                       ret = WIMLIB_ERR_NOMEM;
-                       goto out_free_stream_tmp_filename;
-               }
-               lte_tmp->resource_location = RESOURCE_IN_FILE_ON_DISK;
-               lte_tmp->file_on_disk = stream_tmp_filename;
-               lte_override = lte_tmp;
-
-               raw_fd = topen(stream_tmp_filename,
-                              O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0600);
-               if (raw_fd < 0) {
-                       ERROR_WITH_ERRNO("Failed to open temporary file");
-                       ret = WIMLIB_ERR_OPEN;
-                       goto out_free_lte_tmp;
-               }
-               filedes_init(&fd, raw_fd);
-               ret = extract_wim_resource_to_fd(lte, &fd,
-                                                wim_resource_size(lte));
-               if (filedes_close(&fd) && !ret)
-                       ret = WIMLIB_ERR_WRITE;
-               if (ret)
-                       goto out_unlink_stream_tmp_file;
-       }
-
-       /* Extract all instances of the stream, reading either from the stream
-        * in the WIM file or from the temporary file containing the stream.
-        * dentry->tmp_flag is used to ensure that each dentry is processed only
-        * once regardless of how many times this stream appears in the streams
-        * of the corresponding inode.  */
        for (i = 0; i < lte->out_refcnt; i++) {
                struct wim_dentry *dentry = lte_dentries[i];
 
@@ -1277,8 +1260,7 @@ extract_stream_instances(struct wim_lookup_table_entry *lte,
                        continue;
                if (!build_extraction_path(path, dentry, ctx))
                        continue;
-               ret = extract_streams(path, ctx, dentry,
-                                     lte, lte_override);
+               ret = extract_streams(path, ctx, dentry, lte, lte_override);
                if (ret)
                        goto out_clear_tmp_flags;
                dentry->tmp_flag = 1;
@@ -1287,38 +1269,140 @@ extract_stream_instances(struct wim_lookup_table_entry *lte,
 out_clear_tmp_flags:
        for (i = 0; i < lte->out_refcnt; i++)
                lte_dentries[i]->tmp_flag = 0;
-out_unlink_stream_tmp_file:
-       if (stream_tmp_filename)
-               tunlink(stream_tmp_filename);
-out_free_lte_tmp:
-       FREE(lte_tmp);
-out_free_stream_tmp_filename:
-       FREE(stream_tmp_filename);
-out:
+       return ret;
+}
+
+/* Determine whether the specified stream needs to be extracted to a temporary
+ * file or not.
+ *
+ * @lte->out_refcnt specifies the number of instances of this stream that must
+ * be extracted.
+ *
+ * @is_partial_res is %true if this stream is just one of multiple in a single
+ * WIM resource being extracted.  */
+static bool
+need_tmpfile_to_extract(struct wim_lookup_table_entry *lte,
+                       bool is_partial_res)
+{
+       /* Temporary file is always required when reading a partial resource,
+        * since in that case we retrieve all the contained streams in one pass.
+        * */
+       if (is_partial_res)
+               return true;
+
+       /* Otherwise we don't need a temporary file if only a single instance of
+        * the stream is needed.  */
+       if (lte->out_refcnt == 1)
+               return false;
+
+       wimlib_assert(lte->out_refcnt >= 2);
+
+       /* We also don't need a temporary file if random access to the stream is
+        * allowed.  */
+       if (lte->resource_location != RESOURCE_IN_WIM ||
+           filedes_is_seekable(&lte->rspec->wim->in_fd))
+               return false;
+
+       return true;
+}
+
+static int
+begin_extract_stream_to_tmpfile(struct wim_lookup_table_entry *lte,
+                               bool is_partial_res, void *_ctx)
+{
+       struct apply_ctx *ctx = _ctx;
+       int ret;
+
+       if (!need_tmpfile_to_extract(lte, is_partial_res)) {
+               DEBUG("Temporary file not needed "
+                     "for stream (size=%"PRIu64")", lte->size);
+               ret = extract_stream_instances(lte, lte, ctx);
+               if (ret)
+                       return ret;
+
+               /* Negative return value here means the function was successful,
+                * but the consume_chunk and end_chunk callbacks need not be
+                * called.  */
+               return -1;
+       }
+
+       DEBUG("Temporary file needed for stream (size=%"PRIu64")", lte->size);
+       return create_temporary_file(&ctx->tmpfile_fd, &ctx->tmpfile_name);
+}
+
+static int
+end_extract_stream_to_tmpfile(struct wim_lookup_table_entry *lte,
+                             int status, void *_ctx)
+{
+       struct apply_ctx *ctx = _ctx;
+       struct wim_lookup_table_entry lte_override;
+       int ret;
+       int errno_save = errno;
+
+       ret = filedes_close(&ctx->tmpfile_fd);
+
+       if (status) {
+               ret = status;
+               errno = errno_save;
+               goto out_delete_tmpfile;
+       }
+
+       if (ret) {
+               ERROR_WITH_ERRNO("Error writing temporary file %"TS, ctx->tmpfile_name);
+               ret = WIMLIB_ERR_WRITE;
+               goto out_delete_tmpfile;
+       }
+
+       /* Now that a full stream has been extracted to a temporary file,
+        * extract all instances of it to the actual target.  */
+
+       memcpy(&lte_override, lte, sizeof(struct wim_lookup_table_entry));
+       lte_override.resource_location = RESOURCE_IN_FILE_ON_DISK;
+       lte_override.file_on_disk = ctx->tmpfile_name;
+
+       ret = extract_stream_instances(lte, &lte_override, ctx);
+
+out_delete_tmpfile:
+       errno_save = errno;
+       tunlink(ctx->tmpfile_name);
+       FREE(ctx->tmpfile_name);
+       errno = errno_save;
        return ret;
 }
 
 /* Extracts a list of streams (ctx.stream_list), assuming that the directory
  * structure and empty files were already created.  This relies on the
  * per-`struct wim_lookup_table_entry' list of dentries that reference each
- * stream that was constructed earlier.  Streams are extracted exactly in the
- * order of the stream list; however, unless the WIM's file descriptor is
- * detected to be non-seekable, streams may be read from the WIM file more than
- * one time if multiple copies need to be extracted.  */
+ * stream that was constructed earlier.  */
 static int
 extract_stream_list(struct apply_ctx *ctx)
 {
-       struct wim_lookup_table_entry *lte;
-       bool can_seek;
-       int ret;
+       if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_SEQUENTIAL) {
+               /* Sequential extraction: read the streams in the order in which
+                * they appear in the WIM file.  */
+               struct read_stream_list_callbacks cbs = {
+                       .begin_stream           = begin_extract_stream_to_tmpfile,
+                       .begin_stream_ctx       = ctx,
+                       .consume_chunk          = extract_chunk_to_fd,
+                       .consume_chunk_ctx      = &ctx->tmpfile_fd,
+                       .end_stream             = end_extract_stream_to_tmpfile,
+                       .end_stream_ctx         = ctx,
+               };
+               return read_stream_list(&ctx->stream_list,
+                                       offsetof(struct wim_lookup_table_entry, extraction_list),
+                                       0, &cbs);
+       } else {
+               /* Extract the streams in unsorted order.  */
+               struct wim_lookup_table_entry *lte;
+               int ret;
 
-       can_seek = (lseek(ctx->wim->in_fd.fd, 0, SEEK_CUR) != -1);
-       list_for_each_entry(lte, &ctx->stream_list, extraction_list) {
-               ret = extract_stream_instances(lte, ctx, can_seek);
-               if (ret)
-                       return ret;
+               list_for_each_entry(lte, &ctx->stream_list, extraction_list) {
+                       ret = extract_stream_instances(lte, lte, ctx);
+                       if (ret)
+                               return ret;
+               }
+               return 0;
        }
-       return 0;
 }
 
 #define PWM_ALLOW_WIM_HDR 0x00001
@@ -1327,12 +1411,14 @@ extract_stream_list(struct apply_ctx *ctx)
 /* Read the header from a stream in a pipable WIM.  */
 static int
 read_pwm_stream_header(WIMStruct *pwm, struct wim_lookup_table_entry *lte,
+                      struct wim_resource_spec *rspec,
                       int flags, struct wim_header_disk *hdr_ret)
 {
        union {
                struct pwm_stream_hdr stream_hdr;
                struct wim_header_disk pwm_hdr;
        } buf;
+       struct wim_reshdr reshdr;
        int ret;
 
        ret = full_read(&pwm->in_fd, &buf.stream_hdr, sizeof(buf.stream_hdr));
@@ -1356,20 +1442,17 @@ read_pwm_stream_header(WIMStruct *pwm, struct wim_lookup_table_entry *lte,
                return WIMLIB_ERR_INVALID_PIPABLE_WIM;
        }
 
-       lte->resource_entry.original_size = le64_to_cpu(buf.stream_hdr.uncompressed_size);
        copy_hash(lte->hash, buf.stream_hdr.hash);
-       lte->resource_entry.flags = le32_to_cpu(buf.stream_hdr.flags);
-       lte->resource_entry.offset = pwm->in_fd.offset;
-       lte->resource_location = RESOURCE_IN_WIM;
-       lte->wim = pwm;
-       if (lte->resource_entry.flags & WIM_RESHDR_FLAG_COMPRESSED) {
-               lte->compression_type = pwm->compression_type;
-               lte->resource_entry.size = 0;
-       } else {
-               lte->compression_type = WIMLIB_COMPRESSION_TYPE_NONE;
-               lte->resource_entry.size = lte->resource_entry.original_size;
-       }
-       lte->is_pipable = 1;
+
+       reshdr.size_in_wim = 0;
+       reshdr.flags = le32_to_cpu(buf.stream_hdr.flags);
+       reshdr.offset_in_wim = pwm->in_fd.offset;
+       reshdr.uncompressed_size = le64_to_cpu(buf.stream_hdr.uncompressed_size);
+       wim_res_hdr_to_spec(&reshdr, pwm, rspec);
+       lte_bind_wim_resource_spec(lte, rspec);
+       lte->flags = rspec->flags;
+       lte->size = rspec->uncompressed_size;
+       lte->offset_in_res = 0;
        return 0;
 
 read_error:
@@ -1378,23 +1461,11 @@ read_error:
        return ret;
 }
 
-/* Skip over an unneeded stream in a pipable WIM being read from a pipe.  */
-static int
-skip_pwm_stream(struct wim_lookup_table_entry *lte)
-{
-       return read_partial_wim_resource(lte,
-                                        wim_resource_size(lte),
-                                        NULL,
-                                        wim_resource_chunk_size(lte),
-                                        NULL,
-                                        WIMLIB_READ_RESOURCE_FLAG_SEEK_ONLY,
-                                        0);
-}
-
 static int
 extract_streams_from_pipe(struct apply_ctx *ctx)
 {
        struct wim_lookup_table_entry *found_lte;
+       struct wim_resource_spec *rspec;
        struct wim_lookup_table_entry *needed_lte;
        struct wim_lookup_table *lookup_table;
        struct wim_header_disk pwm_hdr;
@@ -1403,9 +1474,13 @@ extract_streams_from_pipe(struct apply_ctx *ctx)
 
        ret = WIMLIB_ERR_NOMEM;
        found_lte = new_lookup_table_entry();
-       if (!found_lte)
+       if (found_lte == NULL)
                goto out;
 
+       rspec = MALLOC(sizeof(struct wim_resource_spec));
+       if (rspec == NULL)
+               goto out_free_found_lte;
+
        lookup_table = ctx->wim->lookup_table;
        pwm_flags = PWM_ALLOW_WIM_HDR;
        if ((ctx->extract_flags & WIMLIB_EXTRACT_FLAG_RESUME))
@@ -1417,8 +1492,10 @@ extract_streams_from_pipe(struct apply_ctx *ctx)
                ctx->progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN,
                                   &ctx->progress);
        while (ctx->num_streams_remaining) {
-               ret = read_pwm_stream_header(ctx->wim, found_lte, pwm_flags,
-                                            &pwm_hdr);
+               if (found_lte->resource_location != RESOURCE_NONEXISTENT)
+                       lte_unbind_wim_resource_spec(found_lte);
+               ret = read_pwm_stream_header(ctx->wim, found_lte, rspec,
+                                            pwm_flags, &pwm_hdr);
                if (ret) {
                        if (ret == WIMLIB_ERR_UNEXPECTED_END_OF_FILE &&
                            (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_RESUME))
@@ -1429,23 +1506,64 @@ extract_streams_from_pipe(struct apply_ctx *ctx)
                }
 
                if ((found_lte->resource_location != RESOURCE_NONEXISTENT)
-                   && !(found_lte->resource_entry.flags & WIM_RESHDR_FLAG_METADATA)
+                   && !(found_lte->flags & WIM_RESHDR_FLAG_METADATA)
                    && (needed_lte = lookup_resource(lookup_table, found_lte->hash))
                    && (needed_lte->out_refcnt))
                {
-                       copy_resource_entry(&needed_lte->resource_entry,
-                                           &found_lte->resource_entry);
-                       needed_lte->resource_location = found_lte->resource_location;
-                       needed_lte->wim               = found_lte->wim;
-                       needed_lte->compression_type  = found_lte->compression_type;
-                       needed_lte->is_pipable        = found_lte->is_pipable;
-
-                       ret = extract_stream_instances(needed_lte, ctx, false);
+                       char *tmpfile_name = NULL;
+                       struct wim_lookup_table_entry *lte_override;
+                       struct wim_lookup_table_entry tmpfile_lte;
+
+                       needed_lte->offset_in_res = found_lte->offset_in_res;
+                       needed_lte->flags = found_lte->flags;
+                       needed_lte->size = found_lte->size;
+
+                       lte_unbind_wim_resource_spec(found_lte);
+                       lte_bind_wim_resource_spec(needed_lte, rspec);
+
+                       if (needed_lte->out_refcnt > 1) {
+
+                               struct filedes tmpfile_fd;
+
+                               /* Extract stream to temporary file.  */
+                               ret = create_temporary_file(&tmpfile_fd, &tmpfile_name);
+                               if (ret)
+                                       goto out_free_found_lte;
+
+                               ret = extract_stream_to_fd(needed_lte, &tmpfile_fd,
+                                                          needed_lte->size);
+                               if (ret) {
+                                       filedes_close(&tmpfile_fd);
+                                       goto delete_tmpfile;
+                               }
+
+                               if (filedes_close(&tmpfile_fd)) {
+                                       ERROR_WITH_ERRNO("Error writing to temporary "
+                                                        "file \"%"TS"\"", tmpfile_name);
+                                       ret = WIMLIB_ERR_WRITE;
+                                       goto delete_tmpfile;
+                               }
+                               memcpy(&tmpfile_lte, needed_lte,
+                                      sizeof(struct wim_lookup_table_entry));
+                               tmpfile_lte.resource_location = RESOURCE_IN_FILE_ON_DISK;
+                               tmpfile_lte.file_on_disk = tmpfile_name;
+                               lte_override = &tmpfile_lte;
+                       } else {
+                               lte_override = needed_lte;
+                       }
+
+                       ret = extract_stream_instances(needed_lte, lte_override, ctx);
+               delete_tmpfile:
+                       lte_unbind_wim_resource_spec(needed_lte);
+                       if (tmpfile_name) {
+                               tunlink(tmpfile_name);
+                               FREE(tmpfile_name);
+                       }
                        if (ret)
                                goto out_free_found_lte;
                        ctx->num_streams_remaining--;
                } else if (found_lte->resource_location != RESOURCE_NONEXISTENT) {
-                       ret = skip_pwm_stream(found_lte);
+                       ret = skip_wim_stream(found_lte);
                        if (ret)
                                goto out_free_found_lte;
                } else {
@@ -1472,6 +1590,8 @@ extract_streams_from_pipe(struct apply_ctx *ctx)
        }
        ret = 0;
 out_free_found_lte:
+       if (found_lte->resource_location != RESOURCE_IN_WIM)
+               FREE(rspec);
        free_lookup_table_entry(found_lte);
 out:
        return ret;
@@ -1531,8 +1651,7 @@ extract_dentry_to_stdout(struct wim_dentry *dentry)
                if (lte) {
                        struct filedes _stdout;
                        filedes_init(&_stdout, STDOUT_FILENO);
-                       ret = extract_wim_resource_to_fd(lte, &_stdout,
-                                                        wim_resource_size(lte));
+                       ret = extract_stream_to_fd(lte, &_stdout, lte->size);
                }
        }
        return ret;
@@ -2256,21 +2375,6 @@ extract_tree(WIMStruct *wim, const tchar *wim_source_path, const tchar *target,
                goto out_teardown_stream_list;
        }
 
-       /* If a sequential extraction was specified, sort the streams to be
-        * extracted by their position in the WIM file so that the WIM file can
-        * be read sequentially.  */
-       if ((extract_flags & (WIMLIB_EXTRACT_FLAG_SEQUENTIAL |
-                             WIMLIB_EXTRACT_FLAG_FROM_PIPE))
-                                       == WIMLIB_EXTRACT_FLAG_SEQUENTIAL)
-       {
-               ret = sort_stream_list_by_sequential_order(
-                               &ctx.stream_list,
-                               offsetof(struct wim_lookup_table_entry,
-                                        extraction_list));
-               if (ret)
-                       goto out_teardown_stream_list;
-       }
-
        if (ctx.ops->realpath_works_on_nonexisting_files &&
            ((extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX) ||
             ctx.ops->requires_realtarget_in_paths))
@@ -2643,7 +2747,7 @@ extract_single_image(WIMStruct *wim, int image,
 {
        int ret;
        tchar *target_copy = canonicalize_fs_path(target);
-       if (!target_copy)
+       if (target_copy == NULL)
                return WIMLIB_ERR_NOMEM;
        struct wimlib_extract_command cmd = {
                .wim_source_path = T(""),
@@ -2815,11 +2919,12 @@ wimlib_extract_image_from_pipe(int pipe_fd, const tchar *image_num_or_name,
         * WIMs.)  */
        {
                struct wim_lookup_table_entry xml_lte;
-               ret = read_pwm_stream_header(pwm, &xml_lte, 0, NULL);
+               struct wim_resource_spec xml_rspec;
+               ret = read_pwm_stream_header(pwm, &xml_lte, &xml_rspec, 0, NULL);
                if (ret)
                        goto out_wimlib_free;
 
-               if (!(xml_lte.resource_entry.flags & WIM_RESHDR_FLAG_METADATA))
+               if (!(xml_lte.flags & WIM_RESHDR_FLAG_METADATA))
                {
                        ERROR("Expected XML data, but found non-metadata "
                              "stream.");
@@ -2827,12 +2932,12 @@ wimlib_extract_image_from_pipe(int pipe_fd, const tchar *image_num_or_name,
                        goto out_wimlib_free;
                }
 
-               copy_resource_entry(&pwm->hdr.xml_res_entry,
-                                   &xml_lte.resource_entry);
+               wim_res_spec_to_hdr(&xml_rspec, &pwm->hdr.xml_data_reshdr);
 
                ret = read_wim_xml_data(pwm);
                if (ret)
                        goto out_wimlib_free;
+
                if (wim_info_get_num_images(pwm->wim_info) != pwm->hdr.image_count) {
                        ERROR("Image count in XML data is not the same as in WIM header.");
                        ret = WIMLIB_ERR_XML;
@@ -2868,22 +2973,29 @@ wimlib_extract_image_from_pipe(int pipe_fd, const tchar *image_num_or_name,
        for (i = 1; i <= pwm->hdr.image_count; i++) {
                struct wim_lookup_table_entry *metadata_lte;
                struct wim_image_metadata *imd;
+               struct wim_resource_spec *metadata_rspec;
 
                metadata_lte = new_lookup_table_entry();
-               if (!metadata_lte) {
+               if (metadata_lte == NULL) {
                        ret = WIMLIB_ERR_NOMEM;
                        goto out_wimlib_free;
                }
+               metadata_rspec = MALLOC(sizeof(struct wim_resource_spec));
+               if (metadata_rspec == NULL) {
+                       ret = WIMLIB_ERR_NOMEM;
+                       free_lookup_table_entry(metadata_lte);
+                       goto out_wimlib_free;
+               }
 
-               ret = read_pwm_stream_header(pwm, metadata_lte, 0, NULL);
+               ret = read_pwm_stream_header(pwm, metadata_lte, metadata_rspec, 0, NULL);
                imd = pwm->image_metadata[i - 1];
                imd->metadata_lte = metadata_lte;
-               if (ret)
+               if (ret) {
+                       FREE(metadata_rspec);
                        goto out_wimlib_free;
+               }
 
-               if (!(metadata_lte->resource_entry.flags &
-                     WIM_RESHDR_FLAG_METADATA))
-               {
+               if (!(metadata_lte->flags & WIM_RESHDR_FLAG_METADATA)) {
                        ERROR("Expected metadata resource, but found "
                              "non-metadata stream.");
                        ret = WIMLIB_ERR_INVALID_PIPABLE_WIM;
@@ -2900,7 +3012,7 @@ wimlib_extract_image_from_pipe(int pipe_fd, const tchar *image_num_or_name,
                } else {
                        /* Metadata resource is not for the image being
                         * extracted.  Skip over it.  */
-                       ret = skip_pwm_stream(metadata_lte);
+                       ret = skip_wim_stream(metadata_lte);
                        if (ret)
                                goto out_wimlib_free;
                }