]> wimlib.net Git - wimlib/blob - src/export_image.c
wimlib_export_image(): cleanups and fixes for rollback
[wimlib] / src / export_image.c
1 /*
2  * export_image.c
3  */
4
5 /*
6  * Copyright (C) 2012, 2013, 2014 Eric Biggers
7  *
8  * This file is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU Lesser General Public License as published by the Free
10  * Software Foundation; either version 3 of the License, or (at your option) any
11  * later version.
12  *
13  * This file is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this file; if not, see http://www.gnu.org/licenses/.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #  include "config.h"
24 #endif
25
26 #include "wimlib.h"
27 #include "wimlib/error.h"
28 #include "wimlib/inode.h"
29 #include "wimlib/lookup_table.h"
30 #include "wimlib/metadata.h"
31 #include "wimlib/xml.h"
32
33 static int
34 lte_set_not_exported(struct wim_lookup_table_entry *lte, void *_ignore)
35 {
36         lte->out_refcnt = 0;
37         lte->was_exported = 0;
38         return 0;
39 }
40
41 static int
42 lte_rollback_export(struct wim_lookup_table_entry *lte, void *_lookup_table)
43 {
44         struct wim_lookup_table *lookup_table = _lookup_table;
45
46         lte->refcnt -= lte->out_refcnt;
47         if (lte->was_exported) {
48                 lookup_table_unlink(lookup_table, lte);
49                 free_lookup_table_entry(lte);
50         }
51         return 0;
52 }
53
54 static int
55 inode_export_streams(struct wim_inode *inode,
56                      struct wim_lookup_table *src_lookup_table,
57                      struct wim_lookup_table *dest_lookup_table,
58                      bool gift)
59 {
60         unsigned i;
61         const u8 *hash;
62         struct wim_lookup_table_entry *src_lte, *dest_lte;
63
64         inode_unresolve_streams(inode);
65         for (i = 0; i <= inode->i_num_ads; i++) {
66
67                 /* Retrieve SHA1 message digest of stream to export.  */
68                 hash = inode_stream_hash(inode, i);
69                 if (is_zero_hash(hash))  /* Empty stream?  */
70                         continue;
71
72                 /* Search for the stream (via SHA1 message digest) in the
73                  * destination WIM.  */
74                 dest_lte = lookup_stream(dest_lookup_table, hash);
75                 if (!dest_lte) {
76                         /* Stream not yet present in destination WIM.  Search
77                          * for it in the source WIM, then export it into the
78                          * destination WIM.  */
79                         src_lte = lookup_stream(src_lookup_table, hash);
80                         if (!src_lte)
81                                 return stream_not_found_error(inode, hash);
82
83                         if (gift) {
84                                 dest_lte = src_lte;
85                                 lookup_table_unlink(src_lookup_table, src_lte);
86                         } else {
87                                 dest_lte = clone_lookup_table_entry(src_lte);
88                                 if (!dest_lte)
89                                         return WIMLIB_ERR_NOMEM;
90                         }
91                         dest_lte->refcnt = 0;
92                         dest_lte->out_refcnt = 0;
93                         dest_lte->was_exported = 1;
94                         lookup_table_insert(dest_lookup_table, dest_lte);
95                 }
96
97                 /* Stream is present in destination WIM (either pre-existing,
98                  * already exported, or just exported above).  Increment its
99                  * reference count appropriately.   Note: we use 'refcnt' for
100                  * the raw reference count, but 'out_refcnt' for references
101                  * arising just from the export operation; this is used to roll
102                  * back a failed export if needed.  */
103                 dest_lte->refcnt += inode->i_nlink;
104                 dest_lte->out_refcnt += inode->i_nlink;
105         }
106         return 0;
107 }
108
109 /* API function documented in wimlib.h  */
110 WIMLIBAPI int
111 wimlib_export_image(WIMStruct *src_wim,
112                     int src_image,
113                     WIMStruct *dest_wim,
114                     const tchar *dest_name,
115                     const tchar *dest_description,
116                     int export_flags)
117 {
118         int ret;
119         int start_src_image;
120         int end_src_image;
121         int orig_dest_image_count;
122         int image;
123         bool all_images = (src_image == WIMLIB_ALL_IMAGES);
124
125         /* Check for sane parameters.  */
126         if (export_flags & ~(WIMLIB_EXPORT_FLAG_BOOT |
127                              WIMLIB_EXPORT_FLAG_NO_NAMES |
128                              WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS |
129                              WIMLIB_EXPORT_FLAG_GIFT |
130                              WIMLIB_EXPORT_FLAG_WIMBOOT))
131                 return WIMLIB_ERR_INVALID_PARAM;
132
133         if (src_wim == NULL || dest_wim == NULL)
134                 return WIMLIB_ERR_INVALID_PARAM;
135
136         if (!wim_has_metadata(src_wim) || !wim_has_metadata(dest_wim))
137                 return WIMLIB_ERR_METADATA_NOT_FOUND;
138
139         if (all_images) {
140                 /* Multi-image export.  */
141                 if ((!(export_flags & WIMLIB_EXPORT_FLAG_NO_NAMES) &&
142                         dest_name) ||
143                     (!(export_flags & WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS) &&
144                         dest_description))
145                 {
146                         ERROR("Image name or image description was "
147                               "specified, but we are exporting "
148                               "multiple images");
149                         return WIMLIB_ERR_INVALID_PARAM;
150                 }
151                 start_src_image = 1;
152                 end_src_image = src_wim->hdr.image_count;
153         } else {
154                 start_src_image = src_image;
155                 end_src_image = src_image;
156         }
157         orig_dest_image_count = dest_wim->hdr.image_count;
158
159         /* Stream checksums must be known before proceeding.  */
160         ret = wim_checksum_unhashed_streams(src_wim);
161         if (ret)
162                 return ret;
163         ret = wim_checksum_unhashed_streams(dest_wim);
164         if (ret)
165                 return ret;
166
167         /* Enable rollbacks  */
168         for_lookup_table_entry(dest_wim->lookup_table, lte_set_not_exported, NULL);
169
170         /* Export each requested image.  */
171         for (src_image = start_src_image;
172              src_image <= end_src_image;
173              src_image++)
174         {
175                 const tchar *next_dest_name, *next_dest_description;
176                 struct wim_image_metadata *src_imd;
177                 struct wim_inode *inode;
178
179                 /* Determine destination image name and description.  */
180
181                 if (export_flags & WIMLIB_EXPORT_FLAG_NO_NAMES)
182                         next_dest_name = T("");
183                 else if (dest_name)
184                         next_dest_name = dest_name;
185                 else
186                         next_dest_name = wimlib_get_image_name(src_wim, src_image);
187
188                 if (export_flags & WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS)
189                         next_dest_description = T("");
190                 else if (dest_description)
191                         next_dest_description = dest_description;
192                 else
193                         next_dest_description = wimlib_get_image_description(src_wim, src_image);
194
195                 /* Check for name conflict.  */
196                 if (wimlib_image_name_in_use(dest_wim, next_dest_name)) {
197                         ERROR("There is already an image named \"%"TS"\" "
198                               "in the destination WIM", next_dest_name);
199                         ret = WIMLIB_ERR_IMAGE_NAME_COLLISION;
200                         goto out_rollback;
201                 }
202
203                 /* Load metadata for source image into memory.  */
204                 ret = select_wim_image(src_wim, src_image);
205                 if (ret)
206                         goto out_rollback;
207
208                 src_imd = wim_get_current_image_metadata(src_wim);
209
210                 /* Iterate through inodes in the source image and export their
211                  * streams into the destination WIM.  */
212                 image_for_each_inode(inode, src_imd) {
213                         ret = inode_export_streams(inode,
214                                                    src_wim->lookup_table,
215                                                    dest_wim->lookup_table,
216                                                    export_flags & WIMLIB_EXPORT_FLAG_GIFT);
217                         if (ret)
218                                 goto out_rollback;
219                 }
220
221                 /* Export XML information into the destination WIM.  */
222                 ret = xml_export_image(src_wim->wim_info, src_image,
223                                        &dest_wim->wim_info, next_dest_name,
224                                        next_dest_description);
225                 if (ret)
226                         goto out_rollback;
227
228                 /* Reference the source image metadata from the destination WIM.
229                  */
230                 ret = append_image_metadata(dest_wim, src_imd);
231                 if (ret)
232                         goto out_rollback;
233                 src_imd->refcnt++;
234
235                 /* Lock the metadata into memory.  XXX: need better solution for
236                  * this.  */
237                 src_imd->modified = 1;
238
239         }
240
241         /* Image export complete.  Finish by setting any needed special metadata
242          * on the destination WIM.  */
243
244         if (src_wim->hdr.flags & WIM_HDR_FLAG_RP_FIX)
245                 dest_wim->hdr.flags |= WIM_HDR_FLAG_RP_FIX;
246
247         for (src_image = start_src_image;
248              src_image <= end_src_image;
249              src_image++)
250         {
251                 int dst_image = orig_dest_image_count + 1 +
252                                 (src_image - start_src_image);
253
254                 if (export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
255                         wim_info_set_wimboot(dest_wim->wim_info, dst_image, true);
256
257                 if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
258                     (!all_images || src_image == src_wim->hdr.boot_idx))
259                         dest_wim->hdr.boot_idx = dst_image;
260         }
261
262         if (export_flags & WIMLIB_EXPORT_FLAG_GIFT) {
263                 free_lookup_table(src_wim->lookup_table);
264                 src_wim->lookup_table = NULL;
265         }
266         return 0;
267
268 out_rollback:
269         while ((image = wim_info_get_num_images(dest_wim->wim_info))
270                > orig_dest_image_count)
271         {
272                 xml_delete_image(&dest_wim->wim_info, image);
273         }
274         while (dest_wim->hdr.image_count > orig_dest_image_count)
275         {
276                 put_image_metadata(dest_wim->image_metadata[
277                                         --dest_wim->hdr.image_count], NULL);
278         }
279         for_lookup_table_entry(dest_wim->lookup_table, lte_rollback_export,
280                                dest_wim->lookup_table);
281         return ret;
282 }