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 inodes_consistent(const struct wim_inode *inode_1,
44 const struct wim_inode *inode_2)
46 /* This certainly isn't the only thing we need to check to make sure the
47 * inodes are consistent. However, this seems to be the only thing that
48 * the MS implementation checks when working around its own bug.
50 * (Tested: If two dentries share the same hard link group ID, Windows
51 * 8.1 DISM will link them if they have the same unnamed stream hash,
52 * even if the dentries provide different timestamps, attributes,
53 * alternate data streams, and security IDs! And the one that gets used
54 * will change if you merely swap the filenames. But if you use
55 * different unnamed stream hashes with everything else the same, it
56 * doesn't link the dentries.)
58 * For non-buggy WIMs this function will always return true. */
59 return hashes_equal(inode_unnamed_stream_hash(inode_1),
60 inode_unnamed_stream_hash(inode_2));
64 inode_table_insert(struct wim_dentry *dentry, void *_params)
66 struct inode_fixup_params *params = _params;
67 struct wim_inode_table *table = ¶ms->inode_table;
68 struct wim_inode *d_inode = dentry->d_inode;
70 struct wim_inode *inode;
71 struct hlist_node *cur;
73 if (d_inode->i_ino == 0) {
74 list_add_tail(&d_inode->i_list, &table->extra_inodes);
78 /* Try adding this dentry to an existing inode. */
79 pos = d_inode->i_ino % table->capacity;
80 hlist_for_each_entry(inode, cur, &table->array[pos], i_hlist) {
81 if (inode->i_ino != d_inode->i_ino) {
84 if (unlikely(!inodes_consistent(inode, d_inode))) {
85 params->num_inconsistent_inodes++;
88 if (unlikely((d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) ||
89 (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)))
91 params->num_dir_hard_links++;
92 if (params->num_dir_hard_links <=
93 MAX_DIR_HARD_LINK_WARNINGS)
95 WARNING("Unsupported directory hard link "
96 "\"%"TS"\" <=> \"%"TS"\"",
97 dentry_full_path(dentry),
98 inode_first_full_path(inode));
99 } else if (params->num_dir_hard_links ==
100 MAX_DIR_HARD_LINK_WARNINGS + 1)
102 WARNING("Suppressing additional warnings about "
103 "directory hard links...");
107 /* Transfer this dentry to the existing inode. */
109 dentry->d_inode = inode;
111 inode_add_dentry(dentry, inode);
115 /* Keep this dentry's inode. */
116 hlist_add_head(&d_inode->i_hlist, &table->array[pos]);
120 /* Move the inodes from the 'struct wim_inode_table' to the 'inode_list'. */
122 build_inode_list(struct wim_inode_table *inode_table,
123 struct list_head *inode_list)
125 list_splice(&inode_table->extra_inodes, inode_list);
126 for (size_t i = 0; i < inode_table->capacity; i++) {
127 while (!hlist_empty(&inode_table->array[i])) {
128 struct wim_inode *inode;
130 inode = hlist_entry(inode_table->array[i].first,
131 struct wim_inode, i_hlist);
132 hlist_del(&inode->i_hlist);
133 list_add(&inode->i_list, inode_list);
138 /* Re-assign inode numbers to the inodes in the list. */
140 reassign_inode_numbers(struct list_head *inode_list)
142 struct wim_inode *inode;
145 list_for_each_entry(inode, inode_list, i_list)
146 inode->i_ino = cur_ino++;
150 * Given a WIM image's tree of dentries such that each dentry initially
151 * has a unique inode associated with it, determine the actual
152 * dentry/inode information. Following this, a single inode may be named
153 * by more than one dentry (usually called a hard link).
155 * The 'hard_link_group_id' field of the on-disk WIM dentry, which we
156 * have read into 'i_ino' of each dentry's initial inode, determines
157 * which dentries share the same inode. Ideally, dentries share the same
158 * inode if and only if they have the same value in this field. However,
161 * - If 'hard_link_group_id' is 0, the corresponding dentry is the sole
162 * name for its inode.
163 * - Due to bugs in the Microsoft implementation, dentries with different
164 * 'hard_link_group_id' fields may, in fact, need to be interpreted as
165 * naming different inodes. This seems to mostly affect images in
166 * install.wim for Windows 7. I try to work around this in the same way
167 * the Microsoft implementation works around this.
169 * Returns 0 or WIMLIB_ERR_NOMEM. On success, the resulting inodes will be
170 * appended to the @inode_list, and they will have consistent numbers in their
174 dentry_tree_fix_inodes(struct wim_dentry *root, struct list_head *inode_list)
176 struct inode_fixup_params params;
179 /* We use a hash table to map inode numbers to inodes. */
181 ret = init_inode_table(¶ms.inode_table, 9001);
185 params.num_dir_hard_links = 0;
186 params.num_inconsistent_inodes = 0;
188 for_dentry_in_tree(root, inode_table_insert, ¶ms);
190 /* Generate the resulting list of inodes, and if needed reassign
191 * the inode numbers. */
192 build_inode_list(¶ms.inode_table, inode_list);
193 destroy_inode_table(¶ms.inode_table);
195 if (unlikely(params.num_inconsistent_inodes))
196 WARNING("Fixed %lu invalid hard links in WIM image",
197 params.num_inconsistent_inodes);
199 if (unlikely(params.num_dir_hard_links))
200 WARNING("Ignoring %lu directory hard links",
201 params.num_dir_hard_links);
203 if (unlikely(params.num_inconsistent_inodes ||
204 params.num_dir_hard_links))
205 reassign_inode_numbers(inode_list);