read_wim_lookup_table(): Ignore metadata entries with refcnt == 0
[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/lookup_table.h"
32 #include "wimlib/metadata.h"
33 #include "wimlib/xml.h"
34 #include <stdlib.h>
35
36 static int
37 inode_export_streams(struct wim_inode *inode,
38                      const struct wim_lookup_table *src_lookup_table,
39                      struct wim_lookup_table *dest_lookup_table)
40 {
41         unsigned i;
42         const u8 *hash;
43         struct wim_lookup_table_entry *src_lte, *dest_lte;
44
45         inode_unresolve_ltes(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_resource(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_resource(src_lookup_table, hash);
61                         if (!src_lte)
62                                 return resource_not_found_error(inode, hash);
63
64                         dest_lte = clone_lookup_table_entry(src_lte);
65                         if (!dest_lte)
66                                 return WIMLIB_ERR_NOMEM;
67                         dest_lte->refcnt = 0;
68                         dest_lte->out_refcnt = 0;
69                         lookup_table_insert(dest_lookup_table, dest_lte);
70                 }
71
72                 /* Stream is present in destination WIM (either pre-existing,
73                  * already exported, or just exported above).  Increment its
74                  * reference count appropriately.   Note: we use 'refcnt' for
75                  * the raw reference count, but 'out_refcnt' for references
76                  * arising just from the export operation; this is used to roll
77                  * back a failed export if needed.  */
78                 dest_lte->refcnt += inode->i_nlink;
79                 dest_lte->out_refcnt += inode->i_nlink;
80         }
81         return 0;
82 }
83
84 static int
85 lte_unexport(struct wim_lookup_table_entry *lte, void *_lookup_table)
86 {
87         struct wim_lookup_table *lookup_table = _lookup_table;
88
89         lte->refcnt -= lte->out_refcnt;
90         if (lte->refcnt == 0) {
91                 lookup_table_unlink(lookup_table, lte);
92                 free_lookup_table_entry(lte);
93         }
94         return 0;
95 }
96
97 /* API function documented in wimlib.h  */
98 WIMLIBAPI int
99 wimlib_export_image(WIMStruct *src_wim,
100                     int src_image,
101                     WIMStruct *dest_wim,
102                     const tchar *dest_name,
103                     const tchar *dest_description,
104                     int export_flags,
105                     wimlib_progress_func_t progress_func)
106 {
107         int ret;
108         int start_image;
109         int end_image;
110         int image;
111         u32 orig_dest_boot_idx;
112         u32 orig_dest_image_count;
113
114         /* Check for sane parameters.  */
115         if (src_wim == NULL || dest_wim == NULL)
116                 return WIMLIB_ERR_INVALID_PARAM;
117
118         if (!wim_has_metadata(dest_wim))
119                 return WIMLIB_ERR_METADATA_NOT_FOUND;
120
121         /* Destination WIM must be writable.  */
122         ret = can_modify_wim(dest_wim);
123         if (ret)
124                 return ret;
125
126         if (src_image == WIMLIB_ALL_IMAGES) {
127                 /* Multi-image export.  */
128                 if ((!(export_flags & WIMLIB_EXPORT_FLAG_NO_NAMES) &&
129                         dest_name) ||
130                     (!(export_flags & WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS) &&
131                         dest_description))
132                 {
133                         ERROR("Image name or image description was "
134                               "specified, but we are exporting "
135                               "multiple images");
136                         return WIMLIB_ERR_INVALID_PARAM;
137                 }
138                 start_image = 1;
139                 end_image = src_wim->hdr.image_count;
140         } else {
141                 start_image = src_image;
142                 end_image = src_image;
143         }
144
145         /* Stream checksums must be known before proceeding.  */
146         ret = wim_checksum_unhashed_streams(src_wim);
147         if (ret)
148                 return ret;
149         ret = wim_checksum_unhashed_streams(dest_wim);
150         if (ret)
151                 return ret;
152
153         /* Zero 'out_refcnt' in all lookup table entries in the destination WIM;
154          * this tracks the number of references found from the source WIM
155          * image(s).  */
156         for_lookup_table_entry(dest_wim->lookup_table, lte_zero_out_refcnt,
157                                NULL);
158
159         /* Save the original count of images in the destination WIM and the boot
160          * index (used if rollback necessary).  */
161         orig_dest_image_count = dest_wim->hdr.image_count;
162         orig_dest_boot_idx = dest_wim->hdr.boot_idx;
163
164         /* Export each requested image.  */
165         for (image = start_image; image <= end_image; image++) {
166                 const tchar *next_dest_name, *next_dest_description;
167                 struct wim_image_metadata *src_imd;
168                 struct wim_inode *inode;
169
170                 DEBUG("Exporting image %d from \"%"TS"\"",
171                       image, src_wim->filename);
172
173                 /* Determine destination image name and description.  */
174
175                 if (export_flags & WIMLIB_EXPORT_FLAG_NO_NAMES) {
176                         next_dest_name = NULL;
177                 } else if (dest_name) {
178                         next_dest_name = dest_name;
179                 } else {
180                         next_dest_name = wimlib_get_image_name(src_wim,
181                                                                image);
182                 }
183
184                 DEBUG("Using name \"%"TS"\"", next_dest_name);
185
186                 if (export_flags & WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS) {
187                         next_dest_description = NULL;
188                 } if (dest_description) {
189                         next_dest_description = dest_description;
190                 } else {
191                         next_dest_description = wimlib_get_image_description(
192                                                         src_wim, image);
193                 }
194
195                 DEBUG("Using description \"%"TS"\"", next_dest_description);
196
197                 /* Check for name conflict.  */
198                 if (wimlib_image_name_in_use(dest_wim, next_dest_name)) {
199                         ERROR("There is already an image named \"%"TS"\" "
200                               "in the destination WIM", next_dest_name);
201                         ret = WIMLIB_ERR_IMAGE_NAME_COLLISION;
202                         goto out_rollback;
203                 }
204
205                 /* Load metadata for source image into memory.  */
206                 ret = select_wim_image(src_wim, image);
207                 if (ret)
208                         goto out_rollback;
209
210                 src_imd = wim_get_current_image_metadata(src_wim);
211
212                 /* Iterate through inodes in the source image and export their
213                  * streams into the destination WIM.  */
214                 image_for_each_inode(inode, src_imd) {
215                         ret = inode_export_streams(inode,
216                                                    src_wim->lookup_table,
217                                                    dest_wim->lookup_table);
218                         if (ret)
219                                 goto out_rollback;
220                 }
221
222                 /* Export XML information into the destination WIM.  */
223                 ret = xml_export_image(src_wim->wim_info, image,
224                                        &dest_wim->wim_info, next_dest_name,
225                                        next_dest_description);
226                 if (ret)
227                         goto out_rollback;
228
229                 /* Reference the source image metadata from the destination WIM.
230                  */
231                 ret = append_image_metadata(dest_wim, src_imd);
232                 if (ret)
233                         goto out_rollback;
234                 src_imd->refcnt++;
235
236                 /* Lock the metadata into memory.  XXX: need better solution for
237                  * this.  */
238                 src_imd->modified = 1;
239
240                 /* Set boot index in destination WIM.  */
241                 if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
242                     (src_image != WIMLIB_ALL_IMAGES ||
243                      image == src_wim->hdr.boot_idx))
244                 {
245                         DEBUG("Marking destination image %u as bootable.",
246                               dest_wim->hdr.image_count);
247                         dest_wim->hdr.boot_idx = dest_wim->hdr.image_count;
248                 }
249
250         }
251         /* Set the reparse point fixup flag on the destination WIM if the flag
252          * is set on the source WIM. */
253         if (src_wim->hdr.flags & WIM_HDR_FLAG_RP_FIX)
254                 dest_wim->hdr.flags |= WIM_HDR_FLAG_RP_FIX;
255         DEBUG("Export operation successful.");
256         return 0;
257
258 out_rollback:
259         while ((image = wim_info_get_num_images(dest_wim->wim_info))
260                > orig_dest_image_count)
261         {
262                 xml_delete_image(&dest_wim->wim_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_lookup_table_entry(dest_wim->lookup_table, lte_unexport,
270                                dest_wim->lookup_table);
271         dest_wim->hdr.boot_idx = orig_dest_boot_idx;
272         return ret;
273 }