1 /* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later */
3 #include <commonlib/bsd/cbfs_private.h>
6 static enum cb_err
read_next_header(cbfs_dev_t dev
, size_t *offset
, struct cbfs_file
*buffer
,
9 DEBUG("Looking for next file @%#zx...\n", *offset
);
10 *offset
= ALIGN_UP(*offset
, CBFS_ALIGNMENT
);
11 while (*offset
+ sizeof(*buffer
) < devsize
) {
12 if (cbfs_dev_read(dev
, buffer
, *offset
, sizeof(*buffer
)) != sizeof(*buffer
))
15 if (memcmp(buffer
->magic
, CBFS_FILE_MAGIC
, sizeof(buffer
->magic
)) == 0)
18 *offset
+= CBFS_ALIGNMENT
;
21 DEBUG("End of CBFS reached\n");
22 return CB_CBFS_NOT_FOUND
;
25 enum cb_err
cbfs_walk(cbfs_dev_t dev
, enum cb_err (*walker
)(cbfs_dev_t dev
, size_t offset
,
26 const union cbfs_mdata
*mdata
,
27 size_t already_read
, void *arg
),
28 void *arg
, struct vb2_hash
*metadata_hash
, enum cbfs_walk_flags flags
)
30 const bool do_hash
= CBFS_ENABLE_HASHING
&& metadata_hash
;
31 const size_t devsize
= cbfs_dev_size(dev
);
32 struct vb2_digest_context dc
;
34 assert(CBFS_ENABLE_HASHING
|| (!metadata_hash
&& !(flags
& CBFS_WALK_WRITEBACK_HASH
)));
35 if (do_hash
&& vb2_digest_init(&dc
, CBFS_HASH_HWCRYPTO
, metadata_hash
->algo
, 0))
39 enum cb_err ret_header
;
40 enum cb_err ret_walker
= CB_CBFS_NOT_FOUND
;
41 union cbfs_mdata mdata
;
42 while ((ret_header
= read_next_header(dev
, &offset
, &mdata
.h
, devsize
)) == CB_SUCCESS
) {
43 const uint32_t attr_offset
= be32toh(mdata
.h
.attributes_offset
);
44 const uint32_t data_offset
= be32toh(mdata
.h
.offset
);
45 const uint32_t data_length
= be32toh(mdata
.h
.len
);
46 const uint32_t type
= be32toh(mdata
.h
.type
);
47 const bool empty
= (type
== CBFS_TYPE_DELETED
|| type
== CBFS_TYPE_NULL
);
49 DEBUG("Found CBFS header @%#zx (type %d, attr +%#x, data +%#x, length %#x)\n",
50 offset
, type
, attr_offset
, data_offset
, data_length
);
51 if (data_offset
> sizeof(mdata
) || data_length
> devsize
||
52 offset
+ data_offset
+ data_length
> devsize
) {
53 ERROR("File @%#zx too large\n", offset
);
54 offset
+= CBFS_ALIGNMENT
;
58 if (empty
&& !(flags
& CBFS_WALK_INCLUDE_EMPTY
))
61 /* When hashing we need to read everything. Otherwise skip the attributes.
62 attr_offset may be 0, which means there are no attributes. */
64 if (do_hash
|| attr_offset
== 0)
65 todo
= data_offset
- sizeof(mdata
.h
);
67 todo
= attr_offset
- sizeof(mdata
.h
);
68 if (todo
<= 0 || data_offset
< attr_offset
) {
69 ERROR("Corrupt file header @%#zx\n", offset
);
73 /* Read the rest of the metadata (filename, and possibly attributes). */
74 assert(todo
> 0 && todo
<= sizeof(mdata
) - sizeof(mdata
.h
));
75 if (cbfs_dev_read(dev
, mdata
.raw
+ sizeof(mdata
.h
),
76 offset
+ sizeof(mdata
.h
), todo
) != todo
)
78 /* Force filename null-termination, just in case. */
79 mdata
.raw
[attr_offset
? attr_offset
- 1 : data_offset
- 1] = '\0';
80 DEBUG("File name: '%s'\n", mdata
.h
.filename
);
82 if (do_hash
&& !empty
&& vb2_digest_extend(&dc
, mdata
.raw
, data_offset
))
85 if (walker
&& ret_walker
== CB_CBFS_NOT_FOUND
)
86 ret_walker
= walker(dev
, offset
, &mdata
, sizeof(mdata
.h
) + todo
, arg
);
88 /* Return IO errors immediately. For others, finish the hash first if needed. */
89 if (ret_walker
== CB_CBFS_IO
|| (ret_walker
!= CB_CBFS_NOT_FOUND
&& !do_hash
))
93 offset
+= data_offset
+ data_length
;
96 if (ret_header
!= CB_CBFS_NOT_FOUND
)
100 uint8_t real_hash
[VB2_MAX_DIGEST_SIZE
];
101 size_t hash_size
= vb2_digest_size(metadata_hash
->algo
);
102 if (vb2_digest_finalize(&dc
, real_hash
, hash_size
))
104 if (flags
& CBFS_WALK_WRITEBACK_HASH
)
105 memcpy(metadata_hash
->raw
, real_hash
, hash_size
);
106 else if (memcmp(metadata_hash
->raw
, real_hash
, hash_size
) != 0)
107 return CB_CBFS_HASH_MISMATCH
;
113 enum cb_err
cbfs_copy_fill_metadata(union cbfs_mdata
*dst
, const union cbfs_mdata
*src
,
114 size_t already_read
, cbfs_dev_t dev
, size_t offset
)
116 /* First, copy the stuff that cbfs_walk() already read for us. */
117 memcpy(dst
, src
, already_read
);
119 /* Then read in whatever metadata may be left (will only happen in non-hashing case). */
120 const size_t todo
= be32toh(src
->h
.offset
) - already_read
;
121 assert(todo
<= sizeof(*dst
) - already_read
);
122 if (todo
&& cbfs_dev_read(dev
, dst
->raw
+ already_read
, offset
+ already_read
,
128 struct cbfs_lookup_args
{
129 union cbfs_mdata
*mdata_out
;
132 size_t *data_offset_out
;
135 static enum cb_err
lookup_walker(cbfs_dev_t dev
, size_t offset
, const union cbfs_mdata
*mdata
,
136 size_t already_read
, void *arg
)
138 struct cbfs_lookup_args
*args
= arg
;
139 /* Check if the name we're looking for could fit, then we can safely memcmp() it. */
140 if (args
->namesize
> already_read
- offsetof(union cbfs_mdata
, h
.filename
) ||
141 memcmp(args
->name
, mdata
->h
.filename
, args
->namesize
) != 0)
142 return CB_CBFS_NOT_FOUND
;
144 LOG("Found '%s' @%#zx size %#x\n", args
->name
, offset
, be32toh(mdata
->h
.len
));
145 if (cbfs_copy_fill_metadata(args
->mdata_out
, mdata
, already_read
, dev
, offset
))
148 *args
->data_offset_out
= offset
+ be32toh(mdata
->h
.offset
);
152 enum cb_err
cbfs_lookup(cbfs_dev_t dev
, const char *name
, union cbfs_mdata
*mdata_out
,
153 size_t *data_offset_out
, struct vb2_hash
*metadata_hash
)
155 struct cbfs_lookup_args args
= {
156 .mdata_out
= mdata_out
,
158 .namesize
= strlen(name
) + 1, /* Count trailing \0 so we can memcmp() it. */
159 .data_offset_out
= data_offset_out
,
161 return cbfs_walk(dev
, lookup_walker
, &args
, metadata_hash
, 0);
164 const void *cbfs_find_attr(const union cbfs_mdata
*mdata
, uint32_t attr_tag
, size_t size_check
)
166 uint32_t offset
= be32toh(mdata
->h
.attributes_offset
);
167 uint32_t end
= be32toh(mdata
->h
.offset
);
172 while (offset
+ sizeof(struct cbfs_file_attribute
) <= end
) {
173 const struct cbfs_file_attribute
*attr
= (const void *)mdata
->raw
+ offset
;
174 const uint32_t tag
= be32toh(attr
->tag
);
175 const uint32_t len
= be32toh(attr
->len
);
177 if (len
< sizeof(struct cbfs_file_attribute
) || len
> end
- offset
) {
178 ERROR("Attribute %s[%x] invalid length: %u\n",
179 mdata
->h
.filename
, tag
, len
);
182 if (tag
== attr_tag
) {
183 if (size_check
&& len
!= size_check
) {
184 ERROR("Attribute %s[%x] size mismatch: %u != %zu\n",
185 mdata
->h
.filename
, tag
, len
, size_check
);
196 const struct vb2_hash
*cbfs_file_hash(const union cbfs_mdata
*mdata
)
198 /* Hashes are variable-length attributes, so need to manually check the length. */
199 const struct cbfs_file_attr_hash
*attr
=
200 cbfs_find_attr(mdata
, CBFS_FILE_ATTR_TAG_HASH
, 0);
202 return NULL
; /* no hash */
203 const size_t asize
= be32toh(attr
->len
);
205 const struct vb2_hash
*hash
= &attr
->hash
;
206 const size_t hsize
= vb2_digest_size(hash
->algo
);
208 ERROR("Hash algo %u for '%s' unsupported.\n", hash
->algo
, mdata
->h
.filename
);
211 if (hsize
!= asize
- offsetof(struct cbfs_file_attr_hash
, hash
.raw
)) {
212 ERROR("Hash attribute size for '%s' (%zu) incorrect for algo %u.\n",
213 mdata
->h
.filename
, asize
, hash
->algo
);