int nffs_crc_flash(uint16_t initial_crc, uint8_t area_idx, uint32_t area_offset, uint32_t len, uint16_t *out_crc) { uint32_t chunk_len; uint16_t crc; int rc; crc = initial_crc; /* Copy data in chunks small enough to fit in the flash buffer. */ while (len > 0) { if (len > sizeof nffs_flash_buf) { chunk_len = sizeof nffs_flash_buf; } else { chunk_len = len; } rc = nffs_flash_read(area_idx, area_offset, nffs_flash_buf, chunk_len); if (rc != 0) { return rc; } crc = crc16_ccitt(crc, nffs_flash_buf, chunk_len); area_offset += chunk_len; len -= chunk_len; } *out_crc = crc; return 0; }
/** * Turns a scratch area into a non-scratch area. If the specified area is not * actually a scratch area, this function falls back to a slower full format * operation. */ int nffs_format_from_scratch_area(uint8_t area_idx, uint8_t area_id) { struct nffs_disk_area disk_area; int rc; assert(area_idx < nffs_num_areas); rc = nffs_flash_read(area_idx, 0, &disk_area, sizeof disk_area); if (rc != 0) { return rc; } nffs_areas[area_idx].na_id = area_id; if (!nffs_area_is_scratch(&disk_area)) { rc = nffs_format_area(area_idx, 0); if (rc != 0) { return rc; } } else { disk_area.nda_id = area_id; rc = nffs_flash_write(area_idx, NFFS_AREA_OFFSET_ID, &disk_area.nda_id, sizeof disk_area.nda_id); if (rc != 0) { return rc; } } return 0; }
/** * Copies a chunk of data from one region of flash to another. * * @param area_idx_from The index of the area to copy from. * @param area_offset_from The offset within the area to copy from. * @param area_idx_to The index of the area to copy to. * @param area_offset_to The offset within the area to copy to. * @param len The number of bytes to copy. * * @return 0 on success; nonzero on failure. */ int nffs_flash_copy(uint8_t area_idx_from, uint32_t area_offset_from, uint8_t area_idx_to, uint32_t area_offset_to, uint32_t len) { uint32_t chunk_len; int rc; /* Copy data in chunks small enough to fit in the flash buffer. */ while (len > 0) { if (len > sizeof nffs_flash_buf) { chunk_len = sizeof nffs_flash_buf; } else { chunk_len = len; } rc = nffs_flash_read(area_idx_from, area_offset_from, nffs_flash_buf, chunk_len); if (rc != 0) { return rc; } rc = nffs_flash_write(area_idx_to, area_offset_to, nffs_flash_buf, chunk_len); if (rc != 0) { return rc; } area_offset_from += chunk_len; area_offset_to += chunk_len; len -= chunk_len; } return 0; }
/** * Reads a single disk object from flash. * * @param area_idx The area to read the object from. * @param area_offset The offset within the area to read from. * @param out_disk_object On success, the restored object gets written * here. * * @return 0 on success; nonzero on failure. */ static int nffs_restore_disk_object(int area_idx, uint32_t area_offset, struct nffs_disk_object *out_disk_object) { int rc; rc = nffs_flash_read(area_idx, area_offset, &out_disk_object->ndo_un_obj, sizeof(out_disk_object->ndo_un_obj)); if (rc != 0) { return rc; } STATS_INC(nffs_stats, nffs_readcnt_object); if (nffs_hash_id_is_inode(out_disk_object->ndo_disk_inode.ndi_id)) { out_disk_object->ndo_type = NFFS_OBJECT_TYPE_INODE; } else if (nffs_hash_id_is_block(out_disk_object->ndo_disk_block.ndb_id)) { out_disk_object->ndo_type = NFFS_OBJECT_TYPE_BLOCK; } else if (out_disk_object->ndo_disk_block.ndb_id == NFFS_ID_NONE) { return FS_EEMPTY; } else { return FS_ECORRUPT; } out_disk_object->ndo_area_idx = area_idx; out_disk_object->ndo_offset = area_offset; return 0; }
/** * Reads a data block header from flash. * * @param area_idx The index of the area to read from. * @param area_offset The offset within the area to read from. * @param out_disk_block On success, the block header is writteh here. * * @return 0 on success; nonzero on failure. */ int nffs_block_read_disk(uint8_t area_idx, uint32_t area_offset, struct nffs_disk_block *out_disk_block) { int rc; rc = nffs_flash_read(area_idx, area_offset, out_disk_block, sizeof *out_disk_block); if (rc != 0) { return rc; } if (out_disk_block->ndb_magic != NFFS_BLOCK_MAGIC) { return FS_EUNEXP; } return 0; }
/** * Reads a data block header from flash. * * @param area_idx The index of the area to read from. * @param area_offset The offset within the area to read from. * @param out_disk_block On success, the block header is writteh here. * * @return 0 on success; * 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_read_disk(uint8_t area_idx, uint32_t area_offset, struct nffs_disk_block *out_disk_block) { int rc; rc = nffs_flash_read(area_idx, area_offset, out_disk_block, sizeof *out_disk_block); if (rc != 0) { return rc; } if (!nffs_hash_id_is_block(out_disk_block->ndb_id)) { return FS_EUNEXP; } return 0; }
/** * Reads a single disk object from flash. * * @param area_idx The area to read the object from. * @param area_offset The offset within the area to read from. * @param out_disk_object On success, the restored object gets written * here. * * @return 0 on success; nonzero on failure. */ static int nffs_restore_disk_object(int area_idx, uint32_t area_offset, struct nffs_disk_object *out_disk_object) { uint32_t magic; int rc; rc = nffs_flash_read(area_idx, area_offset, &magic, sizeof magic); if (rc != 0) { return rc; } switch (magic) { case NFFS_INODE_MAGIC: out_disk_object->ndo_type = NFFS_OBJECT_TYPE_INODE; rc = nffs_inode_read_disk(area_idx, area_offset, &out_disk_object->ndo_disk_inode); break; case NFFS_BLOCK_MAGIC: out_disk_object->ndo_type = NFFS_OBJECT_TYPE_BLOCK; rc = nffs_block_read_disk(area_idx, area_offset, &out_disk_object->ndo_disk_block); break; case 0xffffffff: rc = FS_EEMPTY; break; default: rc = FS_ECORRUPT; break; } if (rc != 0) { return rc; } out_disk_object->ndo_area_idx = area_idx; out_disk_object->ndo_offset = area_offset; return 0; }
int nffs_block_read_data(const struct nffs_block *block, uint16_t offset, uint16_t length, void *dst) { uint32_t area_offset; uint8_t area_idx; int rc; nffs_flash_loc_expand(block->nb_hash_entry->nhe_flash_loc, &area_idx, &area_offset); area_offset += sizeof (struct nffs_disk_block); area_offset += offset; rc = nffs_flash_read(area_idx, area_offset, dst, length); if (rc != 0) { return rc; } 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; }