X-Git-Url: https://wimlib.net/git/?a=blobdiff_plain;f=src%2Fhardlink.c;h=7603670df55c8083146c15d2090bbe9e8315ef7a;hb=8f16067f71da5f45eda84813e9a07e68641bc283;hp=d2876e5e35185dc4ca51f58343e0ad56c47d72d3;hpb=67f45cecd793345416d5d85fbe37ec54b1bb6ef8;p=wimlib diff --git a/src/hardlink.c b/src/hardlink.c index d2876e5e..7603670d 100644 --- a/src/hardlink.c +++ b/src/hardlink.c @@ -32,22 +32,22 @@ /* NULL NULL * ^ ^ - * dentry | | - * / \ ----------- ----------- + * dentry | | + * / \ ----------- ----------- * | dentry<---| struct | | struct |---> dentry - * \ / |inode| |inode| + * \ / | inode | | inode | * dentry ------------ ------------ * ^ ^ * | | * | | dentry * ----------- ----------- / \ * dentry<---| struct | | struct |---> dentry dentry - * / |inode| |inode| \ / + * / | inode | | inode | \ / * dentry ------------ ------------ dentry * ^ ^ * | | * ----------------- - * inode_table->array | idx 0 | idx 1 | + * inode_table->array | idx 0 | idx 1 | * ----------------- */ @@ -59,7 +59,7 @@ struct inode_table { 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 @@ -73,32 +73,34 @@ struct inode_table { 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) +static inline void destroy_inode_table(struct inode_table *table) { - struct inode_table *table; - struct hlist_head *array; + FREE(table->array); +} - table = MALLOC(sizeof(struct inode_table)); - if (!table) - goto err; - array = CALLOC(capacity, sizeof(array[0])); - if (!array) { - FREE(table); - goto err; +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; 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 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 inode table based on its inode * ID. * @@ -113,39 +115,46 @@ err: * we keep a linked list of the single dentries, and assign them inode * numbers later. */ -int inode_table_insert(struct dentry *dentry, void *__table) +static 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; + struct inode *d_inode = dentry->d_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 */ pos = d_inode->ino % table->capacity; hlist_for_each_entry(inode, cur, &table->array[pos], hlist) { if (inode->ino == d_inode->ino) { - list_add(&dentry->inode_dentry_list, - &inode->dentry_list); + inode_add_dentry(dentry, inode); + 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,37 +162,15 @@ 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) { + DEBUG("Assigning inode numbers"); struct inode *inode; struct hlist_node *cur; 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++; } @@ -191,172 +178,142 @@ u64 assign_inode_numbers(struct hlist_head *inode_list) } -static void -print_inode_dentries(const struct inode *inode) +static void print_inode_dentries(const struct inode *inode) { struct dentry *dentry; - list_for_each_entry(dentry, &inode->dentry_list, inode_dentry_list) + inode_for_each_dentry(dentry, inode) printf("`%s'\n", dentry->full_path_utf8); } static void inconsistent_inode(const struct inode *inode) { - 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:"); +#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->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; } -#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->inode_dentry_list.next, - struct dentry, - inode_dentry_list)) != 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) +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; - list_for_each_entry(dentry, &inode->dentry_list, inode_dentry_list) { - if (!ref_dentry || ref_dentry->inode->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_inode(inode); - return WIMLIB_ERR_INVALID_DENTRY; - } else { - found_short_name = true; - } - } - if (dentry->inode->creation_time > last_ctime) - last_ctime = dentry->inode->creation_time; - if (dentry->inode->last_write_time > last_mtime) - last_mtime = dentry->inode->last_write_time; - if (dentry->inode->last_access_time > last_atime) - last_atime = dentry->inode->last_access_time; + 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; } - list_for_each_entry(dentry, &inode->dentry_list, inode_dentry_list) { + 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_inode(inode); + if (!inodes_consistent(ref_inode, dentry->d_inode)) { + inconsistent_inode(ref_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++; + free_inode(dentry->d_inode); + dentry->d_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; } -/* +/* * Fixes up a nominal inode. * * 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 inode are found to be inconsistent, we may split the inode - * into several "true" inodes. @new_inodes points to a linked list of - * these split inodes, and if we create any, they will be added to this list. + * into several "true" inodes. * - * After splitting up each nominal inode into the "true" inodes we - * will canonicalize the link group by getting rid of all the superfluous - * `struct inodes'. There will be just one `struct inode' for each hard link - * group remaining. + * 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_inode(struct inode *inode, struct hlist_head *inode_list) +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; + 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); @@ -364,10 +321,10 @@ fix_nominal_inode(struct inode *inode, struct hlist_head *inode_list) /* 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. */ - list_for_each_entry(dentry, &inode->dentry_list, inode_dentry_list) { - for (unsigned i = 0; i <= dentry->inode->num_ads; i++) { + inode_for_each_dentry(dentry, inode) { + for (unsigned i = 0; i <= dentry->d_inode->num_ads; i++) { const u8 *hash; - hash = inode_stream_hash(dentry->inode, i); + hash = inode_stream_hash(dentry->d_inode, i); if (!is_zero_hash(hash)) { list_add(&dentry->tmp_list, &dentries_with_data_streams); @@ -384,42 +341,37 @@ fix_nominal_inode(struct inode *inode, struct hlist_head *inode_list) * inode to be a true inode */ if (list_empty(&dentries_with_data_streams)) { #ifdef ENABLE_DEBUG - { - if (inode->link_count > 1) { - DEBUG("Found link group of size %zu 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."); - } + 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 - hlist_add_head(&inode->hlist, inode_list); - return fix_true_inode(inode); + 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 * 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, make a new one. */ + 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_dentries_consistent(inode_first_dentry(inode), dentry)) { - list_add(&dentry->inode_dentry_list, - &inode->dentry_list); + if (ref_inodes_consistent(inode, dentry->d_inode)) { + inode_add_dentry(dentry, inode); 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); + 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: ; } @@ -435,37 +387,36 @@ next_dentry_2: 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("inode 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 + inode = container_of(true_inodes.first, struct inode, hlist); + /* Assign the streamless dentries to the one and only true * 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); + inode_add_dentry(dentry, inode); } + #ifdef ENABLE_DEBUG if (num_true_inodes != 1) { - #ifdef ENABLE_DEBUG - { - 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'); - } - puts("------------------------------------------------------------------------------"); + 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 - hlist_for_each_entry(inode, cur, &true_inodes, hlist) { - hlist_add_head(&inode->hlist, inode_list); - ret = fix_true_inode(inode); + hlist_for_each_entry_safe(inode, cur, tmp, &true_inodes, hlist) { + ret = fix_true_inode(inode, inode_list); if (ret != 0) return ret; } @@ -473,26 +424,52 @@ next_dentry_2: } /* - * Goes through each inode and shares the inodes among members of a hard - * inode. + * 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 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. * - * In the process, the dentries in each inode are checked for consistency. - * If they contain data features that indicate they cannot really be in 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. + * 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_inodes(struct inode_table *table, struct hlist_head *inode_list) +static 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; } } + 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; }