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