+static void calculate_bytes_to_extract(struct list_head *stream_list,
+ int extract_flags,
+ union wimlib_progress_info *progress)
+{
+ struct 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, staging_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 lookup_table_entry *lte,
+ struct list_head *stream_list)
+{
+ if (++lte->out_refcnt == 1) {
+ INIT_LIST_HEAD(<e->inode_list);
+ list_add_tail(<e->staging_list, stream_list);
+ }
+}
+
+static void inode_find_streams_for_extraction(struct inode *inode,
+ struct list_head *stream_list,
+ int extract_flags)
+{
+ struct lookup_table_entry *lte;
+ bool inode_added = false;
+
+ lte = inode_unnamed_lte_resolved(inode);
+
+ if (lte) {
+ maybe_add_stream_for_extraction(lte, stream_list);
+ list_add_tail(&inode->lte_inode_list, <e->inode_list);
+ inode_added = true;
+ }
+#ifdef WITH_NTFS_3G
+ if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
+ for (unsigned i = 0; i < inode->num_ads; i++) {
+ if (inode->ads_entries[i].stream_name_len != 0) {
+ lte = inode->ads_entries[i].lte;
+ if (lte) {
+ maybe_add_stream_for_extraction(lte,
+ stream_list);
+ if (!inode_added) {
+ list_add_tail(&inode->lte_inode_list,
+ <e->inode_list);
+ inode_added = true;
+ }
+ }
+ }
+ }
+ }
+#endif
+}
+
+static void find_streams_for_extraction(struct hlist_head *inode_list,
+ struct list_head *stream_list,
+ struct lookup_table *lookup_table,
+ int extract_flags)
+{
+ struct inode *inode;
+ struct hlist_node *cur;
+ struct dentry *dentry;
+
+ for_lookup_table_entry(lookup_table, lte_zero_out_refcnt, NULL);
+ INIT_LIST_HEAD(stream_list);
+ hlist_for_each_entry(inode, cur, inode_list, hlist) {
+ if (!inode->resolved)
+ inode_resolve_ltes(inode, lookup_table);
+ inode_for_each_dentry(dentry, inode)
+ dentry->is_extracted = 0;
+ inode_find_streams_for_extraction(inode, stream_list,
+ extract_flags);
+ }
+}
+
+struct apply_operations {
+ int (*apply_dentry)(struct dentry *dentry, void *arg);
+ int (*apply_dentry_timestamps)(struct dentry *dentry, void *arg);
+};
+
+static const struct apply_operations normal_apply_operations = {
+ .apply_dentry = apply_dentry_normal,
+ .apply_dentry_timestamps = apply_dentry_timestamps_normal,
+};
+
+#ifdef WITH_NTFS_3G
+static const struct apply_operations ntfs_apply_operations = {
+ .apply_dentry = apply_dentry_ntfs,
+ .apply_dentry_timestamps = apply_dentry_timestamps_ntfs,
+};
+#endif
+
+static int apply_stream_list(struct list_head *stream_list,
+ struct apply_args *args,
+ const struct apply_operations *ops,
+ wimlib_progress_func_t progress_func)
+{
+ uint64_t bytes_per_progress = args->progress.extract.total_bytes / 100;
+ uint64_t next_progress = bytes_per_progress;
+ struct lookup_table_entry *lte;
+ struct inode *inode;
+ struct dentry *dentry;
+ int ret = 0;
+
+ /* This complicated loop is actually just looping through the dentries
+ * (as for_dentry_in_tree() does), but the outer loop is actually over
+ * the distinct streams to be extracted so that sequential reading of
+ * the WIM can be implemented. */
+
+ /* For each distinct stream to be extracted */
+ list_for_each_entry(lte, stream_list, staging_list) {
+ /* For each inode that contains the stream */
+ list_for_each_entry(inode, <e->inode_list, lte_inode_list) {
+ /* For each dentry that points to the inode */
+ inode_for_each_dentry(dentry, inode) {
+ ret = ops->apply_dentry(dentry, args);
+ if (ret != 0)
+ goto out;
+ if (progress_func &&
+ args->progress.extract.completed_bytes >= next_progress &&
+ args->progress.extract.total_bytes != 0)
+ {
+ progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS,
+ &args->progress);
+ next_progress += bytes_per_progress;
+ }
+ }
+ }
+ }
+out:
+ return ret;
+}
+