|
@@ -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);
|
|
|
}
|