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