]> wimlib.net Git - wimlib/blobdiff - src/hardlink.c
inode updates
[wimlib] / src / hardlink.c
index d2876e5e35185dc4ca51f58343e0ad56c47d72d3..87f75a872a5a6d56d87835b51111b946bd231a81 100644 (file)
  *                            -----------------
  */
 
-/* Hash table to find inodes, identified by their inode ID.
- * */
-struct inode_table {
-       /* Fields for the hash table */
-       struct hlist_head *array;
-       u64 num_entries;
-       u64 capacity;
-
-       /* 
-        * Linked list of "extra" inodes.  These may be:
-        *
-        * - inodes with link count 1, which are all allowed to have 0 for their
-        *   inode number, meaning we cannot insert them into the hash table
-        *   before calling assign_inode_numbers().
-         *
-        * - Groups we create ourselves by splitting a nominal inode due to
-        *   inconsistencies in the dentries.  These inodes will share a inode
-        *   ID with some other inode until assign_inode_numbers() is called.
-        */
-       struct hlist_head extra_inodes;
-};
-
-/* Returns pointer to a new inode table having the specified capacity */
-struct inode_table *new_inode_table(size_t capacity)
+
+int init_inode_table(struct inode_table *table, size_t capacity)
 {
-       struct inode_table *table;
-       struct hlist_head *array;
-
-       table = MALLOC(sizeof(struct inode_table));
-       if (!table)
-               goto err;
-       array = CALLOC(capacity, sizeof(array[0]));
-       if (!array) {
-               FREE(table);
-               goto err;
+       table->array = CALLOC(capacity, sizeof(table->array[0]));
+       if (!table->array) {
+               ERROR("Cannot initalize inode table: out of memory");
+               return WIMLIB_ERR_NOMEM;
        }
        table->num_entries  = 0;
        table->capacity     = capacity;
-       table->array        = array;
        INIT_HLIST_HEAD(&table->extra_inodes);
-       return table;
-err:
-       ERROR("Failed to allocate memory for inode table with capacity %zu",
-             capacity);
-       return NULL;
+       return 0;
+}
+
+
+static size_t inode_link_count(const struct inode *inode)
+{
+       const struct list_head *cur;
+       size_t size = 0;
+       list_for_each(cur, &inode->dentry_list)
+               size++;
+       return size;
+}
+
+static struct dentry *inode_first_dentry(struct inode *inode)
+{
+       return container_of(inode->dentry_list.next, struct dentry,
+                           inode_dentry_list);
 }
 
 /* 
@@ -116,21 +99,24 @@ err:
 int inode_table_insert(struct dentry *dentry, void *__table)
 {
        struct inode_table *table = __table;
-       size_t pos;
-       struct inode *inode;
        struct inode *d_inode = dentry->inode;
 
        if (d_inode->ino == 0) {
-
                /* Single inode--- Add to the list of extra inodes (we can't put
                 * it in the table itself because all the singles have a link
                 * inode ID of 0) */
-               list_add(&dentry->inode_dentry_list, &d_inode->dentry_list);
                hlist_add_head(&d_inode->hlist, &table->extra_inodes);
+
+               wimlib_assert(d_inode->dentry_list.next == &dentry->inode_dentry_list);
+               wimlib_assert(d_inode->dentry_list.prev == &dentry->inode_dentry_list);
+               wimlib_assert(d_inode->link_count == 1);
        } else {
-                /* Hard inode that may contain multiple dentries (the code
-                 * will work even if the inode actually contains only 1 dentry
-                 * though) */
+               /* Inode that may have multiple corresponding dentries (the code
+                * will work even if the inode actually contains only 1 dentry
+                * though) */
+
+               size_t pos;
+               struct inode *inode;
                struct hlist_node *cur;
 
                /* Try adding to existing inode */
@@ -139,13 +125,18 @@ int inode_table_insert(struct dentry *dentry, void *__table)
                        if (inode->ino == d_inode->ino) {
                                list_add(&dentry->inode_dentry_list,
                                         &inode->dentry_list);
+                               inode->link_count++;
+                               return 0;
                        }
                }
 
                /* Add new inode to the table */
