da4ac406ec70226bf8975821eb4937bc425eeb53
[wimlib] / src / join.c
1 /*
2  * join.c
3  *
4  * Join split WIMs (sometimes named as .swm files) together into one WIM.
5  */
6
7 /*
8  * Copyright (C) 2012, 2013 Eric Biggers
9  *
10  * This file is free software; you can redistribute it and/or modify it under
11  * the terms of the GNU Lesser General Public License as published by the Free
12  * Software Foundation; either version 3 of the License, or (at your option) any
13  * later version.
14  *
15  * This file is distributed in the hope that it will be useful, but WITHOUT
16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this file; 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.h"
29 #include "wimlib/error.h"
30 #include "wimlib/types.h"
31 #include "wimlib/util.h"
32 #include "wimlib/wim.h"
33
34 /*
35  * verify_swm_set: - Sanity checks to make sure a set of WIMs correctly
36  *                   correspond to a spanned set.
37  *
38  * @wim:
39  *      Part 1 of the set.
40  *
41  * @additional_swms:
42  *      All parts of the set other than part 1.
43  *
44  * @num_additional_swms:
45  *      Number of WIMStructs in @additional_swms.  Or, the total number of parts
46  *      in the set minus 1.
47  *
48  * @return:
49  *      0 on success; WIMLIB_ERR_SPLIT_INVALID if the set is not valid.
50  */
51 static int
52 verify_swm_set(WIMStruct *wim, WIMStruct **additional_swms,
53                unsigned num_additional_swms)
54 {
55         unsigned total_parts = wim->hdr.total_parts;
56         int ctype;
57         u32 chunk_size;
58         const u8 *guid;
59
60         if (total_parts != num_additional_swms + 1) {
61                 ERROR("`%"TS"' says there are %u parts in the spanned set, "
62                       "but %"TS"%u part%"TS" provided",
63                       wim->filename, total_parts,
64                       (num_additional_swms + 1 < total_parts) ? T("only ") : T(""),
65                       num_additional_swms + 1,
66                       (num_additional_swms) ? T("s were") : T(" was"));
67                 return WIMLIB_ERR_SPLIT_INVALID;
68         }
69         if (wim->hdr.part_number != 1) {
70                 ERROR("WIM `%"TS"' is not the first part of the split WIM.",
71                       wim->filename);
72                 return WIMLIB_ERR_SPLIT_INVALID;
73         }
74         for (unsigned i = 0; i < num_additional_swms; i++) {
75                 if (additional_swms[i]->hdr.total_parts != total_parts) {
76                         ERROR("WIM `%"TS"' says there are %u parts in the "
77                               "spanned set, but %u parts were provided",
78                               additional_swms[i]->filename,
79                               additional_swms[i]->hdr.total_parts,
80                               total_parts);
81                         return WIMLIB_ERR_SPLIT_INVALID;
82                 }
83         }
84
85         /* Keep track of the compression type, chunk size, and GUID to make sure
86          * they are the same for all the WIMs.  */
87         ctype = wim->compression_type;
88         chunk_size = wim->chunk_size;
89         guid = wim->hdr.guid;
90
91         {
92                 /* parts_to_swms is not allocated at function scope because it
93                  * should only be allocated after num_additional_swms was
94                  * checked to be the same as wim->hdr.total_parts.  Otherwise, it
95                  * could be unexpectedly high and cause a stack overflow. */
96                 WIMStruct *parts_to_swms[num_additional_swms];
97                 ZERO_ARRAY(parts_to_swms);
98                 for (unsigned i = 0; i < num_additional_swms; i++) {
99
100                         WIMStruct *swm = additional_swms[i];
101
102                         if (swm->compression_type != ctype) {
103                                 ERROR("The split WIMs do not all have the same "
104                                       "compression type");
105                                 return WIMLIB_ERR_SPLIT_INVALID;
106                         }
107                         if (swm->chunk_size != chunk_size &&
108                             ctype != WIMLIB_COMPRESSION_TYPE_NONE) {
109                                 ERROR("The split WIMs do not all have the same "
110                                       "chunk size");
111                                 return WIMLIB_ERR_SPLIT_INVALID;
112                         }
113                         if (memcmp(guid, swm->hdr.guid, WIM_GUID_LEN) != 0) {
114                                 ERROR("The split WIMs do not all have the same "
115                                       "GUID");
116                                 return WIMLIB_ERR_SPLIT_INVALID;
117                         }
118                         if (swm->hdr.part_number == 1) {
119                                 ERROR("WIMs `%"TS"' and `%"TS"' both are marked "
120                                       "as the first WIM in the spanned set",
121                                       wim->filename, swm->filename);
122                                 return WIMLIB_ERR_SPLIT_INVALID;
123                         }
124                         if (swm->hdr.part_number == 0 ||
125                             swm->hdr.part_number > total_parts)
126                         {
127                                 ERROR("WIM `%"TS"' says it is part %u in the "
128                                       "spanned set, but the part number must "
129                                       "be in the range [1, %u]",
130                                       swm->filename, swm->hdr.part_number, total_parts);
131                                 return WIMLIB_ERR_SPLIT_INVALID;
132                         }
133                         if (parts_to_swms[swm->hdr.part_number - 2])
134                         {
135                                 ERROR("`%"TS"' and `%"TS"' are both marked as "
136                                       "part %u of %u in the spanned set",
137                                       parts_to_swms[swm->hdr.part_number - 2]->filename,
138                                       swm->filename,
139                                       swm->hdr.part_number,
140                                       total_parts);
141                                 return WIMLIB_ERR_SPLIT_INVALID;
142                         } else {
143                                 parts_to_swms[swm->hdr.part_number - 2] = swm;
144                         }
145                 }
146         }
147         return 0;
148 }
149
150 WIMLIBAPI int
151 wimlib_join_with_progress(const tchar * const *swm_names,
152                           unsigned num_swms,
153                           const tchar *output_path,
154                           int swm_open_flags,
155                           int wim_write_flags,
156                           wimlib_progress_func_t progfunc,
157                           void *progctx)
158 {
159         int ret;
160         unsigned i;
161         unsigned j;
162         WIMStruct *swm0;
163         WIMStruct **additional_swms;
164         unsigned num_additional_swms;
165
166         if (num_swms < 1 || num_swms > 0xffff)
167                 return WIMLIB_ERR_INVALID_PARAM;
168         num_additional_swms = num_swms - 1;
169
170         additional_swms = CALLOC((num_additional_swms + 1),
171                                  sizeof(additional_swms[0]));
172         if (!additional_swms)
173                 return WIMLIB_ERR_NOMEM;
174
175         swm0 = NULL;
176         for (i = 0, j = 0; i < num_swms; i++) {
177                 WIMStruct *swm;
178
179                 ret = wimlib_open_wim_with_progress(swm_names[i],
180                                                     swm_open_flags,
181                                                     &swm,
182                                                     progfunc,
183                                                     progctx);
184                 if (ret)
185                         goto out_free_swms;
186                 if (swm->hdr.part_number == 1 && swm0 == NULL)
187                         swm0 = swm;
188                 else
189                         additional_swms[j++] = swm;
190         }
191
192         if (!swm0) {
193                 ERROR("Part 1 of the split WIM was not specified!");
194                 ret = WIMLIB_ERR_SPLIT_INVALID;
195                 goto out_free_swms;
196         }
197
198         ret = verify_swm_set(swm0, additional_swms, num_additional_swms);
199         if (ret)
200                 goto out_free_swms;
201
202         ret = wimlib_reference_resources(swm0, additional_swms,
203                                          num_additional_swms, 0);
204         if (ret)
205                 goto out_free_swms;
206
207         /* It is reasonably safe to provide, WIMLIB_WRITE_FLAG_STREAMS_OK, as we
208          * have verified that the specified split WIM parts form a spanned set.
209          */
210         ret = wimlib_write(swm0, output_path, WIMLIB_ALL_IMAGES,
211                            wim_write_flags |
212                                 WIMLIB_WRITE_FLAG_STREAMS_OK |
213                                 WIMLIB_WRITE_FLAG_RETAIN_GUID,
214                            1);
215 out_free_swms:
216         for (i = 0; i < num_additional_swms + 1; i++)
217                 wimlib_free(additional_swms[i]);
218         FREE(additional_swms);
219         wimlib_free(swm0);
220         return ret;
221 }
222
223 /* API function documented in wimlib.h  */
224 WIMLIBAPI int
225 wimlib_join(const tchar * const *swm_names,
226             unsigned num_swms,
227             const tchar *output_path,
228             int swm_open_flags,
229             int wim_write_flags)
230 {
231         return wimlib_join_with_progress(swm_names, num_swms, output_path,
232                                          swm_open_flags, wim_write_flags,
233                                          NULL, NULL);
234 }