+ struct apply_ctx *ctx = _ctx;
+ struct wim_inode *inode = dentry->d_inode;
+ struct wim_lookup_table_entry *lte;
+ int ret;
+ bool force = false;
+
+ if (dentry->extraction_skipped)
+ return 0;
+
+ /* Special case: when extracting from a pipe, the WIM lookup table is
+ * initially empty, so "resolving" an inode's streams is initially not
+ * possible. However, we still need to keep track of which streams,
+ * identified by SHA1 message digests, need to be extracted, so we
+ * "resolve" the inode's streams anyway by allocating new entries. */
+ if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_FROM_PIPE)
+ force = true;
+ ret = inode_resolve_ltes(inode, ctx->wim->lookup_table, force);
+ if (ret)
+ return ret;
+ for (unsigned i = 0; i <= inode->i_num_ads; i++) {
+ lte = inode_stream_lte_resolved(inode, i);
+ if (lte)
+ lte->out_refcnt = 0;
+ }
+ return 0;
+}
+
+static inline bool
+is_linked_extraction(const struct apply_ctx *ctx)
+{
+ return 0 != (ctx->extract_flags & (WIMLIB_EXTRACT_FLAG_HARDLINK |
+ WIMLIB_EXTRACT_FLAG_SYMLINK));
+}
+
+static inline bool
+can_extract_named_data_streams(const struct apply_ctx *ctx)
+{
+ return ctx->supported_features.named_data_streams &&
+ !is_linked_extraction(ctx);
+}
+
+static int
+ref_stream_to_extract(struct wim_lookup_table_entry *lte,
+ struct wim_dentry *dentry, struct apply_ctx *ctx)
+{
+ if (!lte)
+ return 0;
+
+ /* Tally the size only for each extraction of the stream (not hard
+ * links). */
+ if (!(dentry->d_inode->i_visited &&
+ ctx->supported_features.hard_links) &&
+ (!is_linked_extraction(ctx) || (lte->out_refcnt == 0 &&
+ lte->extracted_file == NULL)))
+ {
+ ctx->progress.extract.total_bytes += wim_resource_size(lte);
+ ctx->progress.extract.num_streams++;
+ }
+
+ /* Add stream to the extraction_list only one time, even if it's going
+ * to be extracted to multiple locations. */
+ if (lte->out_refcnt == 0) {
+ list_add_tail(<e->extraction_list, &ctx->stream_list);
+ ctx->num_streams_remaining++;
+ }
+
+ if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_SEQUENTIAL) {
+ struct wim_dentry **lte_dentries;
+
+ /* Append dentry to this stream's array of dentries referencing
+ * it. Use inline array to avoid memory allocation until the
+ * number of dentries becomes too large. */
+ if (lte->out_refcnt < ARRAY_LEN(lte->inline_lte_dentries)) {
+ lte_dentries = lte->inline_lte_dentries;
+ } else {
+ struct wim_dentry **prev_lte_dentries;
+ size_t alloc_lte_dentries;
+
+ if (lte->out_refcnt == ARRAY_LEN(lte->inline_lte_dentries)) {
+ prev_lte_dentries = NULL;
+ alloc_lte_dentries = ARRAY_LEN(lte->inline_lte_dentries);
+ } else {
+ prev_lte_dentries = lte->lte_dentries;
+ alloc_lte_dentries = lte->alloc_lte_dentries;
+ }
+
+ if (lte->out_refcnt == alloc_lte_dentries) {
+ alloc_lte_dentries *= 2;
+ lte_dentries = REALLOC(prev_lte_dentries,
+ alloc_lte_dentries *
+ sizeof(lte_dentries[0]));
+ if (!lte_dentries)
+ return WIMLIB_ERR_NOMEM;
+ if (prev_lte_dentries == NULL) {
+ memcpy(lte_dentries,
+ lte->inline_lte_dentries,
+ sizeof(lte->inline_lte_dentries));
+ }
+ lte->lte_dentries = lte_dentries;
+ lte->alloc_lte_dentries = alloc_lte_dentries;
+ }
+ lte_dentries = lte->lte_dentries;
+ }
+ lte_dentries[lte->out_refcnt] = dentry;
+ }
+ lte->out_refcnt++;
+ return 0;
+}
+
+/* Given a WIM dentry in the tree to be extracted, iterate through streams that
+ * need to be extracted. For each one, add it to the list of streams to be
+ * extracted (ctx->stream_list) if not already done so, and also update the
+ * progress information (ctx->progress) with the stream. Furthermore, if doing
+ * a sequential extraction, build a mapping from each the stream to the dentries
+ * referencing it.
+ *
+ * This uses the i_visited member of the inodes (assumed to be 0 initially). */
+static int
+dentry_add_streams_to_extract(struct wim_dentry *dentry, void *_ctx)
+{
+ struct apply_ctx *ctx = _ctx;
+ struct wim_inode *inode = dentry->d_inode;
+ int ret;
+
+ /* Don't process dentries marked as skipped. */
+ if (dentry->extraction_skipped)
+ return 0;
+
+ /* The unnamed data stream will always be extracted, except in an
+ * unlikely case. */
+ if (!inode_is_encrypted_directory(inode)) {
+ ret = ref_stream_to_extract(inode_unnamed_lte_resolved(inode),
+ dentry, ctx);
+ if (ret)
+ return ret;
+ }
+
+ /* Named data streams will be extracted only if supported in the current
+ * extraction mode and volume, and to avoid complications, if not doing
+ * a linked extraction. */
+ if (can_extract_named_data_streams(ctx)) {
+ for (u16 i = 0; i < inode->i_num_ads; i++) {
+ if (!ads_entry_is_named_stream(&inode->i_ads_entries[i]))
+ continue;
+ ret = ref_stream_to_extract(inode->i_ads_entries[i].lte,
+ dentry, ctx);
+ if (ret)
+ return ret;
+ }
+ }
+ inode->i_visited = 1;
+ return 0;
+}
+
+/* Inform library user of progress of stream extraction following the successful
+ * extraction of a copy of the stream specified by @lte. */
+static void
+update_extract_progress(struct apply_ctx *ctx,
+ const struct wim_lookup_table_entry *lte)
+{
+ wimlib_progress_func_t progress_func = ctx->progress_func;
+ union wimlib_progress_info *progress = &ctx->progress;
+
+ progress->extract.completed_bytes += wim_resource_size(lte);
+ if (progress_func &&
+ progress->extract.completed_bytes >= ctx->next_progress)
+ {
+ progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS, progress);
+ if (progress->extract.completed_bytes >=
+ progress->extract.total_bytes)
+ {
+ ctx->next_progress = ~0ULL;
+ } else {
+ ctx->next_progress += progress->extract.total_bytes / 128;
+ if (ctx->next_progress > progress->extract.total_bytes)
+ ctx->next_progress = progress->extract.total_bytes;
+ }
+ }
+}
+
+#ifndef __WIN32__
+/* Extract a symbolic link (not directly as reparse data), handling fixing up
+ * the target of absolute symbolic links and updating the extract progress.
+ *
+ * @inode must specify the WIM inode for a symbolic link or junction reparse
+ * point.
+ *
+ * @lte_override overrides the resource used as the reparse data for the
+ * symbolic link. */
+static int
+extract_symlink(const tchar *path, struct apply_ctx *ctx,
+ struct wim_inode *inode,
+ struct wim_lookup_table_entry *lte_override)
+{
+ ssize_t bufsize = ctx->ops->path_max;
+ tchar target[bufsize];
+ tchar *buf = target;
+ tchar *fixed_target;
+ ssize_t sret;
+ int ret;
+
+ /* If absolute symbolic link fixups requested, reserve space in the link
+ * target buffer for the absolute path of the target directory. */
+ if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX)
+ {
+ buf += ctx->realtarget_nchars;
+ bufsize -= ctx->realtarget_nchars;
+ }
+
+ /* Translate the WIM inode's reparse data into the link target. */
+ sret = wim_inode_readlink(inode, buf, bufsize - 1, lte_override);
+ if (sret < 0) {
+ errno = -sret;
+ return WIMLIB_ERR_READLINK;
+ }
+ buf[sret] = '\0';
+
+ if ((ctx->extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX) &&
+ buf[0] == '/')
+ {
+ /* Fix absolute symbolic link target to point into the
+ * actual extraction destination. */
+ tmemcpy(target, ctx->realtarget, ctx->realtarget_nchars);
+ fixed_target = target;
+ } else {
+ /* Keep same link target. */
+ fixed_target = buf;
+ }
+
+ /* Call into the apply_operations to create the symbolic link. */
+ DEBUG("Creating symlink \"%"TS"\" => \"%"TS"\"",
+ path, fixed_target);
+ ret = ctx->ops->create_symlink(fixed_target, path, ctx);
+ if (ret) {
+ ERROR_WITH_ERRNO("Failed to create symlink "
+ "\"%"TS"\" => \"%"TS"\"", path, fixed_target);
+ return ret;
+ }
+
+ /* Account for reparse data consumed. */
+ update_extract_progress(ctx,
+ (lte_override ? lte_override :
+ inode_unnamed_lte_resolved(inode)));
+ return 0;
+}
+#endif /* !__WIN32__ */
+
+/* Create a file, directory, or symbolic link. */
+static int
+extract_inode(const tchar *path, struct apply_ctx *ctx, struct wim_inode *inode)
+{
+ int ret;
+
+#ifndef __WIN32__
+ if (ctx->supported_features.symlink_reparse_points &&
+ !ctx->supported_features.reparse_points &&
+ inode_is_symlink(inode))
+ {
+ ret = extract_symlink(path, ctx, inode, NULL);
+ } else
+#endif /* !__WIN32__ */
+ if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
+ ret = ctx->ops->create_directory(path, ctx, &inode->extract_cookie);
+ if (ret) {
+ ERROR_WITH_ERRNO("Failed to create the directory "
+ "\"%"TS"\"", path);
+ }
+ } else if ((inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) &&
+ ctx->ops->extract_encrypted_stream_creates_file &&
+ ctx->supported_features.encrypted_files) {
+ ret = ctx->ops->extract_encrypted_stream(
+ path, inode_unnamed_lte_resolved(inode), ctx);
+ if (ret) {
+ ERROR_WITH_ERRNO("Failed to create and extract "
+ "encrypted file \"%"TS"\"", path);
+ }
+ } else {
+ ret = ctx->ops->create_file(path, ctx, &inode->extract_cookie);
+ if (ret) {
+ ERROR_WITH_ERRNO("Failed to create the file "
+ "\"%"TS"\"", path);
+ }
+ }
+ return ret;
+}
+
+static int
+extract_hardlink(const tchar *oldpath, const tchar *newpath,
+ struct apply_ctx *ctx)
+{
+ int ret;
+
+ DEBUG("Creating hardlink \"%"TS"\" => \"%"TS"\"", newpath, oldpath);
+ ret = ctx->ops->create_hardlink(oldpath, newpath, ctx);
+ if (ret) {
+ ERROR_WITH_ERRNO("Failed to create hardlink "
+ "\"%"TS"\" => \"%"TS"\"",
+ newpath, oldpath);
+ }
+ return ret;
+}
+
+#ifdef __WIN32__
+static int
+try_extract_rpfix(u8 *rpbuf,
+ u16 *rpbuflen_p,
+ const wchar_t *extract_root_realpath,
+ unsigned extract_root_realpath_nchars)
+{
+ struct reparse_data rpdata;
+ wchar_t *target;
+ size_t target_nchars;
+ size_t stripped_nchars;
+ wchar_t *stripped_target;
+ wchar_t stripped_target_nchars;
+ int ret;
+
+ utf16lechar *new_target;
+ utf16lechar *new_print_name;
+ size_t new_target_nchars;
+ size_t new_print_name_nchars;
+ utf16lechar *p;
+
+ ret = parse_reparse_data(rpbuf, *rpbuflen_p, &rpdata);
+ if (ret)
+ return ret;
+
+ if (extract_root_realpath[0] == L'\0' ||
+ extract_root_realpath[1] != L':' ||
+ extract_root_realpath[2] != L'\\')
+ return WIMLIB_ERR_REPARSE_POINT_FIXUP_FAILED;
+
+ ret = parse_substitute_name(rpdata.substitute_name,
+ rpdata.substitute_name_nbytes,
+ rpdata.rptag);
+ if (ret < 0)
+ return 0;
+ stripped_nchars = ret;
+ target = rpdata.substitute_name;
+ target_nchars = rpdata.substitute_name_nbytes / sizeof(utf16lechar);
+ stripped_target = target + stripped_nchars;
+ stripped_target_nchars = target_nchars - stripped_nchars;
+
+ new_target = alloca((6 + extract_root_realpath_nchars +
+ stripped_target_nchars) * sizeof(utf16lechar));
+
+ p = new_target;
+ if (stripped_nchars == 6) {
+ /* Include \??\ prefix if it was present before */
+ p = wmempcpy(p, L"\\??\\", 4);
+ }
+
+ /* Print name excludes the \??\ if present. */
+ new_print_name = p;
+ if (stripped_nchars != 0) {
+ /* Get drive letter from real path to extract root, if a drive
+ * letter was present before. */
+ *p++ = extract_root_realpath[0];
+ *p++ = extract_root_realpath[1];
+ }
+ /* Copy the rest of the extract root */
+ p = wmempcpy(p, extract_root_realpath + 2, extract_root_realpath_nchars - 2);
+
+ /* Append the stripped target */
+ p = wmempcpy(p, stripped_target, stripped_target_nchars);
+ new_target_nchars = p - new_target;
+ new_print_name_nchars = p - new_print_name;
+
+ if (new_target_nchars * sizeof(utf16lechar) >= REPARSE_POINT_MAX_SIZE ||
+ new_print_name_nchars * sizeof(utf16lechar) >= REPARSE_POINT_MAX_SIZE)
+ return WIMLIB_ERR_REPARSE_POINT_FIXUP_FAILED;
+
+ rpdata.substitute_name = new_target;
+ rpdata.substitute_name_nbytes = new_target_nchars * sizeof(utf16lechar);
+ rpdata.print_name = new_print_name;
+ rpdata.print_name_nbytes = new_print_name_nchars * sizeof(utf16lechar);
+ return make_reparse_buffer(&rpdata, rpbuf, rpbuflen_p);
+}
+#endif /* __WIN32__ */
+
+/* Set reparse data on extracted file or directory that has
+ * FILE_ATTRIBUTE_REPARSE_POINT set. */
+static int
+extract_reparse_data(const tchar *path, struct apply_ctx *ctx,
+ struct wim_inode *inode,
+ struct wim_lookup_table_entry *lte_override)
+{
+ int ret;
+ u8 rpbuf[REPARSE_POINT_MAX_SIZE];
+ u16 rpbuflen;
+
+ ret = wim_inode_get_reparse_data(inode, rpbuf, &rpbuflen, lte_override);
+ if (ret)
+ goto error;
+
+#ifdef __WIN32__
+ /* Fix up target of absolute symbolic link or junction points so
+ * that they point into the actual extraction target. */
+ if ((ctx->extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX) &&
+ (inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
+ inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT) &&
+ !inode->i_not_rpfixed)
+ {
+ ret = try_extract_rpfix(rpbuf, &rpbuflen, ctx->realtarget,
+ ctx->realtarget_nchars);
+ if (ret && !(ctx->extract_flags &
+ WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS))
+ {
+ WARNING("Reparse point fixup of \"%"TS"\" "
+ "failed", path);
+ ret = 0;
+ }
+ if (ret)
+ goto error;
+ }
+#endif
+
+ ret = ctx->ops->set_reparse_data(path, rpbuf, rpbuflen, ctx);
+
+ /* On Windows, the SeCreateSymbolicLink privilege is required to create
+ * symbolic links. To be more friendly towards non-Administrator users,
+ * we merely warn the user if symbolic links cannot be created due to
+ * insufficient permissions or privileges, unless
+ * WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS was provided. */
+#ifdef __WIN32__
+ if (ret && inode_is_symlink(inode) &&
+ (errno == EACCES || errno == EPERM) &&
+ !(ctx->extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS))
+ {
+ WARNING("Can't set reparse data on \"%"TS"\": "
+ "Access denied!\n"
+ " You may be trying to "
+ "extract a symbolic link without the\n"
+ " SeCreateSymbolicLink privilege, "
+ "which by default non-Administrator\n"
+ " accounts do not have.",
+ path);
+ ret = 0;
+ }
+#endif
+ if (ret)
+ goto error;
+
+ /* Account for reparse data consumed. */
+ update_extract_progress(ctx,
+ (lte_override ? lte_override :
+ inode_unnamed_lte_resolved(inode)));
+ return 0;
+
+error:
+ ERROR_WITH_ERRNO("Failed to set reparse data on \"%"TS"\"", path);
+ return ret;
+}
+
+/*
+ * Extract zero or more streams to a file.
+ *
+ * This function operates slightly differently depending on whether @lte_spec is
+ * NULL or not. When @lte_spec is NULL, the behavior is to extract the default
+ * file contents (unnamed stream), and, if named data streams are supported in
+ * the extract mode and volume, any named data streams. When @lte_spec is NULL,
+ * the behavior is to extract only all copies of the stream @lte_spec, and in
+ * addition use @lte_spec to set the reparse data or create the symbolic link if
+ * appropriate.
+ *
+ * @path
+ * Path to file to extract (as can be passed to apply_operations
+ * functions).
+ * @ctx
+ * Apply context.
+ * @dentry
+ * WIM dentry that corresponds to the file being extracted.
+ * @lte_spec
+ * If non-NULL, specifies the lookup table entry for a stream to extract,
+ * and only that stream will be extracted (although there may be more than
+ * one instance of it).
+ * @lte_override
+ * Used only if @lte_spec != NULL; it is passed to the extraction functions
+ * rather than @lte_spec, allowing the location of the stream to be
+ * overridden. (This is used when the WIM is being read from a nonseekable
+ * file, such as a pipe, when streams need to be used more than once; each
+ * such stream is extracted to a temporary file.)
+ */
+static int
+extract_streams(const tchar *path, struct apply_ctx *ctx,
+ struct wim_dentry *dentry,
+ struct wim_lookup_table_entry *lte_spec,
+ struct wim_lookup_table_entry *lte_override)
+{
+ struct wim_inode *inode = dentry->d_inode;
+ struct wim_lookup_table_entry *lte;
+ file_spec_t file_spec;
+ int ret;
+
+ if (dentry->was_hardlinked)
+ return 0;
+
+#ifdef ENABLE_DEBUG
+ if (lte_spec) {
+ char sha1_str[100];
+ char *p = sha1_str;
+ for (unsigned i = 0; i < SHA1_HASH_SIZE; i++)
+ p += sprintf(p, "%02x", lte_override->hash[i]);
+ DEBUG("Extracting stream SHA1=%s to \"%"TS"\"",
+ sha1_str, path, inode->i_ino);
+ } else {
+ DEBUG("Extracting streams to \"%"TS"\"", path, inode->i_ino);
+ }
+#endif
+
+ if (ctx->ops->uses_cookies)
+ file_spec.cookie = inode->extract_cookie;
+ else
+ file_spec.path = path;
+
+ /* Unnamed data stream. */
+ lte = inode_unnamed_lte_resolved(inode);
+ if (lte && (!lte_spec || lte == lte_spec)) {
+ if (lte_spec)
+ lte = lte_override;
+ if (!(inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY |
+ FILE_ATTRIBUTE_REPARSE_POINT)))
+ {
+ if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED &&
+ ctx->supported_features.encrypted_files) {
+ if (!ctx->ops->extract_encrypted_stream_creates_file) {
+ ret = ctx->ops->extract_encrypted_stream(
+ path, lte, ctx);
+ if (ret)
+ goto error;
+ }
+ } else {
+ ret = ctx->ops->extract_unnamed_stream(
+ file_spec, lte, ctx);
+ if (ret)
+ goto error;
+ }
+ update_extract_progress(ctx, lte);
+ }
+ else if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)
+ {
+ ret = 0;
+ if (ctx->supported_features.reparse_points)
+ ret = extract_reparse_data(path, ctx, inode, lte);
+ #ifndef __WIN32__
+ else if ((inode_is_symlink(inode) &&
+ ctx->supported_features.symlink_reparse_points))
+ ret = extract_symlink(path, ctx, inode, lte);
+ #endif
+ if (ret)
+ return ret;
+ }
+ }
+
+ /* Named data streams. */
+ if (can_extract_named_data_streams(ctx)) {
+ for (u16 i = 0; i < inode->i_num_ads; i++) {
+ struct wim_ads_entry *entry = &inode->i_ads_entries[i];
+
+ if (!ads_entry_is_named_stream(entry))
+ continue;
+ lte = entry->lte;
+ if (!lte)
+ continue;
+ if (lte_spec && lte_spec != lte)
+ continue;
+ if (lte_spec)
+ lte = lte_override;
+ ret = ctx->ops->extract_named_stream(file_spec, entry->stream_name,
+ entry->stream_name_nbytes / 2,
+ lte, ctx);
+ if (ret)
+ goto error;
+ update_extract_progress(ctx, lte);
+ }
+ }
+ return 0;
+
+error:
+ ERROR_WITH_ERRNO("Failed to extract data of \"%"TS"\"", path);
+ return ret;
+}
+
+/* Set attributes on an extracted file or directory if supported by the
+ * extraction mode. */
+static int
+extract_file_attributes(const tchar *path, struct apply_ctx *ctx,
+ struct wim_dentry *dentry, unsigned pass)
+{
+ int ret;
+
+ if (ctx->ops->set_file_attributes &&
+ !(dentry == ctx->extract_root && ctx->root_dentry_is_special)) {
+ u32 attributes = dentry->d_inode->i_attributes;
+
+ /* Clear unsupported attributes. */
+ attributes &= ctx->supported_attributes_mask;
+
+ if ((attributes & FILE_ATTRIBUTE_DIRECTORY &&
+ !ctx->supported_features.encrypted_directories) ||
+ (!(attributes & FILE_ATTRIBUTE_DIRECTORY) &&
+ !ctx->supported_features.encrypted_files))
+ {
+ attributes &= ~FILE_ATTRIBUTE_ENCRYPTED;
+ }
+
+ if (attributes == 0)
+ attributes = FILE_ATTRIBUTE_NORMAL;
+
+ ret = ctx->ops->set_file_attributes(path, attributes, ctx, pass);
+ if (ret) {
+ ERROR_WITH_ERRNO("Failed to set attributes on "
+ "\"%"TS"\"", path);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+
+/* Set or remove the short (DOS) name on an extracted file or directory if
+ * supported by the extraction mode. Since DOS names are unimportant and it's
+ * easy to run into problems setting them on Windows (SetFileShortName()
+ * requires SE_RESTORE privilege, which only the Administrator can request, and
+ * also requires DELETE access to the file), failure is ignored unless
+ * WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES is set. */
+static int
+extract_short_name(const tchar *path, struct apply_ctx *ctx,
+ struct wim_dentry *dentry)
+{
+ int ret;
+
+ /* The root of the dentry tree being extracted may not be extracted to
+ * its original name, so its short name should be ignored. */
+ if (dentry == ctx->extract_root)
+ return 0;
+
+ if (ctx->supported_features.short_names) {
+ ret = ctx->ops->set_short_name(path,
+ dentry->short_name,
+ dentry->short_name_nbytes / 2,
+ ctx);
+ if (ret && (ctx->extract_flags &
+ WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES))
+ {
+ ERROR_WITH_ERRNO("Failed to set short name of "
+ "\"%"TS"\"", path);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+/* Set security descriptor, UNIX data, or neither on an extracted file, taking
+ * into account the current extraction mode and flags. */
+static int
+extract_security(const tchar *path, struct apply_ctx *ctx,
+ struct wim_dentry *dentry)
+{
+ int ret;
+ struct wim_inode *inode = dentry->d_inode;
+
+ if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS)
+ return 0;
+
+ if ((ctx->extract_root == dentry) && ctx->root_dentry_is_special)
+ return 0;
+
+#ifndef __WIN32__
+ if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) {
+ struct wimlib_unix_data data;
+
+ ret = inode_get_unix_data(inode, &data, NULL);
+ if (ret < 0)
+ ret = 0;
+ else if (ret == 0)
+ ret = ctx->ops->set_unix_data(path, &data, ctx);
+ if (ret) {
+ if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS) {
+ ERROR_WITH_ERRNO("Failed to set UNIX owner, "
+ "group, and/or mode on "
+ "\"%"TS"\"", path);
+ return ret;
+ } else {
+ WARNING_WITH_ERRNO("Failed to set UNIX owner, "
+ "group, and/or/mode on "
+ "\"%"TS"\"", path);
+ }
+ }
+ }
+ else
+#endif /* __WIN32__ */
+ if (ctx->supported_features.security_descriptors &&
+ inode->i_security_id != -1)
+ {
+ const struct wim_security_data *sd;
+ const u8 *desc;
+ size_t desc_size;
+
+ sd = wim_const_security_data(ctx->wim);
+ desc = sd->descriptors[inode->i_security_id];
+ desc_size = sd->sizes[inode->i_security_id];
+
+ ret = ctx->ops->set_security_descriptor(path, desc,
+ desc_size, ctx);
+ if (ret) {
+ if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS) {
+ ERROR_WITH_ERRNO("Failed to set security "
+ "descriptor on \"%"TS"\"", path);
+ return ret;
+ } else {
+ #if 0
+ if (errno != EACCES) {
+ WARNING_WITH_ERRNO("Failed to set "
+ "security descriptor "
+ "on \"%"TS"\"", path);
+ }
+ #endif
+ ctx->no_security_descriptors++;
+ }
+ }
+ }
+ return 0;
+}
+
+/* Set timestamps on an extracted file. Failure is warning-only unless
+ * WIMLIB_EXTRACT_FLAG_STRICT_TIMESTAMPS is set. */
+static int
+extract_timestamps(const tchar *path, struct apply_ctx *ctx,
+ struct wim_dentry *dentry)
+{
+ struct wim_inode *inode = dentry->d_inode;
+ int ret;
+
+ if ((ctx->extract_root == dentry) && ctx->root_dentry_is_special)
+ return 0;
+
+ if (ctx->ops->set_timestamps) {
+ ret = ctx->ops->set_timestamps(path,
+ inode->i_creation_time,
+ inode->i_last_write_time,
+ inode->i_last_access_time,
+ ctx);
+ if (ret) {
+ if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_TIMESTAMPS) {
+ ERROR_WITH_ERRNO("Failed to set timestamps "
+ "on \"%"TS"\"", path);
+ return ret;
+ } else {
+ WARNING_WITH_ERRNO("Failed to set timestamps "
+ "on \"%"TS"\"", path);
+ }
+ }
+ }
+ return 0;
+}
+
+/* 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;
+}
+
+/* Given a WIM dentry to extract, build the path to which to extract it, in the
+ * format understood by the callbacks in the apply_operations being used.
+ *
+ * Write the resulting path into @path, which must have room for at least
+ * ctx->ops->max_path characters including the null-terminator.
+ *
+ * Return %true if successful; %false if this WIM dentry doesn't actually need
+ * to be extracted or if the calculated path exceeds ctx->ops->max_path
+ * characters.
+ *
+ * This function clobbers the tmp_list member of @dentry and its ancestors up
+ * until the extraction root. */
+static bool
+build_extraction_path(tchar path[], struct wim_dentry *dentry,
+ struct apply_ctx *ctx)
+{
+ size_t path_nchars;