inode updates (IN PROGRESS)
authorEric Biggers <ebiggers3@gmail.com>
Mon, 3 Sep 2012 20:39:03 +0000 (15:39 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Mon, 3 Sep 2012 20:39:03 +0000 (15:39 -0500)
src/dentry.c
src/dentry.h
src/hardlink.c
src/join.c
src/list.h
src/lookup_table.c
src/lookup_table.h
src/mount.c

index 043ceade5803e5690c1943940b7003015b420a14..a9d13f690d01afffde23d30f53017279b5ff19ff 100644 (file)
@@ -131,7 +131,8 @@ void inode_update_all_timestamps(struct inode *inode)
 /* Returns the alternate data stream entry belonging to @inode that has the
  * stream name @stream_name. */
 struct ads_entry *inode_get_ads_entry(struct inode *inode,
-                                     const char *stream_name)
+                                     const char *stream_name,
+                                     u16 *idx_ret)
 {
        size_t stream_name_len;
        if (!stream_name)
@@ -142,7 +143,11 @@ struct ads_entry *inode_get_ads_entry(struct inode *inode,
                do {
                        if (ads_entry_has_name(inode->ads_entries[i],
                                               stream_name, stream_name_len))
+                       {
+                               if (idx_ret)
+                                       *idx_ret = i;
                                return inode->ads_entries[i];
+                       }
                } while (++i != inode->num_ads);
        }
        return NULL;
@@ -154,8 +159,6 @@ static struct ads_entry *new_ads_entry(const char *name)
        struct ads_entry *ads_entry = CALLOC(1, sizeof(struct ads_entry));
        if (!ads_entry)
                return NULL;
-       INIT_LIST_HEAD(&ads_entry->lte_group_list.list);
-       ads_entry->lte_group_list.type = STREAM_TYPE_ADS;
        if (name && *name) {
                if (change_ads_name(ads_entry, name)) {
                        FREE(ads_entry);
@@ -175,7 +178,7 @@ struct ads_entry *inode_add_ads(struct inode *inode, const char *stream_name)
        struct ads_entry **ads_entries;
        struct ads_entry *new_entry;
 
-       if (inode->num_ads == 0xffff) {
+       if (inode->num_ads >= 0xfffe) {
                ERROR("Too many alternate data streams in one inode!");
                return NULL;
        }
@@ -212,7 +215,7 @@ int for_dentry_in_tree(struct dentry *root,
        if (ret != 0)
                return ret;
 
-       child = root->children;
+       child = root->inode->children;
 
        if (!child)
                return 0;
@@ -222,7 +225,7 @@ int for_dentry_in_tree(struct dentry *root,
                if (ret != 0)
                        return ret;
                child = child->next;
-       } while (child != root->children);
+       } while (child != root->inode->children);
        return 0;
 }
 
@@ -237,7 +240,7 @@ int for_dentry_in_tree_depth(struct dentry *root,
        struct dentry *child;
        struct dentry *next;
 
-       child = root->children;
+       child = root->inode->children;
        if (child) {
                do {
                        next = child->next;
@@ -245,7 +248,7 @@ int for_dentry_in_tree_depth(struct dentry *root,
                        if (ret != 0)
                                return ret;
                        child = next;
-               } while (child != root->children);
+               } while (child != root->inode->children);
        }
        return visitor(root, arg);
 }
@@ -311,7 +314,7 @@ void calculate_subdir_offsets(struct dentry *dentry, u64 *subdir_offset_p)
 {
        struct dentry *child;
 
-       child = dentry->children;
+       child = dentry->inode->children;
        dentry->subdir_offset = *subdir_offset_p;
 
        if (child) {
@@ -320,7 +323,7 @@ void calculate_subdir_offsets(struct dentry *dentry, u64 *subdir_offset_p)
                do {
                        *subdir_offset_p += dentry_correct_total_length(child);
                        child = child->next;
-               } while (child != dentry->children);
+               } while (child != dentry->inode->children);
 
                /* End-of-directory dentry on disk. */
                *subdir_offset_p += 8;
@@ -330,7 +333,7 @@ void calculate_subdir_offsets(struct dentry *dentry, u64 *subdir_offset_p)
                do {
                        calculate_subdir_offsets(child, subdir_offset_p);
                        child = child->next;
-               } while (child != dentry->children);
+               } while (child != dentry->inode->children);
        } else {
                /* On disk, childless directories have a valid subdir_offset
                 * that points to an 8-byte end-of-directory dentry.  Regular
@@ -351,14 +354,14 @@ struct dentry *get_dentry_child_with_name(const struct dentry *dentry,
        struct dentry *child;
        size_t name_len;
        
-       child = dentry->children;
+       child = dentry->inode->children;
        if (child) {
                name_len = strlen(name);
                do {
                        if (dentry_has_name(child, name, name_len))
                                return child;
                        child = child->next;
-               } while (child != dentry->children);
+               } while (child != dentry->inode->children);
        }
        return NULL;
 }
@@ -375,14 +378,14 @@ static struct dentry *get_dentry_relative_path(struct dentry *cur_dir,
        if (*path == '\0')
                return cur_dir;
 
-       child = cur_dir->children;
+       child = cur_dir->inode->children;
        if (child) {
                new_path = path_next_part(path, &base_len);
                do {
                        if (dentry_has_name(child, path, base_len))
                                return get_dentry_relative_path(child, new_path);
                        child = child->next;
-               } while (child != cur_dir->children);
+               } while (child != cur_dir->inode->children);
        }
        return NULL;
 }
@@ -397,6 +400,16 @@ struct dentry *get_dentry(WIMStruct *w, const char *path)
        return get_dentry_relative_path(root, path);
 }
 
+struct inode *wim_pathname_to_inode(WIMStruct *w, const char *path)
+{
+       struct dentry *dentry;
+       dentry = get_dentry(w, path);
+       if (!dentry)
+               return NULL;
+       else
+               return dentry->inode;
+}
+
 /* Returns the dentry that corresponds to the parent directory of @path, or NULL
  * if the dentry is not found. */
 struct dentry *get_parent_dentry(WIMStruct *w, const char *path)
@@ -591,8 +604,7 @@ struct dentry *new_dentry_with_inode(const char *name)
        if (dentry) {
                dentry->inode = new_inode();
                if (dentry->inode) {
-                       list_add(&dentry->inode_dentry_list,
-                                &dentry->inode->dentry_list);
+                       inode_add_dentry(dentry, dentry->inode);
                } else {
                        free_dentry(dentry);
                        dentry = NULL;
@@ -601,7 +613,7 @@ struct dentry *new_dentry_with_inode(const char *name)
        return dentry;
 }
 
-static void free_ads_entry(struct ads_entry *entry)
+void free_ads_entry(struct ads_entry *entry)
 {
        if (entry) {
                FREE(entry->stream_name);
@@ -610,53 +622,7 @@ static void free_ads_entry(struct ads_entry *entry)
        }
 }
 
-
-#ifdef WITH_FUSE
-/* Remove an alternate data stream from a dentry.
- *
- * The corresponding lookup table entry for the stream is NOT changed.
- *
- * @dentry:    The dentry
- * @ads_entry: The alternate data stream entry (it MUST be one of the
- *                ads_entry's in the array dentry->ads_entries).
- */
-void dentry_remove_ads(struct dentry *dentry, struct ads_entry *ads_entry)
-{
-       u16 idx;
-       u16 following;
-
-       wimlib_assert(dentry->num_ads);
-       idx = ads_entry - dentry->ads_entries;
-       wimlib_assert(idx < dentry->num_ads);
-       following = dentry->num_ads - idx - 1;
-
-       destroy_ads_entry(ads_entry);
-       memcpy(ads_entry, ads_entry + 1, following * sizeof(struct ads_entry));
-
-       /* We moved the ADS entries.  Adjust the stream lists. */
-       for (u16 i = 0; i < following; i++) {
-               struct list_head *cur = &ads_entry[i].lte_group_list.list;
-               struct list_head *prev = cur->prev;
-               struct list_head *next;
-               if ((u8*)prev >= (u8*)(ads_entry + 1)
-                   && (u8*)prev < (u8*)(ads_entry + following + 1)) {
-                       cur->prev = (struct list_head*)((u8*)prev - sizeof(struct ads_entry));
-               } else {
-                       prev->next = cur;
-               }
-               next = cur->next;
-               if ((u8*)next >= (u8*)(ads_entry + 1)
-                   && (u8*)next < (u8*)(ads_entry + following + 1)) {
-                       cur->next = (struct list_head*)((u8*)next - sizeof(struct ads_entry));
-               } else {
-                       next->prev = cur;
-               }
-       }
-       dentry->num_ads--;
-}
-#endif
-
-static void inode_free_ads_entries(struct inode *inode)
+void inode_free_ads_entries(struct inode *inode)
 {
        if (inode->ads_entries) {
                for (u16 i = 0; i < inode->num_ads; i++)
@@ -665,36 +631,66 @@ static void inode_free_ads_entries(struct inode *inode)
        }
 }
 
+/* Frees an inode. */
 void free_inode(struct inode *inode)
 {
        if (inode) {
                inode_free_ads_entries(inode);
+       #ifdef WITH_FUSE
+               wimlib_assert(inode->num_opened_fds == 0);
+               FREE(inode->fds);
+       #endif
                FREE(inode);
        }
 }
 
-void put_inode(struct inode *inode)
+/* Decrements link count on an inode and frees it if the link count reaches 0.
+ * */
+struct inode *put_inode(struct inode *inode)
 {
        if (inode) {
                wimlib_assert(inode->link_count);
-               if (--inode->link_count == 0)
-                       free_inode(inode);
+               if (--inode->link_count == 0) {
+               #ifdef WITH_FUSE
+                       if (inode->num_opened_fds == 0)
+               #endif
+                       {
+                               free_inode(inode);
+                               inode = NULL;
+                       }
+               }
        }
+       return inode;
 }
 
-/* Frees a WIM dentry. */
-void free_dentry(struct dentry *dentry)
+/* Frees a WIM dentry. 
+ *
+ * The inode is freed only if its link count is decremented to 0.
+ */
+struct inode *free_dentry(struct dentry *dentry)
 {
        wimlib_assert(dentry);
+       struct inode *inode;
 
        FREE(dentry->file_name);
        FREE(dentry->file_name_utf8);
        FREE(dentry->short_name);
        FREE(dentry->full_path_utf8);
-       put_inode(dentry->inode);
+       inode = put_inode(dentry->inode);
        FREE(dentry);
+       return inode;
 }
 
+void put_dentry(struct dentry *dentry)
+{
+       wimlib_assert(dentry);
+       wimlib_assert(dentry->refcnt);
+
+       if (--dentry->refcnt == 0)
+               free_dentry(dentry);
+}
+
+#if 0
 /* Partically clones a dentry.
  *
  * Beware:
@@ -716,6 +712,7 @@ struct dentry *clone_dentry(struct dentry *old)
        new->short_name_len     = 0;
        return new;
 }
+#endif
 
 /* 
  * This function is passed as an argument to for_dentry_in_tree_depth() in order
@@ -774,36 +771,35 @@ void link_dentry(struct dentry *dentry, struct dentry *parent)
 {
        wimlib_assert(dentry_is_directory(parent));
        dentry->parent = parent;
-       if (parent->children) {
+       if (parent->inode->children) {
                /* Not an only child; link to siblings. */
-               dentry->next = parent->children;
-               dentry->prev = parent->children->prev;
+               dentry->next = parent->inode->children;
+               dentry->prev = parent->inode->children->prev;
                dentry->next->prev = dentry;
                dentry->prev->next = dentry;
        } else {
                /* Only child; link to parent. */
-               parent->children = dentry;
+               parent->inode->children = dentry;
                dentry->next = dentry;
                dentry->prev = dentry;
        }
 }
 
 
-/* Unlink a dentry from the directory tree. 
+/* 
+ * Unlink a dentry from the directory tree. 
  *
- * Note: This merely removes it from the in-memory tree structure.  See
- * remove_dentry() in mount.c for a function implemented on top of this one that
- * frees the dentry and implements reference counting for the lookup table
- * entries. */
+ * Note: This merely removes it from the in-memory tree structure.
+ */
 void unlink_dentry(struct dentry *dentry)
 {
        if (dentry_is_root(dentry))
                return;
        if (dentry_is_only_child(dentry)) {
-               dentry->parent->children = NULL;
+               dentry->parent->inode->children = NULL;
        } else {
                if (dentry_is_first_sibling(dentry))
-                       dentry->parent->children = dentry->next;
+                       dentry->parent->inode->children = dentry->next;
                dentry->next->prev = dentry->prev;
                dentry->prev->next = dentry->next;
        }
@@ -1521,12 +1517,12 @@ static u8 *write_dentry_tree_recursive(const struct dentry *parent, u8 *p)
         * recursively writing the directory trees rooted at each of the child
         * dentries, since the on-disk dentries for a dentry's children are
         * always located at consecutive positions in the metadata resource! */
-       child = parent->children;
+       child = parent->inode->children;
        if (child) {
                do {
                        p = write_dentry(child, p);
                        child = child->next;
-               } while (child != parent->children);
+               } while (child != parent->inode->children);
        }
 
        /* write end of directory entry */
@@ -1537,7 +1533,7 @@ static u8 *write_dentry_tree_recursive(const struct dentry *parent, u8 *p)
                do {
                        p = write_dentry_tree_recursive(child, p);
                        child = child->next;
-               } while (child != parent->children);
+               } while (child != parent->inode->children);
        }
        return p;
 }
