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