f2fs: support FALLOC_FL_INSERT_RANGE
FALLOC_FL_INSERT_RANGE flag for ->fallocate was introduced in commit
dd46c78778 ("fs: Add support FALLOC_FL_INSERT_RANGE for fallocate").
The effect of FALLOC_FL_INSERT_RANGE command is the opposite of
FALLOC_FL_COLLAPSE_RANGE, if this command was performed, all data from
offset to EOF in our file will be shifted to right as given length, and
then range [offset, offset + length] becomes a hole.
This command is useful for our user who wants to add some data in the
middle of the file, for example: video/music editor will insert a keyframe
in specified position of media file, with this command we can easily create
a hole for inserting without removing original data.
This patch introduces f2fs_insert_range() to support FALLOC_FL_INSERT_RANGE.
Signed-off-by: Chao Yu <chao2.yu@samsung.com>
Signed-off-by: Yuan Zhong <yuan.mark.zhong@samsung.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
			
			
This commit is contained in:
		
							parent
							
								
									528e34593d
								
							
						
					
					
						commit
						f62185d0e2
					
				
							
								
								
									
										102
									
								
								fs/f2fs/file.c
									
									
									
									
									
								
							
							
						
						
									
										102
									
								
								fs/f2fs/file.c
									
									
									
									
									
								
							| @ -1010,6 +1010,100 @@ out: | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) | ||||
| { | ||||
| 	struct f2fs_sb_info *sbi = F2FS_I_SB(inode); | ||||
| 	pgoff_t pg_start, pg_end, delta, nrpages, idx; | ||||
| 	loff_t new_size; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (!S_ISREG(inode->i_mode)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	new_size = i_size_read(inode) + len; | ||||
| 	if (new_size > inode->i_sb->s_maxbytes) | ||||
| 		return -EFBIG; | ||||
| 
 | ||||
| 	if (offset >= i_size_read(inode)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	/* insert range should be aligned to block size of f2fs. */ | ||||
| 	if (offset & (F2FS_BLKSIZE - 1) || len & (F2FS_BLKSIZE - 1)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	f2fs_balance_fs(sbi); | ||||
| 
 | ||||
| 	ret = truncate_blocks(inode, i_size_read(inode), true); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	/* write out all dirty pages from offset */ | ||||
| 	ret = filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	truncate_pagecache(inode, offset); | ||||
| 
 | ||||
| 	pg_start = offset >> PAGE_CACHE_SHIFT; | ||||
| 	pg_end = (offset + len) >> PAGE_CACHE_SHIFT; | ||||
| 	delta = pg_end - pg_start; | ||||
| 	nrpages = (i_size_read(inode) + PAGE_SIZE - 1) / PAGE_SIZE; | ||||
| 
 | ||||
| 	for (idx = nrpages - 1; idx >= pg_start && idx != -1; idx--) { | ||||
| 		struct dnode_of_data dn; | ||||
| 		struct page *ipage; | ||||
| 		block_t new_addr, old_addr; | ||||
| 
 | ||||
| 		f2fs_lock_op(sbi); | ||||
| 
 | ||||
| 		set_new_dnode(&dn, inode, NULL, NULL, 0); | ||||
| 		ret = get_dnode_of_data(&dn, idx, LOOKUP_NODE_RA); | ||||
| 		if (ret && ret != -ENOENT) { | ||||
| 			goto out; | ||||
| 		} else if (ret == -ENOENT) { | ||||
| 			goto next; | ||||
| 		} else if (dn.data_blkaddr == NULL_ADDR) { | ||||
| 			f2fs_put_dnode(&dn); | ||||
| 			goto next; | ||||
| 		} else { | ||||
| 			new_addr = dn.data_blkaddr; | ||||
| 			truncate_data_blocks_range(&dn, 1); | ||||
| 			f2fs_put_dnode(&dn); | ||||
| 		} | ||||
| 
 | ||||
| 		ipage = get_node_page(sbi, inode->i_ino); | ||||
| 		if (IS_ERR(ipage)) { | ||||
| 			ret = PTR_ERR(ipage); | ||||
| 			goto out; | ||||
| 		} | ||||
| 
 | ||||
| 		set_new_dnode(&dn, inode, ipage, NULL, 0); | ||||
| 		ret = f2fs_reserve_block(&dn, idx + delta); | ||||
| 		if (ret) | ||||
| 			goto out; | ||||
| 
 | ||||
| 		old_addr = dn.data_blkaddr; | ||||
| 		f2fs_bug_on(sbi, old_addr != NEW_ADDR); | ||||
| 
 | ||||
| 		if (new_addr != NEW_ADDR) { | ||||
| 			struct node_info ni; | ||||
| 
 | ||||
| 			get_node_info(sbi, dn.nid, &ni); | ||||
| 			f2fs_replace_block(sbi, &dn, old_addr, new_addr, | ||||
| 							ni.version, true); | ||||
| 		} | ||||
| 		f2fs_put_dnode(&dn); | ||||
| next: | ||||
| 		f2fs_unlock_op(sbi); | ||||
| 	} | ||||
| 
 | ||||
| 	i_size_write(inode, new_size); | ||||
| 	return 0; | ||||
| out: | ||||
| 	f2fs_unlock_op(sbi); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int expand_inode_data(struct inode *inode, loff_t offset, | ||||
| 					loff_t len, int mode) | ||||
| { | ||||
| @ -1077,11 +1171,13 @@ static long f2fs_fallocate(struct file *file, int mode, | ||||
| 	struct inode *inode = file_inode(file); | ||||
| 	long ret = 0; | ||||
| 
 | ||||
| 	if (f2fs_encrypted_inode(inode) && (mode & FALLOC_FL_COLLAPSE_RANGE)) | ||||
| 	if (f2fs_encrypted_inode(inode) && | ||||
| 		(mode & (FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_INSERT_RANGE))) | ||||
| 		return -EOPNOTSUPP; | ||||
| 
 | ||||
| 	if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE | | ||||
| 			FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE)) | ||||
| 			FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE | | ||||
| 			FALLOC_FL_INSERT_RANGE)) | ||||
| 		return -EOPNOTSUPP; | ||||
| 
 | ||||
| 	mutex_lock(&inode->i_mutex); | ||||
| @ -1095,6 +1191,8 @@ static long f2fs_fallocate(struct file *file, int mode, | ||||
| 		ret = f2fs_collapse_range(inode, offset, len); | ||||
| 	} else if (mode & FALLOC_FL_ZERO_RANGE) { | ||||
| 		ret = f2fs_zero_range(inode, offset, len, mode); | ||||
| 	} else if (mode & FALLOC_FL_INSERT_RANGE) { | ||||
| 		ret = f2fs_insert_range(inode, offset, len); | ||||
| 	} else { | ||||
| 		ret = expand_inode_data(inode, offset, len, mode); | ||||
| 	} | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user