-               list_add(&dentry->inode_dentry_list, &d_inode->dentry_list);
                hlist_add_head(&d_inode->hlist, &table->array[pos]);
 
+               wimlib_assert(d_inode->dentry_list.next == &dentry->inode_dentry_list);
+               wimlib_assert(d_inode->dentry_list.prev == &dentry->inode_dentry_list);
+               wimlib_assert(d_inode->link_count == 1);
+
                /* XXX Make the table grow when too many entries have been
                 * inserted. */
                table->num_entries++;
@@ -153,26 +144,6 @@ int inode_table_insert(struct dentry *dentry, void *__table)
        return 0;
 }
 
-/* Frees a inode table. */
-void free_inode_table(struct inode_table *table)
-{
-       if (table) {
-               FREE(table->array);
-                FREE(table);
-        }
-}
-
-static u64
-assign_inos_to_list(struct hlist_head *head, u64 cur_ino)
-{
-       struct inode *inode;
-       struct hlist_node *cur;
-       struct dentry *dentry;
-       hlist_for_each_entry(inode, cur, head, hlist) {
-       }
-       return cur_ino;
-}
-
 /* Assign the inode numbers to dentries in a inode table, and return the
  * next available inode ID. */
 u64 assign_inode_numbers(struct hlist_head *inode_list)
@@ -182,8 +153,6 @@ u64 assign_inode_numbers(struct hlist_head *inode_list)
        u64 cur_ino = 1;
        struct dentry *dentry;
        hlist_for_each_entry(inode, cur, inode_list, hlist) {
-               list_for_each_entry(dentry, &inode->dentry_list, inode_dentry_list)
-                       dentry->link_group_id = cur_ino;
                inode->ino = cur_ino;
                cur_ino++;
        }
@@ -207,49 +176,49 @@ static void inconsistent_inode(const struct inode *inode)
        print_inode_dentries(inode);
 }
 
