/**
 * Populates the nffs RAM state with the memory representation of the specified
 * disk data block.
 *
 * @param disk_block            The source disk block to insert.
 * @param area_idx              The ID of the area containing the block.
 * @param area_offset           The area_offset within the area of the block.
 *
 * @return                      0 on success; nonzero on failure.
 */
static int
nffs_restore_block(const struct nffs_disk_block *disk_block, uint8_t area_idx,
                   uint32_t area_offset)
{
    struct nffs_inode_entry *inode_entry;
    struct nffs_hash_entry *entry;
    struct nffs_block block;
    int do_replace;
    int new_block;
    int rc;

    new_block = 0;

    /* Check the block's CRC.  If the block is corrupt, discard it.  If this
     * block would have superseded another, the old block becomes current.
     */
    rc = nffs_crc_disk_block_validate(disk_block, area_idx, area_offset);
    if (rc != 0) {
        goto err;
    }

    entry = nffs_hash_find_block(disk_block->ndb_id);
    if (entry != NULL) {
        rc = nffs_block_from_hash_entry_no_ptrs(&block, entry);
        if (rc != 0) {
            goto err;
        }

        rc = nffs_restore_block_gets_replaced(&block, disk_block, &do_replace);
        if (rc != 0) {
            goto err;
        }

        if (!do_replace) {
            /* The new block is superseded by the old; nothing to do. */
            return 0;
        }

        nffs_block_delete_from_ram(entry);
    }

    entry = nffs_block_entry_alloc();
    if (entry == NULL) {
        rc = FS_ENOMEM;
        goto err;
    }
    new_block = 1;
    entry->nhe_id = disk_block->ndb_id;
    entry->nhe_flash_loc = nffs_flash_loc(area_idx, area_offset);

    /* The block is ready to be inserted into the hash. */

    inode_entry = nffs_hash_find_inode(disk_block->ndb_inode_id);
    if (inode_entry == NULL) {
        rc = nffs_restore_dummy_inode(disk_block->ndb_inode_id, &inode_entry);
        if (rc != 0) {
            goto err;
        }
    }

    if (inode_entry->nie_last_block_entry == NULL ||
        inode_entry->nie_last_block_entry->nhe_id == disk_block->ndb_prev_id) {

        inode_entry->nie_last_block_entry = entry;
    }

    nffs_hash_insert(entry);

    if (disk_block->ndb_id >= nffs_hash_next_block_id) {
        nffs_hash_next_block_id = disk_block->ndb_id + 1;
    }

    /* Make sure the maximum block data size is not set lower than the size of
     * an existing block.
     */
    if (disk_block->ndb_data_len > nffs_restore_largest_block_data_len) {
        nffs_restore_largest_block_data_len = disk_block->ndb_data_len;
    }

    return 0;

err:
    if (new_block) {
        nffs_block_entry_free(entry);
    }
    return rc;
}
Ejemplo n.º 2
0
/**
 * Populates the nffs RAM state with the memory representation of the specified
 * disk data block.
 *
 * @param disk_block            The source disk block to insert.
 * @param area_idx              The ID of the area containing the block.
 * @param area_offset           The area_offset within the area of the block.
 *
 * @return                      0 on success; nonzero on failure.
 */
static int
nffs_restore_block(const struct nffs_disk_block *disk_block, uint8_t area_idx,
                   uint32_t area_offset)
{
    struct nffs_inode_entry *inode_entry;
    struct nffs_hash_entry *entry;
    struct nffs_block block;
    int do_replace;
    int new_block;
    int rc;

    new_block = 0;

    /* Check the block's CRC.  If the block is corrupt, discard it.  If this
     * block would have superseded another, the old block becomes current.
     */
    rc = nffs_crc_disk_block_validate(disk_block, area_idx, area_offset);
    if (rc != 0) {
        goto err;
    }

    entry = nffs_hash_find_block(disk_block->ndb_id);
    if (entry != NULL) {

        rc = nffs_block_from_hash_entry_no_ptrs(&block, entry);
        if (rc != 0 && rc != FS_ENOENT) {
            goto err;
        }

        /*
         * If the old block reference is for a 'dummy' block, it was added
         * because the owning inode's lastblock was not yet restored.
         * Update the block hash entry and inode to reference the entry.
         */
        if (nffs_block_is_dummy(entry)) {

            assert(entry->nhe_id == disk_block->ndb_id);

            /*
             * Entry is no longer dummy as it references the correct location
             */
            entry->nhe_flash_loc = nffs_flash_loc(area_idx, area_offset);

            inode_entry = nffs_hash_find_inode(disk_block->ndb_inode_id);

            /*
             * Turn off flags in previously restored inode recording the
             * allocation of a dummy block
             */
            if (inode_entry) {
                nffs_inode_unsetflags(inode_entry, NFFS_INODE_FLAG_DUMMYLSTBLK);
            }
        }

        rc = nffs_restore_block_gets_replaced(&block, disk_block,
                                              &do_replace);
        if (rc != 0) {
            goto err;
        }

        if (!do_replace) {
            /* The new block is superseded by the old; nothing to do. */
            return 0;
        }

        /*
         * update the existing hash entry to reference the new flash location
         */
        entry->nhe_flash_loc = nffs_flash_loc(area_idx, area_offset);

    } else {
        entry = nffs_block_entry_alloc();
        if (entry == NULL) {
            rc = FS_ENOMEM;
            goto err;
        }
        new_block = 1;
        entry->nhe_id = disk_block->ndb_id;
        entry->nhe_flash_loc = nffs_flash_loc(area_idx, area_offset);

        /* The block is ready to be inserted into the hash. */

        nffs_hash_insert(entry);

        if (disk_block->ndb_id >= nffs_hash_next_block_id) {
            nffs_hash_next_block_id = disk_block->ndb_id + 1;
        }
    }

    /* Make sure the maximum block data size is not set lower than the size of
     * an existing block.
     */
    if (disk_block->ndb_data_len > nffs_restore_largest_block_data_len) {
        nffs_restore_largest_block_data_len = disk_block->ndb_data_len;
    }

    NFFS_LOG(DEBUG, "restoring block; id=0x%08x seq=%u inode_id=%u prev_id=%u "
             "data_len=%u\n",
             (unsigned int)disk_block->ndb_id,
             (unsigned int)disk_block->ndb_seq,
             (unsigned int)disk_block->ndb_inode_id,
             (unsigned int)disk_block->ndb_prev_id,
             disk_block->ndb_data_len);

    inode_entry = nffs_hash_find_inode(disk_block->ndb_inode_id);

    if (inode_entry == NULL) {
        /*
         * Owning inode not yet restored.
         * Allocate a dummy inode which temporarily owns this block.
         * It is not yet linked to a parent.
         */
        rc = nffs_restore_dummy_inode(disk_block->ndb_inode_id, &inode_entry);
        if (rc != 0) {
            goto err;
        }
        /*
         * Record that this inode was created because a block was restored
         * before the inode
         */
        nffs_inode_setflags(inode_entry, NFFS_INODE_FLAG_DUMMYINOBLK);
        inode_entry->nie_last_block_entry = entry;
    } else {
        if (nffs_inode_getflags(inode_entry, NFFS_INODE_FLAG_DELETED)) {
            /*
             * don't restore blocks for deleted inodes
             */
            rc = FS_ENOENT;
            goto err;
        }
    }

    return 0;

err:
    if (new_block) {
        nffs_hash_remove(entry);
        nffs_block_entry_free(entry);
    }
    return rc;
}