/** * 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; }
/** * Performs a single write operation. The data written must be no greater * than the maximum block data length. If old data gets overwritten, then * the existing data blocks are superseded as necessary. * * @param write_info Describes the write operation being perfomred. * @param inode_entry The file inode to write to. * @param data The new data to write. * @param data_len The number of bytes of new data to write. * * @return 0 on success; nonzero on failure. */ static int nffs_write_chunk(struct nffs_inode_entry *inode_entry, uint32_t file_offset, const void *data, uint16_t data_len) { struct nffs_cache_inode *cache_inode; struct nffs_cache_block *cache_block; unsigned int gc_count; uint32_t append_len; uint32_t data_offset; uint32_t block_end; uint32_t dst_off; uint16_t chunk_off; uint16_t chunk_sz; int rc; assert(data_len <= nffs_block_max_data_sz); rc = nffs_cache_inode_ensure(&cache_inode, inode_entry); if (rc != 0) { return rc; } /** Handle the simple append case first. */ if (file_offset == cache_inode->nci_file_size) { rc = nffs_write_append(cache_inode, data, data_len); return rc; } /** This is not an append; i.e., old data is getting overwritten. */ dst_off = file_offset + data_len; data_offset = data_len; cache_block = NULL; if (dst_off > cache_inode->nci_file_size) { append_len = dst_off - cache_inode->nci_file_size; } else { append_len = 0; } do { if (cache_block == NULL) { rc = nffs_cache_seek(cache_inode, dst_off - 1, &cache_block); if (rc != 0) { return rc; } } if (cache_block->ncb_file_offset < file_offset) { chunk_off = file_offset - cache_block->ncb_file_offset; } else { chunk_off = 0; } chunk_sz = cache_block->ncb_block.nb_data_len - chunk_off; block_end = cache_block->ncb_file_offset + cache_block->ncb_block.nb_data_len; if (block_end != dst_off) { chunk_sz += (int)(dst_off - block_end); } /* Remember the current garbage collection count. If the count * increases during the write, then the block cache has been * invalidated and we need to reset our pointers. */ gc_count = nffs_gc_count; data_offset = cache_block->ncb_file_offset + chunk_off - file_offset; rc = nffs_write_over_block(cache_block->ncb_block.nb_hash_entry, chunk_off, data + data_offset, chunk_sz); if (rc != 0) { return rc; } dst_off -= chunk_sz; if (gc_count != nffs_gc_count) { /* Garbage collection occurred; the current cached block pointer is * invalid, so reset it. The cached inode is still valid. */ cache_block = NULL; } else { cache_block = TAILQ_PREV(cache_block, nffs_cache_block_list, ncb_link); } } while (data_offset > 0); cache_inode->nci_file_size += append_len; return 0; }
/** * Performs a single write operation. The data written must be no greater * than the maximum block data length. If old data gets overwritten, then * the existing data blocks are superseded as necessary. * * @param write_info Describes the write operation being perfomred. * @param inode_entry The file inode to write to. * @param data The new data to write. * @param data_len The number of bytes of new data to write. * * @return 0 on success; nonzero on failure. */ static int nffs_write_chunk(struct nffs_cache_inode *cache_inode, uint32_t file_offset, const void *data, uint16_t data_len) { struct nffs_cache_block *cache_block; uint32_t append_len; uint32_t data_offset; uint32_t block_end; uint32_t dst_off; uint16_t chunk_off; uint16_t chunk_sz; int rc; assert(data_len <= nffs_block_max_data_sz); /** Handle the simple append case first. */ if (file_offset == cache_inode->nci_file_size) { rc = nffs_write_append(cache_inode, data, data_len); return rc; } /** This is not an append; i.e., old data is getting overwritten. */ dst_off = file_offset + data_len; data_offset = data_len; cache_block = NULL; if (dst_off > cache_inode->nci_file_size) { append_len = dst_off - cache_inode->nci_file_size; } else { append_len = 0; } do { if (cache_block == NULL) { rc = nffs_cache_seek(cache_inode, dst_off - 1, &cache_block); if (rc != 0) { return rc; } } if (cache_block->ncb_file_offset < file_offset) { chunk_off = file_offset - cache_block->ncb_file_offset; } else { chunk_off = 0; } chunk_sz = cache_block->ncb_block.nb_data_len - chunk_off; block_end = cache_block->ncb_file_offset + cache_block->ncb_block.nb_data_len; if (block_end != dst_off) { chunk_sz += (int)(dst_off - block_end); } data_offset = cache_block->ncb_file_offset + chunk_off - file_offset; rc = nffs_write_over_block(cache_block->ncb_block.nb_hash_entry, chunk_off, data + data_offset, chunk_sz); if (rc != 0) { return rc; } dst_off -= chunk_sz; cache_block = TAILQ_PREV(cache_block, nffs_cache_block_list, ncb_link); } while (data_offset > 0); cache_inode->nci_file_size += append_len; return 0; }