]> wimlib.net Git - wimlib/blob - src/split.c
v1.14.4
[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
100                 if (part_number != 1) {
101                         tsprintf(swm_name_buf + swm_base_name_len,
102                                  T("%u%"TS), part_number, swm_suffix);
103                 }
104
105                 progress.split.cur_part_number = part_number;
106                 progress.split.part_name = swm_name_buf;
107
108                 ret = call_progress(orig_wim->progfunc,
109                                     WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART,
110                                     &progress,
111                                     orig_wim->progctx);
112                 if (ret)
113                         return ret;
114
115                 part_write_flags = write_flags;
116                 part_write_flags |= WIMLIB_WRITE_FLAG_USE_EXISTING_TOTALBYTES;
117                 if (part_number != 1)
118                         part_write_flags |= WIMLIB_WRITE_FLAG_NO_METADATA;
119
120                 ret = write_wim_part(orig_wim,
121                                      progress.split.part_name,
122                                      WIMLIB_ALL_IMAGES,
123                                      part_write_flags,
124                                      1,
125                                      part_number,
126                                      swm_info->num_parts,
127                                      &swm_info->parts[part_number - 1].blob_list,
128                                      guid);
129                 if (ret)
130                         return ret;
131
132                 progress.split.completed_bytes += swm_info->parts[part_number - 1].size;
133
134                 ret = call_progress(orig_wim->progfunc,
135                                     WIMLIB_PROGRESS_MSG_SPLIT_END_PART,
136                                     &progress,
137                                     orig_wim->progctx);
138                 if (ret)
139                         return ret;
140         }
141         return 0;
142 }
143
144 static int
145 start_new_swm_part(struct swm_info *swm_info)
146 {
147         if (swm_info->num_parts == swm_info->num_alloc_parts) {
148                 struct swm_part_info *parts;
149                 size_t num_alloc_parts = swm_info->num_alloc_parts;
150
151                 num_alloc_parts += 8;
152                 parts = MALLOC(num_alloc_parts * sizeof(parts[0]));
153                 if (!parts)
154                         return WIMLIB_ERR_NOMEM;
155
156                 for (unsigned i = 0; i < swm_info->num_parts; i++)
157                         copy_part_info(&parts[i], &swm_info->parts[i]);
158
159                 FREE(swm_info->parts);
160                 swm_info->parts = parts;
161                 swm_info->num_alloc_parts = num_alloc_parts;
162         }
163         swm_info->num_parts++;
164         INIT_LIST_HEAD(&swm_info->parts[swm_info->num_parts - 1].blob_list);
165         swm_info->parts[swm_info->num_parts - 1].size = 0;
166         return 0;
167 }
168
169 static int
170 add_blob_to_swm(struct blob_descriptor *blob, void *_swm_info)
171 {
172         struct swm_info *swm_info = _swm_info;
173         u64 blob_stored_size;
174         int ret;
175
176         if (blob->blob_location == BLOB_IN_WIM)
177                 blob_stored_size = blob->rdesc->size_in_wim;
178         else
179                 blob_stored_size = blob->size;
180
181         /* Start the next part if adding this blob exceeds the maximum part
182          * size, UNLESS the blob is metadata or if no blobs at all have been
183          * added to the current part.  */
184         if ((swm_info->parts[swm_info->num_parts - 1].size +
185              blob_stored_size >= swm_info->max_part_size)
186             && !(blob->is_metadata ||
187                  swm_info->parts[swm_info->num_parts - 1].size == 0))
188         {
189                 ret = start_new_swm_part(swm_info);
190                 if (ret)
191                         return ret;
192         }
193         swm_info->parts[swm_info->num_parts - 1].size += blob_stored_size;
194         if (!blob->is_metadata) {
195                 list_add_tail(&blob->write_blobs_list,
196                               &swm_info->parts[swm_info->num_parts - 1].blob_list);
197         }
198         swm_info->total_bytes += blob_stored_size;
199         return 0;
200 }
201
202 /* API function documented in wimlib.h  */
203 WIMLIBAPI int
204 wimlib_split(WIMStruct *wim, const tchar *swm_name,
205              u64 part_size, int write_flags)
206 {
207         struct swm_info swm_info;
208         unsigned i;
209         int ret;
210
211         if (swm_name == NULL || swm_name[0] == T('\0') || part_size == 0)
212                 return WIMLIB_ERR_INVALID_PARAM;
213
214         if (write_flags & ~WIMLIB_WRITE_MASK_PUBLIC)
215                 return WIMLIB_ERR_INVALID_PARAM;
216
217         if (!wim_has_metadata(wim))
218                 return WIMLIB_ERR_METADATA_NOT_FOUND;
219
220         if (wim_has_solid_resources(wim)) {
221                 ERROR("Splitting of WIM containing solid resources is not supported.\n"
222                       "        Export it in non-solid format first.");
223                 return WIMLIB_ERR_UNSUPPORTED;
224         }
225
226         for (i = 0; i < wim->hdr.image_count; i++) {
227                 if (!is_image_unchanged_from_wim(wim->image_metadata[i], wim)) {
228                         ERROR("Only an unmodified, on-disk WIM file can be split.");
229                         return WIMLIB_ERR_UNSUPPORTED;
230                 }
231         }
232
233         memset(&swm_info, 0, sizeof(swm_info));
234         swm_info.max_part_size = part_size;
235
236         ret = start_new_swm_part(&swm_info);
237         if (ret)
238                 goto out_free_swm_info;
239
240         for (i = 0; i < wim->hdr.image_count; i++) {
241                 ret = add_blob_to_swm(wim->image_metadata[i]->metadata_blob,
242                                       &swm_info);
243                 if (ret)
244                         goto out_free_swm_info;
245         }
246
247         ret = for_blob_in_table_sorted_by_sequential_order(wim->blob_table,
248                                                            add_blob_to_swm,
249                                                            &swm_info);
250         if (ret)
251                 goto out_free_swm_info;
252
253         ret = write_split_wim(wim, swm_name, &swm_info, write_flags);
254 out_free_swm_info:
255         FREE(swm_info.parts);
256         return ret;
257 }