vfs: avoid problematic remapping requests into partial EOF block
[linux-2.6-block.git] / fs / read_write.c
index 2456da3f8a414f8ec420ed29bf5c79c0925f0ecd..0f0a6efdd502c6e2682a06cb83b517636a474142 100644 (file)
@@ -1708,6 +1708,34 @@ static int clone_verify_area(struct file *file, loff_t pos, u64 len, bool write)
 
        return security_file_permission(file, write ? MAY_WRITE : MAY_READ);
 }
+/*
+ * Ensure that we don't remap a partial EOF block in the middle of something
+ * else.  Assume that the offsets have already been checked for block
+ * alignment.
+ *
+ * For deduplication we always scale down to the previous block because we
+ * can't meaningfully compare post-EOF contents.
+ *
+ * For clone we only link a partial EOF block above the destination file's EOF.
+ */
+static int generic_remap_check_len(struct inode *inode_in,
+                                  struct inode *inode_out,
+                                  loff_t pos_out,
+                                  u64 *len,
+                                  bool is_dedupe)
+{
+       u64 blkmask = i_blocksize(inode_in) - 1;
+
+       if ((*len & blkmask) == 0)
+               return 0;
+
+       if (is_dedupe)
+               *len &= ~blkmask;
+       else if (pos_out + *len < i_size_read(inode_out))
+               return -EINVAL;
+
+       return 0;
+}
 
 /*
  * Check that the two inodes are eligible for cloning, the ranges make
@@ -1787,6 +1815,11 @@ int vfs_clone_file_prep(struct file *file_in, loff_t pos_in,
                        return -EBADE;
        }
 
+       ret = generic_remap_check_len(inode_in, inode_out, pos_out, len,
+                       is_dedupe);
+       if (ret)
+               return ret;
+
        return 1;
 }
 EXPORT_SYMBOL(vfs_clone_file_prep);