@@ -1658,6 +1654,6 @@ int read_dentry_tree(const u8 metadata_resource[], u64 metadata_resource_len,
                prev_child->next = first_child;
                first_child->prev = prev_child;
        }
-       dentry->children = first_child;
+       dentry->inode->children = first_child;
        return ret;
 }
index 355c492a901383d0c28891ae51010ec65f665a01..28d5ab3359e791392f7ec20f7f947f9047be0feb 100644 (file)
@@ -80,11 +80,9 @@ struct ads_entry {
        /* Stream name (UTF-8) */
        char *stream_name_utf8;
 
-       /* Doubly linked list of streams that share the same lookup table entry */
-       struct stream_list_head lte_group_list;
-
-       /* Containing inode */
-       struct inode *inode;
+#ifdef WITH_FUSE
+       u32 stream_id;
+#endif
 };
 
 /* Returns the total length of a WIM alternate data stream entry on-disk,
@@ -115,6 +113,8 @@ static inline bool ads_entries_have_same_name(const struct ads_entry *entry_1,
                      entry_1->stream_name_len) == 0;
 }
 
+struct wimlib_fd;
+
 struct inode {
        /* Timestamps for the inode.  The timestamps are the number of
         * 100-nanosecond intervals that have elapsed since 12:00 A.M., January
@@ -169,15 +169,24 @@ struct inode {
        u64 ino;
 
        struct list_head dentry_list;
-       union {
-               struct stream_list_head lte_group_list;
-               struct hlist_node hlist;
-       };
+       struct hlist_node hlist;
        char *extracted_file;
+
+       struct dentry *children;
+
+#ifdef WITH_FUSE
+       u16 num_opened_fds;
+       u16 num_allocated_fds;
+       struct wimlib_fd **fds;
+       u32 next_stream_id;
+#endif
 };
 
 #define inode_for_each_dentry(dentry, inode) \
-               list_for_each_entry(dentry, &inode->dentry_list, inode_dentry_list)
+               list_for_each_entry((dentry), &(inode)->dentry_list, inode_dentry_list)
+
+#define inode_add_dentry(dentry, inode) \
+               list_add(&(dentry)->inode_dentry_list, &(inode)->dentry_list)
 
 /* 
  * In-memory structure for a WIM directory entry (dentry).  There is a directory
@@ -203,6 +212,9 @@ struct inode {
  * conflicting fields to split up the hard link groups.
  */
 struct dentry {
+       /* The inode for this dentry */
+       struct inode *inode;
+
        /* The parent of this directory entry. */
        struct dentry *parent;
 
@@ -210,11 +222,6 @@ struct dentry {
        struct dentry *next;
        struct dentry *prev;
 
-       /* Pointer to a child of this directory entry. */
-       struct dentry *children;
-
-       struct inode *inode;
-
        /* 
         * Size of directory entry on disk, in bytes.  Typical size is around
         * 104 to 120 bytes.
@@ -276,15 +283,9 @@ struct dentry {
        char *full_path_utf8;
        u32   full_path_utf8_len;
 
-       union {
-               /* Number of references to the dentry tree itself, as in multiple
-                * WIMStructs */
-               u32 refcnt;
-
-               /* Number of times this dentry has been opened (only for
-                * directories!) */
-               u32 num_times_opened;
-       };
+       /* Number of references to the dentry tree itself, as in multiple
+        * WIMStructs */
+       u32 refcnt;
 
        /* List of dentries in the hard link set */
        struct list_head inode_dentry_list;
@@ -292,13 +293,12 @@ struct dentry {
 };
 
 
-extern struct ads_entry *dentry_get_ads_entry(struct dentry *dentry,
-                                             const char *stream_name);
-
-extern struct ads_entry *dentry_add_ads(struct dentry *dentry,
-                                       const char *stream_name);
+extern struct ads_entry *inode_get_ads_entry(struct inode *inode,
+                                            const char *stream_name,
+                                            u16 *idx_ret);
 
-extern void dentry_remove_ads(struct dentry *dentry, struct ads_entry *entry);
+extern struct ads_entry *inode_add_ads(struct inode *dentry,
+                                      const char *stream_name);
 
 extern const char *path_stream_name(const char *path);
 
@@ -330,6 +330,7 @@ extern int print_dentry(struct dentry *dentry, void *lookup_table);
 extern int print_dentry_full_path(struct dentry *entry, void *ignore);
 
 extern struct dentry *get_dentry(struct WIMStruct *w, const char *path);
+extern struct inode *wim_pathname_to_inode(WIMStruct *w, const char *path);
 extern struct dentry *get_parent_dentry(struct WIMStruct *w, const char *path);
 extern struct dentry *get_dentry_child_with_name(const struct dentry *dentry, 
                                                        const char *name);
@@ -340,12 +341,13 @@ extern struct inode *new_inode();
 extern struct inode *new_timeless_inode();
 extern struct dentry *new_dentry_with_inode(const char *name);
 
-extern void dentry_free_ads_entries(struct dentry *dentry);
-extern void free_dentry(struct dentry *dentry);
+extern void free_ads_entry(struct ads_entry *entry);
+extern void inode_free_ads_entries(struct inode *inode);
+extern struct inode *free_dentry(struct dentry *dentry);
 extern void free_inode(struct inode *inode);
-extern void put_inode(struct inode *inode);
-extern void put_dentry(struct dentry *dentry);
+extern struct inode *put_inode(struct inode *inode);
 extern struct dentry *clone_dentry(struct dentry *old);
+extern void put_dentry(struct dentry *dentry);
 extern void free_dentry_tree(struct dentry *root,
                             struct lookup_table *lookup_table);
 extern int increment_dentry_refcnt(struct dentry *dentry, void *ignore);
@@ -369,16 +371,13 @@ extern int read_dentry_tree(const u8 metadata_resource[],
 extern u8 *write_dentry_tree(const struct dentry *tree, u8 *p);
 
 
-/* Return the number of dentries in the hard link group */
-static inline size_t dentry_link_group_size(const struct dentry *dentry)
+static inline struct dentry *inode_first_dentry(struct inode *inode)
 {
-       const struct list_head *cur;
-       size_t size = 0;
-       list_for_each(cur, &dentry->inode_dentry_list)
-               size++;
-       return size;
+       return container_of(inode->dentry_list.next, struct dentry,
+                           inode_dentry_list);
 }
 
+
 static inline bool dentry_is_root(const struct dentry *dentry)
 {
        return dentry->parent == dentry;
@@ -386,7 +385,7 @@ static inline bool dentry_is_root(const struct dentry *dentry)
 
 static inline bool dentry_is_first_sibling(const struct dentry *dentry)
 {
-       return dentry_is_root(dentry) || dentry->parent->children == dentry;
+       return dentry_is_root(dentry) || dentry->parent->inode->children == dentry;
 }
 
 static inline bool dentry_is_only_child(const struct dentry *dentry)
@@ -419,14 +418,19 @@ static inline bool dentry_is_symlink(const struct dentry *dentry)
        return inode_is_symlink(dentry->inode);
 }
 
+static inline bool inode_is_regular_file(const struct inode *inode)
+{
+       return !inode_is_directory(inode) && !inode_is_symlink(inode);
+}
+
 static inline bool dentry_is_regular_file(const struct dentry *dentry)
 {
-       return !dentry_is_directory(dentry) && !dentry_is_symlink(dentry);
+       return inode_is_regular_file(dentry->inode);
 }
 
 static inline bool dentry_is_empty_directory(const struct dentry *dentry)
 {
-       return dentry_is_directory(dentry) && dentry->children == NULL;
+       return dentry_is_directory(dentry) && dentry->inode->children == NULL;
 }
 
 #endif
index 26c9e05d5e50b343c10944c74175ad048316e387..7aedf8e1629cb6ca44a5cc0bc896512d143b69ed 100644 (file)
@@ -75,12 +75,6 @@ static size_t inode_link_count(const struct inode *inode)
        return size;
 }
 
-static struct dentry *inode_first_dentry(struct inode *inode)
-{
-       return container_of(inode->dentry_list.next, struct dentry,
-                           inode_dentry_list);
-}
-
 /* 
  * Insert a dentry into the inode table based on its inode
  * ID.
index a1639e3627bb8641b9a87394b8c1bf13f3e8afe7..bf84efa503d96e7364725d423808d1986484c2be 100644 (file)
@@ -35,7 +35,6 @@ static int copy_lte_to_table(struct lookup_table_entry *lte, void *table)
        if (!copy)
                return WIMLIB_ERR_NOMEM;
        memcpy(copy, lte, sizeof(struct lookup_table_entry));
-       INIT_LIST_HEAD(&copy->lte_group_list);
        lookup_table_insert(table, copy);
        return 0;
 }
index b5717c489093e89e079cf9fadac6848e72d3c6b9..7c0a74430b1a875041f7a8f3a9833e996f3e81e0 100644 (file)
@@ -39,21 +39,6 @@ struct hlist_node {
        struct hlist_node *next, **pprev;
 };
 
-/* 
- * Structure used to create a linked list of streams that share the same lookup
- * table entry.  This structure may be embedded in either a inode (for the
- * un-named data stream) or an ads_entry (for an alternate data stream).  The
- * @type field indicates which of these structures the stream_list_head is
- * embedded in.
- */
-struct stream_list_head {
-       struct list_head list;
-       enum {
-               STREAM_TYPE_NORMAL = 0,
-               STREAM_TYPE_ADS,
-       } type;
-};
-
 /*
  * Simple doubly linked list implementation.
  *
index d4a1ba4229ddb30b4aa10b7e540cfb003fb19ac6..776ad94b41a72769768e161971208ffd903435ac 100644 (file)
 #include "io.h"
 #include <errno.h>
 
+#ifdef WITH_FUSE
+#include <unistd.h>
+#endif
+
 struct lookup_table *new_lookup_table(size_t capacity)
 {
        struct lookup_table *table;
@@ -66,7 +70,6 @@ struct lookup_table_entry *new_lookup_table_entry()
 
        lte->part_number  = 1;
        lte->refcnt       = 1;
-       INIT_LIST_HEAD(&lte->lte_group_list);
        return lte;
 }
 
@@ -142,7 +145,14 @@ void lookup_table_insert(struct lookup_table *table,
        table->num_entries++;
 }
 
-
+static void finalize_lte(struct lookup_table_entry *lte)
+{
+       #ifdef WITH_FUSE
+       if (lte->resource_location == RESOURCE_IN_STAGING_FILE)
+               unlink(lte->staging_file_name);
+       #endif
+       free_lookup_table_entry(lte);
+}
 
 /* Decrements the reference count for the lookup table entry @lte.  If its
  * reference count reaches 0, it is unlinked from the lookup table.  If,
@@ -159,7 +169,7 @@ lte_decrement_refcnt(struct lookup_table_entry *lte, struct lookup_table *table)
                        if (lte->num_opened_fds == 0)
                #endif
                        {
-                               free_lookup_table_entry(lte);
+                               finalize_lte(lte);
                                lte = NULL;
                        }
                }
@@ -167,6 +177,23 @@ lte_decrement_refcnt(struct lookup_table_entry *lte, struct lookup_table *table)
        return lte;
 }
 
+#ifdef WITH_FUSE
+struct lookup_table_entry *
+lte_decrement_num_opened_fds(struct lookup_table_entry *lte,
+                            struct lookup_table *table)
+{
+       if (lte) {
+               wimlib_assert(lte->num_opened_fds);
+               if (--lte->num_opened_fds == 0 && lte->refcnt == 0) {
+                       lookup_table_unlink(table, lte);
+                       finalize_lte(lte);
+                       lte = NULL;
+               }
+       }
+       return lte;
+}
+#endif
+
 /* 
  * Calls a function on all the entries in the lookup table.  Stop early and
  * return nonzero if any call to the function returns nonzero.
@@ -429,11 +456,11 @@ int lookup_resource(WIMStruct *w, const char *path,
                    int lookup_flags,
                    struct dentry **dentry_ret,
                    struct lookup_table_entry **lte_ret,
-                   unsigned *stream_idx_ret)
+                   u16 *stream_idx_ret)
 {
        struct dentry *dentry;
        struct lookup_table_entry *lte;
-       unsigned stream_idx;
+       u16 stream_idx;
        const char *stream_name = NULL;
        struct inode *inode;
        char *p = NULL;
@@ -460,21 +487,21 @@ int lookup_resource(WIMStruct *w, const char *path,
              && inode_is_directory(inode))
                return -EISDIR;
 
-       lte = inode->lte;
-       stream_idx = 0;
        if (stream_name) {
-               size_t stream_name_len = strlen(stream_name);
-               for (u16 i = 0; i < inode->num_ads; i++) {
-                       if (ads_entry_has_name(inode->ads_entries[i],
-                                              stream_name,
-                                              stream_name_len))
-                       {
-                               stream_idx = i + 1;
-                               lte = inode->ads_entries[i]->lte;
-                               goto out;
-                       }
+               struct 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;
                }
-               return -ENOENT;
+       } else {
+               lte = inode->lte;
+               stream_idx = 0;
        }
 out:
        if (dentry_ret)
@@ -492,12 +519,7 @@ static int inode_resolve_ltes(struct inode *inode, struct lookup_table *table)
 
        /* Resolve the default file stream */
        lte = __lookup_resource(table, inode->hash);
-       if (lte)
-               list_add(&inode->lte_group_list.list, &lte->lte_group_list);
-       else
-               INIT_LIST_HEAD(&inode->lte_group_list.list);
        inode->lte = lte;
-       inode->lte_group_list.type = STREAM_TYPE_NORMAL;
        inode->resolved = true;
 
        /* Resolve the alternate data streams */
@@ -505,13 +527,7 @@ static int inode_resolve_ltes(struct inode *inode, struct lookup_table *table)
                struct ads_entry *cur_entry = inode->ads_entries[i];
 
                lte = __lookup_resource(table, cur_entry->hash);
-               if (lte)
-                       list_add(&cur_entry->lte_group_list.list,
-                                &lte->lte_group_list);
-               else
-                       INIT_LIST_HEAD(&cur_entry->lte_group_list.list);
                cur_entry->lte = lte;
-               cur_entry->lte_group_list.type = STREAM_TYPE_ADS;
        }
        return 0;
 }
