/** * 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; }
/** * Determines if the specified inode should be added to the RAM representation * and adds it if appropriate. * * @param disk_inode The inode just read from flash. * @param area_idx The index of the area containing the inode. * @param area_offset The offset within the area of the inode. * * @return 0 on success; nonzero on failure. */ static int nffs_restore_inode(const struct nffs_disk_inode *disk_inode, uint8_t area_idx, uint32_t area_offset) { struct nffs_inode_entry *inode_entry; struct nffs_inode_entry *parent; struct nffs_inode inode; int new_inode; int do_add; int rc; new_inode = 0; /* Check the inode's CRC. If the inode is corrupt, discard it. */ rc = nffs_crc_disk_inode_validate(disk_inode, area_idx, area_offset); if (rc != 0) { goto err; } inode_entry = nffs_hash_find_inode(disk_inode->ndi_id); if (inode_entry != NULL) { rc = nffs_restore_inode_gets_replaced(inode_entry, disk_inode, &do_add); if (rc != 0) { goto err; } if (do_add) { if (inode_entry->nie_hash_entry.nhe_flash_loc != NFFS_FLASH_LOC_NONE) { rc = nffs_inode_from_entry(&inode, inode_entry); if (rc != 0) { return rc; } if (inode.ni_parent != NULL) { nffs_inode_remove_child(&inode); } } inode_entry->nie_hash_entry.nhe_flash_loc = nffs_flash_loc(area_idx, area_offset); } } else { inode_entry = nffs_inode_entry_alloc(); if (inode_entry == NULL) { rc = FS_ENOMEM; goto err; } new_inode = 1; do_add = 1; inode_entry->nie_hash_entry.nhe_id = disk_inode->ndi_id; inode_entry->nie_hash_entry.nhe_flash_loc = nffs_flash_loc(area_idx, area_offset); nffs_hash_insert(&inode_entry->nie_hash_entry); } if (do_add) { inode_entry->nie_refcnt = 1; if (disk_inode->ndi_parent_id != NFFS_ID_NONE) { parent = nffs_hash_find_inode(disk_inode->ndi_parent_id); if (parent == NULL) { rc = nffs_restore_dummy_inode(disk_inode->ndi_parent_id, &parent); if (rc != 0) { goto err; } } rc = nffs_inode_add_child(parent, inode_entry); if (rc != 0) { goto err; } } if (inode_entry->nie_hash_entry.nhe_id == NFFS_ID_ROOT_DIR) { nffs_root_dir = inode_entry; } } if (nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id)) { if (inode_entry->nie_hash_entry.nhe_id >= nffs_hash_next_file_id) { nffs_hash_next_file_id = inode_entry->nie_hash_entry.nhe_id + 1; } } else { if (inode_entry->nie_hash_entry.nhe_id >= nffs_hash_next_dir_id) { nffs_hash_next_dir_id = inode_entry->nie_hash_entry.nhe_id + 1; } } return 0; err: if (new_inode) { nffs_inode_entry_free(inode_entry); } return rc; }
/** * Determines if the specified inode should be added to the RAM representation * and adds it if appropriate. * * @param disk_inode The inode just read from flash. * @param area_idx The index of the area containing the inode. * @param area_offset The offset within the area of the inode. * * @return 0 on success; nonzero on failure. */ static int nffs_restore_inode(const struct nffs_disk_inode *disk_inode, uint8_t area_idx, uint32_t area_offset) { struct nffs_inode_entry *inode_entry; struct nffs_inode_entry *parent; struct nffs_inode inode; struct nffs_hash_entry *lastblock_entry = NULL; int new_inode; int do_add; int rc; new_inode = 0; /* Check the inode's CRC. If the inode is corrupt, discard it. */ rc = nffs_crc_disk_inode_validate(disk_inode, area_idx, area_offset); if (rc != 0) { goto err; } inode_entry = nffs_hash_find_inode(disk_inode->ndi_id); /* * Inode has already been restored. Determine whether this version * from disk should replace the previous version referenced in RAM. */ if (inode_entry != NULL) { if (disk_inode->ndi_flags & NFFS_INODE_FLAG_DELETED) { /* * Restore this inode even though deleted on disk * so the additional restored blocks have a place to go */ NFFS_LOG(DEBUG, "restoring deleted inode %x\n", (unsigned int)disk_inode->ndi_id); nffs_inode_setflags(inode_entry, NFFS_INODE_FLAG_DELETED); } /* * inodes get replaced if they're dummy entries (i.e. allocated * as place holders until the actual inode is restored), or this is * a more recent version of the inode which supercedes the old. */ rc = nffs_restore_inode_gets_replaced(inode_entry, disk_inode, &do_add); if (rc != 0) { goto err; } if (do_add) { /* replace in this case */ if (!nffs_inode_is_dummy(inode_entry)) { /* * if it's not a dummy, read block from flash */ rc = nffs_inode_from_entry(&inode, inode_entry); if (rc != 0) { return rc; } /* * inode is known to be obsolete */ if (nffs_inode_getflags(inode_entry, NFFS_INODE_FLAG_OBSOLETE)) { nffs_inode_unsetflags(inode_entry, NFFS_INODE_FLAG_OBSOLETE); } if (inode.ni_parent != NULL) { nffs_inode_remove_child(&inode); } /* * If this is a delete record, subsequent inode and restore * records from flash may be ignored. * If the parent is NULL, this inode has been deleted. (old) * XXX if we could count on delete records for every inode, * we wouldn't need to check for the root directory looking * like a delete record because of it's parent ID. */ if (inode_entry->nie_hash_entry.nhe_id != NFFS_ID_ROOT_DIR) { if (disk_inode->ndi_flags & NFFS_INODE_FLAG_DELETED || disk_inode->ndi_parent_id == NFFS_ID_NONE) { nffs_inode_setflags(inode_entry, NFFS_INODE_FLAG_DELETED); } } } else { /* * The existing inode entry was added as dummy. * The restore operation clears that state. */ /* If it's a directory, it was added as a parent to * one of it's children who were restored first. */ if (nffs_inode_getflags(inode_entry, NFFS_INODE_FLAG_DUMMYPARENT)) { assert(nffs_hash_id_is_dir(inode_entry->nie_hash_entry.nhe_id)); nffs_inode_unsetflags(inode_entry, NFFS_INODE_FLAG_DUMMYPARENT); } /* * If it's a file, it was added to store a lastblock */ if (nffs_inode_getflags(inode_entry, NFFS_INODE_FLAG_DUMMYINOBLK)) { assert(nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id)); nffs_inode_unsetflags(inode_entry, NFFS_INODE_FLAG_DUMMYINOBLK); } /* * Also, since it's a dummy, clear this flag too */ if (nffs_inode_getflags(inode_entry, NFFS_INODE_FLAG_DUMMY)) { nffs_inode_unsetflags(inode_entry, NFFS_INODE_FLAG_DUMMY); } } /* * Update location to reference new location in flash */ inode_entry->nie_hash_entry.nhe_flash_loc = nffs_flash_loc(area_idx, area_offset); } } else { inode_entry = nffs_inode_entry_alloc(); if (inode_entry == NULL) { rc = FS_ENOMEM; goto err; } new_inode = 1; do_add = 1; inode_entry->nie_hash_entry.nhe_id = disk_inode->ndi_id; inode_entry->nie_hash_entry.nhe_flash_loc = nffs_flash_loc(area_idx, area_offset); inode_entry->nie_last_block_entry = NULL; /* for now */ nffs_hash_insert(&inode_entry->nie_hash_entry); } /* * inode object has been restored and the entry is in the hash * Check whether the lastblock and parent have also been restored * and link up or allocate dummy entries as appropriate. */ if (do_add) { inode_entry->nie_refcnt = 1; if (disk_inode->ndi_flags & NFFS_INODE_FLAG_DELETED) { /* * Restore this inode even though deleted on disk * so the additional restored blocks have a place to go */ NFFS_LOG(DEBUG, "restoring deleted inode %x\n", (unsigned int)disk_inode->ndi_id); nffs_inode_setflags(inode_entry, NFFS_INODE_FLAG_DELETED); } /* * Inode has a lastblock on disk. * Add reference to last block entry if in hash table * otherwise add a dummy block entry for later update */ if (disk_inode->ndi_lastblock_id != NFFS_ID_NONE && nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id)) { lastblock_entry = nffs_hash_find_block(disk_inode->ndi_lastblock_id); /* * Lastblock has already been restored. */ if (lastblock_entry != NULL) { if (lastblock_entry->nhe_id == disk_inode->ndi_lastblock_id) { inode_entry->nie_last_block_entry = lastblock_entry; } } else { /* * Insert a temporary reference to a 'dummy' block entry * When block is restored, it will update this dummy and * the entry of this inode is updated to flash location */ rc = nffs_block_entry_reserve(&lastblock_entry); if (lastblock_entry == NULL) { rc = FS_ENOMEM; goto err; } lastblock_entry->nhe_id = disk_inode->ndi_lastblock_id; lastblock_entry->nhe_flash_loc = NFFS_FLASH_LOC_NONE; inode_entry->nie_last_block_entry = lastblock_entry; nffs_inode_setflags(inode_entry, NFFS_INODE_FLAG_DUMMYLSTBLK); nffs_hash_insert(lastblock_entry); if (lastblock_entry->nhe_id >= nffs_hash_next_block_id) { nffs_hash_next_block_id = lastblock_entry->nhe_id + 1; } } } if (disk_inode->ndi_parent_id != NFFS_ID_NONE) { parent = nffs_hash_find_inode(disk_inode->ndi_parent_id); /* * The parent directory for this inode hasn't been restored yet. * Add a dummy directory so it can be added as a child. * When the parent inode is restored, it's hash entry will be * updated with the flash location. */ if (parent == NULL) { rc = nffs_restore_dummy_inode(disk_inode->ndi_parent_id, &parent); /* * Set the dummy parent flag in the new parent. * It's turned off above when restored. */ nffs_inode_setflags(parent, NFFS_INODE_FLAG_DUMMYPARENT); if (rc != 0) { goto err; } } rc = nffs_inode_add_child(parent, inode_entry); if (rc != 0) { goto err; } } if (inode_entry->nie_hash_entry.nhe_id == NFFS_ID_ROOT_DIR) { nffs_root_dir = inode_entry; nffs_inode_setflags(nffs_root_dir, NFFS_INODE_FLAG_INTREE); } } if (nffs_hash_id_is_file(inode_entry->nie_hash_entry.nhe_id)) { NFFS_LOG(DEBUG, "restoring file; id=0x%08x\n", (unsigned int)inode_entry->nie_hash_entry.nhe_id); if (inode_entry->nie_hash_entry.nhe_id >= nffs_hash_next_file_id) { nffs_hash_next_file_id = inode_entry->nie_hash_entry.nhe_id + 1; } } else { NFFS_LOG(DEBUG, "restoring dir; id=0x%08x\n", (unsigned int)inode_entry->nie_hash_entry.nhe_id); if (inode_entry->nie_hash_entry.nhe_id >= nffs_hash_next_dir_id) { nffs_hash_next_dir_id = inode_entry->nie_hash_entry.nhe_id + 1; } } return 0; err: if (new_inode) { assert(nffs_inode_getflags(inode_entry, NFFS_INODE_FLAG_INHASH)); nffs_hash_remove(&inode_entry->nie_hash_entry); nffs_inode_entry_free(inode_entry); } return rc; }