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