+/* Returns the length of the longest string that might need to be appended to
+ * the path to an alias of an inode to open or create a named data stream.
+ *
+ * If the inode has no named data streams, this will be 0. Otherwise, this will
+ * be 1 plus the length of the longest-named data stream, since the data stream
+ * name must be separated form the path by the ':' character. */
+static size_t
+inode_longest_named_data_stream_spec(const struct wim_inode *inode)
+{
+ size_t max = 0;
+ for (u16 i = 0; i < inode->i_num_ads; i++) {
+ size_t len = inode->i_ads_entries[i].stream_name_nbytes;
+ if (len > max)
+ max = len;
+ }
+ if (max)
+ max = 1 + (max / sizeof(wchar_t));
+ return max;
+}
+
+/* Find the length, in wide characters, of the longest path needed for
+ * extraction of any file in @dentry_list relative to the target directory.
+ *
+ * Accounts for named data streams, but does not include null terminator (not
+ * needed for NtCreateFile). */
+static size_t
+compute_path_max(struct list_head *dentry_list)
+{
+ size_t max = 0;
+ const struct wim_dentry *dentry;
+
+ list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
+ size_t len;
+
+ len = dentry_extraction_path_length(dentry);
+
+ /* Account for named data streams */
+ len += inode_longest_named_data_stream_spec(dentry->d_inode);
+
+ if (len > max)
+ max = len;
+ }
+
+ return max;
+}
+
+/* Build the path at which to extract the @dentry, relative to the target
+ * directory.
+ *
+ * The path is saved in ctx->pathbuf. */
+static void
+build_extraction_path(const struct wim_dentry *dentry,
+ struct win32_apply_ctx *ctx)
+{
+ size_t len;
+ wchar_t *p;
+ const struct wim_dentry *d;
+
+ len = dentry_extraction_path_length(dentry);
+
+ ctx->pathbuf.Length = len * sizeof(wchar_t);
+ p = ctx->pathbuf.Buffer + len;
+ for (d = dentry;
+ !dentry_is_root(d->parent) && will_extract_dentry(d->parent);
+ d = d->parent)
+ {
+ p -= d->d_extraction_name_nchars;
+ wmemcpy(p, d->d_extraction_name, d->d_extraction_name_nchars);
+ *--p = '\\';
+ }
+ /* No leading slash */
+ p -= d->d_extraction_name_nchars;
+ wmemcpy(p, d->d_extraction_name, d->d_extraction_name_nchars);
+}
+
+/* Build the path at which to extract the @dentry, relative to the target
+ * directory, adding the suffix for a named data stream.
+ *
+ * The path is saved in ctx->pathbuf. */
+static void
+build_extraction_path_with_ads(const struct wim_dentry *dentry,
+ struct win32_apply_ctx *ctx,
+ const wchar_t *stream_name,
+ size_t stream_name_nchars)
+{
+ wchar_t *p;
+
+ build_extraction_path(dentry, ctx);
+
+ /* Add :NAME for named data stream */
+ p = ctx->pathbuf.Buffer + (ctx->pathbuf.Length / sizeof(wchar_t));
+ *p++ = L':';
+ wmemcpy(p, stream_name, stream_name_nchars);
+ ctx->pathbuf.Length += (1 + stream_name_nchars) * sizeof(wchar_t);
+}
+
+/* Build the Win32 namespace path to the specified @dentry when extracted.
+ *
+ * The path is saved in ctx->pathbuf and will be null terminated.
+ *
+ * XXX: We could get rid of this if it wasn't needed for the file encryption
+ * APIs. */
+static void
+build_win32_extraction_path(const struct wim_dentry *dentry,
+ struct win32_apply_ctx *ctx)
+{
+ build_extraction_path(dentry, ctx);
+
+ /* Prepend target_ntpath to our relative path, then change \??\ into \\?\ */
+
+ memmove(ctx->pathbuf.Buffer +
+ (ctx->target_ntpath.Length / sizeof(wchar_t)) + 1,
+ ctx->pathbuf.Buffer, ctx->pathbuf.Length);
+ memcpy(ctx->pathbuf.Buffer, ctx->target_ntpath.Buffer,
+ ctx->target_ntpath.Length);
+ ctx->pathbuf.Buffer[ctx->target_ntpath.Length / sizeof(wchar_t)] = L'\\';
+ ctx->pathbuf.Length += ctx->target_ntpath.Length + sizeof(wchar_t);
+ ctx->pathbuf.Buffer[ctx->pathbuf.Length / sizeof(wchar_t)] = L'\0';
+
+ wimlib_assert(ctx->pathbuf.Length >= 4 * sizeof(wchar_t) &&
+ !wmemcmp(ctx->pathbuf.Buffer, L"\\??\\", 4));
+
+ ctx->pathbuf.Buffer[1] = L'\\';
+
+}
+
+/* Returns a "printable" representation of the last relative NT path that was
+ * constructed with build_extraction_path() or build_extraction_path_with_ads().
+ *
+ * This will be overwritten by the next call to this function. */
+static const wchar_t *
+current_path(struct win32_apply_ctx *ctx)
+{
+ wchar_t *p = ctx->print_buffer;
+
+ p = wmempcpy(p, ctx->common.target, ctx->common.target_nchars);
+ *p++ = L'\\';
+ p = wmempcpy(p, ctx->pathbuf.Buffer, ctx->pathbuf.Length / sizeof(wchar_t));
+ *p = L'\0';
+ return ctx->print_buffer;
+}
+
+/*
+ * Ensures the target directory exists and opens a handle to it, in preparation
+ * of using paths relative to it.
+ */