4 * Split a WIM file into parts.
8 * Copyright (C) 2012, 2013 Eric Biggers
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
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
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/.
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"
44 struct swm_part_info {
45 struct list_head stream_list;
50 copy_part_info(struct swm_part_info *dst, struct swm_part_info *src)
52 list_transfer(&src->stream_list, &dst->stream_list);
53 dst->size = src->size;
57 struct swm_part_info *parts;
59 unsigned num_alloc_parts;
65 write_split_wim(WIMStruct *orig_wim, const tchar *swm_name,
66 struct swm_info *swm_info, int write_flags)
72 size_t swm_base_name_len;
74 union wimlib_progress_info progress;
77 u8 guid[WIMLIB_GUID_LEN];
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('.'));
84 swm_base_name_len = dot - swm_name_buf;
85 swm_suffix = alloca((tstrlen(dot) + 1) * sizeof(tchar));
86 tstrcpy(swm_suffix, dot);
88 swm_base_name_len = swm_name_len;
89 swm_suffix = alloca(1 * sizeof(tchar));
90 swm_suffix[0] = T('\0');
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;
99 randomize_byte_array(guid, WIMLIB_GUID_LEN);
101 for (part_number = 1; part_number <= swm_info->num_parts; part_number++) {
102 int part_write_flags;
103 wimlib_progress_func_t progfunc;
105 if (part_number != 1) {
106 tsprintf(swm_name_buf + swm_base_name_len,
107 T("%u%"TS), part_number, swm_suffix);
110 progress.split.cur_part_number = part_number;
111 progress.split.part_name = swm_name_buf;
113 ret = call_progress(orig_wim->progfunc,
114 WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART,
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;
125 progfunc = orig_wim->progfunc;
126 orig_wim->progfunc = NULL;
127 ret = write_wim_part(orig_wim,
128 progress.split.part_name,
134 &swm_info->parts[part_number - 1].stream_list,
136 orig_wim->progfunc = progfunc;
140 progress.split.completed_bytes += swm_info->parts[part_number - 1].size;
142 ret = call_progress(orig_wim->progfunc,
143 WIMLIB_PROGRESS_MSG_SPLIT_END_PART,
153 add_stream_to_swm(struct wim_lookup_table_entry *lte, void *_swm_info)
155 struct swm_info *swm_info = _swm_info;
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;
163 if (lte->resource_location == RESOURCE_IN_WIM)
164 stream_size = lte->rspec->size_in_wim;
166 stream_size = lte->size;
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.
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)))
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;
183 num_alloc_parts += 8;
184 parts = MALLOC(num_alloc_parts * sizeof(parts[0]));
186 return WIMLIB_ERR_NOMEM;
188 for (unsigned i = 0; i < swm_info->num_parts; i++)
189 copy_part_info(&parts[i], &swm_info->parts[i]);
191 FREE(swm_info->parts);
192 swm_info->parts = parts;
193 swm_info->num_alloc_parts = num_alloc_parts;
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;
199 swm_info->parts[swm_info->num_parts - 1].size += stream_size;
200 if (!(lte->flags & WIM_RESHDR_FLAG_METADATA)) {
201 list_add_tail(<e->write_streams_list,
202 &swm_info->parts[swm_info->num_parts - 1].stream_list);
204 swm_info->total_bytes += stream_size;
208 /* API function documented in wimlib.h */
210 wimlib_split(WIMStruct *wim, const tchar *swm_name,
211 u64 part_size, int write_flags)
213 struct swm_info swm_info;
217 if (swm_name == NULL || swm_name[0] == T('\0') || part_size == 0)
218 return WIMLIB_ERR_INVALID_PARAM;
220 if (write_flags & ~WIMLIB_WRITE_MASK_PUBLIC)
221 return WIMLIB_ERR_INVALID_PARAM;
223 if (!wim_has_metadata(wim))
224 return WIMLIB_ERR_METADATA_NOT_FOUND;
226 memset(&swm_info, 0, sizeof(swm_info));
227 swm_info.max_part_size = part_size;
229 for (i = 0; i < wim->hdr.image_count; i++) {
230 ret = add_stream_to_swm(wim->image_metadata[i]->metadata_lte,
233 goto out_free_swm_info;
236 ret = for_lookup_table_entry_pos_sorted(wim->lookup_table,
240 goto out_free_swm_info;
242 ret = write_split_wim(wim, swm_name, &swm_info, write_flags);
244 FREE(swm_info.parts);