-static bool ref_dentries_consistent(const struct dentry * restrict ref_dentry_1,
-                                   const struct dentry * restrict ref_dentry_2)
+static bool ref_inodes_consistent(const struct inode * restrict ref_inode_1,
+                                 const struct inode * restrict ref_inode_2)
 {
-       wimlib_assert(ref_dentry_1 != ref_dentry_2);
+       wimlib_assert(ref_inode_1 != ref_inode_2);
 
-       if (ref_dentry_1->inode->num_ads != ref_dentry_2->inode->num_ads)
+       if (ref_inode_1->num_ads != ref_inode_2->num_ads)
                return false;
-       if (ref_dentry_1->inode->security_id != ref_dentry_2->inode->security_id
-           || ref_dentry_1->inode->attributes != ref_dentry_2->inode->attributes)
+       if (ref_inode_1->security_id != ref_inode_2->security_id
+           || ref_inode_1->attributes != ref_inode_2->attributes)
                return false;
-       for (unsigned i = 0; i <= ref_dentry_1->inode->num_ads; i++) {
+       for (unsigned i = 0; i <= ref_inode_1->num_ads; i++) {
                const u8 *ref_1_hash, *ref_2_hash;
-               ref_1_hash = inode_stream_hash(ref_dentry_1->inode, i);
-               ref_2_hash = inode_stream_hash(ref_dentry_2->inode, i);
+               ref_1_hash = inode_stream_hash(ref_inode_1, i);
+               ref_2_hash = inode_stream_hash(ref_inode_2, i);
                if (!hashes_equal(ref_1_hash, ref_2_hash))
                        return false;
-               if (i && !ads_entries_have_same_name(ref_dentry_1->inode->ads_entries[i - 1],
-                                                    ref_dentry_2->inode->ads_entries[i - 1]))
+               if (i && !ads_entries_have_same_name(ref_inode_1->ads_entries[i - 1],
+                                                    ref_inode_2->ads_entries[i - 1]))
                        return false;
 
        }
        return true;
 }
 
-static bool dentries_consistent(const struct dentry * restrict ref_dentry,
-                               const struct dentry * restrict dentry)
+static bool inodes_consistent(const struct inode * restrict ref_inode,
+                             const struct inode * restrict inode)
 {
-       wimlib_assert(ref_dentry != dentry);
+       wimlib_assert(ref_inode != inode);
 
-       if (ref_dentry->inode->num_ads != dentry->inode->num_ads &&
-           dentry->inode->num_ads != 0)
+       if (ref_inode->num_ads != inode->num_ads &&
+           inode->num_ads != 0)
                return false;
-       if (ref_dentry->inode->security_id != dentry->inode->security_id
-           || ref_dentry->inode->attributes != dentry->inode->attributes)
+       if (ref_inode->security_id != inode->security_id
+           || ref_inode->attributes != inode->attributes)
                return false;
-       for (unsigned i = 0; i <= min(ref_dentry->inode->num_ads, dentry->inode->num_ads); i++) {
+       for (unsigned i = 0; i <= min(ref_inode->num_ads, inode->num_ads); i++) {
                const u8 *ref_hash, *hash;
-               ref_hash = inode_stream_hash(ref_dentry->inode, i);
-               hash = inode_stream_hash(dentry->inode, i);
+               ref_hash = inode_stream_hash(ref_inode, i);
+               hash = inode_stream_hash(inode, i);
                if (!hashes_equal(ref_hash, hash) && !is_zero_hash(hash))
                        return false;
-               if (i && !ads_entries_have_same_name(ref_dentry->inode->ads_entries[i - 1],
-                                                    dentry->inode->ads_entries[i - 1]))
+               if (i && !ads_entries_have_same_name(ref_inode->ads_entries[i - 1],
+                                                    inode->ads_entries[i - 1]))
                        return false;
        }
        return true;
@@ -269,26 +238,13 @@ print_dentry_list(const struct dentry *first_dentry)
 
 #endif
 
-static size_t inode_link_count(const struct inode *inode)
-{
-       const struct list_head *cur;
-       size_t size = 0;
-       list_for_each(cur, &inode->dentry_list)
-               size++;
-       return size;
-}
-
-static struct dentry *inode_first_dentry(struct inode *inode)
-{
-       return container_of(inode->dentry_list.next, struct dentry,
-                           inode_dentry_list);
-}
 
 /* Fix up a "true" inode and check for inconsistencies */
 static int fix_true_inode(struct inode *inode)
 {
        struct dentry *dentry;
        struct dentry *ref_dentry = NULL;
+       struct inode *ref_inode;
        u64 last_ctime = 0;
        u64 last_mtime = 0;
        u64 last_atime = 0;
@@ -315,22 +271,25 @@ static int fix_true_inode(struct inode *inode)
                        last_atime = dentry->inode->last_access_time;
        }
 
+       ref_inode = ref_dentry->inode;
+       ref_inode->link_count = 1;
+
        list_for_each_entry(dentry, &inode->dentry_list, inode_dentry_list) {
                if (dentry != ref_dentry) {
-                       if (!dentries_consistent(ref_dentry, dentry)) {
-                               inconsistent_inode(inode);
+                       if (!inodes_consistent(ref_inode, dentry->inode)) {
+                               inconsistent_inode(dentry->inode);
                                return WIMLIB_ERR_INVALID_DENTRY;
                        }
                        /* Free the unneeded `struct inode'. */
                        free_inode(dentry->inode);
-                       dentry->inode = ref_dentry->inode;
-                       ref_dentry->inode->link_count++;
+                       dentry->inode = ref_inode;
+                       ref_inode->link_count++;
                }
        }
-       ref_dentry->inode->creation_time = last_ctime;
-       ref_dentry->inode->last_write_time = last_mtime;
-       ref_dentry->inode->last_access_time = last_atime;
-       wimlib_assert(inode_link_count(inode) == inode->link_count);
+       ref_inode->creation_time = last_ctime;
+       ref_inode->last_write_time = last_mtime;
+       ref_inode->last_access_time = last_atime;
+       wimlib_assert(inode_link_count(ref_inode) == ref_inode->link_count);
        return 0;
 }
 
@@ -352,11 +311,13 @@ static int fix_true_inode(struct inode *inode)
 static int
 fix_nominal_inode(struct inode *inode, struct hlist_head *inode_list)
 {
-       struct dentry *tmp, *dentry, *ref_dentry;
-       struct hlist_node *cur;
+       struct dentry *dentry, *ref_dentry;
+       struct hlist_node *cur, *tmp;
        int ret;
        size_t num_true_inodes;
 
+       wimlib_assert(inode->link_count == inode_link_count(inode));
+
        LIST_HEAD(dentries_with_data_streams);
        LIST_HEAD(dentries_with_no_data_streams);
        HLIST_HEAD(true_inodes);
@@ -383,6 +344,7 @@ fix_nominal_inode(struct inode *inode, struct hlist_head *inode_list)
        /* If there are no dentries with data streams, we require the nominal
         * inode to be a true inode */
        if (list_empty(&dentries_with_data_streams)) {
+               DEBUG("No data streams");
        #ifdef ENABLE_DEBUG
                {
                        if (inode->link_count > 1) {
@@ -410,16 +372,16 @@ fix_nominal_inode(struct inode *inode, struct hlist_head *inode_list)
                 * of the true inodes are consistent with this
                 * dentry, make a new one. */
                hlist_for_each_entry(inode, cur, &true_inodes, hlist) {
-                       if (ref_dentries_consistent(inode_first_dentry(inode), dentry)) {
+                       if (ref_inodes_consistent(inode, dentry->inode)) {
                                list_add(&dentry->inode_dentry_list,
                                         &inode->dentry_list);
                                goto next_dentry_2;
                        }
                }
                num_true_inodes++;
-               hlist_add_head(&dentry->inode->hlist, &true_inodes);
                INIT_LIST_HEAD(&dentry->inode->dentry_list);
                list_add(&dentry->inode_dentry_list, &dentry->inode->dentry_list);
+               hlist_add_head(&dentry->inode->hlist, &true_inodes);
 next_dentry_2:
                ;
        }
@@ -439,9 +401,11 @@ next_dentry_2:
                        ERROR("inode to assign them to.");
                        return WIMLIB_ERR_INVALID_DENTRY;
                }
+               inode = container_of(true_inodes.first,
+                                    struct inode,
+                                    hlist);
                /* Assign the streamless dentries to the one and only true link
                 * inode. */
-               ref_dentry = inode_first_dentry(inode);
                list_for_each_entry(dentry, &dentries_with_no_data_streams, tmp_list)
                        list_add(&dentry->inode_dentry_list, &inode->dentry_list);
        }
@@ -463,7 +427,7 @@ next_dentry_2:
                #endif
         }
 
-       hlist_for_each_entry(inode, cur, &true_inodes, hlist) {
+       hlist_for_each_entry_safe(inode, cur, tmp, &true_inodes, hlist) {
                hlist_add_head(&inode->hlist, inode_list);
                ret = fix_true_inode(inode);
                if (ret != 0)
@@ -485,14 +449,16 @@ int fix_inodes(struct inode_table *table, struct hlist_head *inode_list)
 {
        struct inode *inode;
        struct hlist_node *cur, *tmp;
-       int ret = 0;
+       int ret;
        INIT_HLIST_HEAD(inode_list);
        for (u64 i = 0; i < table->capacity; i++) {
                hlist_for_each_entry_safe(inode, cur, tmp, &table->array[i], hlist) {
                        ret = fix_nominal_inode(inode, inode_list);
                        if (ret != 0)
-                               break;
+                               return ret;
                }
        }
-       return ret;
+       hlist_for_each_safe(cur, tmp, &table->extra_inodes)
+               hlist_add_head(cur, inode_list);
+       return 0;
 }