1 /* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later */
3 #include <commonlib/bsd/cbfs_private.h>
4 #include <commonlib/bsd/helpers.h>
7 static enum cb_err
read_next_header(cbfs_dev_t dev
, size_t *offset
, struct cbfs_file
*buffer
,
10 DEBUG("Looking for next file @%#zx...\n", *offset
);
11 *offset
= ALIGN_UP(*offset
, CBFS_ALIGNMENT
);
12 while (*offset
+ sizeof(*buffer
) < devsize
) {
13 if (cbfs_dev_read(dev
, buffer
, *offset
, sizeof(*buffer
)) != sizeof(*buffer
))
16 if (memcmp(buffer
->magic
, CBFS_FILE_MAGIC
, sizeof(buffer
->magic
)) == 0)
19 *offset
+= CBFS_ALIGNMENT
;
22 DEBUG("End of CBFS reached\n");
23 return CB_CBFS_NOT_FOUND
;
26 enum cb_err
cbfs_walk(cbfs_dev_t dev
, enum cb_err (*walker
)(cbfs_dev_t dev
, size_t offset
,
27 const union cbfs_mdata
*mdata
,
28 size_t already_read
, void *arg
),
29 void *arg
, struct vb2_hash
*metadata_hash
, enum cbfs_walk_flags flags
)
31 const bool do_hash
= CBFS_ENABLE_HASHING
&& metadata_hash
;
32 const size_t devsize
= cbfs_dev_size(dev
);
33 struct vb2_digest_context dc
;
35 assert(CBFS_ENABLE_HASHING
|| (!metadata_hash
&& !(flags
& CBFS_WALK_WRITEBACK_HASH
)));
36 if (do_hash
&& vb2_digest_init(&dc
, CBFS_HASH_HWCRYPTO
, metadata_hash
->algo
, 0))
40 enum cb_err ret_header
;
41 enum cb_err ret_walker
= CB_CBFS_NOT_FOUND
;
42 union cbfs_mdata mdata
;
43 while ((ret_header
= read_next_header(dev
, &offset
, &mdata
.h
, devsize
)) == CB_SUCCESS
) {
44 const uint32_t attr_offset
= be32toh(mdata
.h
.attributes_offset
);
45 const uint32_t data_offset
= be32toh(mdata
.h
.offset
);
46 const uint32_t data_length
= be32toh(mdata
.h
.len
);
47 const uint32_t type
= be32toh(mdata
.h
.type
);
48 const bool empty
= (type
== CBFS_TYPE_DELETED
|| type
== CBFS_TYPE_NULL
);
50 DEBUG("Found CBFS header @%#zx (type %d, attr +%#x, data +%#x, length %#x)\n",
51 offset
, type
, attr_offset
, data_offset
, data_length
);
52 if (data_offset
> sizeof(mdata
) || data_length
> devsize
||
53 offset
+ data_offset
+ data_length
> devsize
) {
54 ERROR("File @%#zx too large\n", offset
);
55 offset
+= CBFS_ALIGNMENT
;
59 if (empty
&& !(flags
& CBFS_WALK_INCLUDE_EMPTY
))
62 /* When hashing we need to read everything. Otherwise skip the attributes.
63 attr_offset may be 0, which means there are no attributes. */
65 if (do_hash
|| attr_offset
== 0)
66 todo
= data_offset
- sizeof(mdata
.h
);
68 todo
= attr_offset
- sizeof(mdata
.h
);
69 if (todo
<= 0 || data_offset
< attr_offset
) {
70 ERROR("Corrupt file header @%#zx\n", offset
);
74 /* Read the rest of the metadata (filename, and possibly attributes). */
75 assert(todo
> 0 && todo
<= sizeof(mdata
) - sizeof(mdata
.h
));
76 if (cbfs_dev_read(dev
, mdata
.raw
+ sizeof(mdata
.h
),
77 offset
+ sizeof(mdata
.h
), todo
) != todo
)
79 /* Force filename null-termination, just in case. */
80 mdata
.raw
[attr_offset
? attr_offset
- 1 : data_offset
- 1] = '\0';
81 DEBUG("File name: '%s'\n", mdata
.h
.filename
);
83 if (do_hash
&& !empty
&& vb2_digest_extend(&dc
, mdata
.raw
, data_offset
))
86 if (walker
&& ret_walker
== CB_CBFS_NOT_FOUND
)
87 ret_walker
= walker(dev
, offset
, &mdata
, sizeof(mdata
.h
) + todo
, arg
);
89 /* Return IO errors immediately. For others, finish the hash first if needed. */
90 if (ret_walker
== CB_CBFS_IO
|| (ret_walker
!= CB_CBFS_NOT_FOUND
&& !do_hash
))
94 offset
+= data_offset
+ data_length
;
97 if (ret_header
!= CB_CBFS_NOT_FOUND
)
101 uint8_t real_hash
[VB2_MAX_DIGEST_SIZE
];
102 size_t hash_size
= vb2_digest_size(metadata_hash
->algo
);
103 if (vb2_digest_finalize(&dc
, real_hash
, hash_size
))
105 if (flags
& CBFS_WALK_WRITEBACK_HASH
)
106 memcpy(metadata_hash
->raw
, real_hash
, hash_size
);
107 else if (memcmp(metadata_hash
->raw
, real_hash
, hash_size
) != 0)
108 return CB_CBFS_HASH_MISMATCH
;
114 enum cb_err
cbfs_copy_fill_metadata(union cbfs_mdata
*dst
, const union cbfs_mdata
*src
,
115 size_t already_read
, cbfs_dev_t dev
, size_t offset
)
117 /* First, copy the stuff that cbfs_walk() already read for us. */
118 memcpy(dst
, src
, already_read
);
120 /* Then read in whatever metadata may be left (will only happen in non-hashing case). */
121 const size_t todo
= be32toh(src
->h
.offset
) - already_read
;
122 assert(todo
<= sizeof(*dst
) - already_read
);
123 if (todo
&& cbfs_dev_read(dev
, dst
->raw
+ already_read
, offset
+ already_read
,
129 struct cbfs_lookup_args
{
130 union cbfs_mdata
*mdata_out
;
133 size_t *data_offset_out
;
136 static enum cb_err
lookup_walker(cbfs_dev_t dev
, size_t offset
, const union cbfs_mdata
*mdata
,
137 size_t already_read
, void *arg
)
139 struct cbfs_lookup_args
*args
= arg
;
140 /* Check if the name we're looking for could fit, then we can safely memcmp() it. */
141 if (args
->namesize
> already_read
- offsetof(union cbfs_mdata
, h
.filename
) ||
142 memcmp(args
->name
, mdata
->h
.filename
, args
->namesize
) != 0)
143 return CB_CBFS_NOT_FOUND
;
145 LOG("Found '%s' @%#zx size %#x\n", args
->name
, offset
, be32toh(mdata
->h
.len
));
146 if (cbfs_copy_fill_metadata(args
->mdata_out
, mdata
, already_read
, dev
, offset
))
149 *args
->data_offset_out
= offset
+ be32toh(mdata
->h
.offset
);
153 enum cb_err
cbfs_lookup(cbfs_dev_t dev
, const char *name
, union cbfs_mdata
*mdata_out
,
154 size_t *data_offset_out
, struct vb2_hash
*metadata_hash
)
156 struct cbfs_lookup_args args
= {
157 .mdata_out
= mdata_out
,
159 .namesize
= strlen(name
) + 1, /* Count trailing \0 so we can memcmp() it. */
160 .data_offset_out
= data_offset_out
,
162 return cbfs_walk(dev
, lookup_walker
, &args
, metadata_hash
, 0);
165 const void *cbfs_find_attr(const union cbfs_mdata
*mdata
, uint32_t attr_tag
, size_t size_check
)
167 uint32_t offset
= be32toh(mdata
->h
.attributes_offset
);
168 uint32_t end
= be32toh(mdata
->h
.offset
);
173 while (offset
+ sizeof(struct cbfs_file_attribute
) <= end
) {
174 const struct cbfs_file_attribute
*attr
= (const void *)mdata
->raw
+ offset
;
175 const uint32_t tag
= be32toh(attr
->tag
);
176 const uint32_t len
= be32toh(attr
->len
);
178 if (len
< sizeof(struct cbfs_file_attribute
) || len
> end
- offset
) {
179 ERROR("Attribute %s[%x] invalid length: %u\n",
180 mdata
->h
.filename
, tag
, len
);
183 if (tag
== attr_tag
) {
184 if (size_check
&& len
!= size_check
) {
185 ERROR("Attribute %s[%x] size mismatch: %u != %zu\n",
186 mdata
->h
.filename
, tag
, len
, size_check
);
197 const struct vb2_hash
*cbfs_file_hash(const union cbfs_mdata
*mdata
)
199 /* Hashes are variable-length attributes, so need to manually check the length. */
200 const struct cbfs_file_attr_hash
*attr
=
201 cbfs_find_attr(mdata
, CBFS_FILE_ATTR_TAG_HASH
, 0);
203 return NULL
; /* no hash */
204 const size_t asize
= be32toh(attr
->len
);
206 const struct vb2_hash
*hash
= &attr
->hash
;
207 const size_t hsize
= vb2_digest_size(hash
->algo
);
209 ERROR("Hash algo %u for '%s' unsupported.\n", hash
->algo
, mdata
->h
.filename
);
212 if (hsize
!= asize
- offsetof(struct cbfs_file_attr_hash
, hash
.raw
)) {
213 ERROR("Hash attribute size for '%s' (%zu) incorrect for algo %u.\n",
214 mdata
->h
.filename
, asize
, hash
->algo
);