From 79110a74ebc8452b1e5b70455c2ce9a95db0b572 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 6 Sep 2017 10:19:15 -0700 Subject: [PATCH] scoutfs: prevent partial block stage, except final The staging ioctl is just a thin wrapper around writing. If we allowed partial-block staging then the write would zero a newly allocated block and only stage in the partial region of the block, leaving zeros in the file that didn't exist before. We prevent staging when the starting offset isn't block aligned. We prevent staging when the final offset isn't block aligned unless it matches the size because the stage ends in the final partial block of the file. This is verified by an xfstest (scoutfs/003) that is in flight. Signed-off-by: Zach Brown --- kmod/src/ioctl.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/kmod/src/ioctl.c b/kmod/src/ioctl.c index 2013b31c..80abaf4a 100644 --- a/kmod/src/ioctl.c +++ b/kmod/src/ioctl.c @@ -412,14 +412,21 @@ static long scoutfs_ioc_stage(struct file *file, unsigned long arg) struct kiocb kiocb; struct iovec iov; size_t written; + loff_t end_size; + loff_t isize; loff_t pos; int ret; if (copy_from_user(&args, (void __user *)arg, sizeof(args))) return -EFAULT; - if (args.count < 0 || (args.offset + args.count < args.offset)) + end_size = args.offset + args.count; + + /* verify arg constraints that aren't dependent on file */ + if (args.count < 0 || (end_size < args.offset) || + args.offset & SCOUTFS_BLOCK_MASK) return -EINVAL; + if (args.count == 0) return 0; @@ -437,11 +444,14 @@ static long scoutfs_ioc_stage(struct file *file, unsigned long arg) mutex_lock(&inode->i_mutex); + isize = i_size_read(inode); + if (!S_ISREG(inode->i_mode) || !(file->f_mode & FMODE_WRITE) || (file->f_flags & (O_APPEND | O_DIRECT | O_DSYNC)) || IS_SYNC(file->f_mapping->host) || - (args.offset + args.count > i_size_read(inode))) { + (end_size > isize) || + ((end_size & SCOUTFS_BLOCK_MASK) && (end_size != isize))) { ret = -EINVAL; goto out; }