w -> wim
[wimlib] / src / swm.c
1 /*
2  * swm.c
3  *
4  * Functions to help handle split WIMs.
5  */
6
7 /*
8  * Copyright (C) 2012, 2013 Eric Biggers
9  *
10  * This file is part of wimlib, a library for working with WIM files.
11  *
12  * wimlib is free software; you can redistribute it and/or modify it under the
13  * terms of the GNU General Public License as published by the Free
14  * Software Foundation; either version 3 of the License, or (at your option)
15  * any later version.
16  *
17  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
18  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
19  * A PARTICULAR PURPOSE. See the GNU General Public License for more
20  * details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with wimlib; if not, see http://www.gnu.org/licenses/.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #  include "config.h"
28 #endif
29
30 #include "wimlib/error.h"
31 #include "wimlib/lookup_table.h"
32 #include "wimlib/swm.h"
33 #include "wimlib/wim.h"
34
35 static int
36 move_lte_to_table(struct wim_lookup_table_entry *lte, void *combined_table)
37 {
38         hlist_del(&lte->hash_list);
39         lookup_table_insert((struct wim_lookup_table*)combined_table, lte);
40         return 0;
41 }
42
43 static void
44 lookup_table_join(struct wim_lookup_table *combined_table,
45                   struct wim_lookup_table *part_table)
46 {
47         for_lookup_table_entry(part_table, move_lte_to_table, combined_table);
48         part_table->num_entries = 0;
49 }
50
51 /*
52  * merge_lookup_tables() - Merge lookup tables from the parts of a split WIM.
53  *
54  * @wim specifies the first part, while @additional_swms and @num_additional_swms
55  * specify an array of pointers to the WIMStruct's for additional split WIM parts.
56  *
57  * The reason we join the lookup tables is so we only have to search one lookup
58  * table to find the location of a resource in the entire WIM.
59  */
60 void
61 merge_lookup_tables(WIMStruct *wim,
62                     WIMStruct **additional_swms,
63                     unsigned num_additional_swms)
64 {
65         for (unsigned i = 0; i < num_additional_swms; i++)
66                 lookup_table_join(wim->lookup_table, additional_swms[i]->lookup_table);
67 }
68
69 static int
70 move_lte_to_orig_table(struct wim_lookup_table_entry *lte, void *_wim)
71 {
72         WIMStruct *wim = _wim;
73         if (lte->wim != wim) {
74                 move_lte_to_table(lte, lte->wim->lookup_table);
75                 wim->lookup_table->num_entries--;
76         }
77         return 0;
78 }
79
80 /* Undo merge_lookup_tables(), given the first WIM part that contains the merged
81  * lookup table. */
82 void
83 unmerge_lookup_table(WIMStruct *wim)
84 {
85         for_lookup_table_entry(wim->lookup_table, move_lte_to_orig_table, wim);
86 }
87
88 /*
89  * verify_swm_set: - Sanity checks to make sure a set of WIMs correctly
90  *                   correspond to a spanned set.
91  *
92  * @wim:
93  *      Part 1 of the set.
94  *
95  * @additional_swms:
96  *      All parts of the set other than part 1.
97  *
98  * @num_additional_swms:
99  *      Number of WIMStructs in @additional_swms.  Or, the total number of parts
100  *      in the set minus 1.
101  *
102  * @return:
103  *      0 on success; WIMLIB_ERR_SPLIT_INVALID if the set is not valid.
104  */
105 int
106 verify_swm_set(WIMStruct *wim, WIMStruct **additional_swms,
107                unsigned num_additional_swms)
108 {
109         unsigned total_parts = wim->hdr.total_parts;
110         int ctype;
111         const u8 *guid;
112
113         if (total_parts != num_additional_swms + 1) {
114                 ERROR("`%"TS"' says there are %u parts in the spanned set, "
115                       "but %"TS"%u part%"TS" provided",
116                       wim->filename, total_parts,
117                       (num_additional_swms + 1 < total_parts) ? T("only ") : T(""),
118                       num_additional_swms + 1,
119                       (num_additional_swms) ? T("s were") : T(" was"));
120                 return WIMLIB_ERR_SPLIT_INVALID;
121         }
122         if (wim->hdr.part_number != 1) {
123                 ERROR("WIM `%"TS"' is not the first part of the split WIM.",
124                       wim->filename);
125                 return WIMLIB_ERR_SPLIT_INVALID;
126         }
127         for (unsigned i = 0; i < num_additional_swms; i++) {
128                 if (additional_swms[i]->hdr.total_parts != total_parts) {
129                         ERROR("WIM `%"TS"' says there are %u parts in the "
130                               "spanned set, but %u parts were provided",
131                               additional_swms[i]->filename,
132                               additional_swms[i]->hdr.total_parts,
133                               total_parts);
134                         return WIMLIB_ERR_SPLIT_INVALID;
135                 }
136         }
137
138         /* keep track of ctype and guid just to make sure they are the same for
139          * all the WIMs. */
140         ctype = wim->compression_type;
141         guid = wim->hdr.guid;
142
143         {
144                 /* parts_to_swms is not allocated at function scope because it
145                  * should only be allocated after num_additional_swms was
146                  * checked to be the same as wim->hdr.total_parts.  Otherwise, it
147                  * could be unexpectedly high and cause a stack overflow. */
148                 WIMStruct *parts_to_swms[num_additional_swms];
149                 ZERO_ARRAY(parts_to_swms);
150                 for (unsigned i = 0; i < num_additional_swms; i++) {
151
152                         WIMStruct *swm = additional_swms[i];
153
154                         if (swm->compression_type != ctype) {
155                                 ERROR("The split WIMs do not all have the same "
156                                       "compression type");
157                                 return WIMLIB_ERR_SPLIT_INVALID;
158                         }
159                         if (memcmp(guid, swm->hdr.guid, WIM_GID_LEN) != 0) {
160                                 ERROR("The split WIMs do not all have the same "
161                                       "GUID");
162                                 return WIMLIB_ERR_SPLIT_INVALID;
163                         }
164                         if (swm->hdr.part_number == 1) {
165                                 ERROR("WIMs `%"TS"' and `%"TS"' both are marked "
166                                       "as the first WIM in the spanned set",
167                                       wim->filename, swm->filename);
168                                 return WIMLIB_ERR_SPLIT_INVALID;
169                         }
170                         if (swm->hdr.part_number == 0 ||
171                             swm->hdr.part_number > total_parts)
172                         {
173                                 ERROR("WIM `%"TS"' says it is part %u in the "
174                                       "spanned set, but the part number must "
175                                       "be in the range [1, %u]",
176                                       swm->filename, swm->hdr.part_number, total_parts);
177                                 return WIMLIB_ERR_SPLIT_INVALID;
178                         }
179                         if (parts_to_swms[swm->hdr.part_number - 2])
180                         {
181                                 ERROR("`%"TS"' and `%"TS"' are both marked as "
182                                       "part %u of %u in the spanned set",
183                                       parts_to_swms[swm->hdr.part_number - 2]->filename,
184                                       swm->filename,
185                                       swm->hdr.part_number,
186                                       total_parts);
187                                 return WIMLIB_ERR_SPLIT_INVALID;
188                         } else {
189                                 parts_to_swms[swm->hdr.part_number - 2] = swm;
190                         }
191                 }
192         }
193         return 0;
194 }