+#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 | \
+ WIMLIB_EXTRACT_FLAG_NORPFIX | \
+ WIMLIB_EXTRACT_FLAG_TO_STDOUT | \
+ WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES | \
+ WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS | \
+ WIMLIB_EXTRACT_FLAG_STRICT_TIMESTAMPS | \
+ WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES | \
+ WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS | \
+ WIMLIB_EXTRACT_FLAG_GLOB_PATHS | \
+ WIMLIB_EXTRACT_FLAG_STRICT_GLOB | \
+ WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES | \
+ WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE | \
+ WIMLIB_EXTRACT_FLAG_WIMBOOT)
+
+/* Check whether the extraction of a dentry should be skipped completely. */
+static bool
+dentry_is_supported(struct wim_dentry *dentry,
+ const struct wim_features *supported_features)
+{
+ struct wim_inode *inode = dentry->d_inode;
+
+ if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+ return supported_features->reparse_points ||
+ (inode_is_symlink(inode) &&
+ supported_features->symlink_reparse_points);
+ }
+ if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) {
+ if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
+ return supported_features->encrypted_directories != 0;
+ else
+ return supported_features->encrypted_files != 0;
+ }
+ return true;
+}
+
+
+#define PWM_ALLOW_WIM_HDR 0x00001
+
+/* 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));
+ if (ret)
+ goto read_error;
+
+ if ((flags & PWM_ALLOW_WIM_HDR) && 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));
+
+ if (ret)
+ goto read_error;
+ lte->resource_location = RESOURCE_NONEXISTENT;
+ memcpy(hdr_ret, &buf.pwm_hdr, sizeof(buf.pwm_hdr));
+ return 0;
+ }
+
+ if (le64_to_cpu(buf.stream_hdr.magic) != PWM_STREAM_MAGIC) {
+ ERROR("Data read on pipe is invalid (expected stream header).");
+ return WIMLIB_ERR_INVALID_PIPABLE_WIM;
+ }
+
+ copy_hash(lte->hash, buf.stream_hdr.hash);
+
+ 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:
+ ERROR_WITH_ERRNO("Error reading pipable WIM from pipe");
+ return ret;
+}
+
+static int
+load_streams_from_pipe(struct apply_ctx *ctx,
+ const struct read_stream_list_callbacks *cbs)
+{
+ struct wim_lookup_table_entry *found_lte = NULL;
+ struct wim_resource_spec *rspec = NULL;
+ struct wim_lookup_table *lookup_table;
+ int ret;
+
+ ret = WIMLIB_ERR_NOMEM;
+ found_lte = new_lookup_table_entry();
+ if (!found_lte)
+ goto out;
+
+ rspec = MALLOC(sizeof(struct wim_resource_spec));
+ if (!rspec)
+ goto out;
+
+ lookup_table = ctx->wim->lookup_table;
+ 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;
+ 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;
+
+ 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_ALLOW_WIM_HDR, &pwm_hdr);
+ if (ret)
+ goto out;
+
+ if ((found_lte->resource_location != RESOURCE_NONEXISTENT)
+ && !(found_lte->flags & WIM_RESHDR_FLAG_METADATA)
+ && (needed_lte = lookup_stream(lookup_table, found_lte->hash))
+ && (needed_lte->out_refcnt))
+ {
+ 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);
+
+ ret = (*cbs->begin_stream)(needed_lte, 0,
+ cbs->begin_stream_ctx);
+ if (ret) {
+ lte_unbind_wim_resource_spec(needed_lte);
+ goto out;
+ }
+
+ ret = extract_stream(needed_lte, needed_lte->size,
+ cbs->consume_chunk,
+ cbs->consume_chunk_ctx);
+
+ ret = (*cbs->end_stream)(needed_lte, ret,
+ cbs->end_stream_ctx);
+ lte_unbind_wim_resource_spec(needed_lte);
+ if (ret)
+ goto out;
+ ctx->num_streams_remaining--;
+ } else if (found_lte->resource_location != RESOURCE_NONEXISTENT) {
+ ret = skip_wim_stream(found_lte);
+ if (ret)
+ goto out;
+ } else {
+ u16 part_number = le16_to_cpu(pwm_hdr.part_number);
+ u16 total_parts = le16_to_cpu(pwm_hdr.total_parts);
+
+ if (part_number != ctx->progress.extract.part_number ||
+ total_parts != ctx->progress.extract.total_parts ||
+ memcmp(pwm_hdr.guid, ctx->progress.extract.guid,
+ WIM_GUID_LEN))
+ {
+ ctx->progress.extract.part_number = part_number;
+ ctx->progress.extract.total_parts = total_parts;
+ memcpy(ctx->progress.extract.guid,
+ pwm_hdr.guid, WIM_GUID_LEN);
+ ret = extract_progress(ctx,
+ WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN);
+ if (ret)
+ goto out;
+ }