Minor cleanups
[wimlib] / src / export_image.c
1 /*
2  * export_image.c
3  */
4
5 /*
6  * Copyright (C) 2012 Eric Biggers
7  *
8  * This file is part of wimlib, a library for working with WIM files.
9  *
10  * wimlib is free software; you can redistribute it and/or modify it under the
11  * terms of the GNU General Public License as published by the Free
12  * Software Foundation; either version 3 of the License, or (at your option)
13  * any later version.
14  *
15  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
16  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
17  * A PARTICULAR PURPOSE. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with wimlib; if not, see http://www.gnu.org/licenses/.
22  */
23
24 #include "wimlib_internal.h"
25 #include "dentry.h"
26 #include "lookup_table.h"
27 #include "xml.h"
28
29 struct wim_pair {
30         WIMStruct *src_wim;
31         WIMStruct *dest_wim;
32         struct list_head lte_list_head;
33 };
34
35 static int allocate_lte_if_needed(struct dentry *dentry, void *arg)
36 {
37         const WIMStruct *src_wim, *dest_wim;
38         struct list_head *lte_list_head;
39         struct inode *inode;
40
41         src_wim = ((struct wim_pair*)arg)->src_wim;
42         dest_wim = ((struct wim_pair*)arg)->dest_wim;
43         lte_list_head = &((struct wim_pair*)arg)->lte_list_head;
44         inode = dentry->d_inode;
45
46         wimlib_assert(!inode->resolved);
47
48         for (unsigned i = 0; i <= inode->num_ads; i++) {
49                 struct lookup_table_entry *src_lte, *dest_lte;
50                 src_lte = inode_stream_lte_unresolved(inode, i,
51                                                       src_wim->lookup_table);
52
53                 if (src_lte && ++src_lte->out_refcnt == 1) {
54                         dest_lte = inode_stream_lte_unresolved(inode, i,
55                                                                dest_wim->lookup_table);
56
57                         if (!dest_lte) {
58                                 dest_lte = clone_lookup_table_entry(src_lte);
59                                 if (!dest_lte)
60                                         return WIMLIB_ERR_NOMEM;
61                                 list_add_tail(&dest_lte->staging_list, lte_list_head);
62                         }
63                 }
64         }
65         return 0;
66 }
67
68 /*
69  * This function takes in a dentry that was previously located only in image(s)
70  * in @src_wim, but now is being added to @dest_wim.  For each stream associated
71  * with the dentry, if there is already a lookup table entry for that stream in
72  * the lookup table of the destination WIM file, its reference count is
73  * incrementej.  Otherwise, a new lookup table entry is created that points back
74  * to the stream in the source WIM file (through the @hash field combined with
75  * the @wim field of the lookup table entry.)
76  */
77 static int add_lte_to_dest_wim(struct dentry *dentry, void *arg)
78 {
79         WIMStruct *src_wim, *dest_wim;
80         struct inode *inode;
81
82         src_wim = ((struct wim_pair*)arg)->src_wim;
83         dest_wim = ((struct wim_pair*)arg)->dest_wim;
84         inode = dentry->d_inode;
85
86         wimlib_assert(!inode->resolved);
87
88         for (unsigned i = 0; i <= inode->num_ads; i++) {
89                 struct lookup_table_entry *src_lte, *dest_lte;
90                 src_lte = inode_stream_lte_unresolved(inode, i,
91                                                       src_wim->lookup_table);
92
93                 if (!src_lte) /* Empty or nonexistent stream. */
94                         continue;
95
96                 dest_lte = inode_stream_lte_unresolved(inode, i,
97                                                        dest_wim->lookup_table);
98                 if (dest_lte) {
99                         dest_lte->refcnt++;
100                 } else {
101                         struct list_head *lte_list_head;
102                         struct list_head *next;
103
104                         lte_list_head = &((struct wim_pair*)arg)->lte_list_head;
105                         wimlib_assert(!list_empty(lte_list_head));
106
107                         next = lte_list_head->next;
108                         list_del(next);
109                         dest_lte = container_of(next, struct lookup_table_entry,
110                                                 staging_list);
111                         dest_lte->part_number = 1;
112                         dest_lte->refcnt = 1;
113                         wimlib_assert(hashes_equal(dest_lte->hash, src_lte->hash));
114
115                         lookup_table_insert(dest_wim->lookup_table, dest_lte);
116                 }
117         }
118         return 0;
119 }
120
121 /*
122  * Copies an image, or all the images, from a WIM file, into another WIM file.
123  */
124 WIMLIBAPI int wimlib_export_image(WIMStruct *src_wim,
125                                   int src_image,
126                                   WIMStruct *dest_wim,
127                                   const char *dest_name,
128                                   const char *dest_description,
129                                   int export_flags,
130                                   WIMStruct **additional_swms,
131                                   unsigned num_additional_swms,
132                                   wimlib_progress_func_t progress_func)
133 {
134         int i;
135         int ret;
136         struct dentry *root;
137         struct wim_pair wims;
138         struct wim_security_data *sd;
139         struct lookup_table *joined_tab, *src_wim_tab_save;
140
141         if (dest_wim->hdr.total_parts != 1) {
142                 ERROR("Exporting an image to a split WIM is "
143                       "unsupported");
144                 return WIMLIB_ERR_SPLIT_UNSUPPORTED;
145         }
146
147         if (src_image == WIMLIB_ALL_IMAGES) {
148                 if (src_wim->hdr.image_count > 1) {
149
150                         /* multi-image export. */
151
152                         if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
153                               (src_wim->hdr.boot_idx == 0))
154                         {
155                                 /* Specifying the boot flag on a multi-image
156                                  * source WIM makes the boot index default to
157                                  * the bootable image in the source WIM.  It is
158                                  * an error if there is no such bootable image.
159                                  * */
160                                 ERROR("Cannot specify `boot' flag when "
161                                       "exporting multiple images from a WIM "
162                                       "with no bootable images");
163                                 return WIMLIB_ERR_INVALID_PARAM;
164                         }
165                         if (dest_name || dest_description) {
166                                 ERROR("Image name or image description was "
167                                       "specified, but we are exporting "
168                                       "multiple images");
169                                 return WIMLIB_ERR_INVALID_PARAM;
170                         }
171                         for (i = 1; i <= src_wim->hdr.image_count; i++) {
172                                 int new_flags = export_flags;
173
174                                 if (i != src_wim->hdr.boot_idx)
175                                         new_flags &= ~WIMLIB_EXPORT_FLAG_BOOT;
176
177                                 ret = wimlib_export_image(src_wim, i, dest_wim,
178                                                           NULL, NULL,
179                                                           new_flags,
180                                                           additional_swms,
181                                                           num_additional_swms,
182                                                           progress_func);
183                                 if (ret != 0)
184                                         return ret;
185                         }
186                         return 0;
187                 } else if (src_wim->hdr.image_count == 1) {
188                         src_image = 1;
189                 } else {
190                         return 0;
191                 }
192         }
193
194         if (!dest_name) {
195                 dest_name = wimlib_get_image_name(src_wim, src_image);
196                 DEBUG("Using name `%s' for source image %d",
197                       dest_name, src_image);
198         }
199
200         if (!dest_description) {
201                 dest_description = wimlib_get_image_description(src_wim,
202                                                                 src_image);
203                 DEBUG("Using description `%s' for source image %d",
204                       dest_description, src_image);
205         }
206
207         DEBUG("Exporting image %d from `%s'", src_image, src_wim->filename);
208
209         if (wimlib_image_name_in_use(dest_wim, dest_name)) {
210                 ERROR("There is already an image named `%s' in the "
211                       "destination WIM", dest_name);
212                 return WIMLIB_ERR_IMAGE_NAME_COLLISION;
213         }
214
215         ret = verify_swm_set(src_wim, additional_swms, num_additional_swms);
216         if (ret != 0)
217                 return ret;
218
219         if (num_additional_swms) {
220                 ret = new_joined_lookup_table(src_wim, additional_swms,
221                                               num_additional_swms,
222                                               &joined_tab);
223                 if (ret != 0)
224                         return ret;
225                 src_wim_tab_save = src_wim->lookup_table;
226                 src_wim->lookup_table = joined_tab;
227         }
228
229         ret = select_wim_image(src_wim, src_image);
230         if (ret != 0) {
231                 ERROR("Could not select image %d from the WIM `%s' "
232                       "to export it", src_image, src_wim->filename);
233                 goto out;
234         }
235
236         /* Pre-allocate the new lookup table entries that will be needed.  This
237          * way, it's not possible to run out of memory part-way through
238          * modifying the lookup table of the destination WIM. */
239         wims.src_wim = src_wim;
240         wims.dest_wim = dest_wim;
241         INIT_LIST_HEAD(&wims.lte_list_head);
242         for_lookup_table_entry(src_wim->lookup_table, lte_zero_out_refcnt, NULL);
243         root = wim_root_dentry(src_wim);
244         for_dentry_in_tree(root, dentry_unresolve_ltes, NULL);
245         ret = for_dentry_in_tree(root, allocate_lte_if_needed, &wims);
246         if (ret != 0)
247                 goto out_free_ltes;
248
249         ret = xml_export_image(src_wim->wim_info, src_image,
250                                &dest_wim->wim_info, dest_name,
251                                dest_description);
252         if (ret != 0)
253                 goto out_free_ltes;
254
255         sd = wim_security_data(src_wim);
256         ret = add_new_dentry_tree(dest_wim, root, sd);
257         if (ret != 0)
258                 goto out_xml_delete_image;
259
260
261         /* All memory allocations have been taken care of, so it's no longer
262          * possible for this function to fail.  Go ahead and increment the
263          * reference counts of the dentry tree and security data, then update
264          * the lookup table of the destination WIM and the boot index, if
265          * needed. */
266         for_dentry_in_tree(root, increment_dentry_refcnt, NULL);
267         sd->refcnt++;
268         for_dentry_in_tree(root, add_lte_to_dest_wim, &wims);
269         wimlib_assert(list_empty(&wims.lte_list_head));
270
271         if (export_flags & WIMLIB_EXPORT_FLAG_BOOT)
272                 wimlib_set_boot_idx(dest_wim, dest_wim->hdr.image_count);
273         ret = 0;
274         goto out;
275
276 out_xml_delete_image:
277         xml_delete_image(&dest_wim->wim_info, dest_wim->hdr.image_count);
278 out_free_ltes:
279         {
280                 struct lookup_table_entry *lte, *tmp;
281                 list_for_each_entry_safe(lte, tmp, &wims.lte_list_head, staging_list)
282                         free_lookup_table_entry(lte);
283         }
284
285 out:
286         if (num_additional_swms) {
287                 free_lookup_table(src_wim->lookup_table);
288                 src_wim->lookup_table = src_wim_tab_save;
289         }
290         return ret;
291 }