/** * Indicates whether the specified data block is superseded by the just-read * disk data block. A data block supersedes another if its ID is equal and its * sequence number is greater than that of the other block. * * @param out_should_replace On success, 0 or 1 gets written here, to * indicate whether replacement should occur. * @param old_block The data block which has already been read and * converted to its RAM representation. This * is the block that may be superseded. * @param disk_block The disk data block that was just read from * flash. This is the block which may * supersede the other. * * @return 0 on success; nonzero on failure. */ static int nffs_restore_block_gets_replaced(const struct nffs_block *old_block, const struct nffs_disk_block *disk_block, int *out_should_replace) { assert(old_block->nb_hash_entry->nhe_id == disk_block->ndb_id); if (nffs_block_is_dummy(old_block->nb_hash_entry)) { assert(0); *out_should_replace = 1; return 0; } if (old_block->nb_seq < disk_block->ndb_seq) { *out_should_replace = 2; return 0; } if (old_block->nb_seq == disk_block->ndb_seq) { /* This is a duplicate of an previously-read block. This should never * happen. */ return FS_EEXIST; } *out_should_replace = 0; return 0; }
/** * Constructs a block representation from a minimal block hash entry. If the * hash entry references other objects (inode or previous data block), the * resulting block object is populated with pointers to the referenced objects. * If the any referenced objects are not present in the NFFS RAM * representation, this indicates file system corruption. In this case, the * resulting block is populated with all valid references, and an FS_ECORRUPT * code is returned. * * @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; * FS_ECORRUPT if one or more pointers could not * be filled in due to file system corruption; * FS_EOFFSET on an attempt to read an invalid * address range; * FS_EHW on flash error; * FS_EUNEXP if the specified disk location does * not contain a block. */ int nffs_block_from_hash_entry(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_block_is_dummy(block_entry)) { out_block->nb_hash_entry = block_entry; out_block->nb_inode_entry = NULL; out_block->nb_prev = NULL; /* * Dummy block added when inode was read in before real block * (see nffs_restore_inode()). Return success (because there's * too many places that ned to check for this, * but it's the responsibility fo the upstream code to check * whether this is still a dummy entry. XXX */ return 0; /*return FS_ENOENT;*/ } 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; rc = nffs_block_from_disk(out_block, &disk_block); if (rc != 0) { return rc; } return 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; }