in core/manifest/manifest_flash.c [786:1004]
int manifest_flash_read_element_data (const struct manifest_flash *manifest,
const struct hash_engine *hash, uint8_t type, int start, uint8_t parent_type,
uint32_t read_offset, uint8_t *found, uint8_t *format, size_t *total_len, uint8_t **element,
size_t length)
{
struct manifest_toc_entry entry;
uint8_t entry_hash[SHA512_HASH_LENGTH];
uint8_t validate_hash[SHA512_HASH_LENGTH];
uint32_t entry_addr;
uint32_t hash_addr;
uint32_t toc_end;
int i;
int status;
if ((manifest == NULL) || (hash == NULL)) {
return MANIFEST_INVALID_ARGUMENT;
}
if (!manifest->state->manifest_valid) {
return MANIFEST_NO_MANIFEST;
}
if (start >= manifest->state->toc_header.entry_count) {
return (parent_type == MANIFEST_NO_PARENT) ?
MANIFEST_ELEMENT_NOT_FOUND : MANIFEST_CHILD_NOT_FOUND;
}
entry_addr =
manifest->addr + sizeof (struct manifest_header) + sizeof (struct manifest_toc_header);
hash_addr = entry_addr + (sizeof (entry) * manifest->state->toc_header.entry_count);
toc_end =
hash_addr + (manifest->state->toc_hash_length * manifest->state->toc_header.hash_count);
/* Start hashing to verify the TOC contents. */
status = hash_start_new_hash (hash, manifest->state->toc_hash_type);
if (status != 0) {
return status;
}
status = hash->update (hash, (uint8_t*) &manifest->state->toc_header,
sizeof (manifest->state->toc_header));
if (status != 0) {
goto error;
}
/* Hash the TOC data before the first entry that will be read. */
status = flash_hash_update_contents (manifest->flash, entry_addr, sizeof (entry) * start, hash);
if (status != 0) {
goto error;
}
/* Find the TOC entry for the requested element. */
entry_addr += sizeof (entry) * start;
i = start;
do {
status = manifest->flash->read (manifest->flash, entry_addr, (uint8_t*) &entry,
sizeof (entry));
if (status != 0) {
goto error;
}
/* As soon as we see an element that is not a child, we fail because we have left the
* context of the expected parent. */
if ((parent_type != MANIFEST_NO_PARENT) && (entry.parent == MANIFEST_NO_PARENT)) {
status = MANIFEST_CHILD_NOT_FOUND;
goto error;
}
status = hash->update (hash, (uint8_t*) &entry, sizeof (entry));
if (status != 0) {
goto error;
}
i++;
entry_addr += sizeof (entry);
} while ((entry.type_id != type) && (i < manifest->state->toc_header.entry_count));
if (entry.type_id != type) {
status = (parent_type == MANIFEST_NO_PARENT) ?
MANIFEST_ELEMENT_NOT_FOUND : MANIFEST_CHILD_NOT_FOUND;
goto error;
}
if (entry.hash_id < manifest->state->toc_header.hash_count) {
/* Find the address of the entry hash. */
hash_addr += (manifest->state->toc_hash_length * entry.hash_id);
/* Hash the unneeded TOC data until the entry hash. */
status = flash_hash_update_contents (manifest->flash, entry_addr, hash_addr - entry_addr,
hash);
if (status != 0) {
goto error;
}
/* Read the entry hash for element validation. */
status = manifest->flash->read (manifest->flash, hash_addr, entry_hash,
manifest->state->toc_hash_length);
if (status != 0) {
goto error;
}
status = hash->update (hash, entry_hash, manifest->state->toc_hash_length);
if (status != 0) {
goto error;
}
/* Hash the remaining TOC data. */
hash_addr += manifest->state->toc_hash_length;
status = flash_hash_update_contents (manifest->flash, hash_addr, toc_end - hash_addr, hash);
if (status != 0) {
goto error;
}
}
else {
status = flash_hash_update_contents (manifest->flash, entry_addr, toc_end - entry_addr,
hash);
if (status != 0) {
goto error;
}
}
/* Validate the TOC. */
status = hash->finish (hash, validate_hash, sizeof (validate_hash));
if (status != 0) {
goto error;
}
if (buffer_compare (validate_hash, manifest->state->toc_hash,
manifest->state->toc_hash_length) != 0) {
return MANIFEST_TOC_INVALID;
}
/* Read the element data. */
if ((entry.parent != MANIFEST_NO_PARENT) && (entry.parent != parent_type)) {
return MANIFEST_WRONG_PARENT;
}
if (found) {
*found = i - 1;
}
if (format) {
*format = entry.format;
}
if (total_len) {
*total_len = entry.length;
}
if ((element == NULL) || (read_offset >= entry.length)) {
length = 0;
}
else if (*element == NULL) {
/* This value doesn't matter. It's just set to pass the check for reading element data. */
length = 1;
}
if (length != 0) {
entry.length -= read_offset;
if (*element == NULL) {
*element = platform_malloc (entry.length);
if (*element == NULL) {
return MANIFEST_NO_MEMORY;
}
length = entry.length;
}
if (entry.hash_id < manifest->state->toc_header.hash_count) {
/* Hash the element data to validate the contents. */
status = hash_start_new_hash (hash, manifest->state->toc_hash_type);
if (status != 0) {
return status;
}
status = flash_hash_update_contents (manifest->flash, manifest->addr + entry.offset,
read_offset, hash);
if (status != 0) {
goto error;
}
}
entry.offset += read_offset;
length = min (length, entry.length);
status = manifest->flash->read (manifest->flash, manifest->addr + entry.offset, *element,
length);
if (status != 0) {
goto error;
}
if (entry.hash_id < manifest->state->toc_header.hash_count) {
status = hash->update (hash, *element, length);
if (status != 0) {
goto error;
}
if (length < entry.length) {
status = flash_hash_update_contents (manifest->flash,
manifest->addr + entry.offset + length, entry.length - length, hash);
if (status != 0) {
goto error;
}
}
status = hash->finish (hash, validate_hash, sizeof (validate_hash));
if (status != 0) {
goto error;
}
if (buffer_compare (validate_hash, entry_hash, manifest->state->toc_hash_length) != 0) {
return MANIFEST_ELEMENT_INVALID;
}
}
}
return length;
error:
hash->cancel (hash);
return status;
}