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