X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fhardlink.c;h=93030d8f7e10e6b89874a089c28da575c63e850d;hp=903d0ccd0fe0dd68e4339a7855bcb3fda3c8b47d;hb=266d03613339dbe9a433c9849b6b4c47e0090dc8;hpb=239e67483b8d6759fa97f25a65011cc3480368bc diff --git a/src/hardlink.c b/src/hardlink.c index 903d0ccd..93030d8f 100644 --- a/src/hardlink.c +++ b/src/hardlink.c @@ -2,8 +2,8 @@ * hardlink.c * * Code to deal with hard links in WIMs. Essentially, the WIM dentries are put - * into a hash table indexed by the hard link group ID field, then for each hard - * link group, a linked list is made to connect the dentries. + * into a hash table indexed by the inode ID field, then for each hard + * inode, a linked list is made to connect the dentries. */ /* @@ -32,147 +32,128 @@ /* NULL NULL * ^ ^ - * dentry | | - * / \ ----------- ----------- + * dentry | | + * / \ ----------- ----------- * | dentry<---| struct | | struct |---> dentry - * \ / |link_group| |link_group| + * \ / | inode | | inode | * dentry ------------ ------------ * ^ ^ * | | * | | dentry * ----------- ----------- / \ * dentry<---| struct | | struct |---> dentry dentry - * / |link_group| |link_group| \ / + * / | inode | | inode | \ / * dentry ------------ ------------ dentry * ^ ^ * | | * ----------------- - * link_group_table->array | idx 0 | idx 1 | + * inode_table->array | idx 0 | idx 1 | * ----------------- */ -/* Hard link group; it's identified by its hard link group ID and points to a - * circularly linked list of dentries. */ -struct link_group { - u64 link_group_id; - - /* Pointer to use to make a singly-linked list of link groups. */ - struct link_group *next; - - /* This is a pointer to the circle and not part of the circle itself. - * This makes it easy to iterate through other dentries hard-linked to a - * given dentry without having to find the "head" of the list first. */ - struct list_head *dentry_list; -}; - -/* Hash table to find hard link groups, identified by their hard link group ID. +/* Hash table to find inodes, identified by their inode ID. * */ -struct link_group_table { +struct inode_table { /* Fields for the hash table */ - struct link_group **array; + struct hlist_head *array; u64 num_entries; u64 capacity; - /* - * Linked list of "extra" groups. These may be: + /* + * Linked list of "extra" inodes. These may be: * - * - Hard link groups of size 1, which are all allowed to have 0 for - * their hard link group ID, meaning we cannot insert them into the - * hash table before calling assign_link_group_ids(). + * - 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 hard link group - * due to inconsistencies in the dentries. These groups will share a - * hard link group ID with some other group until - * assign_link_group_ids() is called. + * - 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 link_group *extra_groups; + struct hlist_head extra_inodes; }; -/* Returns pointer to a new link group table having the specified capacity */ -struct link_group_table *new_link_group_table(size_t capacity) +static inline void destroy_inode_table(struct inode_table *table) { - struct link_group_table *table; - struct link_group **array; - - table = MALLOC(sizeof(struct link_group_table)); - if (!table) - goto err; - array = CALLOC(capacity, sizeof(array[0])); - if (!array) { - FREE(table); - goto err; + FREE(table->array); +} + +static int init_inode_table(struct inode_table *table, size_t capacity) +{ + 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; - table->extra_groups = NULL; - return table; -err: - ERROR("Failed to allocate memory for link group table with capacity %zu", - capacity); - return NULL; + INIT_HLIST_HEAD(&table->extra_inodes); + return 0; +} + +static inline 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; } -/* - * Insert a dentry into the hard link group table based on its hard link group +/* + * Insert a dentry into the inode table based on its inode * ID. * - * If there is already a dentry in the table having the same hard link group ID, - * and the hard link group ID is not 0, the dentry is added to the circular - * linked list for that hard link group. + * If there is already a dentry in the table having the same inode ID, + * and the inode ID is not 0, the dentry is added to the circular + * linked list for that inode. * - * If the hard link group ID is 0, this indicates a dentry that's in a hard link - * group by itself (has a link count of 1). We can't insert it into the hash - * table itself because we don't know what hard link group IDs are available to + * If the inode ID is 0, this indicates a dentry that's in a hard link + * inode by itself (has a link count of 1). We can't insert it into the hash + * table itself because we don't know what inode numbers are available to * give it (this could be kept track of but would be more difficult). Instead - * we keep a linked list of the single dentries, and assign them hard link group - * IDs later. + * we keep a linked list of the single dentries, and assign them inode + * numbers later. */ -int link_group_table_insert(struct dentry *dentry, void *__table) +static int inode_table_insert(struct dentry *dentry, void *__table) { - struct link_group_table *table = __table; - size_t pos; - struct link_group *group; + struct inode_table *table = __table; + struct inode *d_inode = dentry->d_inode; - if (dentry->link_group_id == 0) { - /* Single group--- Add to the list of extra groups (we can't put + 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 - * group ID of 0) */ - group = MALLOC(sizeof(struct link_group)); - if (!group) - return WIMLIB_ERR_NOMEM; - group->link_group_id = 0; - group->next = table->extra_groups; - table->extra_groups = group; - INIT_LIST_HEAD(&dentry->link_group_list); - group->dentry_list = &dentry->link_group_list; + * inode ID of 0) */ + 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 link group that may contain multiple dentries (the code - * will work even if the group actually contains only 1 dentry - * though) */ - - /* Try adding to existing hard link group */ - pos = dentry->link_group_id % table->capacity; - group = table->array[pos]; - while (group) { - if (group->link_group_id == dentry->link_group_id) { - list_add(&dentry->link_group_list, - group->dentry_list); + /* 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 */ + pos = d_inode->ino % table->capacity; + hlist_for_each_entry(inode, cur, &table->array[pos], hlist) { + if (inode->ino == d_inode->ino) { + inode_add_dentry(dentry, inode); + inode->link_count++; return 0; } - group = group->next; } - /* Add new hard link group to the table */ + /* Add new inode to the table */ + hlist_add_head(&d_inode->hlist, &table->array[pos]); - group = MALLOC(sizeof(struct link_group)); - if (!group) - return WIMLIB_ERR_NOMEM; - group->link_group_id = dentry->link_group_id; - group->next = table->array[pos]; - INIT_LIST_HEAD(&dentry->link_group_list); - group->dentry_list = &dentry->link_group_list; - table->array[pos] = group; + 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. */ @@ -181,288 +162,171 @@ int link_group_table_insert(struct dentry *dentry, void *__table) return 0; } -static void free_link_group_list(struct link_group *group) +/* 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) { - struct link_group *next_group; - while (group) { - next_group = group->next; - FREE(group); - group = next_group; + DEBUG("Assigning inode numbers"); + struct inode *inode; + struct hlist_node *cur; + u64 cur_ino = 1; + hlist_for_each_entry(inode, cur, inode_list, hlist) { + inode->ino = cur_ino; + cur_ino++; } + return cur_ino; } -/* Frees a link group table. */ -void free_link_group_table(struct link_group_table *table) -{ - if (table) { - if (table->array) - for (size_t i = 0; i < table->capacity; i++) - free_link_group_list(table->array[i]); - free_link_group_list(table->extra_groups); - FREE(table); - } -} -static u64 -assign_link_group_ids_to_list(struct link_group *group, u64 id, - struct link_group **extra_groups) +static void print_inode_dentries(const struct inode *inode) { struct dentry *dentry; - struct list_head *cur_head; - struct link_group *prev_group = NULL; - struct link_group *cur_group = group; - while (cur_group) { - cur_head = cur_group->dentry_list; - do { - dentry = container_of(cur_head, - struct dentry, - link_group_list); - dentry->link_group_id = id; - cur_head = cur_head->next; - } while (cur_head != cur_group->dentry_list); - cur_group->link_group_id = id; - id++; - prev_group = cur_group; - cur_group = cur_group->next; - } - if (group && extra_groups) { - prev_group->next = *extra_groups; - *extra_groups = group; - } - return id; -} - -/* Insert the link groups in the `extra_groups' list into the hash table */ -static void insert_extra_groups(struct link_group_table *table) -{ - struct link_group *group, *next_group; - size_t pos; - - group = table->extra_groups; - while (group) { - next_group = group->next; - pos = group->link_group_id % table->capacity; - group->next = table->array[pos]; - table->array[pos] = group; - group = next_group; - } - table->extra_groups = NULL; -} - -/* Assign the link group IDs to dentries in a link group table, and return the - * next available link group ID. */ -u64 assign_link_group_ids(struct link_group_table *table) -{ - DEBUG("Assigning link groups"); - struct link_group *extra_groups = table->extra_groups; - - /* Assign consecutive link group IDs to each link group in the hash - * table */ - u64 id = 1; - for (size_t i = 0; i < table->capacity; i++) { - id = assign_link_group_ids_to_list(table->array[i], id, - &table->extra_groups); - table->array[i] = NULL; - } - - /* Assign link group IDs to the "extra" link groups and insert them into - * the hash table */ - id = assign_link_group_ids_to_list(extra_groups, id, NULL); - insert_extra_groups(table); - return id; + inode_for_each_dentry(dentry, inode) + printf("`%s'\n", dentry->full_path_utf8); } - - -static void inconsistent_link_group(const struct dentry *first_dentry) +static void inconsistent_inode(const struct inode *inode) { - const struct dentry *dentry = first_dentry; - - ERROR("An inconsistent hard link group that we cannot correct has been " - "detected"); + ERROR("An inconsistent hard link group that cannot be corrected has " + "been detected"); ERROR("The dentries are located at the following paths:"); - do { - ERROR("`%s'", dentry->full_path_utf8); - } while ((dentry = container_of(dentry->link_group_list.next, - const struct dentry, - link_group_list)) != first_dentry); +#ifdef ENABLE_ERROR_MESSAGES + print_inode_dentries(inode); +#endif } -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->num_ads != ref_dentry_2->num_ads) + if (ref_inode_1->num_ads != ref_inode_2->num_ads) return false; - if (ref_dentry_1->security_id != ref_dentry_2->security_id - || ref_dentry_1->attributes != ref_dentry_2->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->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 = dentry_stream_hash(ref_dentry_1, i); - ref_2_hash = dentry_stream_hash(ref_dentry_2, 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->ads_entries[i - 1], - &ref_dentry_2->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->num_ads != dentry->num_ads && dentry->num_ads != 0) + if (ref_inode->num_ads != inode->num_ads && + inode->num_ads != 0) return false; - if (ref_dentry->security_id != dentry->security_id - || ref_dentry->attributes != dentry->attributes) + if (ref_inode->security_id != inode->security_id + || ref_inode->attributes != inode->attributes) return false; - for (unsigned i = 0; i <= min(ref_dentry->num_ads, dentry->num_ads); i++) { + for (unsigned i = 0; i <= min(ref_inode->num_ads, inode->num_ads); i++) { const u8 *ref_hash, *hash; - ref_hash = dentry_stream_hash(ref_dentry, i); - hash = dentry_stream_hash(dentry, 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->ads_entries[i - 1], - &dentry->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; } -#ifdef ENABLE_DEBUG -static void -print_dentry_list(const struct dentry *first_dentry) -{ - const struct dentry *dentry = first_dentry; - do { - printf("`%s'\n", dentry->full_path_utf8); - } while ((dentry = container_of(dentry->link_group_list.next, - struct dentry, - link_group_list)) != first_dentry); -} -#endif - -/* Fix up a "true" link group and check for inconsistencies */ -static int -fix_true_link_group(struct dentry *first_dentry) +/* Fix up a "true" inode and check for inconsistencies */ +static int fix_true_inode(struct inode *inode, struct hlist_head *inode_list) { struct dentry *dentry; struct dentry *ref_dentry = NULL; + struct inode *ref_inode; u64 last_ctime = 0; u64 last_mtime = 0; u64 last_atime = 0; - bool found_short_name = false; - dentry = first_dentry; - do { - if (!ref_dentry || ref_dentry->num_ads == 0) + inode_for_each_dentry(dentry, inode) { + if (!ref_dentry || dentry->d_inode->num_ads > ref_dentry->d_inode->num_ads) ref_dentry = dentry; - if (dentry->short_name_len) { - if (found_short_name) { - ERROR("Multiple short names in hard link " - "group!"); - inconsistent_link_group(first_dentry); - return WIMLIB_ERR_INVALID_DENTRY; - } else { - found_short_name = true; - } - } - if (dentry->creation_time > last_ctime) - last_ctime = dentry->creation_time; - if (dentry->last_write_time > last_mtime) - last_mtime = dentry->last_write_time; - if (dentry->last_access_time > last_atime) - last_atime = dentry->last_access_time; - } while ((dentry = container_of(dentry->link_group_list.next, - struct dentry, - link_group_list)) != first_dentry); - - - ref_dentry->ads_entries_status = ADS_ENTRIES_OWNER; - dentry = first_dentry; - do { + if (dentry->d_inode->creation_time > last_ctime) + last_ctime = dentry->d_inode->creation_time; + if (dentry->d_inode->last_write_time > last_mtime) + last_mtime = dentry->d_inode->last_write_time; + if (dentry->d_inode->last_access_time > last_atime) + last_atime = dentry->d_inode->last_access_time; + } + + ref_inode = ref_dentry->d_inode; + ref_inode->link_count = 1; + hlist_add_head(&ref_inode->hlist, inode_list); + + list_del(&inode->dentry_list); + list_add(&ref_inode->dentry_list, &ref_dentry->inode_dentry_list); + + inode_for_each_dentry(dentry, ref_inode) { if (dentry != ref_dentry) { - if (!dentries_consistent(ref_dentry, dentry)) { - inconsistent_link_group(first_dentry); + if (!inodes_consistent(ref_inode, dentry->d_inode)) { + inconsistent_inode(ref_inode); return WIMLIB_ERR_INVALID_DENTRY; } - copy_hash(dentry->hash, ref_dentry->hash); - dentry_free_ads_entries(dentry); - dentry->num_ads = ref_dentry->num_ads; - dentry->ads_entries = ref_dentry->ads_entries; - dentry->ads_entries_status = ADS_ENTRIES_USER; + /* Free the unneeded `struct inode'. */ + dentry->d_inode->hlist.next = NULL; + dentry->d_inode->hlist.pprev = NULL; + free_inode(dentry->d_inode); + dentry->d_inode = ref_inode; + ref_inode->link_count++; } - dentry->creation_time = last_ctime; - dentry->last_write_time = last_mtime; - dentry->last_access_time = last_atime; - } while ((dentry = container_of(dentry->link_group_list.next, - struct dentry, - link_group_list)) != first_dentry); + } + 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; } -/* - * Fixes up a nominal link group. +/* + * Fixes up a nominal inode. * - * By a nominal link group we mean a group of two or more dentries that share + * By a nominal inode we mean a group of two or more dentries that share * the same hard link group ID. * - * If dentries in the group are found to be inconsistent, we may split the group - * into several "true" hard link groups. @new_groups points to a linked list of - * these split groups, and if we create any, they will be added to this list. - * - * After splitting up each nominal link group into the "true" link groups we - * will canonicalize the link groups. To do this, we: - * - * - Assign all the dentries in the link group the most recent timestamp - * among all the corresponding timestamps in the link group, for each of - * the three categories of time stamps. + * If dentries in the inode are found to be inconsistent, we may split the inode + * into several "true" inodes. * - * - Make sure the dentry->hash field is valid in all the dentries, if - * possible (this field may be all zeroes, and in the context of a hard - * link group this must be interpreted as implicitly refering to the same - * stream as another dentry in the hard link group that does NOT have all - * zeroes for this field). - * - * - Make sure dentry->num_ads is the same in all the dentries in the link - * group. In some cases, it's possible for it to be set to 0 when it - * actually must be interpreted as being the same as the number of - * alternate data streams in another dentry in the hard link group that has - * a nonzero number of alternate data streams. - * - * - Make sure only the dentry->ads_entries array is only allocated for one - * dentry in the hard link group. This dentry will have - * dentry->ads_entries_status set to ADS_ENTRIES_OWNER, while the others - * will have dentry->ads_entries_status set to ADS_ENTRIES_USER. + * After splitting up each nominal inode into the "true" inodes we will + * canonicalize the link group by getting rid of all the unnecessary `struct + * inodes'. There will be just one `struct inode' for each hard link group + * remaining. */ -static int -fix_nominal_link_group(struct link_group *group, - struct link_group **new_groups) +static int fix_nominal_inode(struct inode *inode, + struct hlist_head *inode_list) { - struct dentry *tmp, *dentry, *ref_dentry; + struct dentry *dentry; + struct hlist_node *cur, *tmp; int ret; - size_t num_true_link_groups; - struct list_head *head; + 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); - LIST_HEAD(true_link_groups); + HLIST_HEAD(true_inodes); - /* Create a list of dentries in the nominal hard link group that have at + /* Create a list of dentries in the nominal inode that have at * least one data stream with a non-zero hash, and another list that * contains the dentries that have a zero hash for all data streams. */ - dentry = container_of(group->dentry_list, struct dentry, - link_group_list); - do { - for (unsigned i = 0; i <= dentry->num_ads; i++) { + inode_for_each_dentry(dentry, inode) { + for (unsigned i = 0; i <= dentry->d_inode->num_ads; i++) { const u8 *hash; - hash = dentry_stream_hash(dentry, i); + hash = inode_stream_hash(dentry->d_inode, i); if (!is_zero_hash(hash)) { list_add(&dentry->tmp_list, &dentries_with_data_streams); @@ -472,150 +336,142 @@ fix_nominal_link_group(struct link_group *group, list_add(&dentry->tmp_list, &dentries_with_no_data_streams); next_dentry: - dentry = container_of(dentry->link_group_list.next, - struct dentry, - link_group_list); - } while (&dentry->link_group_list != group->dentry_list); + ; + } /* If there are no dentries with data streams, we require the nominal - * link group to be a true link group */ + * inode to be a true inode */ if (list_empty(&dentries_with_data_streams)) { #ifdef ENABLE_DEBUG - { - size_t size = dentry_link_group_size(dentry); - if (size > 1) { - DEBUG("Found link group of size %zu without " - "any data streams:", size); - print_dentry_list(dentry); - DEBUG("We are going to interpret it as true " - "link group, provided that the dentries " - "are consistent."); - } + if (inode->link_count > 1) { + DEBUG("Found link group of size %u without " + "any data streams:", inode->link_count); + print_inode_dentries(inode); + DEBUG("We are going to interpret it as true " + "link group, provided that the dentries " + "are consistent."); } #endif - return fix_true_link_group(container_of(group->dentry_list, - struct dentry, - link_group_list)); + return fix_true_inode(inode, inode_list); } /* One or more dentries had data streams specified. We check each of * these dentries for consistency with the others to form a set of true - * link groups. */ - num_true_link_groups = 0; - list_for_each_entry_safe(dentry, tmp, &dentries_with_data_streams, - tmp_list) - { - list_del(&dentry->tmp_list); - - /* Look for a true link group that is consistent with - * this dentry and add this dentry to it. Or, if none - * of the true link groups are consistent with this - * dentry, make a new one. */ - list_for_each_entry(ref_dentry, &true_link_groups, tmp_list) { - if (ref_dentries_consistent(ref_dentry, dentry)) { - list_add(&dentry->link_group_list, - &ref_dentry->link_group_list); + * inodes. */ + num_true_inodes = 0; + list_for_each_entry(dentry, &dentries_with_data_streams, tmp_list) { + /* Look for a true inode that is consistent with this dentry and + * add this dentry to it. Or, if none of the true inodes are + * consistent with this dentry, add a new one (if that happens, + * we have split the hard link group). */ + hlist_for_each_entry(inode, cur, &true_inodes, hlist) { + if (ref_inodes_consistent(inode, dentry->d_inode)) { + inode_add_dentry(dentry, inode); goto next_dentry_2; } } - num_true_link_groups++; - list_add(&dentry->tmp_list, &true_link_groups); - INIT_LIST_HEAD(&dentry->link_group_list); + num_true_inodes++; + INIT_LIST_HEAD(&dentry->d_inode->dentry_list); + inode_add_dentry(dentry, dentry->d_inode); + hlist_add_head(&dentry->d_inode->hlist, &true_inodes); next_dentry_2: ; } - wimlib_assert(num_true_link_groups != 0); + wimlib_assert(num_true_inodes != 0); /* If there were dentries with no data streams, we require there to only - * be one true link group so that we know which link group to assign the + * be one true inode so that we know which inode to assign the * streamless dentries to. */ if (!list_empty(&dentries_with_no_data_streams)) { - if (num_true_link_groups != 1) { - ERROR("Hard link group ambiguity detected!"); - ERROR("We split up hard link group 0x%"PRIx64" due to " - "inconsistencies,", group->link_group_id); + if (num_true_inodes != 1) { + ERROR("Hard inode ambiguity detected!"); + ERROR("We split up inode 0x%"PRIx64" due to " + "inconsistencies,", inode->ino); ERROR("but dentries with no stream information remained. " - "We don't know which true hard link"); - ERROR("group to assign them to."); + "We don't know which inode"); + ERROR("to assign them to."); return WIMLIB_ERR_INVALID_DENTRY; } - /* Assign the streamless dentries to the one and only true link - * group. */ - ref_dentry = container_of(true_link_groups.next, - struct dentry, - tmp_list); + inode = container_of(true_inodes.first, struct inode, hlist); + /* Assign the streamless dentries to the one and only true + * inode. */ list_for_each_entry(dentry, &dentries_with_no_data_streams, tmp_list) - list_add(&dentry->link_group_list, &ref_dentry->link_group_list); + inode_add_dentry(dentry, inode); } - if (num_true_link_groups != 1) { - #ifdef ENABLE_DEBUG - { - printf("Split nominal link group 0x%"PRIx64" into %zu " - "link groups:\n", - group->link_group_id, num_true_link_groups); - puts("------------------------------------------------------------------------------"); - size_t i = 1; - list_for_each_entry(dentry, &true_link_groups, tmp_list) { - printf("[Split link group %zu]\n", i++); - print_dentry_list(dentry); - putchar('\n'); - } - puts("------------------------------------------------------------------------------"); + #ifdef ENABLE_DEBUG + if (num_true_inodes != 1) { + inode = container_of(true_inodes.first, struct inode, hlist); + + printf("Split nominal inode 0x%"PRIx64" into %zu " + "inodes:\n", + inode->ino, num_true_inodes); + puts("------------------------------------------------------------------------------"); + size_t i = 1; + hlist_for_each_entry(inode, cur, &true_inodes, hlist) { + printf("[Split inode %zu]\n", i++); + print_inode_dentries(inode); + putchar('\n'); } - #endif + puts("------------------------------------------------------------------------------"); } + #endif - list_for_each_entry(dentry, &true_link_groups, tmp_list) { - ret = fix_true_link_group(dentry); + hlist_for_each_entry_safe(inode, cur, tmp, &true_inodes, hlist) { + ret = fix_true_inode(inode, inode_list); if (ret != 0) return ret; } - - /* Make new `struct link_group's for the new link groups */ - for (head = true_link_groups.next->next; - head != &true_link_groups; - head = head->next) - { - dentry = container_of(head, struct dentry, tmp_list); - group = MALLOC(sizeof(*group)); - if (!group) { - ERROR("Out of memory"); - return WIMLIB_ERR_NOMEM; - } - group->link_group_id = dentry->link_group_id; - group->dentry_list = &dentry->link_group_list; - group->next = *new_groups; - *new_groups = group; - } return 0; } /* - * Goes through each link group and shares the ads_entries (Alternate Data - * Stream entries) field of each dentry among members of a hard link group. + * Goes through each hard link group (dentries sharing the same hard link group + * ID field) that's been inserted into the inode table and shares the `struct + * inode's among members of each hard link group. * - * In the process, the dentries in each link group are checked for consistency. - * If they contain data features that indicate they cannot really be in the same - * hard link group, this should be an error, but in reality this case needs to - * be handled, so we split the dentries into different hard link groups. + * In the process, the dentries belonging to each inode are checked for + * consistency. If they contain data features that indicate they cannot really + * correspond to the same inode, this should be an error, but in reality this + * case needs to be handled, so we split the dentries into different inodes. * - * One of the dentries in each hard link group group is arbitrarily assigned the - * role of "owner" of the memory pointed to by the @ads_entries field, - * (ADS_ENTRIES_OWNER), while the others are "users" (ADS_ENTRIES_USER) who are - * not allowed to free the memory. + * After this function returns, the inodes are no longer in the inode table, and + * the inode table should be destroyed. A list of the inodes, including all + * split inodes as well as the inodes that were good before, is returned in the + * list @inode_list. */ -int fix_link_groups(struct link_group_table *table) +static int fix_inodes(struct inode_table *table, struct hlist_head *inode_list) { + struct inode *inode; + struct hlist_node *cur, *tmp; + int ret; + INIT_HLIST_HEAD(inode_list); for (u64 i = 0; i < table->capacity; i++) { - struct link_group *group = table->array[i]; - while (group) { - int ret; - ret = fix_nominal_link_group(group, &table->extra_groups); + hlist_for_each_entry_safe(inode, cur, tmp, &table->array[i], hlist) { + ret = fix_nominal_inode(inode, inode_list); if (ret != 0) return ret; - group = group->next; } } + hlist_for_each_safe(cur, tmp, &table->extra_inodes) + hlist_add_head(cur, inode_list); return 0; } + +int dentry_tree_fix_inodes(struct dentry *root, struct hlist_head *inode_list) +{ + struct inode_table inode_tab; + int ret; + + DEBUG("Inserting dentries into inode table"); + ret = init_inode_table(&inode_tab, 9001); + if (ret != 0) + return ret; + + for_dentry_in_tree(root, inode_table_insert, &inode_tab); + + DEBUG("Cleaning up the hard link groups"); + ret = fix_inodes(&inode_tab, inode_list); + destroy_inode_table(&inode_tab); + return ret; +}