/** * Appends a new block to an inode block chain. * * @param inode_entry The inode to append a block to. * @param data The contents of the new block. * @param len The number of bytes of data to write. * * @return 0 on success; nonzero on failure. */ static int nffs_write_append(struct nffs_cache_inode *cache_inode, const void *data, uint16_t len) { struct nffs_inode_entry *inode_entry; struct nffs_hash_entry *entry; struct nffs_disk_block disk_block; uint32_t area_offset; uint8_t area_idx; int rc; rc = nffs_block_entry_reserve(&entry); if (entry == NULL) { return FS_ENOMEM; } inode_entry = cache_inode->nci_inode.ni_inode_entry; disk_block.ndb_magic = NFFS_BLOCK_MAGIC; disk_block.ndb_id = nffs_hash_next_block_id++; disk_block.ndb_seq = 0; disk_block.ndb_inode_id = inode_entry->nie_hash_entry.nhe_id; if (inode_entry->nie_last_block_entry == NULL) { disk_block.ndb_prev_id = NFFS_ID_NONE; } else { disk_block.ndb_prev_id = inode_entry->nie_last_block_entry->nhe_id; } disk_block.ndb_data_len = len; nffs_crc_disk_block_fill(&disk_block, data); rc = nffs_block_write_disk(&disk_block, data, &area_idx, &area_offset); if (rc != 0) { return rc; } entry->nhe_id = disk_block.ndb_id; entry->nhe_flash_loc = nffs_flash_loc(area_idx, area_offset); nffs_hash_insert(entry); inode_entry->nie_last_block_entry = entry; /* Update cached inode with the new file size. */ cache_inode->nci_file_size += len; /* Add appended block to the cache. */ nffs_cache_seek(cache_inode, cache_inode->nci_file_size - 1, NULL); return 0; }
/** * Moves a chain of blocks from one area to another. This function attempts to * collate the blocks into a single new block in the destination area. * * @param last_entry The last block entry in the chain. * @param data_len The total length of data to collate. * @param to_area_idx The index of the area to copy to. * @param inout_next This parameter is only necessary if you are * calling this function during an iteration * of the entire hash table; pass null * otherwise. * On input, this points to the next hash entry * you plan on processing. * On output, this points to the next hash entry * that should be processed. * * @return 0 on success; * FS_ENOMEM if there is insufficient heap; * other nonzero on failure. */ static int nffs_gc_block_chain_collate(struct nffs_hash_entry *last_entry, uint32_t data_len, uint8_t to_area_idx, struct nffs_hash_entry **inout_next) { struct nffs_disk_block disk_block; struct nffs_hash_entry *entry; struct nffs_area *to_area; struct nffs_block last_block; struct nffs_block block; uint32_t to_area_offset; uint32_t from_area_offset; uint32_t data_offset; uint8_t *data; uint8_t from_area_idx; int rc; memset(&last_block, 0, sizeof last_block); data = malloc(data_len); if (data == NULL) { rc = FS_ENOMEM; goto done; } memset(&last_block, 0, sizeof(last_block)); to_area = nffs_areas + to_area_idx; entry = last_entry; data_offset = data_len; while (data_offset > 0) { rc = nffs_block_from_hash_entry(&block, entry); if (rc != 0) { goto done; } data_offset -= block.nb_data_len; nffs_flash_loc_expand(block.nb_hash_entry->nhe_flash_loc, &from_area_idx, &from_area_offset); from_area_offset += sizeof disk_block; STATS_INC(nffs_stats, nffs_readcnt_gccollate); rc = nffs_flash_read(from_area_idx, from_area_offset, data + data_offset, block.nb_data_len); if (rc != 0) { goto done; } if (entry != last_entry) { if (inout_next != NULL && *inout_next == entry) { *inout_next = SLIST_NEXT(entry, nhe_next); } nffs_block_delete_from_ram(entry); } else { last_block = block; } entry = block.nb_prev; } /* we had better have found the last block */ assert(last_block.nb_hash_entry); /* The resulting block should inherit its ID from its last constituent * block (this is the ID referenced by the parent inode and subsequent data * block). The previous ID gets inherited from the first constituent * block. */ memset(&disk_block, 0, sizeof disk_block); disk_block.ndb_id = last_block.nb_hash_entry->nhe_id; disk_block.ndb_seq = last_block.nb_seq + 1; disk_block.ndb_inode_id = last_block.nb_inode_entry->nie_hash_entry.nhe_id; if (entry == NULL) { disk_block.ndb_prev_id = NFFS_ID_NONE; } else { disk_block.ndb_prev_id = entry->nhe_id; } disk_block.ndb_data_len = data_len; nffs_crc_disk_block_fill(&disk_block, data); to_area_offset = to_area->na_cur; rc = nffs_flash_write(to_area_idx, to_area_offset, &disk_block, sizeof disk_block); if (rc != 0) { goto done; } rc = nffs_flash_write(to_area_idx, to_area_offset + sizeof disk_block, data, data_len); if (rc != 0) { goto done; } last_entry->nhe_flash_loc = nffs_flash_loc(to_area_idx, to_area_offset); rc = 0; ASSERT_IF_TEST(nffs_crc_disk_block_validate(&disk_block, to_area_idx, to_area_offset) == 0); done: free(data); return rc; }
/** * Moves a chain of blocks from one area to another. This function attempts to * collate the blocks into a single new block in the destination area. * * @param last_entry The last block entry in the chain. * @param data_len The total length of data to collate. * @param to_area_idx The index of the area to copy to. * @param inout_next This parameter is only necessary if you are * calling this function during an iteration * of the entire hash table; pass null * otherwise. * On input, this points to the next hash entry * you plan on processing. * On output, this points to the next hash entry * that should be processed. * * @return 0 on success; * FS_ENOMEM if there is insufficient heap; * other nonzero on failure. */ static int nffs_gc_block_chain_collate(struct nffs_hash_entry *last_entry, uint32_t data_len, uint8_t to_area_idx, struct nffs_hash_entry **inout_next) { struct nffs_disk_block disk_block; struct nffs_hash_entry *entry; struct nffs_area *to_area; struct nffs_block block; uint32_t to_area_offset; uint32_t from_area_offset; uint32_t data_offset; uint8_t *data; uint8_t from_area_idx; int rc; data = malloc(data_len); if (data == NULL) { rc = FS_ENOMEM; goto done; } to_area = nffs_areas + to_area_idx; entry = last_entry; data_offset = data_len; while (data_offset > 0) { rc = nffs_block_from_hash_entry(&block, entry); if (rc != 0) { goto done; } data_offset -= block.nb_data_len; nffs_flash_loc_expand(block.nb_hash_entry->nhe_flash_loc, &from_area_idx, &from_area_offset); from_area_offset += sizeof disk_block; rc = nffs_flash_read(from_area_idx, from_area_offset, data + data_offset, block.nb_data_len); if (rc != 0) { goto done; } if (entry != last_entry) { if (inout_next != NULL && *inout_next == entry) { *inout_next = SLIST_NEXT(entry, nhe_next); } nffs_block_delete_from_ram(entry); } entry = block.nb_prev; } memset(&disk_block, 0, sizeof disk_block); disk_block.ndb_magic = NFFS_BLOCK_MAGIC; disk_block.ndb_id = block.nb_hash_entry->nhe_id; disk_block.ndb_seq = block.nb_seq + 1; disk_block.ndb_inode_id = block.nb_inode_entry->nie_hash_entry.nhe_id; if (entry == NULL) { disk_block.ndb_prev_id = NFFS_ID_NONE; } else { disk_block.ndb_prev_id = entry->nhe_id; } disk_block.ndb_data_len = data_len; nffs_crc_disk_block_fill(&disk_block, data); to_area_offset = to_area->na_cur; rc = nffs_flash_write(to_area_idx, to_area_offset, &disk_block, sizeof disk_block); if (rc != 0) { goto done; } rc = nffs_flash_write(to_area_idx, to_area_offset + sizeof disk_block, data, data_len); if (rc != 0) { goto done; } last_entry->nhe_flash_loc = nffs_flash_loc(to_area_idx, to_area_offset); rc = 0; ASSERT_IF_TEST(nffs_crc_disk_block_validate(&disk_block, to_area_idx, to_area_offset) == 0); done: free(data); return rc; }