/** * 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; }
/** * Creates a new empty file and writes it to the file system. If a file with * the specified path already exists, the behavior is undefined. * * @param parent The parent directory to insert the new file in. * @param filename The name of the file to create. * @param filename_len The length of the filename, in characters. * @param is_dir 1 if this is a directory; 0 if it is a normal * file. * @param out_inode_entry On success, this points to the inode * corresponding to the new file. * * @return 0 on success; nonzero on failure. */ int nffs_file_new(struct nffs_inode_entry *parent, const char *filename, uint8_t filename_len, int is_dir, struct nffs_inode_entry **out_inode_entry) { struct nffs_disk_inode disk_inode; struct nffs_inode_entry *inode_entry; uint32_t offset; uint8_t area_idx; int rc; inode_entry = nffs_inode_entry_alloc(); if (inode_entry == NULL) { rc = FS_ENOMEM; goto err; } rc = nffs_misc_reserve_space(sizeof disk_inode + filename_len, &area_idx, &offset); if (rc != 0) { goto err; } memset(&disk_inode, 0xff, sizeof disk_inode); disk_inode.ndi_magic = NFFS_INODE_MAGIC; if (is_dir) { disk_inode.ndi_id = nffs_hash_next_dir_id++; } else { disk_inode.ndi_id = nffs_hash_next_file_id++; } disk_inode.ndi_seq = 0; if (parent == NULL) { disk_inode.ndi_parent_id = NFFS_ID_NONE; } else { disk_inode.ndi_parent_id = parent->nie_hash_entry.nhe_id; } disk_inode.ndi_filename_len = filename_len; nffs_crc_disk_inode_fill(&disk_inode, filename); rc = nffs_inode_write_disk(&disk_inode, filename, area_idx, offset); if (rc != 0) { goto err; } inode_entry->nie_hash_entry.nhe_id = disk_inode.ndi_id; inode_entry->nie_hash_entry.nhe_flash_loc = nffs_flash_loc(area_idx, offset); inode_entry->nie_refcnt = 1; if (parent != NULL) { rc = nffs_inode_add_child(parent, inode_entry); if (rc != 0) { goto err; } } else { assert(disk_inode.ndi_id == NFFS_ID_ROOT_DIR); } nffs_hash_insert(&inode_entry->nie_hash_entry); *out_inode_entry = inode_entry; return 0; err: 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; }