6602832a94a4fe05ce2061c44baff5ed4be936e7
[wimlib] / src / verify.c
1 /*
2  * verify.c
3  *
4  * Some functions to verify that stuff in the WIM is valid.  Of course, not
5  * *all* the verifications of the input data are in this file.
6  */
7
8 /*
9  * Copyright (C) 2012 Eric Biggers
10  *
11  * wimlib - Library for working with WIM files
12  *
13  * This file is part of wimlib, a library for working with WIM files.
14  *
15  * wimlib is free software; you can redistribute it and/or modify it under the
16  * terms of the GNU General Public License as published by the Free
17  * Software Foundation; either version 3 of the License, or (at your option)
18  * any later version.
19  *
20  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
21  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
22  * A PARTICULAR PURPOSE. See the GNU General Public License for more
23  * details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with wimlib; if not, see http://www.gnu.org/licenses/.
27  */
28
29 #include "wimlib_internal.h"
30 #include "dentry.h"
31 #include "lookup_table.h"
32
33 static int verify_inode(struct wim_inode *inode, const WIMStruct *w)
34 {
35         const struct wim_lookup_table *table = w->lookup_table;
36         const struct wim_security_data *sd = wim_const_security_data(w);
37         const struct wim_dentry *first_dentry = inode_first_dentry(inode);
38         int ret = WIMLIB_ERR_INVALID_DENTRY;
39
40         /* Check the security ID */
41         if (inode->i_security_id < -1) {
42                 ERROR("Dentry `%s' has an invalid security ID (%d)",
43                         first_dentry->full_path_utf8, inode->i_security_id);
44                 goto out;
45         }
46         if (inode->i_security_id >= sd->num_entries) {
47                 ERROR("Dentry `%s' has an invalid security ID (%d) "
48                       "(there are only %u entries in the security table)",
49                         first_dentry->full_path_utf8, inode->i_security_id,
50                         sd->num_entries);
51                 goto out;
52         }
53
54         /* Check that lookup table entries for all the resources exist, except
55          * if the SHA1 message digest is all 0's, which indicates there is
56          * intentionally no resource there.  */
57         if (w->hdr.total_parts == 1) {
58                 for (unsigned i = 0; i <= inode->i_num_ads; i++) {
59                         struct wim_lookup_table_entry *lte;
60                         const u8 *hash;
61                         hash = inode_stream_hash_unresolved(inode, i);
62                         lte = __lookup_resource(table, hash);
63                         if (!lte && !is_zero_hash(hash)) {
64                                 ERROR("Could not find lookup table entry for stream "
65                                       "%u of dentry `%s'", i, first_dentry->full_path_utf8);
66                                 goto out;
67                         }
68                         if (lte)
69                                 lte->real_refcnt += inode->i_nlink;
70
71                         /* The following is now done when required by
72                          * wim_run_full_verifications(). */
73
74                 #if 0
75                         if (lte && !w->full_verification_in_progress &&
76                             lte->real_refcnt > lte->refcnt)
77                         {
78                         #ifdef ENABLE_ERROR_MESSAGES
79                                 WARNING("The following lookup table entry "
80                                         "has a reference count of %u, but",
81                                         lte->refcnt);
82                                 WARNING("We found %u references to it",
83                                         lte->real_refcnt);
84                                 WARNING("(One dentry referencing it is at `%s')",
85                                          first_dentry->full_path_utf8);
86
87                                 print_lookup_table_entry(lte);
88                         #endif
89                                 /* Guess what!  install.wim for Windows 8
90                                  * contains many streams referenced by more
91                                  * dentries than the refcnt stated in the lookup
92                                  * table entry.  So we will need to handle this
93                                  * case and not just make it be an error...  I'm
94                                  * just setting the reference count to the
95                                  * number of references we found.
96                                  * (Unfortunately, even after doing this, the
97                                  * reference count could be too low if it's also
98                                  * referenced in other WIM images) */
99
100                         #if 1
101                                 lte->refcnt = lte->real_refcnt;
102                                 WARNING("Fixing reference count");
103                         #else
104                                 goto out;
105                         #endif
106                         }
107                 #endif
108                 }
109         }
110
111         /* Make sure there is only one un-named stream. */
112         unsigned num_unnamed_streams = 0;
113         for (unsigned i = 0; i <= inode->i_num_ads; i++) {
114                 const u8 *hash;
115                 hash = inode_stream_hash_unresolved(inode, i);
116                 if (inode_stream_name_len(inode, i) == 0 && !is_zero_hash(hash))
117                         num_unnamed_streams++;
118         }
119         if (num_unnamed_streams > 1) {
120                 ERROR("Dentry `%s' has multiple (%u) un-named streams",
121                       first_dentry->full_path_utf8, num_unnamed_streams);
122                 goto out;
123         }
124         inode->i_verified = 1;
125         ret = 0;
126 out:
127         return ret;
128 }
129
130 /* Run some miscellaneous verifications on a WIM dentry */
131 int verify_dentry(struct wim_dentry *dentry, void *wim)
132 {
133         int ret;
134
135         if (!dentry->d_inode->i_verified) {
136                 ret = verify_inode(dentry->d_inode, wim);
137                 if (ret != 0)
138                         return ret;
139         }
140
141         /* Cannot have a short name but no long name */
142         if (dentry->short_name_len && !dentry->file_name_len) {
143                 ERROR("Dentry `%s' has a short name but no long name",
144                       dentry->full_path_utf8);
145                 return WIMLIB_ERR_INVALID_DENTRY;
146         }
147
148         /* Make sure root dentry is unnamed */
149         if (dentry_is_root(dentry)) {
150                 if (dentry->file_name_len) {
151                         ERROR("The root dentry is named `%s', but it must "
152                               "be unnamed", dentry->file_name_utf8);
153                         return WIMLIB_ERR_INVALID_DENTRY;
154                 }
155         }
156
157 #if 0
158         /* Check timestamps */
159         if (inode->i_last_access_time < inode->i_creation_time ||
160             inode->i_last_write_time < inode->i_creation_time) {
161                 WARNING("Dentry `%s' was created after it was last accessed or "
162                       "written to", dentry->full_path_utf8);
163         }
164 #endif
165
166         return 0;
167 }
168
169 static int image_run_full_verifications(WIMStruct *w)
170 {
171         return for_dentry_in_tree(wim_root_dentry(w), verify_dentry, w);
172 }
173
174 static int lte_fix_refcnt(struct wim_lookup_table_entry *lte, void *ctr)
175 {
176         if (lte->refcnt != lte->real_refcnt) {
177                 WARNING("The following lookup table entry has a reference "
178                         "count of %u, but", lte->refcnt);
179                 WARNING("We found %u references to it",
180                         lte->real_refcnt);
181                 print_lookup_table_entry(lte);
182                 lte->refcnt = lte->real_refcnt;
183                 ++*(unsigned long *)ctr;
184         }
185         return 0;
186 }
187
188 /* Ideally this would be unnecessary... however, the WIMs for Windows 8 are
189  * screwed up because some lookup table entries are referenced more times than
190  * their stated reference counts.  So theoretically, if we delete all the
191  * references to a stream and then remove it, it might still be referenced
192  * somewhere else, making a file be missing from the WIM... So, work around this
193  * problem by looking at ALL the images to re-calculate the reference count of
194  * EVERY lookup table entry.  This only absolutely has to be done before an image
195  * is deleted or before an image is mounted read-write. */
196 int wim_run_full_verifications(WIMStruct *w)
197 {
198         int ret;
199
200         for_lookup_table_entry(w->lookup_table, lte_zero_real_refcnt, NULL);
201         w->all_images_verified = 1;
202         w->full_verification_in_progress = 1;
203         ret = for_image(w, WIMLIB_ALL_IMAGES, image_run_full_verifications);
204         w->full_verification_in_progress = 0;
205         if (ret == 0) {
206                 unsigned long num_ltes_with_bogus_refcnt = 0;
207                 for (int i = 0; i < w->hdr.image_count; i++)
208                         w->image_metadata[i].metadata_lte->real_refcnt++;
209                 for_lookup_table_entry(w->lookup_table, lte_fix_refcnt,
210                                        &num_ltes_with_bogus_refcnt);
211                 if (num_ltes_with_bogus_refcnt != 0) {
212                         WARNING("A total of %lu entries in the WIM's stream "
213                                 "lookup table had to have\n"
214                                 "          their reference counts fixed.",
215                                 num_ltes_with_bogus_refcnt);
216                 }
217         } else {
218                 w->all_images_verified = 0;
219         }
220         return ret;
221 }
222
223 /*
224  * verify_swm_set: - Sanity checks to make sure a set of WIMs correctly
225  *                   correspond to a spanned set.
226  *
227  * @w:
228  *      Part 1 of the set.
229  *
230  * @additional_swms:
231  *      All parts of the set other than part 1.
232  *
233  * @num_additional_swms:
234  *      Number of WIMStructs in @additional_swms.  Or, the total number of parts
235  *      in the set minus 1.
236  *
237  * @return:
238  *      0 on success; WIMLIB_ERR_SPLIT_INVALID if the set is not valid.
239  */
240 int verify_swm_set(WIMStruct *w, WIMStruct **additional_swms,
241                    unsigned num_additional_swms)
242 {
243         unsigned total_parts = w->hdr.total_parts;
244         int ctype;
245         const u8 *guid;
246
247         if (total_parts != num_additional_swms + 1) {
248                 ERROR("`%s' says there are %u parts in the spanned set, "
249                       "but %s%u part%s provided",
250                       w->filename, total_parts,
251                       (num_additional_swms + 1 < total_parts) ? "only " : "",
252                       num_additional_swms + 1,
253                       (num_additional_swms) ? "s were" : " was");
254                 return WIMLIB_ERR_SPLIT_INVALID;
255         }
256         if (w->hdr.part_number != 1) {
257                 ERROR("WIM `%s' is not the first part of the split WIM.",
258                       w->filename);
259                 return WIMLIB_ERR_SPLIT_INVALID;
260         }
261         for (unsigned i = 0; i < num_additional_swms; i++) {
262                 if (additional_swms[i]->hdr.total_parts != total_parts) {
263                         ERROR("WIM `%s' says there are %u parts in the spanned set, "
264                               "but %u parts were provided",
265                               additional_swms[i]->filename,
266                               additional_swms[i]->hdr.total_parts,
267                               total_parts);
268                         return WIMLIB_ERR_SPLIT_INVALID;
269                 }
270         }
271
272         /* keep track of ctype and guid just to make sure they are the same for
273          * all the WIMs. */
274         ctype = wimlib_get_compression_type(w);
275         guid = w->hdr.guid;
276
277         WIMStruct *parts_to_swms[num_additional_swms];
278         ZERO_ARRAY(parts_to_swms);
279         for (unsigned i = 0; i < num_additional_swms; i++) {
280
281                 WIMStruct *swm = additional_swms[i];
282
283                 if (wimlib_get_compression_type(swm) != ctype) {
284                         ERROR("The split WIMs do not all have the same "
285                               "compression type");
286                         return WIMLIB_ERR_SPLIT_INVALID;
287                 }
288                 if (memcmp(guid, swm->hdr.guid, WIM_GID_LEN) != 0) {
289                         ERROR("The split WIMs do not all have the same "
290                               "GUID");
291                         return WIMLIB_ERR_SPLIT_INVALID;
292                 }
293                 if (swm->hdr.part_number == 1) {
294                         ERROR("WIMs `%s' and `%s' both are marked as the "
295                               "first WIM in the spanned set",
296                               w->filename, swm->filename);
297                         return WIMLIB_ERR_SPLIT_INVALID;
298                 }
299                 if (swm->hdr.part_number == 0 ||
300                     swm->hdr.part_number > total_parts)
301                 {
302                         ERROR("WIM `%s' says it is part %u in the spanned set, "
303                               "but the part number must be in the range "
304                               "[1, %u]",
305                               swm->filename, swm->hdr.part_number, total_parts);
306                         return WIMLIB_ERR_SPLIT_INVALID;
307                 }
308                 if (parts_to_swms[swm->hdr.part_number - 2])
309                 {
310                         ERROR("`%s' and `%s' are both marked as part %u of %u "
311                               "in the spanned set",
312                               parts_to_swms[swm->hdr.part_number - 2]->filename,
313                               swm->filename,
314                               swm->hdr.part_number,
315                               total_parts);
316                         return WIMLIB_ERR_SPLIT_INVALID;
317                 } else {
318                         parts_to_swms[swm->hdr.part_number - 2] = swm;
319                 }
320         }
321         return 0;
322 }
323