+ for_lookup_table_entry(w->lookup_table,
+ do_print_lookup_table_entry,
+ stdout);
+}
+
+/* Given a SHA1 message digest, return the corresponding entry in the WIM's
+ * lookup table, or NULL if there is none. */
+struct wim_lookup_table_entry *
+__lookup_resource(const struct wim_lookup_table *table, const u8 hash[])
+{
+ size_t i;
+ struct wim_lookup_table_entry *lte;
+ struct hlist_node *pos;
+
+ wimlib_assert(table != NULL);
+ wimlib_assert(hash != NULL);
+
+ i = *(size_t*)hash % table->capacity;
+ hlist_for_each_entry(lte, pos, &table->array[i], hash_list)
+ if (hashes_equal(hash, lte->hash))
+ return lte;
+ return NULL;
+}
+
+#ifdef WITH_FUSE
+/*
+ * Finds the dentry, lookup table entry, and stream index for a WIM file stream,
+ * given a path name.
+ *
+ * This is only for pre-resolved inodes.
+ */
+int lookup_resource(WIMStruct *w, const char *path,
+ int lookup_flags,
+ struct wim_dentry **dentry_ret,
+ struct wim_lookup_table_entry **lte_ret,
+ u16 *stream_idx_ret)
+{
+ struct wim_dentry *dentry;
+ struct wim_lookup_table_entry *lte;
+ u16 stream_idx;
+ const char *stream_name = NULL;
+ struct wim_inode *inode;
+ char *p = NULL;
+
+ if (lookup_flags & LOOKUP_FLAG_ADS_OK) {
+ stream_name = path_stream_name(path);
+ if (stream_name) {
+ p = (char*)stream_name - 1;
+ *p = '\0';
+ }
+ }
+
+ dentry = get_dentry(w, path);
+ if (p)
+ *p = ':';
+ if (!dentry)
+ return -ENOENT;
+
+ inode = dentry->d_inode;
+
+ wimlib_assert(inode->i_resolved);
+
+ if (!(lookup_flags & LOOKUP_FLAG_DIRECTORY_OK)
+ && inode_is_directory(inode))
+ return -EISDIR;
+
+ if (stream_name) {
+ struct wim_ads_entry *ads_entry;
+ u16 ads_idx;
+ ads_entry = inode_get_ads_entry(inode, stream_name,
+ &ads_idx);
+ if (ads_entry) {
+ stream_idx = ads_idx + 1;
+ lte = ads_entry->lte;
+ goto out;
+ } else {
+ return -ENOENT;
+ }
+ } else {
+ lte = inode->i_lte;
+ stream_idx = 0;
+ }
+out:
+ if (dentry_ret)
+ *dentry_ret = dentry;
+ if (lte_ret)
+ *lte_ret = lte;
+ if (stream_idx_ret)
+ *stream_idx_ret = stream_idx;
+ return 0;
+}
+#endif
+
+/* Resolve an inode's lookup table entries
+ *
+ * This replaces the SHA1 hash fields (which are used to lookup an entry in the
+ * lookup table) with pointers directly to the lookup table entries. A circular
+ * linked list of streams sharing the same lookup table entry is created.
+ *
+ * This function always succeeds; unresolved lookup table entries are given a
+ * NULL pointer.
+ */
+void inode_resolve_ltes(struct wim_inode *inode, struct wim_lookup_table *table)
+{
+
+ if (!inode->i_resolved) {
+ struct wim_lookup_table_entry *lte;
+ /* Resolve the default file stream */
+ lte = __lookup_resource(table, inode->i_hash);
+ inode->i_lte = lte;
+ inode->i_resolved = 1;
+
+ /* Resolve the alternate data streams */
+ for (u16 i = 0; i < inode->i_num_ads; i++) {
+ struct wim_ads_entry *cur_entry = &inode->i_ads_entries[i];
+ lte = __lookup_resource(table, cur_entry->hash);
+ cur_entry->lte = lte;
+ }
+ }
+}
+
+void inode_unresolve_ltes(struct wim_inode *inode)
+{
+ if (inode->i_resolved) {
+ if (inode->i_lte)
+ copy_hash(inode->i_hash, inode->i_lte->hash);
+ else
+ zero_out_hash(inode->i_hash);
+
+ for (u16 i = 0; i < inode->i_num_ads; i++) {
+ if (inode->i_ads_entries[i].lte)
+ copy_hash(inode->i_ads_entries[i].hash,
+ inode->i_ads_entries[i].lte->hash);
+ else
+ zero_out_hash(inode->i_ads_entries[i].hash);
+ }
+ inode->i_resolved = 0;
+ }
+}
+
+/*
+ * Returns the lookup table entry for stream @stream_idx of the inode, where
+ * stream_idx = 0 means the default un-named file stream, and stream_idx >= 1
+ * corresponds to an alternate data stream.
+ *
+ * This works for both resolved and un-resolved dentries.
+ */
+struct wim_lookup_table_entry *
+inode_stream_lte(const struct wim_inode *inode, unsigned stream_idx,
+ const struct wim_lookup_table *table)
+{
+ if (inode->i_resolved)
+ return inode_stream_lte_resolved(inode, stream_idx);
+ else
+ return inode_stream_lte_unresolved(inode, stream_idx, table);
+}
+
+
+/* Return the lookup table entry for the unnamed data stream of an inode, or
+ * NULL if there is none.
+ *
+ * You'd think this would be easier than it actually is, since the unnamed data
+ * stream should be the one referenced from the inode itself. Alas, if there
+ * are named data streams, Microsoft's "imagex.exe" program will put the unnamed
+ * data stream in one of the alternate data streams instead of inside the WIM
+ * dentry itself. So we need to check the alternate data streams too.
+ *
+ * Also, note that a dentry may appear to have more than one unnamed stream, but
+ * if the SHA1 message digest is all 0's then the corresponding stream does not
+ * really "count" (this is the case for the inode's own file stream when the
+ * file stream that should be there is actually in one of the alternate stream
+ * entries.). This is despite the fact that we may need to extract such a
+ * missing entry as an empty file or empty named data stream.
+ */
+struct wim_lookup_table_entry *
+inode_unnamed_lte(const struct wim_inode *inode,
+ const struct wim_lookup_table *table)
+{
+ if (inode->i_resolved)
+ return inode_unnamed_lte_resolved(inode);
+ else
+ return inode_unnamed_lte_unresolved(inode, table);
+}
+
+static int lte_add_stream_size(struct wim_lookup_table_entry *lte,
+ void *total_bytes_p)
+{
+ *(u64*)total_bytes_p += lte->resource_entry.size;
+ return 0;
+}
+
+u64 lookup_table_total_stream_size(struct wim_lookup_table *table)
+{
+ u64 total_size = 0;
+ for_lookup_table_entry(table, lte_add_stream_size, &total_size);
+ return total_size;