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