6 * Copyright (C) 2012, 2013, 2014 Eric Biggers
8 * This file is part of wimlib, a library for working with WIM files.
10 * wimlib is free software; you can redistribute it and/or modify it under the
11 * terms of the GNU General Public License as published by the Free
12 * Software Foundation; either version 3 of the License, or (at your option)
15 * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
16 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
17 * A PARTICULAR PURPOSE. See the GNU General Public License for more
20 * You should have received a copy of the GNU General Public License
21 * along with wimlib; if not, see http://www.gnu.org/licenses/.
28 #include "wimlib/dentry.h"
29 #include "wimlib/error.h"
30 #include "wimlib/inode.h"
31 #include "wimlib/inode_table.h"
32 #include "wimlib/lookup_table.h"
34 struct inode_fixup_params {
35 struct wim_inode_table inode_table;
36 unsigned long num_dir_hard_links;
37 unsigned long num_inconsistent_inodes;
40 #define MAX_DIR_HARD_LINK_WARNINGS 8
43 ads_entries_have_same_name(const struct wim_ads_entry *entry_1,
44 const struct wim_ads_entry *entry_2)
46 return entry_1->stream_name_nbytes == entry_2->stream_name_nbytes &&
47 !memcmp(entry_1->stream_name, entry_2->stream_name,
48 entry_1->stream_name_nbytes);
52 inodes_consistent(const struct wim_inode *inode_1,
53 const struct wim_inode *inode_2)
55 if (inode_1->i_num_ads != inode_2->i_num_ads)
58 for (u32 i = 0; i <= inode_1->i_num_ads; i++) {
59 const u8 *hash_1 = inode_stream_hash_unresolved(inode_1, i);
60 const u8 *hash_2 = inode_stream_hash_unresolved(inode_2, i);
62 if (!hashes_equal(hash_1, hash_2) && !is_zero_hash(hash_1))
65 if (i && !ads_entries_have_same_name(&inode_1->i_ads_entries[i - 1],
66 &inode_2->i_ads_entries[i - 1]))
70 if (inode_1->i_attributes != inode_2->i_attributes)
73 if (inode_1->i_security_id != inode_2->i_security_id)
80 inode_table_insert(struct wim_dentry *dentry, void *_params)
82 struct inode_fixup_params *params = _params;
83 struct wim_inode_table *table = ¶ms->inode_table;
84 struct wim_inode *d_inode = dentry->d_inode;
86 struct wim_inode *inode;
87 struct hlist_node *cur;
89 if (d_inode->i_ino == 0) {
90 list_add_tail(&d_inode->i_list, &table->extra_inodes);
94 /* Try adding this dentry to an existing inode. */
95 pos = d_inode->i_ino % table->capacity;
96 hlist_for_each_entry(inode, cur, &table->array[pos], i_hlist) {
97 if (inode->i_ino != d_inode->i_ino) {
100 if (unlikely(!inodes_consistent(inode, d_inode))) {
101 params->num_inconsistent_inodes++;
104 if (unlikely(d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)) {
105 params->num_dir_hard_links++;
106 if (params->num_dir_hard_links <=
107 MAX_DIR_HARD_LINK_WARNINGS)
109 WARNING("Unsupported directory hard link "
110 "\"%"TS"\" <=> \"%"TS"\"",
111 dentry_full_path(dentry),
112 inode_first_full_path(inode));
113 } else if (params->num_dir_hard_links ==
114 MAX_DIR_HARD_LINK_WARNINGS + 1)
116 WARNING("Suppressing additional warnings about "
117 "directory hard links...");
121 /* Transfer this dentry to the existing inode. */
123 dentry->d_inode = inode;
125 inode_add_dentry(dentry, inode);
129 /* Keep this dentry's inode. */
130 hlist_add_head(&d_inode->i_hlist, &table->array[pos]);
134 /* Move the inodes from the 'struct wim_inode_table' to the 'inode_list'. */
136 build_inode_list(struct wim_inode_table *inode_table,
137 struct list_head *inode_list)
139 list_splice(&inode_table->extra_inodes, inode_list);
140 for (size_t i = 0; i < inode_table->capacity; i++) {
141 while (!hlist_empty(&inode_table->array[i])) {
142 struct wim_inode *inode;
144 inode = hlist_entry(inode_table->array[i].first,
145 struct wim_inode, i_hlist);
146 hlist_del(&inode->i_hlist);
147 list_add(&inode->i_list, inode_list);
152 /* Re-assign inode numbers to the inodes in the list. */
154 reassign_inode_numbers(struct list_head *inode_list)
156 struct wim_inode *inode;
159 list_for_each_entry(inode, inode_list, i_list)
160 inode->i_ino = cur_ino++;
164 * Given a WIM image's tree of dentries such that each dentry initially
165 * has a unique inode associated with it, determine the actual
166 * dentry/inode information. Following this, a single inode may be named
167 * by more than one dentry (usually called a hard link).
169 * The 'hard_link_group_id' field of the on-disk WIM dentry, which we
170 * have read into 'i_ino' of each dentry's initial inode, determines
171 * which dentries share the same inode. Ideally, dentries share the same
172 * inode if and only if they have the same value in this field. However,
175 * - If 'hard_link_group_id' is 0, the corresponding dentry is the sole
176 * name for its inode.
177 * - Due to bugs in the Microsoft implementation, dentries with different
178 * 'hard_link_group_id' fields may, in fact, need to be interpreted as
179 * naming different inodes. This seems to mostly affect images in
180 * install.wim for Windows 7. I still have not been able to determine
181 * precisely how Microsoft's implementation generates and handles this
182 * invalid case, but we can at least try doing something reasonable by
183 * only joining a dentry with a different inode when this would not
184 * change the file's meaning (e.g. its contents or attributes).
186 * Returns 0 or WIMLIB_ERR_NOMEM. On success, the resulting inodes will be
187 * appended to the @inode_list, and they will have consistent numbers in their
191 dentry_tree_fix_inodes(struct wim_dentry *root, struct list_head *inode_list)
193 struct inode_fixup_params params;
196 /* We use a hash table to map inode numbers to inodes. */
198 ret = init_inode_table(¶ms.inode_table, 9001);
202 params.num_dir_hard_links = 0;
203 params.num_inconsistent_inodes = 0;
205 for_dentry_in_tree(root, inode_table_insert, ¶ms);
207 /* Generate the resulting list of inodes, and if needed reassign
208 * the inode numbers. */
209 build_inode_list(¶ms.inode_table, inode_list);
210 destroy_inode_table(¶ms.inode_table);
212 if (unlikely(params.num_inconsistent_inodes))
213 WARNING("Fixed %lu invalid hard links in WIM image",
214 params.num_inconsistent_inodes);
216 if (unlikely(params.num_dir_hard_links))
217 WARNING("Ignoring %lu directory hard links",
218 params.num_dir_hard_links);
220 if (unlikely(params.num_inconsistent_inodes ||
221 params.num_dir_hard_links))
222 reassign_inode_numbers(inode_list);