index f0d454a18e22d6f9bb75b3952e0a58f61355110d..d99f4b750ae5b1687daa204b27a55f3a07ce169e 100644 (file)
@@ -154,16 +154,7 @@ struct lookup_table_entry {
        #endif
        };
 #ifdef WITH_FUSE
-       /* File descriptors table for this data stream.  This is used if the WIM
-        * is mounted.  Basically, each time a file is open()ed, a new file
-        * descriptor is added here, and each time a file is close()ed, the file
-        * descriptor is gotten rid of.  If the stream is opened for writing, it
-        * will be extracted to the staging directory and there will be an
-        * actual native file descriptor associated with each "wimlib file
-        * descriptor". */
        u16 num_opened_fds;
-       u16 num_allocated_fds;
-       struct wimlib_fd **fds;
 #endif
 
        /* When a WIM file is written, out_refcnt starts at 0 and is incremented
@@ -189,13 +180,6 @@ struct lookup_table_entry {
         * */
        char *extracted_file;
 
-       /* Circular linked list of streams that share the same lookup table
-        * entry.
-        * 
-        * This list of streams may include streams from different hard link
-        * sets that happen to be the same.  */
-       struct list_head lte_group_list;
-
        /* List of lookup table entries that correspond to streams that have
         * been extracted to the staging directory when modifying a read-write
         * mounted WIM. */
