ceph: add additional offset check in ceph_write_iter()
authorChengguang Xu <cgxu519@gmx.com>
Thu, 19 Jul 2018 14:15:26 +0000 (22:15 +0800)
committerIlya Dryomov <idryomov@gmail.com>
Thu, 2 Aug 2018 19:33:28 +0000 (21:33 +0200)
If the offset is larger or equal to both real file size and
max file size, then return -EFBIG.

Signed-off-by: Chengguang Xu <cgxu519@gmx.com>
Reviewed-by: "Yan, Zheng" <zyan@redhat.com>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
fs/ceph/file.c

index 2c54d2674db6006055e213521db12386194f7c82..b9cd9d271dcbc62405db4867380694c898ee7ecb 100644 (file)
@@ -1384,12 +1384,12 @@ static ssize_t ceph_write_iter(struct kiocb *iocb, struct iov_iter *from)
        struct ceph_file_info *fi = file->private_data;
        struct inode *inode = file_inode(file);
        struct ceph_inode_info *ci = ceph_inode(inode);
-       struct ceph_osd_client *osdc =
-               &ceph_sb_to_client(inode->i_sb)->client->osdc;
+       struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
        struct ceph_cap_flush *prealloc_cf;
        ssize_t count, written = 0;
        int err, want, got;
        loff_t pos;
+       loff_t limit = max(i_size_read(inode), fsc->max_file_size);
 
        if (ceph_snap(inode) != CEPH_NOSNAP)
                return -EROFS;
@@ -1415,6 +1415,13 @@ retry_snap:
                goto out;
 
        pos = iocb->ki_pos;
+       if (unlikely(pos >= limit)) {
+               err = -EFBIG;
+               goto out;
+       } else {
+               iov_iter_truncate(from, limit - pos);
+       }
+
        count = iov_iter_count(from);
        if (ceph_quota_is_max_bytes_exceeded(inode, pos + count)) {
                err = -EDQUOT;
@@ -1436,7 +1443,7 @@ retry_snap:
        }
 
        /* FIXME: not complete since it doesn't account for being at quota */
-       if (ceph_osdmap_flag(osdc, CEPH_OSDMAP_FULL)) {
+       if (ceph_osdmap_flag(&fsc->client->osdc, CEPH_OSDMAP_FULL)) {
                err = -ENOSPC;
                goto out;
        }
@@ -1526,7 +1533,7 @@ retry_snap:
        }
 
        if (written >= 0) {
-               if (ceph_osdmap_flag(osdc, CEPH_OSDMAP_NEARFULL))
+               if (ceph_osdmap_flag(&fsc->client->osdc, CEPH_OSDMAP_NEARFULL))
                        iocb->ki_flags |= IOCB_DSYNC;
                written = generic_write_sync(iocb, written);
        }