+ next = next->next;
+ } while (next != &inode->i_extraction_aliases);
+ return 0;
+}
+
+/* Create a nondirectory file, including all links. */
+static int
+create_nondirectory(const struct wim_inode *inode, struct win32_apply_ctx *ctx)
+{
+ const struct wim_dentry *first_dentry;
+ HANDLE h;
+ int ret;
+
+ first_dentry = first_extraction_alias(inode);
+
+ /* Create first link. */
+ ret = create_nondirectory_inode(&h, first_dentry, ctx);
+ if (ret)
+ return ret;
+
+ /* Set short name. */
+ ret = set_short_name(h, first_dentry, ctx);
+
+ /* Create additional links, OR if hard links are not supported just
+ * create more files. */
+ if (!ret)
+ ret = create_links(h, first_dentry, ctx);
+
+ (*func_NtClose)(h);
+ return ret;
+}
+
+/* Create all the nondirectory files being extracted, including all aliases
+ * (hard links). */
+static int
+create_nondirectories(struct list_head *dentry_list, struct win32_apply_ctx *ctx)
+{
+ const struct wim_dentry *dentry;
+ const struct wim_inode *inode;
+ int ret;
+
+ list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
+ inode = dentry->d_inode;
+ if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
+ continue;
+ /* Call create_nondirectory() only once per inode */
+ if (dentry != inode_first_extraction_dentry(inode))
+ continue;
+ ret = create_nondirectory(inode, ctx);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static void
+close_handles(struct win32_apply_ctx *ctx)
+{
+ for (unsigned i = 0; i < ctx->num_open_handles; i++)
+ (*func_NtClose)(ctx->open_handles[i]);
+}
+
+/* Prepare to read the next stream, which has size @stream_size, into an
+ * in-memory buffer. */
+static int
+prepare_data_buffer(struct win32_apply_ctx *ctx, u64 stream_size)
+{
+ if (stream_size > ctx->data_buffer_size) {
+ /* Larger buffer needed. */
+ void *new_buffer;
+ if ((size_t)stream_size != stream_size)
+ return WIMLIB_ERR_NOMEM;
+ new_buffer = REALLOC(ctx->data_buffer, stream_size);
+ if (!new_buffer)
+ return WIMLIB_ERR_NOMEM;
+ ctx->data_buffer = new_buffer;
+ ctx->data_buffer_size = stream_size;
+ }
+ /* On the first call this changes data_buffer_ptr from NULL, which tells
+ * extract_chunk() that the data buffer needs to be filled while reading
+ * the stream data. */
+ ctx->data_buffer_ptr = ctx->data_buffer;
+ return 0;
+}
+
+static int
+begin_extract_stream_instance(const struct wim_lookup_table_entry *stream,
+ struct wim_dentry *dentry,
+ const wchar_t *stream_name,
+ struct win32_apply_ctx *ctx)
+{
+ const struct wim_inode *inode = dentry->d_inode;
+ size_t stream_name_nchars = 0;
+ LARGE_INTEGER allocation_size;
+ HANDLE h;
+ NTSTATUS status;
+
+ if (unlikely(stream_name))
+ stream_name_nchars = wcslen(stream_name);
+
+ if (unlikely(stream_name_nchars)) {
+ build_extraction_path_with_ads(dentry, ctx,
+ stream_name, stream_name_nchars);
+ } else {
+ build_extraction_path(dentry, ctx);
+ }
+
+ /* Reparse point? */
+ if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)
+ && (stream_name_nchars == 0))
+ {
+ if (!ctx->common.supported_features.reparse_points)
+ return 0;
+
+ /* We can't write the reparse stream directly; we must set it
+ * with FSCTL_SET_REPARSE_POINT, which requires that all the
+ * data be available. So, stage the data in a buffer. */
+
+ list_add_tail(&dentry->tmp_list, &ctx->reparse_dentries);
+ return prepare_data_buffer(ctx, stream->size);
+ }
+
+ /* Encrypted file? */
+ if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED)
+ && (stream_name_nchars == 0))
+ {
+ if (!ctx->common.supported_features.encrypted_files)
+ return 0;
+
+ /* We can't write encrypted file streams directly; we must use
+ * WriteEncryptedFileRaw(), which requires providing the data
+ * through a callback function. This can't easily be combined
+ * with our own callback-based approach.
+ *
+ * The current workaround is to simply read the stream into
+ * memory and write the encrypted file from that.
+ *
+ * TODO: This isn't sufficient for extremely large encrypted
+ * files. Perhaps we should create an extra thread to write
+ * such files... */
+ list_add_tail(&dentry->tmp_list, &ctx->encrypted_dentries);
+ return prepare_data_buffer(ctx, stream->size);
+ }
+
+ /* Extracting unnamed data stream in WIMBoot mode? */
+ if (unlikely(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT)
+ && (stream_name_nchars == 0)
+ && (stream->resource_location == RESOURCE_IN_WIM)
+ && (stream->rspec->wim == ctx->common.wim)
+ && (stream->size == stream->rspec->uncompressed_size))
+ {
+ int ret = calculate_dentry_full_path(dentry);
+ if (ret)
+ return ret;
+ if (in_prepopulate_list(dentry, ctx)) {
+ union wimlib_progress_info info;
+
+ info.wimboot_exclude.path_in_wim = dentry->_full_path;
+ info.wimboot_exclude.extraction_path = current_path(ctx);
+
+ ret = call_progress(ctx->common.progfunc,
+ WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE,
+ &info, ctx->common.progctx);
+ FREE(dentry->_full_path);
+ dentry->_full_path = NULL;
+ return ret;
+ } else {
+ FREE(dentry->_full_path);
+ dentry->_full_path = NULL;
+ return wimboot_set_pointer(&ctx->attr,
+ current_path(ctx),
+ stream,
+ ctx->wimboot.data_source_id,
+ ctx->wimboot.wim_lookup_table_hash,
+ ctx->wimboot.wof_running);