]> wimlib.net Git - wimlib/blob - src/metadata_resource.c
206c274ab0fa0a587344b60a530e376e9feeb68f
[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/lookup_table.h"
30 #include "wimlib/metadata.h"
31 #include "wimlib/resource.h"
32 #include "wimlib/security.h"
33 #include "wimlib/write.h"
34
35 /*
36  * Reads and parses a metadata resource for an image in the WIM file.
37  *
38  * @wim:
39  *      Pointer to the WIMStruct for the WIM file.
40  *
41  * @imd:
42  *      Pointer to the image metadata structure for the image whose metadata
43  *      resource we are reading.  Its `metadata_lte' member specifies the lookup
44  *      table entry for the metadata resource.  The rest of the image metadata
45  *      entry will be filled in by this function.
46  *
47  * Return values:
48  *      WIMLIB_ERR_SUCCESS (0)
49  *      WIMLIB_ERR_INVALID_METADATA_RESOURCE
50  *      WIMLIB_ERR_NOMEM
51  *      WIMLIB_ERR_READ
52  *      WIMLIB_ERR_UNEXPECTED_END_OF_FILE
53  *      WIMLIB_ERR_DECOMPRESSION
54  */
55 int
56 read_metadata_resource(WIMStruct *wim, struct wim_image_metadata *imd)
57 {
58         const struct wim_lookup_table_entry *metadata_lte;
59         void *buf;
60         int ret;
61         struct wim_security_data *sd;
62         struct wim_dentry *root;
63         struct wim_inode *inode;
64
65         metadata_lte = imd->metadata_lte;
66
67         DEBUG("Reading metadata resource (size=%"PRIu64").", metadata_lte->size);
68
69         /* Read the metadata resource into memory.  (It may be compressed.)  */
70         ret = read_full_stream_into_alloc_buf(metadata_lte, &buf);
71         if (ret)
72                 return ret;
73
74         /* Checksum the metadata resource.  */
75         if (!metadata_lte->dont_check_metadata_hash) {
76                 u8 hash[SHA1_HASH_SIZE];
77
78                 sha1_buffer(buf, metadata_lte->size, hash);
79                 if (!hashes_equal(metadata_lte->hash, hash)) {
80                         ERROR("Metadata resource is corrupted "
81                               "(invalid SHA-1 message digest)!");
82                         ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE;
83                         goto out_free_buf;
84                 }
85         }
86
87         /* Parse the metadata resource.
88          *
89          * Notes: The metadata resource consists of the security data, followed
90          * by the directory entry for the root directory, followed by all the
91          * other directory entries in the filesystem.  The subdir_offset field
92          * of each directory entry gives the start of its child entries from the
93          * beginning of the metadata resource.  An end-of-directory is signaled
94          * by a directory entry of length '0', really of length 8, because
95          * that's how long the 'length' field is.  */
96
97         ret = read_wim_security_data(buf, metadata_lte->size, &sd);
98         if (ret)
99                 goto out_free_buf;
100
101         ret = read_dentry_tree(buf, metadata_lte->size, sd->total_length, &root);
102         if (ret)
103                 goto out_free_security_data;
104
105         /* We have everything we need from the buffer now.  */
106         FREE(buf);
107         buf = NULL;
108
109         /* Calculate and validate inodes.  */
110
111         ret = dentry_tree_fix_inodes(root, &imd->inode_list);
112         if (ret)
113                 goto out_free_dentry_tree;
114
115         image_for_each_inode(inode, imd) {
116                 ret = verify_inode(inode, sd);
117                 if (ret)
118                         goto out_free_dentry_tree;
119         }
120
121         /* Success; fill in the image_metadata structure.  */
122         imd->root_dentry = root;
123         imd->security_data = sd;
124         INIT_LIST_HEAD(&imd->unhashed_streams);
125         DEBUG("Done parsing metadata resource.");
126         return 0;
127
128 out_free_dentry_tree:
129         free_dentry_tree(root, NULL);
130 out_free_security_data:
131         free_wim_security_data(sd);
132 out_free_buf:
133         FREE(buf);
134         return ret;
135 }
136
137 static void
138 recalculate_security_data_length(struct wim_security_data *sd)
139 {
140         u32 total_length = sizeof(u64) * sd->num_entries + 2 * sizeof(u32);
141         for (u32 i = 0; i < sd->num_entries; i++)
142                 total_length += sd->sizes[i];
143         sd->total_length = (total_length + 7) & ~7;
144 }
145
146 static int
147 prepare_metadata_resource(WIMStruct *wim, int image,
148                           u8 **buf_ret, size_t *len_ret)
149 {
150         u8 *buf;
151         u8 *p;
152         int ret;
153         u64 subdir_offset;
154         struct wim_dentry *root;
155         size_t len;
156         struct wim_security_data *sd;
157         struct wim_image_metadata *imd;
158
159         DEBUG("Preparing metadata resource for image %d", image);
160
161         ret = select_wim_image(wim, image);
162         if (ret)
163                 return ret;
164
165         imd = wim->image_metadata[image - 1];
166
167         root = imd->root_dentry;
168         sd = imd->security_data;
169
170         if (!root) {
171                 /* Empty image; create a dummy root.  */
172                 ret = new_filler_directory(&root);
173                 if (ret)
174                         return ret;
175                 imd->root_dentry = root;
176         }
177
178         /* Offset of first child of the root dentry.  It's equal to:
179          * - The total length of the security data, rounded to the next 8-byte
180          *   boundary,
181          * - plus the total length of the root dentry,
182          * - plus 8 bytes for an end-of-directory entry following the root
183          *   dentry (shouldn't really be needed, but just in case...)
184          */
185         recalculate_security_data_length(sd);
186         subdir_offset = (((u64)sd->total_length + 7) & ~7) +
187                         dentry_out_total_length(root) + 8;
188
189         /* Calculate the subdirectory offsets for the entire dentry tree.  */
190         calculate_subdir_offsets(root, &subdir_offset);
191
192         /* Total length of the metadata resource (uncompressed).  */
193         len = subdir_offset;
194
195         /* Allocate a buffer to contain the uncompressed metadata resource.  */
196         buf = NULL;
197         if (likely(len == subdir_offset))
198                 buf = MALLOC(len);
199         if (!buf) {
200                 ERROR("Failed to allocate %"PRIu64" bytes for "
201                       "metadata resource", subdir_offset);
202                 return WIMLIB_ERR_NOMEM;
203         }
204
205         /* Write the security data into the resource buffer.  */
206         p = write_wim_security_data(sd, buf);
207
208         /* Write the dentry tree into the resource buffer.  */
209         p = write_dentry_tree(root, p);
210
211         /* We MUST have exactly filled the buffer; otherwise we calculated its
212          * size incorrectly or wrote the data incorrectly.  */
213         wimlib_assert(p - buf == len);
214
215         *buf_ret = buf;
216         *len_ret = len;
217         return 0;
218 }
219
220 int
221 write_metadata_resource(WIMStruct *wim, int image, int write_resource_flags)
222 {
223         int ret;
224         u8 *buf;
225         size_t len;
226         struct wim_image_metadata *imd;
227
228         ret = prepare_metadata_resource(wim, image, &buf, &len);
229         if (ret)
230                 return ret;
231
232         imd = wim->image_metadata[image - 1];
233
234         /* Write the metadata resource to the output WIM using the proper
235          * compression type, in the process updating the lookup table entry for
236          * the metadata resource.  */
237         ret = write_wim_resource_from_buffer(buf, len, WIM_RESHDR_FLAG_METADATA,
238                                              &wim->out_fd,
239                                              wim->out_compression_type,
240                                              wim->out_chunk_size,
241                                              &imd->metadata_lte->out_reshdr,
242                                              imd->metadata_lte->hash,
243                                              write_resource_flags);
244
245         /* Original checksum was overridden; set a flag so it isn't used.  */
246         imd->metadata_lte->dont_check_metadata_hash = 1;
247
248         FREE(buf);
249         return ret;
250 }