int manifest_flash_read_element_data()

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;
}