]> wimlib.net Git - wimlib/blob - src/metadata_resource.c
wimlib.h: Update docs and adjust error codes
[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
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         u8 *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 = wim_resource_size(metadata_lte);
75
76         DEBUG("Reading metadata resource: original_size = %"PRIu64", "
77               "size = %"PRIu64", offset = %"PRIu64"",
78               metadata_lte->resource_entry.original_size,
79               metadata_lte->resource_entry.size,
80               metadata_lte->resource_entry.offset);
81
82         /* There is no way the metadata resource could possibly be less than (8
83          * + WIM_DENTRY_DISK_SIZE) bytes, where the 8 is for security data (with
84          * no security descriptors) and WIM_DENTRY_DISK_SIZE is for the root
85          * entry. */
86         if (metadata_len < 8 + WIM_DENTRY_DISK_SIZE) {
87                 ERROR("Expected at least %u bytes for the metadata resource",
88                       8 + WIM_DENTRY_DISK_SIZE);
89                 return WIMLIB_ERR_INVALID_METADATA_RESOURCE;
90         }
91
92         /* Read the metadata resource into memory.  (It may be compressed.) */
93         ret = read_full_resource_into_alloc_buf(metadata_lte, (void**)&buf);
94         if (ret)
95                 return ret;
96
97         if (!metadata_lte->dont_check_metadata_hash) {
98                 sha1_buffer(buf, metadata_len, hash);
99                 if (!hashes_equal(metadata_lte->hash, hash)) {
100                         ERROR("Metadata resource is corrupted "
101                               "(invalid SHA-1 message digest)!");
102                         ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE;
103                         goto out_free_buf;
104                 }
105         }
106
107         DEBUG("Finished reading metadata resource into memory.");
108
109         ret = read_wim_security_data(buf, metadata_len, &security_data);
110         if (ret)
111                 goto out_free_buf;
112
113         DEBUG("Reading root dentry");
114
115         /* Allocate memory for the root dentry and read it into memory */
116         root = MALLOC(sizeof(struct wim_dentry));
117         if (!root) {
118                 ret = WIMLIB_ERR_NOMEM;
119                 goto out_free_security_data;
120         }
121
122         /* The root directory entry starts after security data, aligned on an
123          * 8-byte boundary within the metadata resource.  Since
124          * security_data->total_length was already rounded up to an 8-byte
125          * boundary, its value can be used as the offset of the root directory
126          * entry.  */
127         ret = read_dentry(buf, metadata_len,
128                           security_data->total_length, root);
129
130         if (ret == 0 && root->length == 0) {
131                 WARNING("Metadata resource begins with end-of-directory entry "
132                         "(treating as empty image)");
133                 FREE(root);
134                 root = NULL;
135                 goto out_success;
136         }
137
138         if (ret) {
139                 FREE(root);
140                 goto out_free_security_data;
141         }
142
143         if (dentry_has_long_name(root) || dentry_has_short_name(root)) {
144                 WARNING("The root directory has a nonempty name (removing it)");
145                 FREE(root->file_name);
146                 FREE(root->short_name);
147                 root->file_name = NULL;
148                 root->short_name = NULL;
149                 root->file_name_nbytes = 0;
150                 root->short_name_nbytes = 0;
151         }
152
153         if (!dentry_is_directory(root)) {
154                 ERROR("Root of the WIM image must be a directory!");
155                 FREE(root);
156                 ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE;
157                 goto out_free_security_data;
158         }
159
160         /* This is the root dentry, so set its parent to itself. */
161         root->parent = root;
162
163         inode_add_dentry(root, root->d_inode);
164
165         /* Now read the entire directory entry tree into memory. */
166         DEBUG("Reading dentry tree");
167         ret = read_dentry_tree(buf, metadata_len, root);
168         if (ret)
169                 goto out_free_dentry_tree;
170
171         /* Build hash table that maps hard link group IDs to dentry sets */
172         ret = dentry_tree_fix_inodes(root, &imd->inode_list);
173         if (ret)
174                 goto out_free_dentry_tree;
175
176
177         DEBUG("Running miscellaneous verifications on the dentry tree");
178         image_for_each_inode(inode, imd) {
179                 ret = verify_inode(inode, security_data);
180                 if (ret)
181                         goto out_free_dentry_tree;
182         }
183         DEBUG("Done reading image metadata");
184 out_success:
185         imd->root_dentry = root;
186         imd->security_data = security_data;
187         INIT_LIST_HEAD(&imd->unhashed_streams);
188         ret = 0;
189         goto out_free_buf;
190 out_free_dentry_tree:
191         free_dentry_tree(root, wim->lookup_table);
192 out_free_security_data:
193         free_wim_security_data(security_data);
194 out_free_buf:
195         FREE(buf);
196         return ret;
197 }
198
199 static void
200 recalculate_security_data_length(struct wim_security_data *sd)
201 {
202         u32 total_length = sizeof(u64) * sd->num_entries + 2 * sizeof(u32);
203         for (u32 i = 0; i < sd->num_entries; i++)
204                 total_length += sd->sizes[i];
205         sd->total_length = (total_length + 7) & ~7;
206 }
207
208 static int
209 prepare_metadata_resource(WIMStruct *wim, int image,
210                           u8 **buf_ret, size_t *len_ret)
211 {
212         u8 *buf;
213         u8 *p;
214         int ret;
215         u64 subdir_offset;
216         struct wim_dentry *root;
217         u64 len;
218         struct wim_security_data *sd;
219         struct wim_image_metadata *imd;
220
221         DEBUG("Preparing metadata resource for image %d", image);
222
223         ret = select_wim_image(wim, image);
224         if (ret)
225                 return ret;
226
227         imd = wim->image_metadata[image - 1];
228
229         root = imd->root_dentry;
230         sd = imd->security_data;
231
232         if (!root) {
233                 /* Empty image; create a dummy root.  */
234                 ret = new_filler_directory(T(""), &root);
235                 if (ret)
236                         return ret;
237                 imd->root_dentry = root;
238         }
239
240         /* Offset of first child of the root dentry.  It's equal to:
241          * - The total length of the security data, rounded to the next 8-byte
242          *   boundary,
243          * - plus the total length of the root dentry,
244          * - plus 8 bytes for an end-of-directory entry following the root
245          *   dentry (shouldn't really be needed, but just in case...)
246          */
247         recalculate_security_data_length(sd);
248         subdir_offset = (((u64)sd->total_length + 7) & ~7) +
249                         dentry_correct_total_length(root) + 8;
250
251         /* Calculate the subdirectory offsets for the entire dentry tree.  */
252         calculate_subdir_offsets(root, &subdir_offset);
253
254         /* Total length of the metadata resource (uncompressed).  */
255         len = subdir_offset;
256
257         /* Allocate a buffer to contain the uncompressed metadata resource.  */
258         buf = MALLOC(len);
259         if (!buf) {
260                 ERROR("Failed to allocate %"PRIu64" bytes for "
261                       "metadata resource", len);
262                 return WIMLIB_ERR_NOMEM;
263         }
264
265         /* Write the security data into the resource buffer.  */
266         p = write_wim_security_data(sd, buf);
267
268         /* Write the dentry tree into the resource buffer.  */
269         p = write_dentry_tree(root, p);
270
271         /* We MUST have exactly filled the buffer; otherwise we calculated its
272          * size incorrectly or wrote the data incorrectly.  */
273         wimlib_assert(p - buf == len);
274
275         *buf_ret = buf;
276         *len_ret = len;
277         return 0;
278 }
279
280 int
281 write_metadata_resource(WIMStruct *wim, int image, int write_resource_flags)
282 {
283         int ret;
284         u8 *buf;
285         size_t len;
286         struct wim_image_metadata *imd;
287
288         ret = prepare_metadata_resource(wim, image, &buf, &len);
289         if (ret)
290                 return ret;
291
292         imd = wim->image_metadata[image - 1];
293
294         /* Write the metadata resource to the output WIM using the proper
295          * compression type, in the process updating the lookup table entry for
296          * the metadata resource.  */
297         ret = write_wim_resource_from_buffer(buf, len, WIM_RESHDR_FLAG_METADATA,
298                                              &wim->out_fd,
299                                              wim->compression_type,
300                                              &imd->metadata_lte->output_resource_entry,
301                                              imd->metadata_lte->hash,
302                                              write_resource_flags);
303
304         /* Original checksum was overridden; set a flag so it isn't used.  */
305         imd->metadata_lte->dont_check_metadata_hash = 1;
306
307         FREE(buf);
308         return ret;
309 }