+#define NUM_PATHBUFS 2 /* We need 2 when creating hard links */
+
+struct unix_apply_ctx {
+ /* Extract flags, the pointer to the WIMStruct, etc. */
+ struct apply_ctx common;
+
+ /* Buffers for building extraction paths (allocated). */
+ char *pathbufs[NUM_PATHBUFS];
+
+ /* Index of next pathbuf to use */
+ unsigned which_pathbuf;
+
+ /* Currently open file descriptors for extraction */
+ struct filedes open_fds[MAX_OPEN_FILES];
+
+ /* Number of currently open file descriptors in open_fds, starting from
+ * the beginning of the array. */
+ unsigned num_open_fds;
+
+ /* Buffer for reading reparse point data into memory */
+ u8 reparse_data[REPARSE_DATA_MAX_SIZE];
+
+ /* Pointer to the next byte in @reparse_data to fill */
+ u8 *reparse_ptr;
+
+ /* Absolute path to the target directory (allocated buffer). Only set
+ * if needed for absolute symbolic link fixups. */
+ char *target_abspath;
+
+ /* Number of characters in target_abspath. */
+ size_t target_abspath_nchars;
+
+ /* Number of special files we couldn't create due to EPERM */
+ unsigned long num_special_files_ignored;
+};
+
+/* Returns the number of characters needed to represent the path to the
+ * specified @dentry when extracted, not including the null terminator or the
+ * path to the target directory itself. */
+static size_t
+unix_dentry_path_length(const struct wim_dentry *dentry)
+{
+ size_t len = 0;
+ const struct wim_dentry *d;
+
+ d = dentry;
+ do {
+ len += d->d_extraction_name_nchars + 1;
+ d = d->d_parent;
+ } while (!dentry_is_root(d) && will_extract_dentry(d));
+
+ return len;
+}
+
+/* Returns the maximum number of characters needed to represent the path to any
+ * dentry in @dentry_list when extracted, including the null terminator and the
+ * path to the target directory itself. */
+static size_t
+unix_compute_path_max(const struct list_head *dentry_list,
+ const struct unix_apply_ctx *ctx)
+{
+ size_t max = 0;
+ size_t len;
+ const struct wim_dentry *dentry;
+
+ list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
+ len = unix_dentry_path_length(dentry);
+ if (len > max)
+ max = len;
+ }
+
+ /* Account for target and null terminator. */
+ return ctx->common.target_nchars + max + 1;
+}
+
+/* Builds and returns the filesystem path to which to extract @dentry.
+ * This cycles through NUM_PATHBUFS different buffers. */
+static const char *
+unix_build_extraction_path(const struct wim_dentry *dentry,
+ struct unix_apply_ctx *ctx)
+{
+ char *pathbuf;
+ char *p;
+ const struct wim_dentry *d;
+
+ pathbuf = ctx->pathbufs[ctx->which_pathbuf];
+ ctx->which_pathbuf = (ctx->which_pathbuf + 1) % NUM_PATHBUFS;
+
+ p = &pathbuf[ctx->common.target_nchars +
+ unix_dentry_path_length(dentry)];
+ *p = '\0';
+ d = dentry;
+ do {
+ p -= d->d_extraction_name_nchars;
+ if (d->d_extraction_name_nchars)
+ memcpy(p, d->d_extraction_name,
+ d->d_extraction_name_nchars);
+ *--p = '/';
+ d = d->d_parent;
+ } while (!dentry_is_root(d) && will_extract_dentry(d));
+
+ return pathbuf;
+}
+
+/* This causes the next call to unix_build_extraction_path() to use the same
+ * path buffer as the previous call. */
+static void
+unix_reuse_pathbuf(struct unix_apply_ctx *ctx)
+{
+ ctx->which_pathbuf = (ctx->which_pathbuf - 1) % NUM_PATHBUFS;
+}
+
+/* Builds and returns the filesystem path to which to extract an unspecified
+ * alias of the @inode. This cycles through NUM_PATHBUFS different buffers. */
+static const char *
+unix_build_inode_extraction_path(const struct wim_inode *inode,
+ struct unix_apply_ctx *ctx)
+{
+ return unix_build_extraction_path(inode_first_extraction_dentry(inode), ctx);
+}
+
+/* Should the specified file be extracted as a directory on UNIX? We extract
+ * the file as a directory if FILE_ATTRIBUTE_DIRECTORY is set and the file does
+ * not have a symlink or junction reparse point. It *may* have a different type
+ * of reparse point. */
+static inline bool
+should_extract_as_directory(const struct wim_inode *inode)
+{
+ return (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) &&
+ !inode_is_symlink(inode);
+}
+
+/* Sets the timestamps on a file being extracted.
+ *
+ * Either @fd or @path must be specified (not -1 and not NULL, respectively).
+ */