/** * 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; }
/** * 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; }