]> wimlib.net Git - wimlib/blob - src/inode_fixup.c
Remove some dead assignments
[wimlib] / src / inode_fixup.c
1 /*
2  * inode_fixup.c
3  */
4
5 /*
6  * Copyright (C) 2012, 2013, 2014 Eric Biggers
7  *
8  * This file is part of wimlib, a library for working with WIM files.
9  *
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)
13  * any later version.
14  *
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
18  * details.
19  *
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/.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27
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"
33
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;
38 };
39
40 #define MAX_DIR_HARD_LINK_WARNINGS 8
41
42 static bool
43 inodes_consistent(const struct wim_inode *inode_1,
44                   const struct wim_inode *inode_2)
45 {
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.
49          *
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.)
57          *
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));
61 }
62
63 static int
64 inode_table_insert(struct wim_dentry *dentry, void *_params)
65 {
66         struct inode_fixup_params *params = _params;
67         struct wim_inode_table *table = &params->inode_table;
68         struct wim_inode *d_inode = dentry->d_inode;
69         size_t pos;
70         struct wim_inode *inode;
71         struct hlist_node *cur;
72
73         if (d_inode->i_ino == 0) {
74                 list_add_tail(&d_inode->i_list, &table->extra_inodes);
75                 return 0;
76         }
77
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) {
82                         continue;
83                 }
84                 if (unlikely(!inodes_consistent(inode, d_inode))) {
85                         params->num_inconsistent_inodes++;
86                         continue;
87                 }
88                 if (unlikely((d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) ||
89                              (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)))
90                 {
91                         params->num_dir_hard_links++;
92                         if (params->num_dir_hard_links <=
93                             MAX_DIR_HARD_LINK_WARNINGS)
94                         {
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)
101                         {
102                                 WARNING("Suppressing additional warnings about "
103                                         "directory hard links...");
104                         }
105                         continue;
106                 }
107                 /* Transfer this dentry to the existing inode.  */
108                 free_inode(d_inode);
109                 dentry->d_inode = inode;
110                 inode->i_nlink++;
111                 inode_add_dentry(dentry, inode);
112                 return 0;
113         }
114
115         /* Keep this dentry's inode.  */
116         hlist_add_head(&d_inode->i_hlist, &table->array[pos]);
117         return 0;
118 }
119
120 /* Move the inodes from the 'struct wim_inode_table' to the 'inode_list'.  */
121 static void
122 build_inode_list(struct wim_inode_table *inode_table,
123                  struct list_head *inode_list)
124 {
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;
129
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);
134                 }
135         }
136 }
137
138 /* Re-assign inode numbers to the inodes in the list.  */
139 static void
140 reassign_inode_numbers(struct list_head *inode_list)
141 {
142         struct wim_inode *inode;
143         u64 cur_ino = 1;
144
145         list_for_each_entry(inode, inode_list, i_list)
146                 inode->i_ino = cur_ino++;
147 }
148
149 /*
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).
154  *
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,
159  * exceptions apply:
160  *
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.
168  *
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
171  * i_ino fields.
172  */
173 int
174 dentry_tree_fix_inodes(struct wim_dentry *root, struct list_head *inode_list)
175 {
176         struct inode_fixup_params params;
177         int ret;
178
179         /* We use a hash table to map inode numbers to inodes.  */
180
181         ret = init_inode_table(&params.inode_table, 9001);
182         if (ret)
183                 return ret;
184
185         params.num_dir_hard_links = 0;
186         params.num_inconsistent_inodes = 0;
187
188         for_dentry_in_tree(root, inode_table_insert, &params);
189
190         /* Generate the resulting list of inodes, and if needed reassign
191          * the inode numbers.  */
192         build_inode_list(&params.inode_table, inode_list);
193         destroy_inode_table(&params.inode_table);
194
195         if (unlikely(params.num_inconsistent_inodes))
196                 WARNING("Fixed %lu invalid hard links in WIM image",
197                         params.num_inconsistent_inodes);
198
199         if (unlikely(params.num_dir_hard_links))
200                 WARNING("Ignoring %lu directory hard links",
201                         params.num_dir_hard_links);
202
203         if (unlikely(params.num_inconsistent_inodes ||
204                      params.num_dir_hard_links))
205                 reassign_inode_numbers(inode_list);
206         return 0;
207 }