fuse: Add initial support for fs-verity
authorRichard Fung <richardfung@google.com>
Tue, 16 Apr 2024 00:16:39 +0000 (00:16 +0000)
committerMiklos Szeredi <mszeredi@redhat.com>
Wed, 8 May 2024 07:31:21 +0000 (09:31 +0200)
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: Richard Fung <richardfung@google.com>
Acked-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/fuse/ioctl.c

index 726640fa439e04a93277697fef4d359d8f7f6822..572ce8a82ceb9c93fa92f589ba8e9d288062f8c9 100644 (file)
@@ -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: