]> wimlib.net Git - wimlib/blob - src/export_image.c
8d73328458a6677e47a625da8ea620446727d838
[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/blob_table.h"
28 #include "wimlib/error.h"
29 #include "wimlib/inode.h"
30 #include "wimlib/metadata.h"
31 #include "wimlib/xml.h"
32
33 static int
34 blob_set_not_exported(struct blob_descriptor *blob, void *_ignore)
35 {
36         blob->out_refcnt = 0;
37         blob->was_exported = 0;
38         return 0;
39 }
40
41 static int
42 blob_rollback_export(struct blob_descriptor *blob, void *_blob_table)
43 {
44         struct blob_table *blob_table = _blob_table;
45
46         blob->refcnt -= blob->out_refcnt;
47         if (blob->was_exported) {
48                 blob_table_unlink(blob_table, blob);
49                 free_blob_descriptor(blob);
50         }
51         return 0;
52 }
53
54 static int
55 inode_export_blobs(struct wim_inode *inode, struct blob_table *src_blob_table,
56                    struct blob_table *dest_blob_table, bool gift)
57 {
58         unsigned i;
59         const u8 *hash;
60         struct blob_descriptor *src_blob, *dest_blob;
61
62         inode_unresolve_streams(inode);
63
64         for (i = 0; i < inode->i_num_streams; i++) {
65
66                 /* Retrieve SHA-1 message digest of blob to export.  */
67                 hash = stream_hash(&inode->i_streams[i]);
68                 if (is_zero_hash(hash))  /* Empty blob?  */
69                         continue;
70
71                 /* Search for the blob (via SHA-1 message digest) in the
72                  * destination WIM.  */
73                 dest_blob = lookup_blob(dest_blob_table, hash);
74                 if (!dest_blob) {
75                         /* Blob not yet present in destination WIM.  Search for
76                          * it in the source WIM, then export it into the
77                          * destination WIM.  */
78                         src_blob = lookup_blob(src_blob_table, hash);
79                         if (!src_blob)
80                                 return blob_not_found_error(inode, hash);
81
82                         if (gift) {
83                                 dest_blob = src_blob;
84                                 blob_table_unlink(src_blob_table, src_blob);
85                         } else {
86                                 dest_blob = clone_blob_descriptor(src_blob);
87                                 if (!dest_blob)
88                                         return WIMLIB_ERR_NOMEM;
89                         }
90                         dest_blob->refcnt = 0;
91                         dest_blob->out_refcnt = 0;
92                         dest_blob->was_exported = 1;
93                         blob_table_insert(dest_blob_table, dest_blob);
94                 }
95
96                 /* Blob is present in destination WIM (either pre-existing,
97                  * already exported, or just exported above).  Increment its
98                  * reference count appropriately.   Note: we use 'refcnt' for
99                  * the raw reference count, but 'out_refcnt' for references
100                  * arising just from the export operation; this is used to roll
101                  * back a failed export if needed.  */
102                 dest_blob->refcnt += inode->i_nlink;
103                 dest_blob->out_refcnt += inode->i_nlink;
104         }
105         return 0;
106 }
107
108 /* API function documented in wimlib.h  */
109 WIMLIBAPI int
110 wimlib_export_image(WIMStruct *src_wim,
111                     int src_image,
112                     WIMStruct *dest_wim,
113                     const tchar *dest_name,
114                     const tchar *dest_description,
115                     int export_flags)
116 {
117         int ret;
118         int start_src_image;
119         int end_src_image;
120         int orig_dest_image_count;
121         int image;
122         bool all_images = (src_image == WIMLIB_ALL_IMAGES);
123
124         /* Check for sane parameters.  */
125         if (export_flags & ~(WIMLIB_EXPORT_FLAG_BOOT |
126                              WIMLIB_EXPORT_FLAG_NO_NAMES |
127                              WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS |
128                              WIMLIB_EXPORT_FLAG_GIFT |
129                              WIMLIB_EXPORT_FLAG_WIMBOOT))
130                 return WIMLIB_ERR_INVALID_PARAM;
131
132         if (src_wim == NULL || dest_wim == NULL)
133                 return WIMLIB_ERR_INVALID_PARAM;
134
135         if (!wim_has_metadata(src_wim) || !wim_has_metadata(dest_wim))
136                 return WIMLIB_ERR_METADATA_NOT_FOUND;
137
138         if (all_images) {
139                 /* Multi-image export.  */
140                 if ((!(export_flags & WIMLIB_EXPORT_FLAG_NO_NAMES) &&
141                         dest_name) ||
142                     (!(export_flags & WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS) &&
143                         dest_description))
144                 {
145                         ERROR("Image name and description must be "
146                               "left NULL for multi-image export");
147                         return WIMLIB_ERR_INVALID_PARAM;
148                 }
149                 start_src_image = 1;
150                 end_src_image = src_wim->hdr.image_count;
151         } else {
152                 start_src_image = src_image;
153                 end_src_image = src_image;
154         }
155         orig_dest_image_count = dest_wim->hdr.image_count;
156
157         /* Blob checksums must be known before proceeding.  */
158         ret = wim_checksum_unhashed_blobs(src_wim);
159         if (ret)
160                 return ret;
161         ret = wim_checksum_unhashed_blobs(dest_wim);
162         if (ret)
163                 return ret;
164
165         /* Enable rollbacks  */
166         for_blob_in_table(dest_wim->blob_table, blob_set_not_exported, NULL);
167
168         /* Export each requested image.  */
169         for (src_image = start_src_image;
170              src_image <= end_src_image;
171              src_image++)
172         {
173                 const tchar *next_dest_name, *next_dest_description;
174                 struct wim_image_metadata *src_imd;
175                 struct wim_inode *inode;
176
177                 /* Determine destination image name and description.  */
178
179                 if (export_flags & WIMLIB_EXPORT_FLAG_NO_NAMES)
180                         next_dest_name = T("");
181                 else if (dest_name)
182                         next_dest_name = dest_name;
183                 else
184                         next_dest_name = wimlib_get_image_name(src_wim, src_image);
185
186                 if (export_flags & WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS)
187                         next_dest_description = T("");
188                 else if (dest_description)
189                         next_dest_description = dest_description;
190                 else
191                         next_dest_description = wimlib_get_image_description(src_wim, src_image);
192
193                 /* Check for name conflict.  */
194                 if (wimlib_image_name_in_use(dest_wim, next_dest_name)) {
195                         ERROR("There is already an image named \"%"TS"\" "
196                               "in the destination WIM", next_dest_name);
197                         ret = WIMLIB_ERR_IMAGE_NAME_COLLISION;
198                         goto out_rollback;
199                 }
200
201                 /* Load metadata for source image into memory.  */
202                 ret = select_wim_image(src_wim, src_image);
203                 if (ret)
204                         goto out_rollback;
205
206                 src_imd = wim_get_current_image_metadata(src_wim);
207
208                 /* Iterate through inodes in the source image and export their
209                  * blobs into the destination WIM.  */
210                 image_for_each_inode(inode, src_imd) {
211                         ret = inode_export_blobs(inode,
212                                                  src_wim->blob_table,
213                                                  dest_wim->blob_table,
214                                                  export_flags & WIMLIB_EXPORT_FLAG_GIFT);
215                         if (ret)
216                                 goto out_rollback;
217                 }
218
219                 /* Export XML information into the destination WIM.  */
220                 ret = xml_export_image(src_wim->wim_info, src_image,
221                                        &dest_wim->wim_info, next_dest_name,
222                                        next_dest_description);
223                 if (ret)
224                         goto out_rollback;
225
226                 /* Reference the source image metadata from the destination WIM.
227                  */
228                 ret = append_image_metadata(dest_wim, src_imd);
229                 if (ret)
230                         goto out_rollback;
231                 src_imd->refcnt++;
232
233                 /* Lock the metadata into memory.  XXX: need better solution for
234                  * this.  */
235                 src_imd->modified = 1;
236
237         }
238
239         /* Image export complete.  Finish by setting any needed special metadata
240          * on the destination WIM.  */
241
242         if (src_wim->hdr.flags & WIM_HDR_FLAG_RP_FIX)
243                 dest_wim->hdr.flags |= WIM_HDR_FLAG_RP_FIX;
244
245         for (src_image = start_src_image;
246              src_image <= end_src_image;
247              src_image++)
248         {
249                 int dst_image = orig_dest_image_count + 1 +
250                                 (src_image - start_src_image);
251
252                 if (export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
253                         wim_info_set_wimboot(dest_wim->wim_info, dst_image, true);
254
255                 if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
256                     (!all_images || src_image == src_wim->hdr.boot_idx))
257                         dest_wim->hdr.boot_idx = dst_image;
258         }
259
260         if (export_flags & WIMLIB_EXPORT_FLAG_GIFT) {
261                 free_blob_table(src_wim->blob_table);
262                 src_wim->blob_table = NULL;
263         }
264         return 0;
265
266 out_rollback:
267         while ((image = wim_info_get_num_images(dest_wim->wim_info))
268                > orig_dest_image_count)
269         {
270                 xml_delete_image(&dest_wim->wim_info, image);
271         }
272         while (dest_wim->hdr.image_count > orig_dest_image_count)
273         {
274                 put_image_metadata(dest_wim->image_metadata[
275                                         --dest_wim->hdr.image_count], NULL);
276         }
277         for_blob_in_table(dest_wim->blob_table, blob_rollback_export,
278                           dest_wim->blob_table);
279         return ret;
280 }