Commit 9fe2a036 authored by Richard Fung's avatar Richard Fung Committed by Miklos Szeredi
Browse files

fuse: Add initial support for fs-verity



This adds support for the FS_IOC_ENABLE_VERITY and FS_IOC_MEASURE_VERITY
ioctls. The FS_IOC_READ_VERITY_METADATA is missing but from the
documentation, "This is a fairly specialized use case, and most fs-verity
users won’t need this ioctl."

Signed-off-by: default avatarRichard Fung <richardfung@google.com>
Acked-by: default avatarEric Biggers <ebiggers@google.com>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent 413e8f01
Loading
Loading
Loading
Loading
+60 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
#include <linux/uio.h>
#include <linux/compat.h>
#include <linux/fileattr.h>
#include <linux/fsverity.h>

static ssize_t fuse_send_ioctl(struct fuse_mount *fm, struct fuse_args *args,
			       struct fuse_ioctl_out *outarg)
@@ -117,6 +118,53 @@ static int fuse_copy_ioctl_iovec(struct fuse_conn *fc, struct iovec *dst,
	return 0;
}

/* For fs-verity, determine iov lengths from input */
static int fuse_setup_measure_verity(unsigned long arg, struct iovec *iov)
{
	__u16 digest_size;
	struct fsverity_digest __user *uarg = (void __user *)arg;

	if (copy_from_user(&digest_size, &uarg->digest_size, sizeof(digest_size)))
		return -EFAULT;

	if (digest_size > SIZE_MAX - sizeof(struct fsverity_digest))
		return -EINVAL;

	iov->iov_len = sizeof(struct fsverity_digest) + digest_size;

	return 0;
}

static int fuse_setup_enable_verity(unsigned long arg, struct iovec *iov,
				    unsigned int *in_iovs)
{
	struct fsverity_enable_arg enable;
	struct fsverity_enable_arg __user *uarg = (void __user *)arg;
	const __u32 max_buffer_len = FUSE_MAX_MAX_PAGES * PAGE_SIZE;

	if (copy_from_user(&enable, uarg, sizeof(enable)))
		return -EFAULT;

	if (enable.salt_size > max_buffer_len || enable.sig_size > max_buffer_len)
		return -ENOMEM;

	if (enable.salt_size > 0) {
		iov++;
		(*in_iovs)++;

		iov->iov_base = u64_to_user_ptr(enable.salt_ptr);
		iov->iov_len = enable.salt_size;
	}

	if (enable.sig_size > 0) {
		iov++;
		(*in_iovs)++;

		iov->iov_base = u64_to_user_ptr(enable.sig_ptr);
		iov->iov_len = enable.sig_size;
	}
	return 0;
}

/*
 * For ioctls, there is no generic way to determine how much memory
@@ -227,6 +275,18 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
			out_iov = iov;
			out_iovs = 1;
		}

		err = 0;
		switch (cmd) {
		case FS_IOC_MEASURE_VERITY:
			err = fuse_setup_measure_verity(arg, iov);
			break;
		case FS_IOC_ENABLE_VERITY:
			err = fuse_setup_enable_verity(arg, iov, &in_iovs);
			break;
		}
		if (err)
			goto out;
	}

 retry: