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:
Родитель
528e34593d
Коммит
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);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче