Merge tag 'for-4.20-rc1-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave...
[linux-block.git] / fs / btrfs / ioctl.c
index bfd99c66723e24608ada6bd3d325548d281fe82c..802a628e9f7d7fe629a76e8d108b75c04ed4246e 100644 (file)
@@ -491,7 +491,6 @@ static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg)
        struct fstrim_range range;
        u64 minlen = ULLONG_MAX;
        u64 num_devices = 0;
-       u64 total_bytes = btrfs_super_total_bytes(fs_info->super_copy);
        int ret;
 
        if (!capable(CAP_SYS_ADMIN))
@@ -515,11 +514,15 @@ static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg)
                return -EOPNOTSUPP;
        if (copy_from_user(&range, arg, sizeof(range)))
                return -EFAULT;
-       if (range.start > total_bytes ||
-           range.len < fs_info->sb->s_blocksize)
+
+       /*
+        * NOTE: Don't truncate the range using super->total_bytes.  Bytenr of
+        * block group is in the logical address space, which can be any
+        * sectorsize aligned bytenr in  the range [0, U64_MAX].
+        */
+       if (range.len < fs_info->sb->s_blocksize)
                return -EINVAL;
 
-       range.len = min(range.len, total_bytes - range.start);
        range.minlen = max(range.minlen, minlen);
        ret = btrfs_trim_fs(fs_info, &range);
        if (ret < 0)
@@ -686,8 +689,7 @@ static noinline int create_subvol(struct inode *dir,
                goto fail;
        }
 
-       ret = btrfs_insert_dir_item(trans, root,
-                                   name, namelen, BTRFS_I(dir), &key,
+       ret = btrfs_insert_dir_item(trans, name, namelen, BTRFS_I(dir), &key,
                                    BTRFS_FT_DIR, index);
        if (ret) {
                btrfs_abort_transaction(trans, ret);
@@ -1324,7 +1326,7 @@ again:
 
        if (i_done != page_cnt) {
                spin_lock(&BTRFS_I(inode)->lock);
-               BTRFS_I(inode)->outstanding_extents++;
+               btrfs_mod_outstanding_extents(BTRFS_I(inode), 1);
                spin_unlock(&BTRFS_I(inode)->lock);
                btrfs_delalloc_release_space(inode, data_reserved,
                                start_index << PAGE_SHIFT,
@@ -3486,6 +3488,8 @@ static int btrfs_extent_same_range(struct inode *src, u64 loff, u64 olen,
                        const u64 sz = BTRFS_I(src)->root->fs_info->sectorsize;
 
                        len = round_down(i_size_read(src), sz) - loff;
+                       if (len == 0)
+                               return 0;
                        olen = len;
                }
        }
@@ -4255,9 +4259,17 @@ static noinline int btrfs_clone_files(struct file *file, struct file *file_src,
                goto out_unlock;
        if (len == 0)
                olen = len = src->i_size - off;
-       /* if we extend to eof, continue to block boundary */
-       if (off + len == src->i_size)
+       /*
+        * If we extend to eof, continue to block boundary if and only if the
+        * destination end offset matches the destination file's size, otherwise
+        * we would be corrupting data by placing the eof block into the middle
+        * of a file.
+        */
+       if (off + len == src->i_size) {
+               if (!IS_ALIGNED(len, bs) && destoff + len < inode->i_size)
+                       goto out_unlock;
                len = ALIGN(src->i_size, bs) - off;
+       }
 
        if (len == 0) {
                ret = 0;
@@ -4328,10 +4340,12 @@ out_unlock:
        return ret;
 }
 
-int btrfs_remap_file_range(struct file *src_file, loff_t off,
-               struct file *dst_file, loff_t destoff, u64 len,
+loff_t btrfs_remap_file_range(struct file *src_file, loff_t off,
+               struct file *dst_file, loff_t destoff, loff_t len,
                unsigned int remap_flags)
 {
+       int ret;
+
        if (remap_flags & ~(REMAP_FILE_DEDUP | REMAP_FILE_ADVISORY))
                return -EINVAL;
 
@@ -4349,10 +4363,11 @@ int btrfs_remap_file_range(struct file *src_file, loff_t off,
                        return -EINVAL;
                }
 
-               return btrfs_extent_same(src, off, len, dst, destoff);
+               ret = btrfs_extent_same(src, off, len, dst, destoff);
+       } else {
+               ret = btrfs_clone_files(dst_file, src_file, off, len, destoff);
        }
-
-       return btrfs_clone_files(dst_file, src_file, off, len, destoff);
+       return ret < 0 ? ret : len;
 }
 
 static long btrfs_ioctl_default_subvol(struct file *file, void __user *argp)
@@ -4394,7 +4409,7 @@ static long btrfs_ioctl_default_subvol(struct file *file, void __user *argp)
                ret = PTR_ERR(new_root);
                goto out;
        }
-       if (!is_fstree(new_root->objectid)) {
+       if (!is_fstree(new_root->root_key.objectid)) {
                ret = -ENOENT;
                goto out;
        }