]> wimlib.net Git - wimlib/blob - src/split.c
split.c: fix finding extension of first split WIM part
[wimlib] / src / split.c
1 /*
2  * split.c
3  *
4  * Split a WIM file into parts.
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/alloca.h"
30 #include "wimlib/blob_table.h"
31 #include "wimlib/error.h"
32 #include "wimlib/list.h"
33 #include "wimlib/metadata.h"
34 #include "wimlib/paths.h"
35 #include "wimlib/progress.h"
36 #include "wimlib/resource.h"
37 #include "wimlib/wim.h"
38 #include "wimlib/write.h"
39
40 struct swm_part_info {
41         struct list_head blob_list;
42         u64 size;
43 };
44
45 static void
46 copy_part_info(struct swm_part_info *dst, struct swm_part_info *src)
47 {
48         list_replace(&src->blob_list, &dst->blob_list);
49         dst->size = src->size;
50 }
51
52 struct swm_info {
53         struct swm_part_info *parts;
54         unsigned num_parts;
55         unsigned num_alloc_parts;
56         u64 total_bytes;
57         u64 max_part_size;
58 };
59
60 static int
61 write_split_wim(WIMStruct *orig_wim, const tchar *swm_name,
62                 struct swm_info *swm_info, int write_flags)
63 {
64         size_t swm_name_len;
65         tchar *swm_name_buf;
66         const tchar *dot;
67         tchar *swm_suffix;
68         size_t swm_base_name_len;
69
70         union wimlib_progress_info progress;
71         unsigned part_number;
72         int ret;
73         u8 guid[GUID_SIZE];
74
75         swm_name_len = tstrlen(swm_name);
76         swm_name_buf = alloca((swm_name_len + 20) * sizeof(tchar));
77         tstrcpy(swm_name_buf, swm_name);
78         dot = tstrrchr(path_basename(swm_name_buf), T('.'));
79         if (dot) {
80                 swm_base_name_len = dot - swm_name_buf;
81                 swm_suffix = alloca((tstrlen(dot) + 1) * sizeof(tchar));
82                 tstrcpy(swm_suffix, dot);
83         } else {
84                 swm_base_name_len = swm_name_len;
85                 swm_suffix = alloca(1 * sizeof(tchar));
86                 swm_suffix[0] = T('\0');
87         }
88
89         progress.split.completed_bytes = 0;
90         progress.split.total_bytes = 0;
91         for (part_number = 1; part_number <= swm_info->num_parts; part_number++)
92                 progress.split.total_bytes += swm_info->parts[part_number - 1].size;
93         progress.split.total_parts = swm_info->num_parts;
94
95         generate_guid(guid);
96
97         for (part_number = 1; part_number <= swm_info->num_parts; part_number++) {
98                 int part_write_flags;
99                 wimlib_progress_func_t progfunc;
100
101                 if (part_number != 1) {
102                         tsprintf(swm_name_buf + swm_base_name_len,
103                                  T("%u%"TS), part_number, swm_suffix);
104                 }
105
106                 progress.split.cur_part_number = part_number;
107                 progress.split.part_name = swm_name_buf;
108
109                 ret = call_progress(orig_wim->progfunc,
110                                     WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART,
111                                     &progress,
112                                     orig_wim->progctx);
113                 if (ret)
114                         return ret;
115
116                 part_write_flags = write_flags;
117                 part_write_flags |= WIMLIB_WRITE_FLAG_USE_EXISTING_TOTALBYTES;
118                 if (part_number != 1)
119                         part_write_flags |= WIMLIB_WRITE_FLAG_NO_METADATA;
120
121                 progfunc = orig_wim->progfunc;
122                 orig_wim->progfunc = NULL;
123                 ret = write_wim_part(orig_wim,
124                                      progress.split.part_name,
125                                      WIMLIB_ALL_IMAGES,
126                                      part_write_flags,
127                                      1,
128                                      part_number,
129                                      swm_info->num_parts,
130                                      &swm_info->parts[part_number - 1].blob_list,
131                                      guid);
132                 orig_wim->progfunc = progfunc;
133                 if (ret)
134                         return ret;
135
136                 progress.split.completed_bytes += swm_info->parts[part_number - 1].size;
137
138                 ret = call_progress(orig_wim->progfunc,
139                                     WIMLIB_PROGRESS_MSG_SPLIT_END_PART,
140                                     &progress,
141                                     orig_wim->progctx);
142                 if (ret)
143                         return ret;
144         }
145         return 0;
146 }
147
148 static int
149 start_new_swm_part(struct swm_info *swm_info)
150 {
151         if (swm_info->num_parts == swm_info->num_alloc_parts) {
152                 struct swm_part_info *parts;
153                 size_t num_alloc_parts = swm_info->num_alloc_parts;
154
155                 num_alloc_parts += 8;
156                 parts = MALLOC(num_alloc_parts * sizeof(parts[0]));
157                 if (!parts)
158                         return WIMLIB_ERR_NOMEM;
159
160                 for (unsigned i = 0; i < swm_info->num_parts; i++)
161                         copy_part_info(&parts[i], &swm_info->parts[i]);
162
163                 FREE(swm_info->parts);
164                 swm_info->parts = parts;
165                 swm_info->num_alloc_parts = num_alloc_parts;
166         }
167         swm_info->num_parts++;
168         INIT_LIST_HEAD(&swm_info->parts[swm_info->num_parts - 1].blob_list);
169         swm_info->parts[swm_info->num_parts - 1].size = 0;
170         return 0;
171 }
172
173 static int
174 add_blob_to_swm(struct blob_descriptor *blob, void *_swm_info)
175 {
176         struct swm_info *swm_info = _swm_info;
177         u64 blob_stored_size;
178         int ret;
179
180         if (blob->blob_location == BLOB_IN_WIM)
181                 blob_stored_size = blob->rdesc->size_in_wim;
182         else
183                 blob_stored_size = blob->size;
184
185         /* Start the next part if adding this blob exceeds the maximum part
186          * size, UNLESS the blob is metadata or if no blobs at all have been
187          * added to the current part.  */
188         if ((swm_info->parts[swm_info->num_parts - 1].size +
189              blob_stored_size >= swm_info->max_part_size)
190             && !(blob->is_metadata ||
191                  swm_info->parts[swm_info->num_parts - 1].size == 0))
192         {
193                 ret = start_new_swm_part(swm_info);
194                 if (ret)
195                         return ret;
196         }
197         swm_info->parts[swm_info->num_parts - 1].size += blob_stored_size;
198         if (!blob->is_metadata) {
199                 list_add_tail(&blob->write_blobs_list,
200                               &swm_info->parts[swm_info->num_parts - 1].blob_list);
201         }
202         swm_info->total_bytes += blob_stored_size;
203         return 0;
204 }
205
206 /* API function documented in wimlib.h  */
207 WIMLIBAPI int
208 wimlib_split(WIMStruct *wim, const tchar *swm_name,
209              u64 part_size, int write_flags)
210 {
211         struct swm_info swm_info;
212         unsigned i;
213         int ret;
214
215         if (swm_name == NULL || swm_name[0] == T('\0') || part_size == 0)
216                 return WIMLIB_ERR_INVALID_PARAM;
217
218         if (write_flags & ~WIMLIB_WRITE_MASK_PUBLIC)
219                 return WIMLIB_ERR_INVALID_PARAM;
220
221         if (!wim_has_metadata(wim))
222                 return WIMLIB_ERR_METADATA_NOT_FOUND;
223
224         if (wim_has_solid_resources(wim)) {
225                 ERROR("Splitting of WIM containing solid resources is not supported.\n"
226                       "        Export it in non-solid format first.");
227                 return WIMLIB_ERR_UNSUPPORTED;
228         }
229
230         for (i = 0; i < wim->hdr.image_count; i++) {
231                 if (!is_image_unchanged_from_wim(wim->image_metadata[i], wim)) {
232                         ERROR("Only an unmodified, on-disk WIM file can be split.");
233                         return WIMLIB_ERR_UNSUPPORTED;
234                 }
235         }
236
237         memset(&swm_info, 0, sizeof(swm_info));
238         swm_info.max_part_size = part_size;
239
240         ret = start_new_swm_part(&swm_info);
241         if (ret)
242                 goto out_free_swm_info;
243
244         for (i = 0; i < wim->hdr.image_count; i++) {
245                 ret = add_blob_to_swm(wim->image_metadata[i]->metadata_blob,
246                                       &swm_info);
247                 if (ret)
248                         goto out_free_swm_info;
249         }
250
251         ret = for_blob_in_table_sorted_by_sequential_order(wim->blob_table,
252                                                            add_blob_to_swm,
253                                                            &swm_info);
254         if (ret)
255                 goto out_free_swm_info;
256
257         ret = write_split_wim(wim, swm_name, &swm_info, write_flags);
258 out_free_swm_info:
259         FREE(swm_info.parts);
260         return ret;
261 }