@@ -3099,6 +3099,38 @@ static ssize_t fuse_copy_file_range(struct file *file_in, loff_t pos_in,
inode_unlock(inode_out);
+ return err;
+};
+
+static int fuse_file_get_hash(struct file *file, enum hash_algo hash,
+ uint8_t *buf, size_t size)
+{
+ struct fuse_file *ff = file->private_data;
+ struct fuse_conn *fc = ff->fc;
+ FUSE_ARGS(args);
+ struct fuse_gethash_in inarg;
+ int err = 0;
+
+ if (!fc->allow_gethash)
+ return -EOPNOTSUPP;
+
+ memset(&inarg, 0, sizeof(inarg));
+ inarg.size = size;
+ inarg.hash = hash;
+ args.in.h.opcode = FUSE_GETHASH;
+ args.in.h.nodeid = ff->nodeid;
+ args.in.numargs = 1;
+ args.in.args[0].size = sizeof(inarg);
+ args.in.args[0].value = &inarg;
+ args.out.numargs = 1;
+ args.out.args[0].size = size;
+ args.out.args[0].value = buf;
+
+ err = fuse_simple_request(fc, &args);
+
+ if (err == -ENOSYS)
+ err = -EOPNOTSUPP;
+
return err;
}
@@ -3119,6 +3151,7 @@ static const struct file_operations fuse_file_operations = {
.poll = fuse_file_poll,
.fallocate = fuse_file_fallocate,
.copy_file_range = fuse_copy_file_range,
+ .get_hash = fuse_file_get_hash,
};
static const struct file_operations fuse_direct_io_file_operations = {
@@ -3136,6 +3169,7 @@ static const struct file_operations fuse_direct_io_file_operations = {
.compat_ioctl = fuse_file_compat_ioctl,
.poll = fuse_file_poll,
.fallocate = fuse_file_fallocate,
+ .get_hash = fuse_file_get_hash,
/* no splice_read */
};
@@ -705,6 +705,13 @@ struct fuse_conn {
/** Does the filesystem support copy_file_range? */
unsigned no_copy_file_range:1;
+ /*
+ * Allow the underlying filesystem to the hash of a file. This is
+ * used by IMA to avoid needing to calculate the hash on every
+ * measurement
+ */
+ unsigned allow_gethash:1;
+
/** The number of requests waiting for completion */
atomic_t num_waiting;
@@ -70,6 +70,7 @@ struct fuse_mount_data {
unsigned group_id_present:1;
unsigned default_permissions:1;
unsigned allow_other:1;
+ unsigned allow_gethash:1;
unsigned max_read;
unsigned blksize;
};
@@ -453,6 +454,7 @@ enum {
OPT_ALLOW_OTHER,
OPT_MAX_READ,
OPT_BLKSIZE,
+ OPT_ALLOW_GETHASH,
OPT_ERR
};
@@ -465,6 +467,7 @@ static const match_table_t tokens = {
{OPT_ALLOW_OTHER, "allow_other"},
{OPT_MAX_READ, "max_read=%u"},
{OPT_BLKSIZE, "blksize=%u"},
+ {OPT_ALLOW_GETHASH, "allow_gethash"},
{OPT_ERR, NULL}
};
@@ -551,6 +554,15 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev,
d->blksize = value;
break;
+ case OPT_ALLOW_GETHASH:
+ /*
+ * This is relevant to security decisions made in
+ * the root namespace, so restrict it more strongly
+ */
+ if (capable(CAP_SYS_ADMIN))
+ d->allow_gethash = 1;
+ break;
+
default:
return 0;
}
@@ -574,6 +586,8 @@ static int fuse_show_options(struct seq_file *m, struct dentry *root)
seq_puts(m, ",default_permissions");
if (fc->allow_other)
seq_puts(m, ",allow_other");
+ if (fc->allow_gethash)
+ seq_puts(m, ",allow_gethash");
if (fc->max_read != ~0)
seq_printf(m, ",max_read=%u", fc->max_read);
if (sb->s_bdev && sb->s_blocksize != FUSE_DEFAULT_BLKSIZE)
@@ -1163,6 +1177,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
fc->user_id = d.user_id;
fc->group_id = d.group_id;
fc->max_read = max_t(unsigned, 4096, d.max_read);
+ fc->allow_gethash = d.allow_gethash;
/* Used by get_root_inode() */
sb->s_fs_info = fc;
@@ -394,6 +394,7 @@ enum fuse_opcode {
FUSE_RENAME2 = 45,
FUSE_LSEEK = 46,
FUSE_COPY_FILE_RANGE = 47,
+ FUSE_GETHASH = 48,
/* CUSE specific operations */
CUSE_INIT = 4096,
@@ -817,4 +818,9 @@ struct fuse_copy_file_range_in {
uint64_t flags;
};
+struct fuse_gethash_in {
+ uint32_t size;
+ uint32_t hash;
+};
+
#endif /* _LINUX_FUSE_H */