]> wimlib.net Git - wimlib/blob - src/export_image.c
77e9e5414c39c87e2384c604613f080b98c23862
[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 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/dentry.h"
28 #include "wimlib/error.h"
29 #include "wimlib/inode.h"
30 #include "wimlib/lookup_table.h"
31 #include "wimlib/metadata.h"
32 #include "wimlib/xml.h"
33 #include <stdlib.h>
34
35 static int
36 inode_export_streams(struct wim_inode *inode,
37                      struct wim_lookup_table *src_lookup_table,
38                      struct wim_lookup_table *dest_lookup_table,
39                      bool gift)
40 {
41         unsigned i;
42         const u8 *hash;
43         struct wim_lookup_table_entry *src_lte, *dest_lte;
44
45         inode_unresolve_streams(inode);
46         for (i = 0; i <= inode->i_num_ads; i++) {
47
48                 /* Retrieve SHA1 message digest of stream to export.  */
49                 hash = inode_stream_hash(inode, i);
50                 if (is_zero_hash(hash))  /* Empty stream?  */
51                         continue;
52
53                 /* Search for the stream (via SHA1 message digest) in the
54                  * destination WIM.  */
55                 dest_lte = lookup_stream(dest_lookup_table, hash);
56                 if (!dest_lte) {
57                         /* Stream not yet present in destination WIM.  Search
58                          * for it in the source WIM, then export it into the
59                          * destination WIM.  */
60                         src_lte = lookup_stream(src_lookup_table, hash);
61                         if (!src_lte)
62                                 return stream_not_found_error(inode, hash);
63
64                         if (gift) {
65                                 dest_lte = src_lte;
66                                 lookup_table_unlink(src_lookup_table, src_lte);
67                         } else {
68                                 dest_lte = clone_lookup_table_entry(src_lte);
69                                 if (!dest_lte)
70                                         return WIMLIB_ERR_NOMEM;
71                         }
72                         dest_lte->refcnt = 0;
73                         dest_lte->out_refcnt = 0;
74                         lookup_table_insert(dest_lookup_table, dest_lte);
75                 }
76
77                 /* Stream is present in destination WIM (either pre-existing,
78                  * already exported, or just exported above).  Increment its
79                  * reference count appropriately.   Note: we use 'refcnt' for
80                  * the raw reference count, but 'out_refcnt' for references
81                  * arising just from the export operation; this is used to roll
82                  * back a failed export if needed.  */
83                 dest_lte->refcnt += inode->i_nlink;
84                 dest_lte->out_refcnt += inode->i_nlink;
85         }
86         return 0;
87 }
88
89 static int
90 lte_unexport(struct wim_lookup_table_entry *lte, void *_lookup_table)
91 {
92         struct wim_lookup_table *lookup_table = _lookup_table;
93
94         if (lte->out_refcnt) {
95                 lte->refcnt -= lte->out_refcnt;
96                 if (lte->refcnt == 0) {
97                         lookup_table_unlink(lookup_table, lte);
98                         free_lookup_table_entry(lte);
99                 }
100         }
101         return 0;
102 }
103
104 /* API function documented in wimlib.h  */
105 WIMLIBAPI int
106 wimlib_export_image(WIMStruct *src_wim,
107                     int src_image,
108                     WIMStruct *dest_wim,
109                     const tchar *dest_name,
110                     const tchar *dest_description,
111                     int export_flags)
112 {
113         int ret;
114         int start_image;
115         int end_image;
116         int image;
117         u32 orig_dest_boot_idx;
118         u32 orig_dest_image_count;
119
120         /* Check for sane parameters.  */
121         if (export_flags & ~(WIMLIB_EXPORT_FLAG_BOOT |
122                              WIMLIB_EXPORT_FLAG_NO_NAMES |
123                              WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS |
124                              WIMLIB_EXPORT_FLAG_GIFT |
125                              WIMLIB_EXPORT_FLAG_WIMBOOT))
126                 return WIMLIB_ERR_INVALID_PARAM;
127
128         if (src_wim == NULL || dest_wim == NULL)
129                 return WIMLIB_ERR_INVALID_PARAM;
130
131         if (!wim_has_metadata(dest_wim))
132                 return WIMLIB_ERR_METADATA_NOT_FOUND;
133
134         /* Destination WIM must be writable.  */
135         ret = can_modify_wim(dest_wim);
136         if (ret)
137                 return ret;
138
139         if (src_image == WIMLIB_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_image = 1;
152                 end_image = src_wim->hdr.image_count;
153         } else {
154                 start_image = src_image;
155                 end_image = src_image;
156         }
157
158         /* Stream checksums must be known before proceeding.  */
159         ret = wim_checksum_unhashed_streams(src_wim);
160         if (ret)
161                 return ret;
162         ret = wim_checksum_unhashed_streams(dest_wim);
163         if (ret)
164                 return ret;
165
166         /* Zero 'out_refcnt' in all lookup table entries in the destination WIM;
167          * this tracks the number of references found from the source WIM
168          * image(s).  */
169         for_lookup_table_entry(dest_wim->lookup_table, lte_zero_out_refcnt,
170                                NULL);
171
172         /* Save the original count of images in the destination WIM and the boot
173          * index (used if rollback necessary).  */
174         orig_dest_image_count = dest_wim->hdr.image_count;
175         orig_dest_boot_idx = dest_wim->hdr.boot_idx;
176
177         /* Export each requested image.  */
178         for (image = start_image; image <= end_image; image++) {
179                 const tchar *next_dest_name, *next_dest_description;
180                 struct wim_image_metadata *src_imd;
181                 struct wim_inode *inode;
182
183                 DEBUG("Exporting image %d from \"%"TS"\"",
184                       image, src_wim->filename);
185
186                 /* Determine destination image name and description.  */
187
188                 if (export_flags & WIMLIB_EXPORT_FLAG_NO_NAMES) {
189                         next_dest_name = T("");
190                 } else if (dest_name) {
191                         next_dest_name = dest_name;
192                 } else {
193                         next_dest_name = wimlib_get_image_name(src_wim,
194                                                                image);
195                 }
196
197                 DEBUG("Using name \"%"TS"\"", next_dest_name);
198
199                 if (export_flags & WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS) {
200                         next_dest_description = T("");
201                 } else if (dest_description) {
202                         next_dest_description = dest_description;
203                 } else {
204                         next_dest_description = wimlib_get_image_description(
205                                                         src_wim, image);
206                 }
207
208                 DEBUG("Using description \"%"TS"\"", next_dest_description);
209
210                 /* Check for name conflict.  */
211                 if (wimlib_image_name_in_use(dest_wim, next_dest_name)) {
212                         ERROR("There is already an image named \"%"TS"\" "
213                               "in the destination WIM", next_dest_name);
214                         ret = WIMLIB_ERR_IMAGE_NAME_COLLISION;
215                         goto out_rollback;
216                 }
217
218                 /* Load metadata for source image into memory.  */
219                 ret = select_wim_image(src_wim, image);
220                 if (ret)
221                         goto out_rollback;
222
223                 src_imd = wim_get_current_image_metadata(src_wim);
224
225                 /* Iterate through inodes in the source image and export their
226                  * streams into the destination WIM.  */
227                 image_for_each_inode(inode, src_imd) {
228                         ret = inode_export_streams(inode,
229                                                    src_wim->lookup_table,
230                                                    dest_wim->lookup_table,
231                                                    export_flags & WIMLIB_EXPORT_FLAG_GIFT);
232                         if (ret)
233                                 goto out_rollback;
234                 }
235
236                 /* Export XML information into the destination WIM.  */
237                 ret = xml_export_image(src_wim->wim_info, image,
238                                        &dest_wim->wim_info, next_dest_name,
239                                        next_dest_description);
240                 if (ret)
241                         goto out_rollback;
242
243                 /* Reference the source image metadata from the destination WIM.
244                  */
245                 ret = append_image_metadata(dest_wim, src_imd);
246                 if (ret)
247                         goto out_rollback;
248                 src_imd->refcnt++;
249
250                 /* Lock the metadata into memory.  XXX: need better solution for
251                  * this.  */
252                 src_imd->modified = 1;
253
254                 /* Set boot index in destination WIM.  */
255                 if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
256                     (src_image != WIMLIB_ALL_IMAGES ||
257                      image == src_wim->hdr.boot_idx))
258                 {
259                         DEBUG("Marking destination image %u as bootable.",
260                               dest_wim->hdr.image_count);
261                         dest_wim->hdr.boot_idx = dest_wim->hdr.image_count;
262                 }
263
264                 /* Possibly set WIMBoot flag  */
265                 if (export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT) {
266                         wim_info_set_wimboot(dest_wim->wim_info,
267                                              dest_wim->hdr.image_count,
268                                              true);
269                 }
270
271         }
272         /* Set the reparse point fixup flag on the destination WIM if the flag
273          * is set on the source WIM. */
274         if (src_wim->hdr.flags & WIM_HDR_FLAG_RP_FIX)
275                 dest_wim->hdr.flags |= WIM_HDR_FLAG_RP_FIX;
276
277         if (export_flags & WIMLIB_EXPORT_FLAG_GIFT) {
278                 free_lookup_table(src_wim->lookup_table);
279                 src_wim->lookup_table = NULL;
280         }
281         DEBUG("Export operation successful.");
282         return 0;
283
284 out_rollback:
285         while ((image = wim_info_get_num_images(dest_wim->wim_info))
286                > orig_dest_image_count)
287         {
288                 xml_delete_image(&dest_wim->wim_info, image);
289         }
290         while (dest_wim->hdr.image_count > orig_dest_image_count)
291         {
292                 put_image_metadata(dest_wim->image_metadata[
293                                         --dest_wim->hdr.image_count], NULL);
294         }
295         for_lookup_table_entry(dest_wim->lookup_table, lte_unexport,
296                                dest_wim->lookup_table);
297         dest_wim->hdr.boot_idx = orig_dest_boot_idx;
298         return ret;
299 }