blob: e7f800c67aa147e21a6b8012c2bcde19a4e71e45 [file] [log] [blame]
Angel Ponsebda03e2020-04-02 23:48:05 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Aaron Durbin295d58b2015-12-15 13:33:51 -06002
Aaron Durbin295d58b2015-12-15 13:33:51 -06003#include <commonlib/cbfs.h>
4#include <commonlib/endian.h>
5#include <commonlib/helpers.h>
6#include <string.h>
Joel Kitching2eb89c82019-04-25 17:45:12 +08007#include <vb2_sha.h>
Aaron Durbin295d58b2015-12-15 13:33:51 -06008
Aaron Durbin295d58b2015-12-15 13:33:51 -06009static size_t cbfs_next_offset(const struct region_device *cbfs,
10 const struct cbfsf *f)
11{
12 size_t offset;
13
14 if (f == NULL)
15 return 0;
16
Elyes HAOUAS23e3f9d2020-02-03 21:07:19 +010017 /* The region_device objects store absolute offsets over the whole
Aaron Durbin295d58b2015-12-15 13:33:51 -060018 * region. Therefore a relative offset needs to be calculated. */
19 offset = rdev_relative_offset(cbfs, &f->data);
20 offset += region_device_sz(&f->data);
21
22 return ALIGN_UP(offset, CBFS_ALIGNMENT);
23}
24
25static int cbfs_end(const struct region_device *cbfs, size_t offset)
26{
27 if (offset >= region_device_sz(cbfs))
28 return 1;
29
30 return 0;
31}
32
33int cbfs_for_each_file(const struct region_device *cbfs,
34 const struct cbfsf *prev, struct cbfsf *fh)
35{
36 size_t offset;
37
38 offset = cbfs_next_offset(cbfs, prev);
39
40 /* Try to scan the entire cbfs region looking for file name. */
41 while (1) {
42 struct cbfs_file file;
43 const size_t fsz = sizeof(file);
44
45 DEBUG("Checking offset %zx\n", offset);
46
47 /* End of region. */
48 if (cbfs_end(cbfs, offset))
49 return 1;
50
51 /* Can't read file. Nothing else to do but bail out. */
52 if (rdev_readat(cbfs, &file, offset, fsz) != fsz)
53 break;
54
55 if (memcmp(file.magic, CBFS_FILE_MAGIC, sizeof(file.magic))) {
56 offset++;
57 offset = ALIGN_UP(offset, CBFS_ALIGNMENT);
58 continue;
59 }
60
61 file.len = read_be32(&file.len);
62 file.offset = read_be32(&file.offset);
63
64 DEBUG("File @ offset %zx size %x\n", offset, file.len);
65
66 /* Keep track of both the metadata and the data for the file. */
67 if (rdev_chain(&fh->metadata, cbfs, offset, file.offset))
68 break;
69
70 if (rdev_chain(&fh->data, cbfs, offset + file.offset, file.len))
71 break;
72
73 /* Success. */
74 return 0;
75 }
76
77 return -1;
78}
79
Julius Werner71885a42016-08-19 15:24:54 -070080size_t cbfs_for_each_attr(void *metadata, size_t metadata_size,
81 size_t last_offset)
82{
83 struct cbfs_file_attribute *attr;
84
85 if (!last_offset) {
86 struct cbfs_file *file = metadata;
87 size_t start_offset = read_be32(&file->attributes_offset);
88 if (start_offset <= sizeof(struct cbfs_file) ||
89 start_offset + sizeof(*attr) > metadata_size)
90 return 0;
91 return start_offset;
92 }
93
94 attr = metadata + last_offset;
95 size_t next_offset = last_offset + read_be32(&attr->len);
96
97 if (next_offset + sizeof(*attr) > metadata_size)
98 return 0;
99 return next_offset;
100}
101
102int cbfsf_decompression_info(struct cbfsf *fh, uint32_t *algo, size_t *size)
103{
104 size_t metadata_size = region_device_sz(&fh->metadata);
105 void *metadata = rdev_mmap_full(&fh->metadata);
106 size_t offs = 0;
107
108 if (!metadata)
109 return -1;
110
111 while ((offs = cbfs_for_each_attr(metadata, metadata_size, offs))) {
112 struct cbfs_file_attr_compression *attr = metadata + offs;
113 if (read_be32(&attr->tag) != CBFS_FILE_ATTR_TAG_COMPRESSION)
114 continue;
115
116 *algo = read_be32(&attr->compression);
117 *size = read_be32(&attr->decompressed_size);
118 rdev_munmap(&fh->metadata, metadata);
119 return 0;
120 }
121
122 *algo = CBFS_COMPRESS_NONE;
123 *size = region_device_sz(&fh->data);
124 rdev_munmap(&fh->metadata, metadata);
125 return 0;
126}
127
Patrick Rudolpheeb4e202018-04-26 10:26:13 +0200128int cbfsf_file_type(struct cbfsf *fh, uint32_t *ftype)
Aaron Durbincbb6c752015-12-15 15:57:11 -0600129{
130 const size_t sz = sizeof(*ftype);
131
132 if (rdev_readat(&fh->metadata, ftype,
133 offsetof(struct cbfs_file, type), sz) != sz)
134 return -1;
135
136 *ftype = read_be32(ftype);
137
138 return 0;
139}
140
Aaron Durbin295d58b2015-12-15 13:33:51 -0600141int cbfs_locate(struct cbfsf *fh, const struct region_device *cbfs,
142 const char *name, uint32_t *type)
143{
144 struct cbfsf *prev;
145
146 LOG("Locating '%s'\n", name);
147
148 prev = NULL;
149
150 while (1) {
151 int ret;
152 char *fname;
153 int name_match;
154 const size_t fsz = sizeof(struct cbfs_file);
155
156 ret = cbfs_for_each_file(cbfs, prev, fh);
157 prev = fh;
158
159 /* Either failed to read or hit the end of the region. */
160 if (ret < 0 || ret > 0)
161 break;
162
163 fname = rdev_mmap(&fh->metadata, fsz,
164 region_device_sz(&fh->metadata) - fsz);
165
166 if (fname == NULL)
167 break;
168
169 name_match = !strcmp(fname, name);
170 rdev_munmap(&fh->metadata, fname);
171
172 if (!name_match) {
173 DEBUG(" Unmatched '%s' at %zx\n", fname,
174 rdev_relative_offset(cbfs, &fh->metadata));
175 continue;
176 }
177
178 if (type != NULL) {
179 uint32_t ftype;
180
Aaron Durbincbb6c752015-12-15 15:57:11 -0600181 if (cbfsf_file_type(fh, &ftype))
Aaron Durbin295d58b2015-12-15 13:33:51 -0600182 break;
183
Ronald G. Minnich60fd6842018-05-14 13:27:07 -0700184 if (*type != 0 && *type != ftype) {
Aaron Durbin295d58b2015-12-15 13:33:51 -0600185 DEBUG(" Unmatched type %x at %zx\n", ftype,
186 rdev_relative_offset(cbfs,
187 &fh->metadata));
188 continue;
189 }
Ronald G. Minnich60fd6842018-05-14 13:27:07 -0700190 // *type being 0 means we want to know ftype.
191 // We could just do a blind assignment but
192 // if type is pointing to read-only memory
193 // that might be bad.
194 if (*type == 0)
195 *type = ftype;
Aaron Durbin295d58b2015-12-15 13:33:51 -0600196 }
197
198 LOG("Found @ offset %zx size %zx\n",
199 rdev_relative_offset(cbfs, &fh->metadata),
200 region_device_sz(&fh->data));
201
202 /* Success. */
203 return 0;
204 }
205
206 LOG("'%s' not found.\n", name);
207 return -1;
208}
Aaron Durbincbb6c752015-12-15 15:57:11 -0600209
210static int cbfs_extend_hash_buffer(struct vb2_digest_context *ctx,
211 void *buf, size_t sz)
212{
213 return vb2_digest_extend(ctx, buf, sz);
214}
215
216static int cbfs_extend_hash(struct vb2_digest_context *ctx,
217 const struct region_device *rdev)
218{
219 uint8_t buffer[1024];
220 size_t sz_left;
221 size_t offset;
222
223 sz_left = region_device_sz(rdev);
224 offset = 0;
225
226 while (sz_left) {
227 int rv;
228 size_t block_sz = MIN(sz_left, sizeof(buffer));
229
230 if (rdev_readat(rdev, buffer, offset, block_sz) != block_sz)
231 return VB2_ERROR_UNKNOWN;
232
233 rv = cbfs_extend_hash_buffer(ctx, buffer, block_sz);
234
235 if (rv)
236 return rv;
237
238 sz_left -= block_sz;
239 offset += block_sz;
240 }
241
242 return VB2_SUCCESS;
243}
244
245/* Include offsets of child regions within the parent into the hash. */
246static int cbfs_extend_hash_with_offset(struct vb2_digest_context *ctx,
247 const struct region_device *p,
248 const struct region_device *c)
249{
250 int32_t soffset;
251 int rv;
252
253 soffset = rdev_relative_offset(p, c);
254
255 if (soffset < 0)
256 return VB2_ERROR_UNKNOWN;
257
258 /* All offsets in big endian format. */
259 write_be32(&soffset, soffset);
260
261 rv = cbfs_extend_hash_buffer(ctx, &soffset, sizeof(soffset));
262
263 if (rv)
264 return rv;
265
266 return cbfs_extend_hash(ctx, c);
267}
268
269/* Hash in the potential CBFS header sitting at the beginning of the CBFS
270 * region as well as relative offset at the end. */
271static int cbfs_extend_hash_master_header(struct vb2_digest_context *ctx,
272 const struct region_device *cbfs)
273{
274 struct region_device rdev;
275 int rv;
276
277 if (rdev_chain(&rdev, cbfs, 0, sizeof(struct cbfs_header)))
278 return VB2_ERROR_UNKNOWN;
279
280 rv = cbfs_extend_hash_with_offset(ctx, cbfs, &rdev);
281
282 if (rv)
283 return rv;
284
285 /* Include potential relative offset at end of region. */
286 if (rdev_chain(&rdev, cbfs, region_device_sz(cbfs) - sizeof(int32_t),
287 sizeof(int32_t)))
288 return VB2_ERROR_UNKNOWN;
289
290 return cbfs_extend_hash_with_offset(ctx, cbfs, &rdev);
291}
292
293int cbfs_vb2_hash_contents(const struct region_device *cbfs,
294 enum vb2_hash_algorithm hash_alg, void *digest,
295 size_t digest_sz)
296{
297 struct vb2_digest_context ctx;
298 int rv;
299 struct cbfsf f;
300 struct cbfsf *prev;
301 struct cbfsf *fh;
302
303 rv = vb2_digest_init(&ctx, hash_alg);
304
305 if (rv)
306 return rv;
307
308 rv = cbfs_extend_hash_master_header(&ctx, cbfs);
309 if (rv)
310 return rv;
311
312 prev = NULL;
313 fh = &f;
314
315 while (1) {
316 uint32_t ftype;
317
318 rv = cbfs_for_each_file(cbfs, prev, fh);
319 prev = fh;
320
321 if (rv < 0)
322 return VB2_ERROR_UNKNOWN;
323
324 /* End of CBFS. */
325 if (rv > 0)
326 break;
327
328 rv = cbfs_extend_hash_with_offset(&ctx, cbfs, &fh->metadata);
329
330 if (rv)
331 return rv;
332
333 /* Include data contents in hash if file is non-empty. */
334 if (cbfsf_file_type(fh, &ftype))
335 return VB2_ERROR_UNKNOWN;
336
Julius Wernerd4775652020-03-13 16:43:34 -0700337 if (ftype == CBFS_TYPE_DELETED || ftype == CBFS_TYPE_NULL)
Aaron Durbincbb6c752015-12-15 15:57:11 -0600338 continue;
339
340 rv = cbfs_extend_hash_with_offset(&ctx, cbfs, &fh->data);
341
342 if (rv)
343 return rv;
344 }
345
346 return vb2_digest_finalize(&ctx, digest, digest_sz);
347}