Merge tag 'v5.3-rc1' into patchwork
[linux-2.6-microblaze.git] / fs / read_write.c
index c543d96..1f5088d 100644 (file)
@@ -1565,6 +1565,58 @@ COMPAT_SYSCALL_DEFINE4(sendfile64, int, out_fd, int, in_fd,
 }
 #endif
 
+/**
+ * generic_copy_file_range - copy data between two files
+ * @file_in:   file structure to read from
+ * @pos_in:    file offset to read from
+ * @file_out:  file structure to write data to
+ * @pos_out:   file offset to write data to
+ * @len:       amount of data to copy
+ * @flags:     copy flags
+ *
+ * This is a generic filesystem helper to copy data from one file to another.
+ * It has no constraints on the source or destination file owners - the files
+ * can belong to different superblocks and different filesystem types. Short
+ * copies are allowed.
+ *
+ * This should be called from the @file_out filesystem, as per the
+ * ->copy_file_range() method.
+ *
+ * Returns the number of bytes copied or a negative error indicating the
+ * failure.
+ */
+
+ssize_t generic_copy_file_range(struct file *file_in, loff_t pos_in,
+                               struct file *file_out, loff_t pos_out,
+                               size_t len, unsigned int flags)
+{
+       return do_splice_direct(file_in, &pos_in, file_out, &pos_out,
+                               len > MAX_RW_COUNT ? MAX_RW_COUNT : len, 0);
+}
+EXPORT_SYMBOL(generic_copy_file_range);
+
+static ssize_t do_copy_file_range(struct file *file_in, loff_t pos_in,
+                                 struct file *file_out, loff_t pos_out,
+                                 size_t len, unsigned int flags)
+{
+       /*
+        * Although we now allow filesystems to handle cross sb copy, passing
+        * a file of the wrong filesystem type to filesystem driver can result
+        * in an attempt to dereference the wrong type of ->private_data, so
+        * avoid doing that until we really have a good reason.  NFS defines
+        * several different file_system_type structures, but they all end up
+        * using the same ->copy_file_range() function pointer.
+        */
+       if (file_out->f_op->copy_file_range &&
+           file_out->f_op->copy_file_range == file_in->f_op->copy_file_range)
+               return file_out->f_op->copy_file_range(file_in, pos_in,
+                                                      file_out, pos_out,
+                                                      len, flags);
+
+       return generic_copy_file_range(file_in, pos_in, file_out, pos_out, len,
+                                      flags);
+}
+
 /*
  * copy_file_range() differs from regular file read and write in that it
  * specifically allows return partial success.  When it does so is up to
@@ -1574,17 +1626,15 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
                            struct file *file_out, loff_t pos_out,
                            size_t len, unsigned int flags)
 {
-       struct inode *inode_in = file_inode(file_in);
-       struct inode *inode_out = file_inode(file_out);
        ssize_t ret;
 
        if (flags != 0)
                return -EINVAL;
 
-       if (S_ISDIR(inode_in->i_mode) || S_ISDIR(inode_out->i_mode))
-               return -EISDIR;
-       if (!S_ISREG(inode_in->i_mode) || !S_ISREG(inode_out->i_mode))
-               return -EINVAL;
+       ret = generic_copy_file_checks(file_in, pos_in, file_out, pos_out, &len,
+                                      flags);
+       if (unlikely(ret))
+               return ret;
 
        ret = rw_verify_area(READ, file_in, &pos_in, len);
        if (unlikely(ret))
@@ -1594,15 +1644,6 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
        if (unlikely(ret))
                return ret;
 
-       if (!(file_in->f_mode & FMODE_READ) ||
-           !(file_out->f_mode & FMODE_WRITE) ||
-           (file_out->f_flags & O_APPEND))
-               return -EBADF;
-
-       /* this could be relaxed once a method supports cross-fs copies */
-       if (inode_in->i_sb != inode_out->i_sb)
-               return -EXDEV;
-
        if (len == 0)
                return 0;
 
@@ -1612,7 +1653,8 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
         * Try cloning first, this is supported by more file systems, and
         * more efficient if both clone and copy are supported (e.g. NFS).
         */
-       if (file_in->f_op->remap_file_range) {
+       if (file_in->f_op->remap_file_range &&
+           file_inode(file_in)->i_sb == file_inode(file_out)->i_sb) {
                loff_t cloned;
 
                cloned = file_in->f_op->remap_file_range(file_in, pos_in,
@@ -1625,16 +1667,9 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
                }
        }
 
-       if (file_out->f_op->copy_file_range) {
-               ret = file_out->f_op->copy_file_range(file_in, pos_in, file_out,
-                                                     pos_out, len, flags);
-               if (ret != -EOPNOTSUPP)
-                       goto done;
-       }
-
-       ret = do_splice_direct(file_in, &pos_in, file_out, &pos_out,
-                       len > MAX_RW_COUNT ? MAX_RW_COUNT : len, 0);
-
+       ret = do_copy_file_range(file_in, pos_in, file_out, pos_out, len,
+                               flags);
+       WARN_ON_ONCE(ret == -EOPNOTSUPP);
 done:
        if (ret > 0) {
                fsnotify_access(file_in);
@@ -1951,25 +1986,10 @@ int generic_remap_file_range_prep(struct file *file_in, loff_t pos_in,
                return ret;
 
        /* If can't alter the file contents, we're done. */
-       if (!(remap_flags & REMAP_FILE_DEDUP)) {
-               /* Update the timestamps, since we can alter file contents. */
-               if (!(file_out->f_mode & FMODE_NOCMTIME)) {
-                       ret = file_update_time(file_out);
-                       if (ret)
-                               return ret;
-               }
-
-               /*
-                * Clear the security bits if the process is not being run by
-                * root.  This keeps people from modifying setuid and setgid
-                * binaries.
-                */
-               ret = file_remove_privs(file_out);
-               if (ret)
-                       return ret;
-       }
+       if (!(remap_flags & REMAP_FILE_DEDUP))
+               ret = file_modified(file_out);
 
-       return 0;
+       return ret;
 }
 EXPORT_SYMBOL(generic_remap_file_range_prep);
 
@@ -1977,29 +1997,21 @@ loff_t do_clone_file_range(struct file *file_in, loff_t pos_in,
                           struct file *file_out, loff_t pos_out,
                           loff_t len, unsigned int remap_flags)
 {
-       struct inode *inode_in = file_inode(file_in);
-       struct inode *inode_out = file_inode(file_out);
        loff_t ret;
 
        WARN_ON_ONCE(remap_flags & REMAP_FILE_DEDUP);
 
-       if (S_ISDIR(inode_in->i_mode) || S_ISDIR(inode_out->i_mode))
-               return -EISDIR;
-       if (!S_ISREG(inode_in->i_mode) || !S_ISREG(inode_out->i_mode))
-               return -EINVAL;
-
        /*
         * FICLONE/FICLONERANGE ioctls enforce that src and dest files are on
         * the same mount. Practically, they only need to be on the same file
         * system.
         */
-       if (inode_in->i_sb != inode_out->i_sb)
+       if (file_inode(file_in)->i_sb != file_inode(file_out)->i_sb)
                return -EXDEV;
 
-       if (!(file_in->f_mode & FMODE_READ) ||
-           !(file_out->f_mode & FMODE_WRITE) ||
-           (file_out->f_flags & O_APPEND))
-               return -EBADF;
+       ret = generic_file_rw_checks(file_in, file_out);
+       if (ret < 0)
+               return ret;
 
        if (!file_in->f_op->remap_file_range)
                return -EOPNOTSUPP;