+
+static int
+do_apply_op(struct wim_dentry *dentry, struct apply_args *args,
+ int (*apply_dentry_func)(const tchar *, size_t,
+ struct wim_dentry *, struct apply_args *))
+{
+ tchar *p;
+ const tchar *full_path;
+ size_t full_path_nchars;
+
+ wimlib_assert(dentry->_full_path != NULL);
+ full_path = dentry->_full_path + 1;
+ full_path_nchars = dentry->full_path_nbytes / sizeof(tchar) - 1;
+ tchar output_path[args->target_nchars + 1 +
+ (full_path_nchars - args->wim_source_path_nchars) + 1];
+ p = output_path;
+
+ tmemcpy(p, args->target, args->target_nchars);
+ p += args->target_nchars;
+
+ if (dentry != args->extract_root) {
+ *p++ = T('/');
+ tmemcpy(p, full_path + args->wim_source_path_nchars,
+ full_path_nchars - args->wim_source_path_nchars);
+ p += full_path_nchars - args->wim_source_path_nchars;
+ }
+ *p = T('\0');
+ return (*apply_dentry_func)(output_path, p - output_path,
+ dentry, args);
+}
+
+
+/* Extracts a file, directory, or symbolic link from the WIM archive. */
+static int
+apply_dentry_normal(struct wim_dentry *dentry, void *arg)
+{
+#ifdef __WIN32__
+ return do_apply_op(dentry, arg, win32_do_apply_dentry);
+#else
+ return do_apply_op(dentry, arg, unix_do_apply_dentry);
+#endif
+}
+
+
+/* Apply timestamps to an extracted file or directory */
+static int
+apply_dentry_timestamps_normal(struct wim_dentry *dentry, void *arg)
+{
+#ifdef __WIN32__
+ return do_apply_op(dentry, arg, win32_do_apply_dentry_timestamps);
+#else
+ return do_apply_op(dentry, arg, unix_do_apply_dentry_timestamps);
+#endif
+}
+
+/* Extract a dentry if it hasn't already been extracted and either
+ * WIMLIB_EXTRACT_FLAG_NO_STREAMS is not specified, or the dentry is a directory
+ * and/or has no unnamed stream. */
+static int
+maybe_apply_dentry(struct wim_dentry *dentry, void *arg)
+{
+ struct apply_args *args = arg;
+ int ret;
+
+ if (!dentry->needs_extraction)
+ return 0;
+
+ if (args->extract_flags & WIMLIB_EXTRACT_FLAG_NO_STREAMS &&
+ !dentry_is_directory(dentry) &&
+ inode_unnamed_lte_resolved(dentry->d_inode) != NULL)
+ return 0;
+
+ if ((args->extract_flags & WIMLIB_EXTRACT_FLAG_VERBOSE) &&
+ args->progress_func) {
+ args->progress.extract.cur_path = dentry->_full_path;
+ args->progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY,
+ &args->progress);
+ }
+ ret = args->apply_dentry(dentry, args);
+ if (ret == 0)
+ dentry->needs_extraction = 0;
+ return ret;
+}
+
+static void
+calculate_bytes_to_extract(struct list_head *stream_list,
+ int extract_flags,
+ union wimlib_progress_info *progress)
+{
+ struct wim_lookup_table_entry *lte;
+ u64 total_bytes = 0;
+ u64 num_streams = 0;
+
+ /* For each stream to be extracted... */
+ list_for_each_entry(lte, stream_list, extraction_list) {
+ if (extract_flags &
+ (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK))
+ {
+ /* In the symlink or hard link extraction mode, each
+ * stream will be extracted one time regardless of how
+ * many dentries share the stream. */
+ wimlib_assert(!(extract_flags & WIMLIB_EXTRACT_FLAG_NTFS));
+ if (!lte->extracted_file) {
+ num_streams++;
+ total_bytes += wim_resource_size(lte);
+ }
+ } else {
+ num_streams += lte->out_refcnt;
+ total_bytes += lte->out_refcnt * wim_resource_size(lte);
+ }
+ }
+ progress->extract.num_streams = num_streams;
+ progress->extract.total_bytes = total_bytes;
+ progress->extract.completed_bytes = 0;
+}
+
+static void
+maybe_add_stream_for_extraction(struct wim_lookup_table_entry *lte,
+ struct list_head *stream_list)
+{
+ if (++lte->out_refcnt == 1) {
+ INIT_LIST_HEAD(<e->lte_dentry_list);
+ list_add_tail(<e->extraction_list, stream_list);
+ }
+}
+
+struct find_streams_ctx {
+ struct list_head stream_list;
+ int extract_flags;
+};
+
+static int
+dentry_find_streams_to_extract(struct wim_dentry *dentry, void *_ctx)
+{
+ struct find_streams_ctx *ctx = _ctx;
+ struct wim_inode *inode = dentry->d_inode;
+ struct wim_lookup_table_entry *lte;
+ bool dentry_added = false;
+ struct list_head *stream_list = &ctx->stream_list;
+ int extract_flags = ctx->extract_flags;
+
+ dentry->needs_extraction = 1;
+
+ lte = inode_unnamed_lte_resolved(inode);
+ if (lte) {
+ if (!inode->i_visited)
+ maybe_add_stream_for_extraction(lte, stream_list);
+ list_add_tail(&dentry->tmp_list, <e->lte_dentry_list);
+ dentry_added = true;
+ }
+
+ /* Determine whether to include alternate data stream entries or not.
+ *
+ * UNIX: Include them if extracting using NTFS-3g.
+ *
+ * Windows: Include them undconditionally, although if the filesystem is
+ * not NTFS we won't actually be able to extract them. */
+#if defined(WITH_NTFS_3G)
+ if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS)
+#elif defined(__WIN32__)
+ if (1)
+#else
+ if (0)
+#endif
+ {
+ for (unsigned i = 0; i < inode->i_num_ads; i++) {
+ if (inode->i_ads_entries[i].stream_name_nbytes != 0) {
+ lte = inode->i_ads_entries[i].lte;
+ if (lte) {
+ if (!inode->i_visited) {
+ maybe_add_stream_for_extraction(lte,
+ stream_list);
+ }
+ if (!dentry_added) {
+ list_add_tail(&dentry->tmp_list,
+ <e->lte_dentry_list);
+ dentry_added = true;
+ }
+ }
+ }
+ }
+ }
+ inode->i_visited = 1;
+ return 0;
+}
+
+static int
+dentry_resolve_and_zero_lte_refcnt(struct wim_dentry *dentry, void *_lookup_table)
+{
+ struct wim_inode *inode = dentry->d_inode;
+ struct wim_lookup_table *lookup_table = _lookup_table;
+ struct wim_lookup_table_entry *lte;
+
+ inode_resolve_ltes(inode, lookup_table);
+ 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 void
+find_streams_for_extraction(struct wim_dentry *root,
+ struct list_head *stream_list,
+ struct wim_lookup_table *lookup_table,
+ int extract_flags)
+{
+ struct find_streams_ctx ctx;
+
+ INIT_LIST_HEAD(&ctx.stream_list);
+ ctx.extract_flags = extract_flags;
+ for_dentry_in_tree(root, dentry_resolve_and_zero_lte_refcnt, lookup_table);
+ for_dentry_in_tree(root, dentry_find_streams_to_extract, &ctx);
+ list_transfer(&ctx.stream_list, stream_list);
+}
+
+static int
+dentry_reset_needs_extraction(struct wim_dentry *dentry, void *_ignore)
+{
+ dentry->needs_extraction = 0;
+ dentry->d_inode->i_visited = 0;
+ return 0;
+}
+
+struct apply_operations {
+ int (*apply_dentry)(struct wim_dentry *dentry, void *arg);
+ int (*apply_dentry_timestamps)(struct wim_dentry *dentry, void *arg);
+};
+
+static const struct apply_operations normal_apply_operations = {
+ .apply_dentry = apply_dentry_normal,
+ .apply_dentry_timestamps = apply_dentry_timestamps_normal,
+};