static int nffs_gc_copy_object(struct nffs_hash_entry *entry, uint16_t object_size, uint8_t to_area_idx) { uint32_t from_area_offset; uint32_t to_area_offset; uint8_t from_area_idx; int rc; nffs_flash_loc_expand(entry->nhe_flash_loc, &from_area_idx, &from_area_offset); to_area_offset = nffs_areas[to_area_idx].na_cur; rc = nffs_flash_copy(from_area_idx, from_area_offset, to_area_idx, to_area_offset, object_size); if (rc != 0) { return rc; } entry->nhe_flash_loc = nffs_flash_loc(to_area_idx, to_area_offset); return 0; }
/** * Overwrites an existing data block. The resulting block has the same ID as * the old one, but it supersedes it with a greater sequence number. * * @param entry The data block to overwrite. * @param left_copy_len The number of bytes of existing data to retain * before the new data begins. * @param new_data The new data to write to the block. * @param new_data_len The number of new bytes to write to the block. * If this value plus left_copy_len is less * than the existing block's data length, * previous data at the end of the block is * retained. * * @return 0 on success; nonzero on failure. */ static int nffs_write_over_block(struct nffs_hash_entry *entry, uint16_t left_copy_len, const void *new_data, uint16_t new_data_len) { struct nffs_disk_block disk_block; struct nffs_block block; uint32_t src_area_offset; uint32_t dst_area_offset; uint16_t right_copy_len; uint16_t block_off; uint8_t src_area_idx; uint8_t dst_area_idx; int rc; rc = nffs_block_from_hash_entry(&block, entry); if (rc != 0) { return rc; } assert(left_copy_len <= block.nb_data_len); /* Determine how much old data at the end of the block needs to be * retained. If the new data doesn't extend to the end of the block, the * the rest of the block retains its old contents. */ if (left_copy_len + new_data_len > block.nb_data_len) { right_copy_len = 0; } else { right_copy_len = block.nb_data_len - left_copy_len - new_data_len; } block.nb_seq++; block.nb_data_len = left_copy_len + new_data_len + right_copy_len; nffs_block_to_disk(&block, &disk_block); nffs_flash_loc_expand(entry->nhe_flash_loc, &src_area_idx, &src_area_offset); rc = nffs_write_fill_crc16_overwrite(&disk_block, src_area_idx, src_area_offset, left_copy_len, right_copy_len, new_data, new_data_len); if (rc != 0) { return rc; } rc = nffs_misc_reserve_space(sizeof disk_block + disk_block.ndb_data_len, &dst_area_idx, &dst_area_offset); if (rc != 0) { return rc; } block_off = 0; /* Write the block header. */ rc = nffs_flash_write(dst_area_idx, dst_area_offset + block_off, &disk_block, sizeof disk_block); if (rc != 0) { return rc; } block_off += sizeof disk_block; /* Copy data from the start of the old block, in case the new data starts * at a non-zero offset. */ if (left_copy_len > 0) { rc = nffs_flash_copy(src_area_idx, src_area_offset + block_off, dst_area_idx, dst_area_offset + block_off, left_copy_len); if (rc != 0) { return rc; } block_off += left_copy_len; } /* Write the new data into the data block. This may extend the block's * length beyond its old value. */ rc = nffs_flash_write(dst_area_idx, dst_area_offset + block_off, new_data, new_data_len); if (rc != 0) { return rc; } block_off += new_data_len; /* Copy data from the end of the old block, in case the new data doesn't * extend to the end of the block. */ if (right_copy_len > 0) { rc = nffs_flash_copy(src_area_idx, src_area_offset + block_off, dst_area_idx, dst_area_offset + block_off, right_copy_len); if (rc != 0) { return rc; } block_off += right_copy_len; } assert(block_off == sizeof disk_block + block.nb_data_len); entry->nhe_flash_loc = nffs_flash_loc(dst_area_idx, dst_area_offset); ASSERT_IF_TEST(nffs_crc_disk_block_validate(&disk_block, dst_area_idx, dst_area_offset) == 0); return 0; }