@@ -244,6 +228,12 @@ static inline void lookup_table_unlink(struct lookup_table *table,
 extern struct lookup_table_entry *
 lookup_table_decrement_refcnt(struct lookup_table* table, const u8 hash[]);
 
+#ifdef WITH_FUSE
+extern struct lookup_table_entry *
+lte_decrement_num_opened_fds(struct lookup_table_entry *lte,
+                            struct lookup_table *table);
+#endif
+
 extern struct lookup_table_entry *
 lte_decrement_refcnt(struct lookup_table_entry *lte,
                     struct lookup_table *table);
@@ -261,7 +251,7 @@ __lookup_resource(const struct lookup_table *table, const u8 hash[]);
 extern int lookup_resource(WIMStruct *w, const char *path,
                           int lookup_flags, struct dentry **dentry_ret,
                           struct lookup_table_entry **lte_ret,
-                          unsigned *stream_idx_ret);
+                          u16 *stream_idx_ret);
 
 extern int zero_out_refcnts(struct lookup_table_entry *entry, void *ignore);
 
index 530fdf689767ed32dabe817997065ef0be9d36ab..b67f40c6faacaec36cbb8e969fabcce36899bb21 100644 (file)
 
 /* File descriptor to a file open on the WIM filesystem. */
 struct wimlib_fd {
-       /* Index of this file descriptor in the lookup table entry's table of
-        * file descriptors */
-       u16 idx;
-
-       /* Index of the stream we've opened from a dentry */
-       u16 stream_idx;
-
-       /* Native file descriptor to the staging file */
-       int staging_fd;
-
-       /* Pointer to lookup table entry that has the table of file descriptors
-        * containing this file descriptor */
+       struct inode *inode;
        struct lookup_table_entry *lte;
-
-       union {
-               struct dentry *dentry;
-               struct ads_entry *ads_entry;
-       };
-
-       /* Hard link group ID of the dentry containing the stream that was
-        * opened to produce this file descriptor.
-        *
-        * This is always valid even if @dentry is NULL.  It can be used to
-        * identify file descriptors corresponding to the same hard-linked
-        * stream, even if the specific dentry under which the stream was opened
-        * has been unlinked. */
-       u64 link_group_id;
+       int staging_fd;
+       u16 idx;
+       u32 stream_id;
 };
 
-static inline struct dentry *fd_dentry(const struct wimlib_fd *fd)
-{
-       if (fd->stream_idx == 0)
-               return fd->dentry;
-       else
-               return fd->ads_entry ? fd->ads_entry->dentry : NULL;
-}
-
 /* The WIMStruct for the mounted WIM. */
 static WIMStruct *w;
 
@@ -110,9 +80,8 @@ static int mount_flags;
 /* Name of the directory on which the WIM file is mounted. */
 static const char *mount_dir;
 
-/* Next hard link group ID to be assigned.  These are also used as the inode
- * numbers. */
-static u64 next_link_group_id;
+/* Next inode number to be assigned. */
+static u64 next_ino;
 
 /* List of lookup table entries in the staging directory */
 static LIST_HEAD(staging_list);
@@ -131,53 +100,73 @@ static inline int flags_writable(int open_flags)
        return open_flags & (O_RDWR | O_WRONLY);
 }
 
