]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
vfs: enable remap callers that can handle short operations
authorDarrick J. Wong <darrick.wong@oracle.com>
Mon, 29 Oct 2018 23:42:10 +0000 (10:42 +1100)
committerDave Chinner <david@fromorbit.com>
Mon, 29 Oct 2018 23:42:10 +0000 (10:42 +1100)
Plumb in a remap flag that enables the filesystem remap handler to
shorten remapping requests for callers that can handle it.  Now
copy_file_range can report partial success (in case we run up against
alignment problems, resource limits, etc.).

We also enable CAN_SHORTEN for fideduperange to maintain existing
userspace-visible behavior where xfs/btrfs shorten the dedupe range to
avoid stale post-eof data exposure.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
Documentation/filesystems/vfs.txt
fs/read_write.c
include/linux/fs.h
mm/filemap.c

index 1bd2919deaca0fe2f50fc716cefa9cfe2f18556a..5f71a252e2e0f52b17c4fb6076baa57ae34e1ec4 100644 (file)
@@ -970,7 +970,9 @@ otherwise noted.
        negative error code if errors occurred before any bytes were remapped.
        The remap_flags parameter accepts REMAP_FILE_* flags.  If
        REMAP_FILE_DEDUP is set then the implementation must only remap if the
-       requested file ranges have identical contents.
+       requested file ranges have identical contents.  If REMAP_CAN_SHORTEN is
+       set, the caller is ok with the implementation shortening the request
+       length to satisfy alignment or EOF requirements (or any other reason).
 
   fadvise: possibly called by the fadvise64() system call.
 
index ea30666013b0d86b1d7af852a0e5d81b5c2c18df..c0bcc1a20650248574200b93a7bf23d1fc17ac8b 100644 (file)
@@ -1593,7 +1593,8 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
 
                cloned = file_in->f_op->remap_file_range(file_in, pos_in,
                                file_out, pos_out,
-                               min_t(loff_t, MAX_RW_COUNT, len), 0);
+                               min_t(loff_t, MAX_RW_COUNT, len),
+                               REMAP_FILE_CAN_SHORTEN);
                if (cloned > 0) {
                        ret = cloned;
                        goto done;
@@ -1721,6 +1722,8 @@ static int remap_verify_area(struct file *file, loff_t pos, loff_t len,
  * can't meaningfully compare post-EOF contents.
  *
  * For clone we only link a partial EOF block above the destination file's EOF.
+ *
+ * Shorten the request if possible.
  */
 static int generic_remap_check_len(struct inode *inode_in,
                                   struct inode *inode_out,
@@ -1729,16 +1732,24 @@ static int generic_remap_check_len(struct inode *inode_in,
                                   unsigned int remap_flags)
 {
        u64 blkmask = i_blocksize(inode_in) - 1;
+       loff_t new_len = *len;
 
        if ((*len & blkmask) == 0)
                return 0;
 
-       if (remap_flags & REMAP_FILE_DEDUP)
-               *len &= ~blkmask;
-       else if (pos_out + *len < i_size_read(inode_out))
-               return -EINVAL;
+       if ((remap_flags & REMAP_FILE_DEDUP) ||
+           pos_out + *len < i_size_read(inode_out))
+               new_len &= ~blkmask;
 
-       return 0;
+       if (new_len == *len)
+               return 0;
+
+       if (remap_flags & REMAP_FILE_CAN_SHORTEN) {
+               *len = new_len;
+               return 0;
+       }
+
+       return (remap_flags & REMAP_FILE_DEDUP) ? -EBADE : -EINVAL;
 }
 
 /*
@@ -2014,7 +2025,8 @@ loff_t vfs_dedupe_file_range_one(struct file *src_file, loff_t src_pos,
 {
        loff_t ret;
 
-       WARN_ON_ONCE(remap_flags & ~(REMAP_FILE_DEDUP));
+       WARN_ON_ONCE(remap_flags & ~(REMAP_FILE_DEDUP |
+                                    REMAP_FILE_CAN_SHORTEN));
 
        ret = mnt_want_write_file(dst_file);
        if (ret)
@@ -2115,7 +2127,7 @@ int vfs_dedupe_file_range(struct file *file, struct file_dedupe_range *same)
 
                deduped = vfs_dedupe_file_range_one(file, off, dst_file,
                                                    info->dest_offset, len,
-                                                   0);
+                                                   REMAP_FILE_CAN_SHORTEN);
                if (deduped == -EBADE)
                        info->status = FILE_DEDUPE_RANGE_DIFFERS;
                else if (deduped < 0)
index 544ab5083b4882fb9b019d19569185def657f72c..34c22d69501137606ac8f1d8fe56f120bc43808f 100644 (file)
@@ -1727,8 +1727,10 @@ struct block_device_operations;
  * See Documentation/filesystems/vfs.txt for more details about this call.
  *
  * REMAP_FILE_DEDUP: only remap if contents identical (i.e. deduplicate)
+ * REMAP_FILE_CAN_SHORTEN: caller can handle a shortened request
  */
 #define REMAP_FILE_DEDUP               (1 << 0)
+#define REMAP_FILE_CAN_SHORTEN         (1 << 1)
 
 /*
  * These flags signal that the caller is ok with altering various aspects of
@@ -1736,9 +1738,8 @@ struct block_device_operations;
  * implementation; the vfs remap helper functions can take advantage of them.
  * Flags in this category exist to preserve the quirky behavior of the hoisted
  * btrfs clone/dedupe ioctls.
- * There are no flags yet, but subsequent commits will add some.
  */
-#define REMAP_FILE_ADVISORY            (0)
+#define REMAP_FILE_ADVISORY            (REMAP_FILE_CAN_SHORTEN)
 
 struct iov_iter;
 
index e9091d731f8463dd37c95076dd6314dcc6cece95..1775d4ad3317c988a97682c971f48900a2fbe9f6 100644 (file)
@@ -3045,8 +3045,7 @@ int generic_remap_checks(struct file *file_in, loff_t pos_in,
                bcount = ALIGN(size_in, bs) - pos_in;
        } else {
                if (!IS_ALIGNED(count, bs))
-                       return -EINVAL;
-
+                       count = ALIGN_DOWN(count, bs);
                bcount = count;
        }
 
@@ -3056,10 +3055,14 @@ int generic_remap_checks(struct file *file_in, loff_t pos_in,
            pos_out < pos_in + bcount)
                return -EINVAL;
 
-       /* For now we don't support changing the length. */
-       if (*req_count != count)
+       /*
+        * We shortened the request but the caller can't deal with that, so
+        * bounce the request back to userspace.
+        */
+       if (*req_count != count && !(remap_flags & REMAP_FILE_CAN_SHORTEN))
                return -EINVAL;
 
+       *req_count = count;
        return 0;
 }