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