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 <zab@versity.com>
This commit is contained in:
Zach Brown
2017-09-06 10:19:15 -07:00
committed by Mark Fasheh
parent e7b5cd4c66
commit 79110a74eb

View File

@@ -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;
}