diff options
author | Amir Goldstein <amir73il@gmail.com> | 2019-06-05 11:04:48 -0400 |
---|---|---|
committer | Darrick J. Wong <darrick.wong@oracle.com> | 2019-06-09 13:06:19 -0400 |
commit | a31713517dac0862a3f0ec9006df9160ce022b0c (patch) | |
tree | 020aa1dc2d4ad9c646a15c23f12f765271f459a7 | |
parent | 64bf5ff58dff757253cf2142542672de4b21cd1a (diff) |
vfs: introduce generic_file_rw_checks()
Factor out helper with some checks on in/out file that are
common to clone_file_range and copy_file_range.
Suggested-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
-rw-r--r-- | fs/read_write.c | 38 | ||||
-rw-r--r-- | include/linux/fs.h | 1 | ||||
-rw-r--r-- | mm/filemap.c | 24 |
3 files changed, 36 insertions, 27 deletions
diff --git a/fs/read_write.c b/fs/read_write.c index b63dcb4e4fe9..f1900bdb3127 100644 --- a/fs/read_write.c +++ b/fs/read_write.c | |||
@@ -1617,17 +1617,18 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in, | |||
1617 | struct file *file_out, loff_t pos_out, | 1617 | struct file *file_out, loff_t pos_out, |
1618 | size_t len, unsigned int flags) | 1618 | size_t len, unsigned int flags) |
1619 | { | 1619 | { |
1620 | struct inode *inode_in = file_inode(file_in); | ||
1621 | struct inode *inode_out = file_inode(file_out); | ||
1622 | ssize_t ret; | 1620 | ssize_t ret; |
1623 | 1621 | ||
1624 | if (flags != 0) | 1622 | if (flags != 0) |
1625 | return -EINVAL; | 1623 | return -EINVAL; |
1626 | 1624 | ||
1627 | if (S_ISDIR(inode_in->i_mode) || S_ISDIR(inode_out->i_mode)) | 1625 | /* this could be relaxed once a method supports cross-fs copies */ |
1628 | return -EISDIR; | 1626 | if (file_inode(file_in)->i_sb != file_inode(file_out)->i_sb) |
1629 | if (!S_ISREG(inode_in->i_mode) || !S_ISREG(inode_out->i_mode)) | 1627 | return -EXDEV; |
1630 | return -EINVAL; | 1628 | |
1629 | ret = generic_file_rw_checks(file_in, file_out); | ||
1630 | if (unlikely(ret)) | ||
1631 | return ret; | ||
1631 | 1632 | ||
1632 | ret = rw_verify_area(READ, file_in, &pos_in, len); | 1633 | ret = rw_verify_area(READ, file_in, &pos_in, len); |
1633 | if (unlikely(ret)) | 1634 | if (unlikely(ret)) |
@@ -1637,15 +1638,6 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in, | |||
1637 | if (unlikely(ret)) | 1638 | if (unlikely(ret)) |
1638 | return ret; | 1639 | return ret; |
1639 | 1640 | ||
1640 | if (!(file_in->f_mode & FMODE_READ) || | ||
1641 | !(file_out->f_mode & FMODE_WRITE) || | ||
1642 | (file_out->f_flags & O_APPEND)) | ||
1643 | return -EBADF; | ||
1644 | |||
1645 | /* this could be relaxed once a method supports cross-fs copies */ | ||
1646 | if (inode_in->i_sb != inode_out->i_sb) | ||
1647 | return -EXDEV; | ||
1648 | |||
1649 | if (len == 0) | 1641 | if (len == 0) |
1650 | return 0; | 1642 | return 0; |
1651 | 1643 | ||
@@ -2013,29 +2005,21 @@ loff_t do_clone_file_range(struct file *file_in, loff_t pos_in, | |||
2013 | struct file *file_out, loff_t pos_out, | 2005 | struct file *file_out, loff_t pos_out, |
2014 | loff_t len, unsigned int remap_flags) | 2006 | loff_t len, unsigned int remap_flags) |
2015 | { | 2007 | { |
2016 | struct inode *inode_in = file_inode(file_in); | ||
2017 | struct inode *inode_out = file_inode(file_out); | ||
2018 | loff_t ret; | 2008 | loff_t ret; |
2019 | 2009 | ||
2020 | WARN_ON_ONCE(remap_flags & REMAP_FILE_DEDUP); | 2010 | WARN_ON_ONCE(remap_flags & REMAP_FILE_DEDUP); |
2021 | 2011 | ||
2022 | if (S_ISDIR(inode_in->i_mode) || S_ISDIR(inode_out->i_mode)) | ||
2023 | return -EISDIR; | ||
2024 | if (!S_ISREG(inode_in->i_mode) || !S_ISREG(inode_out->i_mode)) | ||
2025 | return -EINVAL; | ||
2026 | |||
2027 | /* | 2012 | /* |
2028 | * FICLONE/FICLONERANGE ioctls enforce that src and dest files are on | 2013 | * FICLONE/FICLONERANGE ioctls enforce that src and dest files are on |
2029 | * the same mount. Practically, they only need to be on the same file | 2014 | * the same mount. Practically, they only need to be on the same file |
2030 | * system. | 2015 | * system. |
2031 | */ | 2016 | */ |
2032 | if (inode_in->i_sb != inode_out->i_sb) | 2017 | if (file_inode(file_in)->i_sb != file_inode(file_out)->i_sb) |
2033 | return -EXDEV; | 2018 | return -EXDEV; |
2034 | 2019 | ||
2035 | if (!(file_in->f_mode & FMODE_READ) || | 2020 | ret = generic_file_rw_checks(file_in, file_out); |
2036 | !(file_out->f_mode & FMODE_WRITE) || | 2021 | if (ret < 0) |
2037 | (file_out->f_flags & O_APPEND)) | 2022 | return ret; |
2038 | return -EBADF; | ||
2039 | 2023 | ||
2040 | if (!file_in->f_op->remap_file_range) | 2024 | if (!file_in->f_op->remap_file_range) |
2041 | return -EOPNOTSUPP; | 2025 | return -EOPNOTSUPP; |
diff --git a/include/linux/fs.h b/include/linux/fs.h index ea17858310ff..89b9b73eb581 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -3049,6 +3049,7 @@ extern ssize_t generic_write_checks(struct kiocb *, struct iov_iter *); | |||
3049 | extern int generic_remap_checks(struct file *file_in, loff_t pos_in, | 3049 | extern int generic_remap_checks(struct file *file_in, loff_t pos_in, |
3050 | struct file *file_out, loff_t pos_out, | 3050 | struct file *file_out, loff_t pos_out, |
3051 | loff_t *count, unsigned int remap_flags); | 3051 | loff_t *count, unsigned int remap_flags); |
3052 | extern int generic_file_rw_checks(struct file *file_in, struct file *file_out); | ||
3052 | extern ssize_t generic_file_read_iter(struct kiocb *, struct iov_iter *); | 3053 | extern ssize_t generic_file_read_iter(struct kiocb *, struct iov_iter *); |
3053 | extern ssize_t __generic_file_write_iter(struct kiocb *, struct iov_iter *); | 3054 | extern ssize_t __generic_file_write_iter(struct kiocb *, struct iov_iter *); |
3054 | extern ssize_t generic_file_write_iter(struct kiocb *, struct iov_iter *); | 3055 | extern ssize_t generic_file_write_iter(struct kiocb *, struct iov_iter *); |
diff --git a/mm/filemap.c b/mm/filemap.c index df2006ba0cfa..a38619a4a6af 100644 --- a/mm/filemap.c +++ b/mm/filemap.c | |||
@@ -3041,6 +3041,30 @@ int generic_remap_checks(struct file *file_in, loff_t pos_in, | |||
3041 | return 0; | 3041 | return 0; |
3042 | } | 3042 | } |
3043 | 3043 | ||
3044 | |||
3045 | /* | ||
3046 | * Performs common checks before doing a file copy/clone | ||
3047 | * from @file_in to @file_out. | ||
3048 | */ | ||
3049 | int generic_file_rw_checks(struct file *file_in, struct file *file_out) | ||
3050 | { | ||
3051 | struct inode *inode_in = file_inode(file_in); | ||
3052 | struct inode *inode_out = file_inode(file_out); | ||
3053 | |||
3054 | /* Don't copy dirs, pipes, sockets... */ | ||
3055 | if (S_ISDIR(inode_in->i_mode) || S_ISDIR(inode_out->i_mode)) | ||
3056 | return -EISDIR; | ||
3057 | if (!S_ISREG(inode_in->i_mode) || !S_ISREG(inode_out->i_mode)) | ||
3058 | return -EINVAL; | ||
3059 | |||
3060 | if (!(file_in->f_mode & FMODE_READ) || | ||
3061 | !(file_out->f_mode & FMODE_WRITE) || | ||
3062 | (file_out->f_flags & O_APPEND)) | ||
3063 | return -EBADF; | ||
3064 | |||
3065 | return 0; | ||
3066 | } | ||
3067 | |||
3044 | int pagecache_write_begin(struct file *file, struct address_space *mapping, | 3068 | int pagecache_write_begin(struct file *file, struct address_space *mapping, |
3045 | loff_t pos, unsigned len, unsigned flags, | 3069 | loff_t pos, unsigned len, unsigned flags, |
3046 | struct page **pagep, void **fsdata) | 3070 | struct page **pagep, void **fsdata) |