]> wimlib.net Git - wimlib/blob - src/export_image.c
Add a clang-format file
[wimlib] / src / export_image.c
1 /*
2  * export_image.c
3  */
4
5 /*
6  * Copyright (C) 2012-2016 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 https://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)
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         /* We don't yet support having a single WIMStruct contain duplicate
157          * 'image_metadata' structures, so we must forbid this from happening.
158          * A duplication is possible if 'src_wim == dest_wim', if the same image
159          * is exported to the same destination WIMStruct multiple times, or if
160          * an image is exported in an A => B => A manner.  */
161         for (src_image = start_src_image;
162              src_image <= end_src_image; src_image++)
163         {
164                 const struct wim_image_metadata *src_imd =
165                                 src_wim->image_metadata[src_image - 1];
166                 for (int i = 0; i < dest_wim->hdr.image_count; i++)
167                         if (dest_wim->image_metadata[i] == src_imd)
168                                 return WIMLIB_ERR_DUPLICATE_EXPORTED_IMAGE;
169         }
170
171         /* Blob checksums must be known before proceeding.  */
172         ret = wim_checksum_unhashed_blobs(src_wim);
173         if (ret)
174                 return ret;
175         ret = wim_checksum_unhashed_blobs(dest_wim);
176         if (ret)
177                 return ret;
178
179         /* Enable rollbacks  */
180         for_blob_in_table(dest_wim->blob_table, blob_set_not_exported, NULL);
181
182         /* Forbid exports where the destination WIM already contains image(s)
183          * with the requested name(s).  However, allow multi-image exports where
184          * there is a duplication among the source names only.  */
185         if (!(export_flags & WIMLIB_EXPORT_FLAG_NO_NAMES)) {
186                 for (src_image = start_src_image;
187                      src_image <= end_src_image;
188                      src_image++)
189                 {
190                         const tchar *name = dest_name ? dest_name :
191                                 wimlib_get_image_name(src_wim, src_image);
192
193                         if (wimlib_image_name_in_use(dest_wim, name)) {
194                                 ERROR("There is already an image named \"%"TS"\" "
195                                       "in the destination WIM", name);
196                                 ret = WIMLIB_ERR_IMAGE_NAME_COLLISION;
197                                 goto out_rollback;
198                         }
199                 }
200         }
201
202         /* Export each requested image.  */
203         for (src_image = start_src_image;
204              src_image <= end_src_image;
205              src_image++)
206         {
207                 const tchar *next_dest_name, *next_dest_description;
208                 struct wim_image_metadata *src_imd;
209                 struct wim_inode *inode;
210
211                 /* Determine destination image name and description.  */
212
213                 if (export_flags & WIMLIB_EXPORT_FLAG_NO_NAMES)
214                         next_dest_name = NULL;
215                 else if (dest_name)
216                         next_dest_name = dest_name;
217                 else
218                         next_dest_name = wimlib_get_image_name(src_wim, src_image);
219
220                 if (export_flags & WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS)
221                         next_dest_description = NULL;
222                 else if (dest_description)
223                         next_dest_description = dest_description;
224                 else
225                         next_dest_description = wimlib_get_image_description(src_wim, src_image);
226
227                 /* Load metadata for source image into memory.  */
228                 ret = select_wim_image(src_wim, src_image);
229                 if (ret)
230                         goto out_rollback;
231
232                 src_imd = wim_get_current_image_metadata(src_wim);
233
234                 /* Iterate through inodes in the source image and export their
235                  * blobs into the destination WIM.  */
236                 image_for_each_inode(inode, src_imd) {
237                         ret = inode_export_blobs(inode,
238                                                  src_wim->blob_table,
239                                                  dest_wim->blob_table,
240                                                  export_flags & WIMLIB_EXPORT_FLAG_GIFT);
241                         if (ret)
242                                 goto out_rollback;
243                 }
244
245                 /* Export XML information into the destination WIM.  */
246                 ret = xml_export_image(src_wim->xml_info, src_image,
247                                        dest_wim->xml_info, next_dest_name,
248                                        next_dest_description,
249                                        export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT);
250                 if (ret)
251                         goto out_rollback;
252
253                 /* Reference the source image metadata from the destination WIM.
254                  */
255                 ret = append_image_metadata(dest_wim, src_imd);
256                 if (ret)
257                         goto out_rollback;
258                 src_imd->refcnt++;
259         }
260
261         /* Image export complete.  Finish by setting any needed special metadata
262          * on the destination WIM.  */
263
264         if (src_wim->hdr.flags & WIM_HDR_FLAG_RP_FIX)
265                 dest_wim->hdr.flags |= WIM_HDR_FLAG_RP_FIX;
266
267         for (src_image = start_src_image;
268              src_image <= end_src_image;
269              src_image++)
270         {
271                 int dst_image = orig_dest_image_count + 1 +
272                                 (src_image - start_src_image);
273
274                 if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
275                     (!all_images || src_image == src_wim->hdr.boot_idx))
276                         dest_wim->hdr.boot_idx = dst_image;
277         }
278
279         return 0;
280
281 out_rollback:
282         while ((image = xml_get_image_count(dest_wim->xml_info))
283                > orig_dest_image_count)
284         {
285                 xml_delete_image(dest_wim->xml_info, image);
286         }
287         while (dest_wim->hdr.image_count > orig_dest_image_count)
288         {
289                 put_image_metadata(dest_wim->image_metadata[
290                                         --dest_wim->hdr.image_count]);
291         }
292         for_blob_in_table(dest_wim->blob_table, blob_rollback_export,
293                           dest_wim->blob_table);
294         return ret;
295 }