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