/**
 * Constructs a full data block representation from the specified minimal
 * block entry.  However, the resultant block's pointers are set to null,
 * rather than populated via hash table lookups.  This behavior is useful when
 * the RAM representation has not been fully constructed yet.
 *
 * @param out_block             On success, this gets populated with the data
 *                                  block information.
 * @param block_entry           The source block entry to convert.
 *
 * @return                      0 on success; nonzero on failure.
 */
int
nffs_block_from_hash_entry_no_ptrs(struct nffs_block *out_block,
                                   struct nffs_hash_entry *block_entry)
{
    struct nffs_disk_block disk_block;
    uint32_t area_offset;
    uint8_t area_idx;
    int rc;

    assert(nffs_hash_id_is_block(block_entry->nhe_id));

    if (nffs_hash_entry_is_dummy(block_entry)) {
        /*
         * We can't read this from disk so we'll be missing filling in anything
         * not already in inode_entry (e.g., prev_id).
         */
        out_block->nb_hash_entry = block_entry;
        return FS_ENOENT; /* let caller know it's a partial inode_entry */
    }

    nffs_flash_loc_expand(block_entry->nhe_flash_loc, &area_idx, &area_offset);
    rc = nffs_block_read_disk(area_idx, area_offset, &disk_block);
    if (rc != 0) {
        return rc;
    }

    out_block->nb_hash_entry = block_entry;
    nffs_block_from_disk_no_ptrs(out_block, &disk_block);

    return 0;
}
/**
 * Checks that each block a chain of data blocks was properly restored.
 *
 * @param last_block_entry      The entry corresponding to the last block in
 *                                  the chain.
 *
 * @return                      0 if the block chain is OK;
 *                              FS_ECORRUPT if corruption is detected;
 *                              nonzero on other error.
 */
static int
nffs_restore_validate_block_chain(struct nffs_hash_entry *last_block_entry)
{
    struct nffs_disk_block disk_block;
    struct nffs_hash_entry *cur;
    struct nffs_block block;
    uint32_t area_offset;
    uint8_t area_idx;
    int rc;

    cur = last_block_entry;

    while (cur != NULL) {
        if (nffs_hash_entry_is_dummy(cur)) {
            return FS_ENOENT;
        }

        nffs_flash_loc_expand(cur->nhe_flash_loc, &area_idx, &area_offset);

        rc = nffs_block_read_disk(area_idx, area_offset, &disk_block);
        if (rc != 0) {
            return rc;
        }

        rc = nffs_block_from_hash_entry(&block, cur);
        if (rc != 0) {
            return rc;
        }

        cur = block.nb_prev;
    }

    return 0;
}
/**
 * Deletes the specified block entry from the nffs RAM representation.
 *
 * @param block_entry           The block entry to delete.
 *
 * @return                      0 on success; nonzero on failure.
 */
int
nffs_block_delete_from_ram(struct nffs_hash_entry *block_entry)
{
    struct nffs_inode_entry *inode_entry;
    struct nffs_block block;
    int rc;

    if (nffs_hash_entry_is_dummy(block_entry)) {
        /*
         * it's very limited to what we can do here as the block doesn't have
         * any way to get to the inode via hash entry. Just delete the
         * block and return FS_ECORRUPT
         */
        nffs_hash_remove(block_entry);
        nffs_block_entry_free(block_entry);
        return FS_ECORRUPT;
    }

    rc = nffs_block_from_hash_entry(&block, block_entry);
    if (rc == 0 || rc == FS_ECORRUPT) {
        /* If file system corruption was detected, the resulting block is still
         * valid and can be removed from RAM.
         * Note that FS_CORRUPT can occur because the owning inode was not
         * found in the hash table - this can occur during the sweep where
         * the inodes were deleted ahead of the blocks.
         */
        inode_entry = block.nb_inode_entry;
        if (inode_entry != NULL &&
            inode_entry->nie_last_block_entry == block_entry) {

            inode_entry->nie_last_block_entry = block.nb_prev;
        }

        nffs_hash_remove(block_entry);
        nffs_block_entry_free(block_entry);
    }

    return rc;
}