]> wimlib.net Git - wimlib/blob - export_image.c
a24950ad30b3a8b9f81c7e3c60c4c8098bbc237a
[wimlib] / 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 static int inode_allocate_needed_ltes(struct inode *inode,
30                                       struct lookup_table *src_lookup_table,
31                                       struct lookup_table *dest_lookup_table,
32                                       struct list_head *lte_list_head)
33 {
34         struct lookup_table_entry *src_lte, *dest_lte;
35         unsigned i;
36
37         inode_unresolve_ltes(inode);
38         for (i = 0; i <= inode->num_ads; i++) {
39                 src_lte = inode_stream_lte_unresolved(inode, i,
40                                                       src_lookup_table);
41                 if (src_lte && src_lte->out_refcnt == 0) {
42                         src_lte->out_refcnt = 1;
43                         dest_lte = inode_stream_lte_unresolved(inode, i,
44                                                                dest_lookup_table);
45                         if (!dest_lte) {
46                                 dest_lte = clone_lookup_table_entry(src_lte);
47                                 if (!dest_lte)
48                                         return WIMLIB_ERR_NOMEM;
49                                 list_add_tail(&dest_lte->staging_list, lte_list_head);
50                         }
51                 }
52         }
53         return 0;
54 }
55
56 static void inode_move_ltes_to_table(struct inode *inode,
57                                      struct lookup_table *src_lookup_table,
58                                      struct lookup_table *dest_lookup_table,
59                                      struct list_head *lte_list_head)
60 {
61         struct lookup_table_entry *src_lte, *dest_lte;
62         unsigned i;
63         struct dentry *dentry;
64
65         inode_for_each_dentry(dentry, inode)
66                 dentry->refcnt++;
67
68         for (i = 0; i <= inode->num_ads; i++) {
69                 src_lte = inode_stream_lte_unresolved(inode, i, src_lookup_table);
70                 if (src_lte) {
71                         dest_lte = inode_stream_lte_unresolved(inode, i,
72                                                                dest_lookup_table);
73                         if (!dest_lte) {
74                                 struct list_head *next;
75
76                                 wimlib_assert(!list_empty(lte_list_head));
77                                 next = lte_list_head->next;
78                                 list_del(next);
79                                 dest_lte = container_of(next,
80                                                         struct lookup_table_entry,
81                                                         staging_list);
82                                 dest_lte->part_number = 1;
83                                 dest_lte->refcnt = 0;
84                                 wimlib_assert(hashes_equal(dest_lte->hash, src_lte->hash));
85                                 lookup_table_insert(dest_lookup_table, dest_lte);
86                         }
87                         dest_lte->refcnt += inode->link_count;
88                 }
89         }
90 }
91
92 /*
93  * Copies an image, or all the images, from a WIM file, into another WIM file.
94  */
95 WIMLIBAPI int wimlib_export_image(WIMStruct *src_wim,
96                                   int src_image,
97                                   WIMStruct *dest_wim,
98                                   const char *dest_name,
99                                   const char *dest_description,
100                                   int export_flags,
101                                   WIMStruct **additional_swms,
102                                   unsigned num_additional_swms,
103                                   wimlib_progress_func_t progress_func)
104 {
105         int ret;
106         struct wim_security_data *sd;
107         struct lookup_table *joined_tab, *src_wim_tab_save;
108         struct image_metadata *src_imd;
109         struct hlist_node *cur_node;
110         struct list_head lte_list_head;
111         struct inode *inode;
112
113         if (dest_wim->hdr.total_parts != 1) {
114                 ERROR("Exporting an image to a split WIM is "
115                       "unsupported");
116                 return WIMLIB_ERR_SPLIT_UNSUPPORTED;
117         }
118
119         if (src_image == WIMLIB_ALL_IMAGES) {
120                 if (src_wim->hdr.image_count > 1) {
121
122                         /* multi-image export. */
123
124                         if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
125                               (src_wim->hdr.boot_idx == 0))
126                         {
127                                 /* Specifying the boot flag on a multi-image
128                                  * source WIM makes the boot index default to
129                                  * the bootable image in the source WIM.  It is
130                                  * an error if there is no such bootable image.
131                                  * */
132                                 ERROR("Cannot specify `boot' flag when "
133                                       "exporting multiple images from a WIM "
134                                       "with no bootable images");
135                                 return WIMLIB_ERR_INVALID_PARAM;
136                         }
137                         if (dest_name || dest_description) {
138                                 ERROR("Image name or image description was "
139                                       "specified, but we are exporting "
140                                       "multiple images");
141                                 return WIMLIB_ERR_INVALID_PARAM;
142                         }
143                         for (int i = 1; i <= src_wim->hdr.image_count; i++) {
144                                 int new_flags = export_flags;
145
146                                 if (i != src_wim->hdr.boot_idx)
147                                         new_flags &= ~WIMLIB_EXPORT_FLAG_BOOT;
148
149                                 ret = wimlib_export_image(src_wim, i, dest_wim,
150                                                           NULL, NULL,
151                                                           new_flags,
152                                                           additional_swms,
153                                                           num_additional_swms,
154                                                           progress_func);
155                                 if (ret != 0)
156                                         return ret;
157                         }
158                         return 0;
159                 } else if (src_wim->hdr.image_count == 1) {
160                         src_image = 1;
161                 } else {
162                         return 0;
163                 }
164         }
165
166         if (!dest_name) {
167                 dest_name = wimlib_get_image_name(src_wim, src_image);
168                 DEBUG("Using name `%s' for source image %d",
169                       dest_name, src_image);
170         }
171
172         if (!dest_description) {
173                 dest_description = wimlib_get_image_description(src_wim,
174                                                                 src_image);
175                 DEBUG("Using description `%s' for source image %d",
176                       dest_description, src_image);
177         }
178
179         DEBUG("Exporting image %d from `%s'", src_image, src_wim->filename);
180
181         if (wimlib_image_name_in_use(dest_wim, dest_name)) {
182                 ERROR("There is already an image named `%s' in the "
183                       "destination WIM", dest_name);
184                 return WIMLIB_ERR_IMAGE_NAME_COLLISION;
185         }
186
187         ret = verify_swm_set(src_wim, additional_swms, num_additional_swms);
188         if (ret != 0)
189                 return ret;
190
191         if (num_additional_swms) {
192                 ret = new_joined_lookup_table(src_wim, additional_swms,
193                                               num_additional_swms,
194                                               &joined_tab);
195                 if (ret != 0)
196                         return ret;
197                 src_wim_tab_save = src_wim->lookup_table;
198                 src_wim->lookup_table = joined_tab;
199         }
200
201         ret = select_wim_image(src_wim, src_image);
202         if (ret != 0) {
203                 ERROR("Could not select image %d from the WIM `%s' "
204                       "to export it", src_image, src_wim->filename);
205                 goto out;
206         }
207
208         /* Pre-allocate the new lookup table entries that will be needed.  This
209          * way, it's not possible to run out of memory part-way through
210          * modifying the lookup table of the destination WIM. */
211         INIT_LIST_HEAD(&lte_list_head);
212         for_lookup_table_entry(src_wim->lookup_table, lte_zero_out_refcnt, NULL);
213         src_imd = wim_get_current_image_metadata(src_wim);
214
215         hlist_for_each_entry(inode, cur_node, &src_imd->inode_list, hlist) {
216                 ret = inode_allocate_needed_ltes(inode,
217                                                  src_wim->lookup_table,
218                                                  dest_wim->lookup_table,
219                                                  &lte_list_head);
220                 if (ret != 0)
221                         goto out_free_ltes;
222         }
223
224         ret = xml_export_image(src_wim->wim_info, src_image,
225                                &dest_wim->wim_info, dest_name,
226                                dest_description);
227         if (ret != 0)
228                 goto out_free_ltes;
229
230         sd = src_imd->security_data;
231         ret = add_new_dentry_tree(dest_wim, src_imd->root_dentry, sd);
232         if (ret != 0)
233                 goto out_xml_delete_image;
234
235         dest_wim->image_metadata[
236                 dest_wim->hdr.image_count - 1].inode_list = src_imd->inode_list;
237
238         /* All memory allocations have been taken care of, so it's no longer
239          * possible for this function to fail.  Go ahead and increment the
240          * reference counts of the dentry tree and security data, then update
241          * the lookup table of the destination WIM and the boot index, if
242          * needed. */
243         sd->refcnt++;
244         hlist_for_each_entry(inode, cur_node, &src_imd->inode_list, hlist) {
245                 inode_move_ltes_to_table(inode,
246                                          src_wim->lookup_table,
247                                          dest_wim->lookup_table,
248                                          &lte_list_head);
249         }
250
251         if (export_flags & WIMLIB_EXPORT_FLAG_BOOT)
252                 wimlib_set_boot_idx(dest_wim, dest_wim->hdr.image_count);
253         ret = 0;
254         goto out;
255
256 out_xml_delete_image:
257         xml_delete_image(&dest_wim->wim_info, dest_wim->hdr.image_count);
258 out_free_ltes:
259         {
260                 struct lookup_table_entry *lte, *tmp;
261                 list_for_each_entry_safe(lte, tmp, &lte_list_head, staging_list)
262                         free_lookup_table_entry(lte);
263         }
264
265 out:
266         if (num_additional_swms) {
267                 free_lookup_table(src_wim->lookup_table);
268                 src_wim->lookup_table = src_wim_tab_save;
269         }
270         return ret;
271 }