+ ctx->num_open_handles = 0;
+ ctx->data_buffer_ptr = NULL;
+ INIT_LIST_HEAD(&ctx->reparse_dentries);
+ INIT_LIST_HEAD(&ctx->encrypted_dentries);
+
+ for (u32 i = 0; i < stream->out_refcnt; i++) {
+ const struct wim_inode *inode = owners[i].inode;
+ const wchar_t *stream_name = owners[i].stream_name;
+ struct wim_dentry *dentry;
+
+ /* A copy of the stream needs to be extracted to @inode. */
+
+ if (ctx->common.supported_features.hard_links) {
+ dentry = inode_first_extraction_dentry(inode);
+ ret = begin_extract_stream_instance(stream, dentry,
+ stream_name, ctx);
+ if (ret)
+ goto fail;
+ } else {
+ /* Hard links not supported. Extract the stream
+ * separately to each alias of the inode. */
+ struct list_head *next;
+
+ next = inode->i_extraction_aliases.next;
+ do {
+ dentry = list_entry(next, struct wim_dentry,
+ d_extraction_alias_node);
+ ret = begin_extract_stream_instance(stream,
+ dentry,
+ stream_name,
+ ctx);
+ if (ret)
+ goto fail;
+ next = next->next;
+ } while (next != &inode->i_extraction_aliases);
+ }
+ }
+
+ if (unlikely(ctx->num_open_handles == 0 && ctx->data_buffer_ptr == NULL)) {
+ /* The data of this stream isn't actually needed!
+ * (This can happen in WIMBoot mode.) */
+ return BEGIN_STREAM_STATUS_SKIP_STREAM;
+ }
+ return 0;
+
+fail:
+ close_handles(ctx);
+ return ret;
+}
+
+/* Called when the next chunk of a stream has been read for extraction on
+ * Windows */
+static int
+extract_chunk(const void *chunk, size_t size, void *_ctx)
+{
+ struct win32_apply_ctx *ctx = _ctx;
+
+ /* Write the data chunk to each open handle */
+ for (unsigned i = 0; i < ctx->num_open_handles; i++) {
+ u8 *bufptr = (u8 *)chunk;
+ size_t bytes_remaining = size;
+ NTSTATUS status;
+ while (bytes_remaining) {
+ ULONG count = min(0xFFFFFFFF, bytes_remaining);
+
+ status = (*func_NtWriteFile)(ctx->open_handles[i],
+ NULL, NULL, NULL,
+ &ctx->iosb, bufptr, count,
+ NULL, NULL);
+ if (!NT_SUCCESS(status)) {
+ set_errno_from_nt_status(status);
+ ERROR_WITH_ERRNO("Error writing data to target "
+ "volume (status=0x%08"PRIx32")",
+ (u32)status);
+ return WIMLIB_ERR_WRITE;
+ }
+ bufptr += ctx->iosb.Information;
+ bytes_remaining -= ctx->iosb.Information;
+ }
+ }
+
+ /* Copy the data chunk into the buffer (if needed) */
+ if (ctx->data_buffer_ptr)
+ ctx->data_buffer_ptr = mempcpy(ctx->data_buffer_ptr,
+ chunk, size);
+ return 0;
+}
+
+/* Called when a stream has been fully read for extraction on Windows */
+static int
+end_extract_stream(struct wim_lookup_table_entry *stream, int status, void *_ctx)
+{
+ struct win32_apply_ctx *ctx = _ctx;
+ int ret;
+ const struct wim_dentry *dentry;
+
+ close_handles(ctx);
+
+ if (status)
+ return status;
+
+ if (likely(!ctx->data_buffer_ptr))
+ return 0;
+
+ if (!list_empty(&ctx->reparse_dentries)) {
+ if (stream->size > REPARSE_DATA_MAX_SIZE) {
+ dentry = list_first_entry(&ctx->reparse_dentries,
+ struct wim_dentry, tmp_list);
+ build_extraction_path(dentry, ctx);
+ ERROR("Reparse data of \"%ls\" has size "
+ "%"PRIu64" bytes (exceeds %u bytes)",
+ current_path(ctx), stream->size,
+ REPARSE_DATA_MAX_SIZE);
+ return WIMLIB_ERR_INVALID_REPARSE_DATA;
+ }
+ /* In the WIM format, reparse streams are just the reparse data
+ * and omit the header. But we can reconstruct the header. */
+ memcpy(ctx->rpbuf.rpdata, ctx->data_buffer, stream->size);
+ ctx->rpbuf.rpdatalen = stream->size;
+ ctx->rpbuf.rpreserved = 0;
+ list_for_each_entry(dentry, &ctx->reparse_dentries, tmp_list) {
+ ctx->rpbuf.rptag = dentry->d_inode->i_reparse_tag;
+ ret = set_reparse_data(dentry, &ctx->rpbuf,
+ stream->size + REPARSE_DATA_OFFSET,
+ ctx);
+ if (ret)
+ return ret;
+ }
+ }
+
+ if (!list_empty(&ctx->encrypted_dentries)) {
+ ctx->encrypted_size = stream->size;
+ list_for_each_entry(dentry, &ctx->encrypted_dentries, tmp_list) {
+ ret = extract_encrypted_file(dentry, ctx);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/* Attributes that can't be set directly */
+#define SPECIAL_ATTRIBUTES \
+ (FILE_ATTRIBUTE_REPARSE_POINT | \
+ FILE_ATTRIBUTE_DIRECTORY | \
+ FILE_ATTRIBUTE_ENCRYPTED | \
+ FILE_ATTRIBUTE_SPARSE_FILE | \
+ FILE_ATTRIBUTE_COMPRESSED)
+
+/* Set the security descriptor @desc, of @desc_size bytes, on the file with open
+ * handle @h. */
+static NTSTATUS
+set_security_descriptor(HANDLE h, const void *desc,
+ size_t desc_size, struct win32_apply_ctx *ctx)
+{
+ SECURITY_INFORMATION info;
+ NTSTATUS status;
+