-/* 
- * Allocate a file descriptor for a lookup table entry
- */
-static int alloc_wimlib_fd(struct lookup_table_entry *lte,
+static int alloc_wimlib_fd(struct inode *inode,
+                          u32 stream_id,
+                          struct lookup_table_entry *lte,
                           struct wimlib_fd **fd_ret)
 {
        static const u16 fds_per_alloc = 8;
        static const u16 max_fds = 0xffff;
 
-       if (lte->num_opened_fds == lte->num_allocated_fds) {
+       if (inode->num_opened_fds == inode->num_allocated_fds) {
                struct wimlib_fd **fds;
                u16 num_new_fds;
 
-               if (lte->num_allocated_fds == max_fds)
+               if (inode->num_allocated_fds == max_fds)
                        return -EMFILE;
-               num_new_fds = min(fds_per_alloc, max_fds - lte->num_allocated_fds);
+               num_new_fds = min(fds_per_alloc, max_fds - inode->num_allocated_fds);
                
-               fds = REALLOC(lte->fds, (lte->num_allocated_fds + num_new_fds) *
-                              sizeof(lte->fds[0]));
+               fds = REALLOC(inode->fds, (inode->num_allocated_fds + num_new_fds) *
+                              sizeof(inode->fds[0]));
                if (!fds)
                        return -ENOMEM;
-               memset(&fds[lte->num_allocated_fds], 0,
+               memset(&fds[inode->num_allocated_fds], 0,
                       num_new_fds * sizeof(fds[0]));
-               lte->fds = fds;
-               lte->num_allocated_fds += num_new_fds;
+               inode->fds = fds;
+               inode->num_allocated_fds += num_new_fds;
        }
        for (u16 i = 0; ; i++) {
-               if (!lte->fds[i]) {
+               if (!inode->fds[i]) {
                        struct wimlib_fd *fd = CALLOC(1, sizeof(*fd));
                        if (!fd)
                                return -ENOMEM;
+                       fd->inode      = inode;
+                       fd->lte        = lte;
                        fd->staging_fd = -1;
                        fd->idx        = i;
-                       fd->lte        = lte;
-                       lte->fds[i]    = fd;
-                       lte->num_opened_fds++;
+                       fd->stream_id  = stream_id;
                        *fd_ret        = fd;
+                       inode->fds[i]  = fd;
+                       inode->num_opened_fds++;
+                       lte->num_opened_fds++;
                        return 0;
                }
        }
 }
 
-static int close_wimlib_fd(struct wimlib_fd *fd)
+static void inode_put_fd(struct inode *inode, struct wimlib_fd *fd)
 {
-       struct lookup_table_entry *lte = fd->lte;
+       wimlib_assert(fd);
+       wimlib_assert(inode);
+       wimlib_assert(fd->inode == inode);
+       wimlib_assert(inode->num_opened_fds);
+       wimlib_assert(fd->idx < inode->num_opened_fds);
+       wimlib_assert(inode->fds[fd->idx] == fd);
+
+       inode->fds[fd->idx] = NULL;
+       FREE(fd);
+       if (--inode->num_opened_fds == 0 && inode->link_count == 0)
+               free_inode(inode);
+}
+
+static int lte_put_fd(struct lookup_table_entry *lte, struct wimlib_fd *fd)
+{
+       wimlib_assert(fd);
+       wimlib_assert(fd->lte == lte);
+
+       if (!lte) /* Empty stream with no lookup table entry */
+               return 0;
 
-       wimlib_assert(lte);
        wimlib_assert(lte->num_opened_fds);
 
        if (lte->resource_location == RESOURCE_IN_STAGING_FILE) {
@@ -186,121 +175,92 @@ static int close_wimlib_fd(struct wimlib_fd *fd)
                if (close(fd->staging_fd) != 0)
                        return -errno;
        }
-       if (--lte->num_opened_fds == 0 && lte->refcnt == 0) {
-               if (lte->resource_location == RESOURCE_IN_STAGING_FILE) {
-                       wimlib_assert(lte->staging_file_name);
-                       unlink(lte->staging_file_name);
-               }
-               free_lookup_table_entry(lte);
-       }
-       wimlib_assert(lte->fds[fd->idx] == fd);
-       lte->fds[fd->idx] = NULL;
-       FREE(fd);
+       lte_decrement_num_opened_fds(lte, w->lookup_table);
+       return 0;
+}
+
+static int close_wimlib_fd(struct wimlib_fd *fd)
+{
+       int ret;
+       ret = lte_put_fd(fd->lte, fd);
+       if (ret != 0)
+               return ret;
+
+       inode_put_fd(fd->inode, fd);
        return 0;
 }
 
-/* Remove a dentry and all its alternate file streams */
+/* Remove a dentry; i.e. remove a reference to the inode.
+ *
+ * If there are no remaining references to the inode either through detnries or
+ * open file descriptors, the inode is freed.
+ *
+ * All lookup table entries referenced by the inode have their reference count
+ * decremented.  If a lookup table entry has no open file descriptors and no
+ * references remaining, it is freed, and the staging file is unlinked.
+ *
+ * Otherwise, the inode is not removed, but the dentry is unlinked and freed. */
 static void remove_dentry(struct dentry *dentry,
                          struct lookup_table *lookup_table)
 {
-       wimlib_assert(dentry);
-       wimlib_assert(dentry->resolved);
+       struct inode *inode = dentry->inode;
+       struct lookup_table_entry *lte;
+       unsigned i;
 
-       if (dentry->ads_entries_status == ADS_ENTRIES_OWNER) {
-               struct dentry *new_owner;
-               list_for_each_entry(new_owner, &dentry->link_group_list,
-                                   link_group_list)
-               {
-                       if (new_owner->ads_entries_status == ADS_ENTRIES_USER) {
-                               new_owner->ads_entries_status = ADS_ENTRIES_OWNER;
-                               for (u16 i = 0; i < dentry->num_ads; i++)
-                                       dentry->ads_entries[i]->dentry = new_owner;
-                               break;
-                       }
-               }
-               dentry->ads_entries_status = ADS_ENTRIES_USER;
-       }
-       list_del(&dentry->link_group_list);
-       list_del(&dentry->lte_group_list);
-
-       u16 i = 0;
-       struct lookup_table_entry *lte = dentry->lte;
-       while (1) {
-               lte = lte_decrement_refcnt(lte, lookup_table);
-               if (lte && lte->num_opened_fds)
-                       for (u16 j = 0; j < lte->num_allocated_fds; j++)
-                               if (lte->fds[j] &&
-                                   lte->fds[j]->link_group_id == dentry->link_group_id
-                                   && ((lte->fds[j].stream_idx == 0 &&
-                                        lte->fds[j].dentry == dentry) ||
-                                        (lte->fds[j].stream_idx &&
-                                         lte->fds[j].ads_entry  &&
-                                         lte->fds[j].ads_entry->dentry == dentry)))
-                                               lte->fds[j].dentry = NULL;
-               if (i == dentry->num_ads)
-                       break;
-               lte = dentry->ads_entries[i].lte;
-               i++;
+       for (i = 0; <= inode->num_ads; i++) {
+               lte = inode_stream_lte_resolved(inode, i);
+               if (lte)
+                       lte_decrement_refcnt(lte, lookup_table);
        }
-
        unlink_dentry(dentry);
-       free_dentry(dentry);
+       put_dentry(dentry);
 }
 
-static void remove_ads(struct dentry *dentry,
-                      struct ads_entry *ads_entry,
-                      struct lookup_table *lookup_table)
+/* Remove an alternate data stream from the inode  */
+static void inode_remove_ads(struct inode *inode, u16 idx,
+                            struct lookup_table *lookup_table)
 {
+       struct ads_entry *ads_entry;
        struct lookup_table_entry *lte;
 
-       wimlib_assert(dentry->resolved);
-       wimlib_assert(ads_entry - dentry->ads_entries < dentry->num_ads);
+       ads_entry = inode->ads_entries[idx];
+
+       wimlib_assert(ads_entry);
+
+       lte = ads_entry->lte;
 
-       lte = lte_decrement_refcnt(ads_entry->lte, lookup_table);
        if (lte)
-               for (u16 i = 0; i < lte->num_allocated_fds; i++)
-                       if (lte->fds[i] && lte->fds[i]->dentry == dentry)
-                               lte->fds[i]->dentry = NULL;
-
-       /* Fix up file descriptor stream indexes */
-       for (u16 i = ads_entry - dentry->ads_entries + 1; i < dentry->num_ads; i++) {
-               struct lookup_table_entry *lte = ads_entry[i].lte;
-               if (lte) {
-                       for (u16 open_fd_idx = 0, fd_idx = 0;
-                                    open_fd_idx < lte->num_opened_fds; fd_idx++)
-                       {
-                               if (lte->fds[fd_idx]) {
-                                       open_fd_idx++;
-                                       if (lte->fds[fd_idx]->dentry == dentry
-                                           && lte->fds[fd_idx]->stream_idx > idx)
-                                               lte->fds[fd_idx]->stream_id
-                               }
-                       }
-               }
-       }
-       dentry_remove_ads(dentry, ads_entry);
+               lte_decrement_refcnt(lte, lookup_table);
+
+       free_ads_entry(ads_entry);
+
+       wimlib_assert(inode->num_ads);
+       memcpy(&inode->ads_entries[idx],
+              &inode->ads_entries[idx + 1],
+              (inode->num_ads - idx - 1) * sizeof(inode->ads_entries[0]));
+       inode->num_ads--;
 }
 
-/* Transfers file attributes from a struct dentry to a `stat' buffer. */
-int dentry_to_stbuf(const struct dentry *dentry, struct stat *stbuf)
+/* Transfers file attributes from a struct inode to a `stat' buffer. 
+ *
+ * The lookup table entry tells us which stream in the inode we are statting.
+ * For a named data stream, everything returned is the same as the unnamed data
+ * stream except possibly the size and block count. */
+int inode_to_stbuf(const struct inode *inode, struct lookup_table_entry *lte,
+                  struct stat *stbuf)
 {
-       struct lookup_table_entry *lte;
-
-       if (dentry_is_symlink(dentry))
+       if (inode_is_symlink(inode))
                stbuf->st_mode = S_IFLNK | 0777;
-       else if (dentry_is_directory(dentry))
+       else if (inode_is_directory(inode))
                stbuf->st_mode = S_IFDIR | 0755;
        else
                stbuf->st_mode = S_IFREG | 0644;
 
-       stbuf->st_ino = (ino_t)dentry->link_group_id;
-
-       stbuf->st_nlink = dentry_link_group_size(dentry);
+       stbuf->st_ino   = (ino_t)inode->ino;
+       stbuf->st_nlink = inode->link_count;
        stbuf->st_uid   = getuid();
        stbuf->st_gid   = getgid();
 
-       /* Use the size of the unnamed (default) file stream. */
-       lte = dentry_unnamed_lte_resolved(dentry);
        if (lte) {
                if (lte->resource_location == RESOURCE_IN_STAGING_FILE) {
                        wimlib_assert(mount_flags & WIMLIB_MOUNT_FLAG_READWRITE);
@@ -319,9 +279,9 @@ int dentry_to_stbuf(const struct dentry *dentry, struct stat *stbuf)
                stbuf->st_size = 0;
        }
 
-       stbuf->st_atime   = wim_timestamp_to_unix(dentry->last_access_time);
-       stbuf->st_mtime   = wim_timestamp_to_unix(dentry->last_write_time);
-       stbuf->st_ctime   = wim_timestamp_to_unix(dentry->creation_time);
+       stbuf->st_atime   = wim_timestamp_to_unix(inode->last_access_time);
+       stbuf->st_mtime   = wim_timestamp_to_unix(inode->last_write_time);
+       stbuf->st_ctime   = wim_timestamp_to_unix(inode->creation_time);
        stbuf->st_blocks  = (stbuf->st_size + 511) / 512;
        return 0;
 }
@@ -381,114 +341,10 @@ static int create_staging_file(char **name_ret, int open_flags)
        return fd;
 }
 
-/* 
- * Removes open file descriptors from a lookup table entry @old_lte where the
- * removed file descriptors have opened the corresponding file resource in the
- * context of a dentry in the hard link group @link_group and a stream at index
- * @stream_idx. These file descriptors are extracted and placed in a new lookup
- * table entry, which is returned.
- *
- * Note we need to examine the link_group_id of each file descriptor and not
- * dentry->link_group_id of each file descriptor, since dentry may be NULL in
- * the case of an un-linked dentry.
- */
-static struct lookup_table_entry *
-lte_extract_fds(struct lookup_table_entry *old_lte, u64 link_group_id,
-               u16 stream_idx)
-{
-       u16 num_transferred_fds;
-       struct lookup_table_entry *new_lte;
-
-       new_lte = new_lookup_table_entry();
-       if (!new_lte)
-               return NULL;
-
-       num_transferred_fds = 0;
-       for (u16 i = 0; i < old_lte->num_allocated_fds; i++)
-               if (old_lte->fds[i]
-                   && old_lte->fds[i]->link_group_id == link_group_id
-                   && old_lte->fds[i]->stream_idx == stream_idx)
-                       num_transferred_fds++;
-       DEBUG("Transferring %u file descriptors",
-             num_transferred_fds);
-       new_lte->fds = MALLOC(num_transferred_fds * sizeof(new_lte->fds[0]));
-       if (!new_lte->fds) {
-               FREE(new_lte);
-               return NULL;
-       }
-       for (u16 i = 0, j = 0; ; i++) {
-               if (old_lte->fds[i]
-                   && old_lte->fds[i]->link_group_id == link_group_id
-                   && old_lte->fds[i]->stream_idx == stream_idx) {
-                       struct wimlib_fd *fd = old_lte->fds[i];
-                       old_lte->fds[i] = NULL;
-                       fd->lte = new_lte;
-                       fd->idx = j;
-                       new_lte->fds[j] = fd;
-                       if (++j == num_transferred_fds)
-                               break;
-               }
-       }
-       DEBUG("old_lte: %u fds open; new_lte: %u fds open",
-             old_lte->num_opened_fds, new_lte->num_opened_fds);
-       old_lte->num_opened_fds -= num_transferred_fds;
-       new_lte->num_opened_fds = num_transferred_fds;
-       new_lte->num_allocated_fds = num_transferred_fds;
-       return new_lte;
-}
-
-/* 
- * Transfers an alternate data stream entry to a new lookup table entry
- */
-static void lte_transfer_ads_entry(struct lookup_table_entry *new_lte,
-                                  struct ads_entry *ads_entry)
-{
-       list_del(&ads_entry->lte_group_list.list);
-       list_add(&ads_entry->lte_group_list.list, &new_lte->lte_group_list);
-       ads_entry->lte = new_lte;
-}
-
-/* 
- * Transfers a dentry to a new lookup table entry
- */
-static void lte_transfer_dentry(struct lookup_table_entry *new_lte,
-                               struct dentry *dentry)
-{
-       wimlib_assert(dentry->lte_group_list.list.next);
-       wimlib_assert(new_lte->lte_group_list.next);
-       list_del(&dentry->lte_group_list.list);
-       list_add(&dentry->lte_group_list.list, &new_lte->lte_group_list);
-       dentry->lte = new_lte;
-}
-
-static void lte_transfer_stream_entries(struct lookup_table_entry *new_lte,
-                                       struct dentry *dentry,
-                                       unsigned stream_idx)
-{
-       INIT_LIST_HEAD(&new_lte->lte_group_list);
-       if (stream_idx == 0) {
-               struct list_head *pos = &dentry->link_group_list;
-               do {
-                       struct dentry *d;
-                       d = container_of(pos, struct dentry, link_group_list);
-                       wimlib_assert(d->link_group_id == dentry->link_group_id);
-                       lte_transfer_dentry(new_lte, d);
-                       pos = pos->next;
-               } while (pos != &dentry->link_group_list);
-       } else {
-               struct ads_entry *ads_entry;
-               wimlib_assert(stream_idx <= dentry->num_ads);
-               ads_entry = &dentry->ads_entries[stream_idx - 1];
-               lte_transfer_ads_entry(new_lte, ads_entry);
-       }
-}
-
 /* 
  * Extract a WIM resource to the staging directory.
  *
- * @dentry, @stream_idx:  The stream on whose behalf we are modifying the lookup
- * table entry (these may be more streams than this that reference the lookup
- * table entry)
+ * @inode:  Inode that contains the stream we are extracting
  *
  * @lte: Pointer to pointer to the lookup table entry for the stream we need to
  * extract, or NULL if there was no lookup table entry present for the stream
@@ -496,8 +352,8 @@ static void lte_transfer_stream_entries(struct lookup_table_entry *new_lte,
  * @size:  Number of bytes of the stream we want to extract (this supports the
  * wimfs_truncate() function).
  */
-static int extract_resource_to_staging_dir(struct dentry *dentry,
-                                          unsigned stream_idx,
+static int extract_resource_to_staging_dir(struct inode *inode,
+                                          u32 stream_id,
                                           struct lookup_table_entry **lte,
                                           off_t size)
 {
@@ -505,25 +361,6 @@ static int extract_resource_to_staging_dir(struct dentry *dentry,
        int ret;
        int fd;
        struct lookup_table_entry *old_lte, *new_lte;
-       size_t link_group_size;
-
-       /*
-        * We need to:
-        * - Create a staging file for the WIM resource
-        * - Extract the resource to it
-        * - Create a new lte for the file resource
-        * - Transfer fds from the old lte to the new lte, but only if they share the
-        *   same hard link group as this dentry.  If there is no old lte, then this
-        *   step does not need to be done
-        * - Transfer stream entries from the old lte's list to the new lte's list.  If
-        *   there is no old lte, we instead transfer entries for the hard link group.
-        *
-        *   Note: *lte is permitted to be NULL, in which case there is no old
-        *   lookup table entry.
-        */
-
-       DEBUG("Extracting dentry `%s' stream %u to staging directory",
-             dentry->full_path_utf8, stream_idx);
 
        old_lte = *lte;
        fd = create_staging_file(&staging_file_name, O_WRONLY);
@@ -543,11 +380,9 @@ static int extract_resource_to_staging_dir(struct dentry *dentry,
                goto out_delete_staging_file;
        }
 
-       link_group_size = dentry_link_group_size(dentry);
-
        if (old_lte) {
                wimlib_assert(old_lte->resource_location == RESOURCE_IN_WIM);
-               if (link_group_size == old_lte->refcnt) {
+               if (inode->link_count == old_lte->refcnt) {
                        /* The reference count of the existing lookup table
                         * entry is the same as the size of the hard link group
                         * associated with the dentry; therefore, ALL the
@@ -560,44 +395,40 @@ static int extract_resource_to_staging_dir(struct dentry *dentry,
                        new_lte = old_lte;
                } else {
                        DEBUG("Splitting lookup table entry "
-                             "(link_group_size = %zu, lte refcnt = %u)",
-                             link_group_size, old_lte->refcnt);
-                       /* The stream we are going to change does not account
-                        * for all the references to the lookup table entry.
-                        * The other references to the lookup table entry may be
-                        * from different hard link groups or from other streams
-                        * in the same hard link group.
-                        */
+                             "(inode->link_count = %zu, lte refcnt = %u)",
+                             inode->link_count, old_lte->refcnt);
 
-                       wimlib_assert(old_lte->refcnt > link_group_size);
+                       wimlib_assert(old_lte->refcnt > inode->link_count);
 
-                       /* First, find the old lookup table entry's file
-                        * descriptors that were opened in the context of the
-                        * stream we're going to change, and allocate a new
-                        * lookup table entry containing those file descriptors.
-                        * */
-                       new_lte = lte_extract_fds(old_lte, link_group_id,
-                                                 stream_idx);
+                       new_lte = new_lookup_table_entry();
                        if (!new_lte) {
                                ret = -ENOMEM;
                                goto out_delete_staging_file;
                        }
-
-                       lte_transfer_stream_entries(new_lte, dentry, stream_idx);
-                       old_lte->refcnt -= link_group_size;
-               } 
+                       old_lte->refcnt -= inode->link_count;
+
+                       for (u16 i = 0, j = 0; j < inode->num_opened_fds; i++) {
+                               if (inode->fds[i]) {
+                                       if (inode->fds[i]->stream_id == stream_id) {
+                                               wimlib_assert(inode->fds[i]->lte == old_lte);
+                                               inode->fds[i]->lte = new_lte;
+                                       }
+                                       j++;
+                               }
+                       }
+               }
        } else {
                /* No old_lte was supplied, so the resource had no lookup table
                 * entry before (it must be an empty resource) */
+               wimlib_assert(inode->num_opened_fds == 0);
                new_lte = new_lookup_table_entry();
                if (!new_lte) {
                        ret = -ENOMEM;
                        goto out_delete_staging_file;
                }
-               lte_transfer_stream_entries(new_lte, dentry, stream_idx);
        }
        new_lte->resource_entry.original_size = size;
-       new_lte->refcnt = link_group_size;
+       new_lte->refcnt = inode->link_count;
        random_hash(new_lte->hash);
        new_lte->staging_file_name = staging_file_name;
        new_lte->resource_location = RESOURCE_IN_STAGING_FILE;
@@ -842,6 +673,7 @@ static int wimfs_access(const char *path, int mask)
        return 0;
 }
 
+#if 0
 /* Closes the staging file descriptor associated with the lookup table entry, if
  * it is opened. */
 static int close_lte_fds(struct lookup_table_entry *lte)
@@ -883,6 +715,7 @@ static void lte_list_change_lte_ptr(struct lookup_table_entry *lte,
                }
        }
 }
+#endif
 
 
 static int update_lte_of_staging_file(struct lookup_table_entry *lte,
@@ -907,10 +740,14 @@ static int update_lte_of_staging_file(struct lookup_table_entry *lte,
        if (duplicate_lte) {
                /* Merge duplicate lookup table entries */
 
+#if 0
                lte_list_change_lte_ptr(lte, duplicate_lte);
+#endif
                duplicate_lte->refcnt += lte->refcnt;
+#if 0
                list_splice(&lte->lte_group_list,
                            &duplicate_lte->lte_group_list);
+#endif
 
                free_lookup_table_entry(lte);
        } else {
@@ -936,12 +773,14 @@ static int rebuild_wim(WIMStruct *w, bool check_integrity)
        struct lookup_table_entry *lte, *tmp;
 
        /* Close all the staging file descriptors. */
+#if 0
        DEBUG("Closing all staging file descriptors.");
        list_for_each_entry(lte, &staging_list, staging_list) {
                ret = close_lte_fds(lte);
                if (ret != 0)
                        return ret;
        }
+#endif
 
        /* Calculate SHA1 checksums for all staging files, and merge unnecessary
         * lookup table entries. */
@@ -1074,14 +913,15 @@ static int wimfs_ftruncate(const char *path, off_t size,
 static int wimfs_getattr(const char *path, struct stat *stbuf)
 {
        struct dentry *dentry;
+       struct lookup_table_entry *lte;
        int ret;
 
        ret = lookup_resource(w, path,
                              get_lookup_flags() | LOOKUP_FLAG_DIRECTORY_OK,
-                             &dentry, NULL, NULL);
+                             &dentry, &lte, NULL);
        if (ret != 0)
                return ret;
-       return dentry_to_stbuf(dentry, stbuf);
+       return inode_to_stbuf(dentry->inode, lte, stbuf);
 }
 
 #ifdef ENABLE_XATTR
@@ -1105,7 +945,7 @@ static int wimfs_getxattr(const char *path, const char *name, char *value,
        dentry = get_dentry(w, path);
        if (!dentry)
                return -ENOENT;
-       ads_entry = dentry_get_ads_entry(dentry, name);
+       ads_entry = inode_get_ads_entry(dentry->inode, name, NULL);
        if (!ads_entry)
                return -ENOATTR;
 
@@ -1128,11 +968,17 @@ static int wimfs_link(const char *to, const char *from)
 {
        struct dentry *to_dentry, *from_dentry, *from_dentry_parent;
        const char *link_name;
+       struct inode *inode;
+       unsigned i;
+       struct lookup_table_entry *lte;
 
        to_dentry = get_dentry(w, to);
        if (!to_dentry)
                return -ENOENT;
-       if (!dentry_is_regular_file(to_dentry))
+
+       inode = to_dentry->inode;
+
+       if (!inode_is_regular_file(inode))
                return -EPERM;
 
        from_dentry_parent = get_parent_dentry(w, from);
@@ -1144,32 +990,20 @@ static int wimfs_link(const char *to, const char *from)
        link_name = path_basename(from);
        if (get_dentry_child_with_name(from_dentry_parent, link_name))
                return -EEXIST;
-       from_dentry = clone_dentry(to_dentry);
+       from_dentry = new_dentry(link_name);
        if (!from_dentry)
                return -ENOMEM;
-       if (change_dentry_name(from_dentry, link_name) != 0) {
-               FREE(from_dentry);
-               return -ENOMEM;
-       }
 
-       /* Add the new dentry to the dentry list for the link group */
-       list_add(&from_dentry->link_group_list, &to_dentry->link_group_list);
 
-       /* Increment reference counts for the unnamed file stream and all
-        * alternate data streams. */
-       if (from_dentry->lte) {
-               list_add(&from_dentry->lte_group_list.list,
-                        &to_dentry->lte_group_list.list);
-               from_dentry->lte->refcnt++;
-       }
-       for (u16 i = 0; i < from_dentry->num_ads; i++) {
-               struct ads_entry *ads_entry = &from_dentry->ads_entries[i];
-               if (ads_entry->lte)
-                       ads_entry->lte->refcnt++;
-       }
+       inode_add_dentry(from_dentry, inode);
+       from_dentry->inode = inode;
+       inode->link_count++;
 
-       /* The ADS entries are owned by another dentry. */
-       from_dentry->ads_entries_status = ADS_ENTRIES_USER;
+       for (i = 0; i <= inode->num_ads; i++) {
+               lte = inode_stream_lte_resolved(inode, i);
+               if (lte)
+                       lte->refcnt++;
+       }
 
        link_dentry(from_dentry, from_dentry_parent);
        return 0;
@@ -1183,6 +1017,7 @@ static int wimfs_listxattr(const char *path, char *list, size_t size)
        char *p = list;
        size_t needed_size;
        unsigned i;
+       struct inode *inode;
        if (!(mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR))
                return -ENOTSUP;
 
@@ -1191,18 +1026,20 @@ static int wimfs_listxattr(const char *path, char *list, size_t size)
        ret = lookup_resource(w, path, get_lookup_flags(), &dentry, NULL, NULL);
        if (ret != 0)
                return ret;
+       inode = dentry->inode;
+
        if (size == 0) {
                needed_size = 0;
-               for (i = 0; i < dentry->num_ads; i++)
-                       needed_size += dentry->ads_entries[i].stream_name_utf8_len + 6;
+               for (i = 0; i < inode->num_ads; i++)
+                       needed_size += inode->ads_entries[i]->stream_name_utf8_len + 6;
                return needed_size;
        } else {
-               for (i = 0; i < dentry->num_ads; i++) {
-                       needed_size = dentry->ads_entries[i].stream_name_utf8_len + 6;
+               for (i = 0; i < inode->num_ads; i++) {
+                       needed_size = inode->ads_entries[i]->stream_name_utf8_len + 6;
                        if (needed_size > size)
                                return -ERANGE;
                        p += sprintf(p, "user.%s",
-                                    dentry->ads_entries[i].stream_name_utf8) + 1;
+                                    inode->ads_entries[i]->stream_name_utf8) + 1;
                        size -= needed_size;
                }
                return p - list;
@@ -1231,10 +1068,10 @@ static int wimfs_mkdir(const char *path, mode_t mode)
        if (get_dentry_child_with_name(parent, basename))
                return -EEXIST;
 
-       newdir = new_dentry(basename);
-       newdir->attributes |= FILE_ATTRIBUTE_DIRECTORY;
-       newdir->resolved = true;
-       newdir->link_group_id = next_link_group_id++;
+       newdir = new_dentry_with_inode(basename);
+       newdir->inode->attributes |= FILE_ATTRIBUTE_DIRECTORY;
+       newdir->inode->resolved = true;
+       newdir->inode->ino = next_ino++;
        link_dentry(newdir, parent);
        return 0;
 }
@@ -1257,9 +1094,9 @@ static int wimfs_mknod(const char *path, mode_t mode, dev_t rdev)
                dentry = get_dentry(w, path);
                if (!dentry || !dentry_is_regular_file(dentry))
                        return -ENOENT;
-               if (dentry_get_ads_entry(dentry, stream_name))
+               if (inode_get_ads_entry(dentry->inode, stream_name, NULL))
                        return -EEXIST;
-               new_entry = dentry_add_ads(dentry, stream_name);
+               new_entry = inode_add_ads(dentry->inode, stream_name);
                if (!new_entry)
                        return -ENOENT;
        } else {
@@ -1280,13 +1117,11 @@ static int wimfs_mknod(const char *path, mode_t mode, dev_t rdev)
                if (get_dentry_child_with_name(parent, path))
                        return -EEXIST;
 
-               dentry = new_dentry(basename);
+               dentry = new_dentry_with_inode(basename);
                if (!dentry)
                        return -ENOMEM;
-               dentry->resolved = true;
-               dentry->link_group_id = next_link_group_id++;
-               dentry->lte_group_list.type = STREAM_TYPE_NORMAL;
-               INIT_LIST_HEAD(&dentry->lte_group_list.list);
+               dentry->inode->resolved = true;
+               dentry->inode->ino = next_ino++;
                link_dentry(dentry, parent);
        }
        return 0;
@@ -1300,52 +1135,41 @@ static int wimfs_open(const char *path, struct fuse_file_info *fi)
        struct lookup_table_entry *lte;
        int ret;
        struct wimlib_fd *fd;
-       unsigned stream_idx;
+       struct inode *inode;
+       u16 stream_idx;
+       u32 stream_id;
 
        ret = lookup_resource(w, path, get_lookup_flags(), &dentry, &lte,
                              &stream_idx);
        if (ret != 0)
                return ret;
 
-       if (!lte) {
-               /* Empty file with no lookup-table entry.  This is fine if it's
-                * a read-only filesystem.  Otherwise we need to create a lookup
-                * table entry so that we can keep track of the file descriptors
-                * (this is important in case someone opens the file for
-                * writing) */
-               if (!(mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)) {
-                       fi->fh = 0;
-                       return 0;
-               }
+       inode = dentry->inode;
 
-               ret = extract_resource_to_staging_dir(dentry, stream_idx,
-                                                     &lte, 0);
+       if (stream_idx == 0)
+               stream_id = 0;
+       else
+               stream_id = inode->ads_entries[stream_idx - 1]->stream_id;
+
+       /* The file resource may be in the staging directory (read-write mounts
+        * only) or in the WIM.  If it's in the staging directory, we need to
+        * open a native file descriptor for the corresponding file.  Otherwise,
+        * we can read the file resource directly from the WIM file if we are
+        * opening it read-only, but we need to extract the resource to the
+        * staging directory if we are opening it writable. */
+       if (flags_writable(fi->flags) &&
+             lte->resource_location != RESOURCE_IN_STAGING_FILE) {
+               ret = extract_resource_to_staging_dir(inode, stream_id,
+                                                     &lte,
+                                                     wim_resource_size(lte));
                if (ret != 0)
                        return ret;
        }
 
-       ret = alloc_wimlib_fd(lte, &fd);
+       ret = alloc_wimlib_fd(inode, stream_id, lte, &fd);
        if (ret != 0)
                return ret;
 
-       fd->dentry = dentry;
-       fd->stream_idx = stream_idx;
-       fd->link_group_id = dentry->link_group_id;
-
-       /* The file resource may be in the staging directory (read-write
-        * mounts only) or in the WIM.  If it's in the staging
-        * directory, we need to open a native file descriptor for the
-        * corresponding file.  Otherwise, we can read the file resource
-        * directly from the WIM file if we are opening it read-only,
-        * but we need to extract the resource to the staging directory
-        * if we are opening it writable. */
-       if (flags_writable(fi->flags) &&
-             lte->resource_location != RESOURCE_IN_STAGING_FILE) {
-               ret = extract_resource_to_staging_dir(dentry, stream_idx, &lte,
-                                                     lte->resource_entry.original_size);
-               if (ret != 0)
-                       return ret;
-       }
        if (lte->resource_location == RESOURCE_IN_STAGING_FILE) {
                fd->staging_fd = open(lte->staging_file_name, fi->flags);
                if (fd->staging_fd == -1) {
@@ -1361,15 +1185,21 @@ static int wimfs_open(const char *path, struct fuse_file_info *fi)
 static int wimfs_opendir(const char *path, struct fuse_file_info *fi)
 {
        struct dentry *dentry;
+       struct inode *inode;
+       int ret;
+       struct wimlib_fd *fd = NULL;
        
        dentry = get_dentry(w, path);
        if (!dentry)
                return -ENOENT;
-       if (!dentry_is_directory(dentry))
+
+       inode = dentry->inode;
+
+       if (!inode_is_directory(inode))
                return -ENOTDIR;
-       dentry->num_times_opened++;
-       fi->fh = (uintptr_t)dentry;
-       return 0;
+       ret = alloc_wimlib_fd(inode, 0, NULL, &fd);
+       fi->fh = (uintptr_t)fd;
+       return ret;
 }
 
 
@@ -1381,14 +1211,11 @@ static int wimfs_read(const char *path, char *buf, size_t size,
 {
        struct wimlib_fd *fd = (struct wimlib_fd*)(uintptr_t)fi->fh;
 
-       if (!fd) {
-               /* Empty file with no lookup table entry on read-only mounted
-                * WIM */
-               wimlib_assert(!(mount_flags & WIMLIB_MOUNT_FLAG_READWRITE));
-               return 0;
-       }
+       if (!fd)
+               return -EBADF;
 
-       wimlib_assert(fd->lte);
+       if (!fd->lte) /* Empty stream with no lookup table entry */
+               return 0;
 
        if (fd->lte->resource_location == RESOURCE_IN_STAGING_FILE) {
                /* Read from staging file */
@@ -1408,14 +1235,12 @@ static int wimfs_read(const char *path, char *buf, size_t size,
        } else {
                /* Read from WIM */
 
-               const struct resource_entry *res_entry;
+               u64 res_size = wim_resource_size(fd->lte);
                
-               res_entry = &fd->lte->resource_entry;
-
-               if (offset > res_entry->original_size)
+               if (offset > res_size)
                        return -EOVERFLOW;
 
-               size = min(size, res_entry->original_size - offset);
+               size = min(size, res_size - offset);
 
                if (read_wim_resource(fd->lte, (u8*)buf,
                                      size, offset, false) != 0)
@@ -1429,15 +1254,20 @@ static int wimfs_read(const char *path, char *buf, size_t size,
 static int wimfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, 
                         off_t offset, struct fuse_file_info *fi)
 {
-       struct dentry *parent, *child;
-       
-       parent = (struct dentry*)(uintptr_t)fi->fh;
-       wimlib_assert(parent);
-       child = parent->children;
+       struct wimlib_fd *fd = (struct wimlib_fd*)(uintptr_t)fi->fh;
+       struct inode *inode;
+       struct dentry *child;
+
+       if (!fd)
+               return -EBADF;
+
+       inode = fd->inode;
 
        filler(buf, ".", NULL, 0);
        filler(buf, "..", NULL, 0);
 
+       child = inode->children;
+
        if (!child)
                return 0;
 
@@ -1445,7 +1275,7 @@ static int wimfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
                if (filler(buf, child->file_name_utf8, NULL, 0))
                        return 0;
                child = child->next;
-       } while (child != parent->children);
+       } while (child != inode->children);
        return 0;
 }
 
@@ -1459,61 +1289,25 @@ static int wimfs_readlink(const char *path, char *buf, size_t buf_len)
        if (!dentry_is_symlink(dentry))
                return -EINVAL;
 
-       ret = dentry_readlink(dentry, buf, buf_len, w);
+       ret = inode_readlink(dentry->inode, buf, buf_len, w);
        if (ret > 0)
                ret = 0;
        return ret;
 }
 
-/* Creation time, write time, access time */
-static void
-dentry_link_group_set_times(struct dentry *dentry, u64 times[3])
-{
-       struct dentry *cur = dentry;
-       do {
-               if (times[0])
-                       cur->creation_time = times[0];
-               if (times[1])
-                       cur->last_write_time = times[1];
-               if (times[2])
-                       cur->last_access_time = times[2];
-       } while ((cur = container_of(dentry->link_group_list.next,
-                                    struct dentry,
-                                    link_group_list)) != dentry);
-}
-
-static void
-dentry_link_group_update_times(struct dentry *dentry)
-{
-       u64 now = get_wim_timestamp();
-       u64 times[3] = {now, now, now};
-       dentry_link_group_set_times(dentry, times);
-}
-
 /* Close a file. */
 static int wimfs_release(const char *path, struct fuse_file_info *fi)
 {
        struct wimlib_fd *fd = (struct wimlib_fd*)(uintptr_t)fi->fh;
-
-       if (!fd) {
-               /* Empty file with no lookup table entry on read-only mounted
-                * WIM */
-               wimlib_assert(!(mount_flags & WIMLIB_MOUNT_FLAG_READWRITE));
-               return 0;
-       }
-
+       wimlib_assert(fd);
        return close_wimlib_fd(fd);
 }
 
 static int wimfs_releasedir(const char *path, struct fuse_file_info *fi)
 {
-       struct dentry *dentry = (struct dentry *)(uintptr_t)fi->fh;
-
-       wimlib_assert(dentry);
-       wimlib_assert(dentry->num_times_opened);
-       if (--dentry->num_times_opened == 0)
-               free_dentry(dentry);
-       return 0;
+       struct wimlib_fd *fd = (struct wimlib_fd*)(uintptr_t)fi->fh;
+       wimlib_assert(fd);
+       return close_wimlib_fd(fd);
 }
 
 #ifdef ENABLE_XATTR
@@ -1522,6 +1316,7 @@ static int wimfs_removexattr(const char *path, const char *name)
 {
        struct dentry *dentry;
        struct ads_entry *ads_entry;
+       u16 ads_idx;
        if (!(mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR))
                return -ENOTSUP;
 
@@ -1533,10 +1328,10 @@ static int wimfs_removexattr(const char *path, const char *name)
        if (!dentry)
                return -ENOENT;
 
-       ads_entry = dentry_get_ads_entry(dentry, name);
+       ads_entry = inode_get_ads_entry(dentry->inode, name, &ads_idx);
        if (!ads_entry)
                return -ENOATTR;
-       remove_ads(dentry, ads_entry, w->lookup_table);
+       inode_remove_ads(dentry->inode, ads_idx, w->lookup_table);
        return 0;
 }
 #endif
@@ -1582,7 +1377,7 @@ static int wimfs_rename(const char *from, const char *to)
                         * directory */
                        if (!dentry_is_directory(dst))
                                return -ENOTDIR;
-                       if (dst->children != NULL)
+                       if (dst->inode->children != NULL)
                                return -ENOTEMPTY;
                }
                parent_of_dst = dst->parent;
@@ -1621,9 +1416,7 @@ static int wimfs_rmdir(const char *path)
        if (!dentry_is_empty_directory(dentry))
                return -ENOTEMPTY;
 
-       unlink_dentry(dentry);
-       if (dentry->num_times_opened == 0)
-               free_dentry(dentry);
+       remove_dentry(dentry, w->lookup_table);
        return 0;
 }
 
@@ -1637,7 +1430,9 @@ static int wimfs_setxattr(const char *path, const char *name,
        struct ads_entry *new_ads_entry;
        struct lookup_table_entry *existing_lte;
        struct lookup_table_entry *lte;
+       struct inode *inode;
        u8 value_hash[SHA1_HASH_SIZE];
+       u16 ads_idx;
 
        if (!(mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR))
                return -ENOTSUP;
@@ -1649,16 +1444,18 @@ static int wimfs_setxattr(const char *path, const char *name,
        dentry = get_dentry(w, path);
        if (!dentry)
                return -ENOENT;
-       existing_ads_entry = dentry_get_ads_entry(dentry, name);
+       inode = dentry->inode;
+
+       existing_ads_entry = inode_get_ads_entry(inode, name, &ads_idx);
        if (existing_ads_entry) {
                if (flags & XATTR_CREATE)
                        return -EEXIST;
-               remove_ads(dentry, existing_ads_entry, w->lookup_table);
+               inode_remove_ads(dentry->inode, ads_idx, w->lookup_table);
        } else {
                if (flags & XATTR_REPLACE)
                        return -ENOATTR;
        }
-       new_ads_entry = dentry_add_ads(dentry, name);
+       new_ads_entry = inode_add_ads(dentry, name);
        if (!new_ads_entry)
                return -ENOMEM;
 
@@ -1765,7 +1562,6 @@ static int wimfs_truncate(const char *path, off_t size)
                ret = extract_resource_to_staging_dir(dentry, stream_idx,
                                                      &lte, size);
        }
-       dentry_link_group_update_times(dentry);
        return ret;
 }
 
@@ -1774,8 +1570,10 @@ static int wimfs_unlink(const char *path)
 {
        struct dentry *dentry;
        struct lookup_table_entry *lte;
+       struct inode *inode;
        int ret;
        unsigned stream_idx;
+       unsigned i;
        
        ret = lookup_resource(w, path, get_lookup_flags(), &dentry,
                              &lte, &stream_idx);
@@ -1783,18 +1581,10 @@ static int wimfs_unlink(const char *path)
        if (ret != 0)
                return ret;
 
-       if (stream_idx == 0) {
-               /* We are removing the full dentry including all alternate data
-                * streams. */
+       if (stream_idx == 0)
                remove_dentry(dentry, w->lookup_table);
-       } else {
-               /* We are removing an alternate data stream. */
-               remove_ads(dentry, &dentry->ads_entries[stream_idx - 1],
-                          w->lookup_table);
-       }
-       /* Beware: The lookup table entry(s) may still be referenced by users
-        * that have opened the corresponding streams.  They are freed later in
-        * wimfs_release() when the last file user has closed the stream. */
+       else
+               inode_remove_ads(dentry->inode, stream_idx - 1, w->lookup_table);
        return 0;
 }
 
@@ -1806,36 +1596,39 @@ static int wimfs_unlink(const char *path)
  */
 static int wimfs_utimens(const char *path, const struct timespec tv[2])
 {
-       struct dentry *dentry = get_dentry(w, path);
+       struct dentry *dentry;
+       struct inode *inode;
+       dentry = get_dentry(w, path);
        if (!dentry)
                return -ENOENT;
-       u64 times[3] = {0, 0, 0};
+       inode = dentry->inode;
+
        if (tv[0].tv_nsec != UTIME_OMIT) {
                if (tv[0].tv_nsec == UTIME_NOW)
-                       times[2] = get_wim_timestamp();
+                       inode->last_access_time = get_wim_timestamp();
                else
-                       times[2] = timespec_to_wim_timestamp(&tv[0]);
+                       inode->last_access_time = timespec_to_wim_timestamp(&tv[0]);
        }
        if (tv[1].tv_nsec != UTIME_OMIT) {
                if (tv[1].tv_nsec == UTIME_NOW)
-                       times[1] = get_wim_timestamp();
+                       inode->last_write_time = get_wim_timestamp();
                else
-                       times[1] = timespec_to_wim_timestamp(&tv[1]);
+                       inode->last_write_time = timespec_to_wim_timestamp(&tv[1]);
        }
-       dentry_link_group_set_times(dentry, times);
        return 0;
 }
 #else
 static int wimfs_utime(const char *path, struct utimbuf *times)
 {
-       struct dentry *dentry = get_dentry(w, path);
+       struct dentry *dentry;
+       struct inode *inode;
+       dentry = get_dentry(w, path);
        if (!dentry)
                return -ENOENT;
-       u64 wim_times[3];
-       wim_times[0] = 0;
-       wim_times[1] = unix_timestamp_to_wim(times->modtime);
-       wim_times[2] = unix_timestamp_to_wim(times->actime);
-       dentry_link_group_set_times(dentry, wim_times);
+       inode = dentry->inode;
+
+       inode->last_write_time = unix_timestamp_to_wim(times->modtime);
+       inode->last_access_time = unix_timestamp_to_wim(times->actime);
        return 0;
 }
 #endif
@@ -1848,11 +1641,15 @@ static int wimfs_write(const char *path, const char *buf, size_t size,
 {
        struct wimlib_fd *fd = (struct wimlib_fd*)(uintptr_t)fi->fh;
        int ret;
+       u64 now;
+
+       if (!fd)
+               return -EBADF;
 
-       wimlib_assert(fd);
        wimlib_assert(fd->lte);
        wimlib_assert(fd->lte->staging_file_name);
        wimlib_assert(fd->staging_fd != -1);
+       wimlib_assert(fd->inode);
 
        /* Seek to the requested position */
        if (lseek(fd->staging_fd, offset, SEEK_SET) == -1)
@@ -1863,12 +1660,9 @@ static int wimfs_write(const char *path, const char *buf, size_t size,
        if (ret == -1)
                return -errno;
 
-       if (fd->dentry) {
-               u64 now = get_wim_timestamp();
-               u64 times[3] = {0, now, now};
-               dentry_link_group_set_times(fd->dentry, times);
-       }
-
+       now = get_wim_timestamp();
+       fd->inode->last_write_time = now;
+       fd->inode->last_access_time = now;
        return ret;
 }