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