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