]> wimlib.net Git - wimlib/blob - src/metadata_resource.c
Split wim_resource_spec from wim_lookup_table_entry
[wimlib] / src / metadata_resource.c
1 /*
2  * metadata_resource.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 Software
12  * Foundation; either version 3 of the License, or (at your option) any later
13  * 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 details.
18  *
19  * You should have received a copy of the GNU General Public License along with
20  * wimlib; if not, see http://www.gnu.org/licenses/.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #  include "config.h"
25 #endif
26
27 #include "wimlib/dentry.h"
28 #include "wimlib/error.h"
29 #include "wimlib/file_io.h"
30 #include "wimlib/lookup_table.h"
31 #include "wimlib/metadata.h"
32 #include "wimlib/resource.h"
33 #include "wimlib/security.h"
34 #include "wimlib/write.h"
35
36 /*
37  * Reads a metadata resource for an image in the WIM file.  The metadata
38  * resource consists of the security data, followed by the directory entry for
39  * the root directory, followed by all the other directory entries in the
40  * filesystem.  The subdir_offset field of each directory entry gives the start
41  * of its child entries from the beginning of the metadata resource.  An
42  * end-of-directory is signaled by a directory entry of length '0', really of
43  * length 8, because that's how long the 'length' field is.
44  *
45  * @wim:
46  *      Pointer to the WIMStruct for the WIM file.
47  *
48  * @imd:
49  *      Pointer to the image metadata structure for the image whose metadata
50  *      resource we are reading.  Its `metadata_lte' member specifies the lookup
51  *      table entry for the metadata resource.  The rest of the image metadata
52  *      entry will be filled in by this function.
53  *
54  * Return values:
55  *      WIMLIB_ERR_SUCCESS (0)
56  *      WIMLIB_ERR_INVALID_METADATA_RESOURCE
57  *      WIMLIB_ERR_NOMEM
58  *      WIMLIB_ERR_READ
59  *      WIMLIB_ERR_UNEXPECTED_END_OF_FILE
60  *      WIMLIB_ERR_DECOMPRESSION
61  */
62 int
63 read_metadata_resource(WIMStruct *wim, struct wim_image_metadata *imd)
64 {
65         void *buf;
66         int ret;
67         struct wim_dentry *root;
68         const struct wim_lookup_table_entry *metadata_lte;
69         u64 metadata_len;
70         u8 hash[SHA1_HASH_SIZE];
71         struct wim_security_data *security_data;
72         struct wim_inode *inode;
73
74         metadata_lte = imd->metadata_lte;
75         metadata_len = metadata_lte->size;
76
77         DEBUG("Reading metadata resource: original_size = %"PRIu64", "
78               "size = %"PRIu64", offset = %"PRIu64"",
79               metadata_lte->rspec->uncompressed_size,
80               metadata_lte->rspec->size_in_wim,
81               metadata_lte->rspec->offset_in_wim);
82
83         /* There is no way the metadata resource could possibly be less than (8
84          * + WIM_DENTRY_DISK_SIZE) bytes, where the 8 is for security data (with
85          * no security descriptors) and WIM_DENTRY_DISK_SIZE is for the root
86          * entry. */
87         if (metadata_len < 8 + WIM_DENTRY_DISK_SIZE) {
88                 ERROR("Expected at least %u bytes for the metadata resource",
89                       8 + WIM_DENTRY_DISK_SIZE);
90                 return WIMLIB_ERR_INVALID_METADATA_RESOURCE;
91         }
92
93         /* Read the metadata resource into memory.  (It may be compressed.) */
94         ret = read_full_resource_into_alloc_buf(metadata_lte, &buf);
95         if (ret)
96                 return ret;
97
98         if (!metadata_lte->dont_check_metadata_hash) {
99                 sha1_buffer(buf, metadata_len, hash);
100                 if (!hashes_equal(metadata_lte->hash, hash)) {
101                         ERROR("Metadata resource is corrupted "
102                               "(invalid SHA-1 message digest)!");
103                         ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE;
104                         goto out_free_buf;
105                 }
106         }
107
108         DEBUG("Finished reading metadata resource into memory.");
109
110         ret = read_wim_security_data(buf, metadata_len, &security_data);
111         if (ret)
112                 goto out_free_buf;
113
114         DEBUG("Reading root dentry");
115
116         /* Allocate memory for the root dentry and read it into memory */
117         root = MALLOC(sizeof(struct wim_dentry));
118         if (!root) {
119                 ret = WIMLIB_ERR_NOMEM;
120                 goto out_free_security_data;
121         }
122
123         /* The root directory entry starts after security data, aligned on an
124          * 8-byte boundary within the metadata resource.  Since
125          * security_data->total_length was already rounded up to an 8-byte
126          * boundary, its value can be used as the offset of the root directory
127          * entry.  */
128         ret = read_dentry(buf, metadata_len,
129                           security_data->total_length, root);
130
131         if (ret == 0 && root->length == 0) {
132                 WARNING("Metadata resource begins with end-of-directory entry "
133                         "(treating as empty image)");
134                 FREE(root);
135                 root = NULL;
136                 goto out_success;
137         }
138
139         if (ret) {
140                 FREE(root);
141                 goto out_free_security_data;
142         }
143
144         if (dentry_has_long_name(root) || dentry_has_short_name(root)) {
145                 WARNING("The root directory has a nonempty name (removing it)");
146                 FREE(root->file_name);
147                 FREE(root->short_name);
148                 root->file_name = NULL;
149                 root->short_name = NULL;
150                 root->file_name_nbytes = 0;
151                 root->short_name_nbytes = 0;
152         }
153
154         if (!dentry_is_directory(root)) {
155                 ERROR("Root of the WIM image must be a directory!");
156                 FREE(root);
157                 ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE;
158                 goto out_free_security_data;
159         }
160
161         /* This is the root dentry, so set its parent to itself. */
162         root->parent = root;
163
164         inode_add_dentry(root, root->d_inode);
165
166         /* Now read the entire directory entry tree into memory. */
167         DEBUG("Reading dentry tree");
168         ret = read_dentry_tree(buf, metadata_len, root);
169         if (ret)
170                 goto out_free_dentry_tree;
171
172         /* Build hash table that maps hard link group IDs to dentry sets */
173         ret = dentry_tree_fix_inodes(root, &imd->inode_list);
174         if (ret)
175                 goto out_free_dentry_tree;
176
177
178         DEBUG("Running miscellaneous verifications on the dentry tree");
179         image_for_each_inode(inode, imd) {
180                 ret = verify_inode(inode, security_data);
181                 if (ret)
182                         goto out_free_dentry_tree;
183         }
184         DEBUG("Done reading image metadata");
185 out_success:
186         imd->root_dentry = root;
187         imd->security_data = security_data;
188         INIT_LIST_HEAD(&imd->unhashed_streams);
189         ret = 0;
190         goto out_free_buf;
191 out_free_dentry_tree:
192         free_dentry_tree(root, wim->lookup_table);
193 out_free_security_data:
194         free_wim_security_data(security_data);
195 out_free_buf:
196         FREE(buf);
197         return ret;
198 }
199
200 static void
201 recalculate_security_data_length(struct wim_security_data *sd)
202 {
203         u32 total_length = sizeof(u64) * sd->num_entries + 2 * sizeof(u32);
204         for (u32 i = 0; i < sd->num_entries; i++)
205                 total_length += sd->sizes[i];
206         sd->total_length = (total_length + 7) & ~7;
207 }
208
209 static int
210 prepare_metadata_resource(WIMStruct *wim, int image,
211                           u8 **buf_ret, size_t *len_ret)
212 {
213         u8 *buf;
214         u8 *p;
215         int ret;
216         u64 subdir_offset;
217         struct wim_dentry *root;
218         u64 len;
219         struct wim_security_data *sd;
220         struct wim_image_metadata *imd;
221
222         DEBUG("Preparing metadata resource for image %d", image);
223
224         ret = select_wim_image(wim, image);
225         if (ret)
226                 return ret;
227
228         imd = wim->image_metadata[image - 1];
229
230         root = imd->root_dentry;
231         sd = imd->security_data;
232
233         if (!root) {
234                 /* Empty image; create a dummy root.  */
235                 ret = new_filler_directory(T(""), &root);
236                 if (ret)
237                         return ret;
238                 imd->root_dentry = root;
239         }
240
241         /* Offset of first child of the root dentry.  It's equal to:
242          * - The total length of the security data, rounded to the next 8-byte
243          *   boundary,
244          * - plus the total length of the root dentry,
245          * - plus 8 bytes for an end-of-directory entry following the root
246          *   dentry (shouldn't really be needed, but just in case...)
247          */
248         recalculate_security_data_length(sd);
249         subdir_offset = (((u64)sd->total_length + 7) & ~7) +
250                         dentry_out_total_length(root) + 8;
251
252         /* Calculate the subdirectory offsets for the entire dentry tree.  */
253         calculate_subdir_offsets(root, &subdir_offset);
254
255         /* Total length of the metadata resource (uncompressed).  */
256         len = subdir_offset;
257
258         /* Allocate a buffer to contain the uncompressed metadata resource.  */
259         buf = MALLOC(len);
260         if (!buf) {
261                 ERROR("Failed to allocate %"PRIu64" bytes for "
262                       "metadata resource", len);
263                 return WIMLIB_ERR_NOMEM;
264         }
265
266         /* Write the security data into the resource buffer.  */
267         p = write_wim_security_data(sd, buf);
268
269         /* Write the dentry tree into the resource buffer.  */
270         p = write_dentry_tree(root, p);
271
272         /* We MUST have exactly filled the buffer; otherwise we calculated its
273          * size incorrectly or wrote the data incorrectly.  */
274         wimlib_assert(p - buf == len);
275
276         *buf_ret = buf;
277         *len_ret = len;
278         return 0;
279 }
280
281 int
282 write_metadata_resource(WIMStruct *wim, int image, int write_resource_flags)
283 {
284         int ret;
285         u8 *buf;
286         size_t len;
287         struct wim_image_metadata *imd;
288
289         ret = prepare_metadata_resource(wim, image, &buf, &len);
290         if (ret)
291                 return ret;
292
293         imd = wim->image_metadata[image - 1];
294
295         /* Write the metadata resource to the output WIM using the proper
296          * compression type, in the process updating the lookup table entry for
297          * the metadata resource.  */
298         ret = write_wim_resource_from_buffer(buf, len, WIM_RESHDR_FLAG_METADATA,
299                                              &wim->out_fd,
300                                              wim->out_compression_type,
301                                              wim->out_chunk_size,
302                                              &imd->metadata_lte->out_reshdr,
303                                              imd->metadata_lte->hash,
304                                              write_resource_flags,
305                                              &wim->lzx_context);
306
307         /* Original checksum was overridden; set a flag so it isn't used.  */
308         imd->metadata_lte->dont_check_metadata_hash = 1;
309
310         FREE(buf);
311         return ret;
312 }