These patches extend FUSE to be able to act as a stacked filesystem. This allows pure passthrough, where the fuse file system simply reflects the lower filesystem, and also allows optional pre and post filtering in BPF and/or the userspace daemon as needed. This can dramatically reduce or even eliminate transitions to and from userspace. See https://lwn.net/Articles/915717/ Note that this patch set has been extensively tested in common-android13-5.10 This is a squash of these changes cherry-picked from common-android13-5.10 ANDROID: fuse-bpf: Make compile and pass test ANDROID: fuse-bpf: set error_in to ENOENT in negative lookup ANDROID: fuse-bpf: Add ability to run ranges of tests to fuse_test ANDROID: fuse-bpf: Add test for lookup postfilter ANDROID: fuse-bpf: readddir postfilter fixes ANDROID: fix kernelci error in fs/fuse/dir.c ANDROID: fuse-bpf: Fix RCU/reference issue ANDROID: fuse-bpf: Always call revalidate for backing ANDROID: fuse-bpf: Adjust backing handle funcs ANDROID: fuse-bpf: Fix revalidate error path and backing handling ANDROID: fuse-bpf: Fix use of get_fuse_inode ANDROID: fuse: Don't use readdirplus w/ nodeid 0 ANDROID: fuse-bpf: Introduce readdirplus test case for fuse bpf ANDROID: fuse-bpf: Make sure force_again flag is false by default ANDROID: fuse-bpf: Make inodes with backing_fd reachable for regular FUSE fuse_iget Revert "ANDROID: fuse-bpf: use target instead of parent inode to execute backing revalidate" ANDROID: fuse-bpf: use target instead of parent inode to execute backing revalidate ANDROID: fuse-bpf: Fix misuse of args.out_args ANDROID: fuse-bpf: Fix non-fusebpf build ANDROID: fuse-bpf: Use fuse_bpf_args in uapi ANDROID: fuse-bpf: Fix read_iter ANDROID: fuse-bpf: Use cache and refcount ANDROID: fuse-bpf: Rename iocb_fuse to iocb_orig ANDROID: fuse-bpf: Fix fixattr in rename ANDROID: fuse-bpf: Fix readdir ANDROID: fuse-bpf: Fix lseek return value for offset 0 ANDROID: fuse-bpf: fix read_iter and write_iter ANDROID: fuse-bpf: fix special devices ANDROID: fuse-bpf: support FUSE_LSEEK ANDROID: fuse-bpf: Add support for FUSE_COPY_FILE_RANGE ANDROID: fuse-bpf: Report errors to finalize ANDROID: fuse-bpf: Avoid reusing uint64_t for file ANDROID: fuse-bpf: Fix CONFIG_FUSE_BPF typo in FUSE_FSYNCDIR ANDROID: fuse-bpf: Move fd operations to be synchronous ANDROID: fuse-bpf: Invalidate if lower is unhashed ANDROID: fuse-bpf: Move bpf earlier in fuse_permission ANDROID: fuse-bpf: Update attributes on file write ANDROID: fuse: allow mounting with no userspace daemon ANDROID: fuse-bpf: Support FUSE_STATFS ANDROID: fuse-bpf: Fix filldir ANDROID: fuse-bpf: fix fuse_create_open_finalize ANDROID: fuse: add bpf support for removexattr ANDROID: fuse-bpf: Fix truncate ANDROID: fuse-bpf: Support inotify ANDROID: fuse-bpf: Make compile with CONFIG_FUSE but no CONFIG_FUSE_BPF ANDROID: fuse-bpf: Fix perms on readdir ANDROID: fuse: Fix umasking in backing ANDROID: fs/fuse: Backing move returns EXDEV if TO not backed ANDROID: bpf-fuse: Fix Setattr ANDROID: fuse-bpf: Check if mkdir dentry setup ANDROID: fuse-bpf: Close backing fds in fuse_dentry_revalidate ANDROID: fuse-bpf: Close backing-fd on both paths ANDROID: fuse-bpf: Partial fix for mmap'd files ANDROID: fuse-bpf: Restore a missing const ANDROID: Add fuse-bpf self tests ANDROID: Add FUSE_BPF to gki_defconfig ANDROID: fuse-bpf v1 ANDROID: fuse: Move functions in preparation for fuse-bpf Bug: 202785178 Test: test_fuse passes on linux. On cuttlefish, atest android.scopedstorage.cts.host.ScopedStorageHostTest passes with fuse-bpf enabled and disabled Change-Id: Idb099c281f9b39ff2c46fa3ebc63e508758416ee Signed-off-by: Paul Lawrence <paullawrence@google.com> Signed-off-by: Daniel Rosenberg <drosen@google.com>
129 lines
3.0 KiB
C
129 lines
3.0 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
// Copyright (c) 2021 Google LLC
|
|
|
|
#include <linux/filter.h>
|
|
#include <linux/android_fuse.h>
|
|
|
|
static const struct bpf_func_proto *
|
|
fuse_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
|
{
|
|
switch (func_id) {
|
|
case BPF_FUNC_trace_printk:
|
|
return bpf_get_trace_printk_proto();
|
|
|
|
case BPF_FUNC_get_current_uid_gid:
|
|
return &bpf_get_current_uid_gid_proto;
|
|
|
|
case BPF_FUNC_get_current_pid_tgid:
|
|
return &bpf_get_current_pid_tgid_proto;
|
|
|
|
case BPF_FUNC_map_lookup_elem:
|
|
return &bpf_map_lookup_elem_proto;
|
|
|
|
case BPF_FUNC_map_update_elem:
|
|
return &bpf_map_update_elem_proto;
|
|
|
|
default:
|
|
pr_debug("Invalid fuse bpf func %d\n", func_id);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static bool fuse_prog_is_valid_access(int off, int size,
|
|
enum bpf_access_type type,
|
|
const struct bpf_prog *prog,
|
|
struct bpf_insn_access_aux *info)
|
|
{
|
|
int i;
|
|
|
|
if (off < 0 || off > offsetofend(struct fuse_bpf_args, out_args))
|
|
return false;
|
|
|
|
/* TODO This is garbage. Do it properly */
|
|
for (i = 0; i < 5; i++) {
|
|
if (off == offsetof(struct fuse_bpf_args, in_args[i].value)) {
|
|
info->reg_type = PTR_TO_BUF;
|
|
info->ctx_field_size = 256;
|
|
if (type != BPF_READ)
|
|
return false;
|
|
return true;
|
|
}
|
|
}
|
|
for (i = 0; i < 3; i++) {
|
|
if (off == offsetof(struct fuse_bpf_args, out_args[i].value)) {
|
|
info->reg_type = PTR_TO_BUF;
|
|
info->ctx_field_size = 256;
|
|
return true;
|
|
}
|
|
}
|
|
if (type != BPF_READ)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
const struct bpf_verifier_ops fuse_verifier_ops = {
|
|
.get_func_proto = fuse_prog_func_proto,
|
|
.is_valid_access = fuse_prog_is_valid_access,
|
|
};
|
|
|
|
const struct bpf_prog_ops fuse_prog_ops = {
|
|
};
|
|
|
|
struct bpf_prog *fuse_get_bpf_prog(struct file *file)
|
|
{
|
|
struct bpf_prog *bpf_prog = ERR_PTR(-EINVAL);
|
|
|
|
if (!file || IS_ERR(file))
|
|
return bpf_prog;
|
|
/**
|
|
* Two ways of getting a bpf prog from another task's fd, since
|
|
* bpf_prog_get_type_dev only works with an fd
|
|
*
|
|
* 1) Duplicate a little of the needed code. Requires access to
|
|
* bpf_prog_fops for validation, which is not exported for modules
|
|
* 2) Insert the bpf_file object into a fd from the current task
|
|
* Stupidly complex, but I think OK, as security checks are not run
|
|
* during the existence of the handle
|
|
*
|
|
* Best would be to upstream 1) into kernel/bpf/syscall.c and export it
|
|
* for use here. Failing that, we have to use 2, since fuse must be
|
|
* compilable as a module.
|
|
*/
|
|
#if 1
|
|
if (file->f_op != &bpf_prog_fops)
|
|
goto out;
|
|
|
|
bpf_prog = file->private_data;
|
|
if (bpf_prog->type == BPF_PROG_TYPE_FUSE)
|
|
bpf_prog_inc(bpf_prog);
|
|
else
|
|
bpf_prog = ERR_PTR(-EINVAL);
|
|
|
|
#else
|
|
{
|
|
int task_fd = get_unused_fd_flags(file->f_flags);
|
|
|
|
if (task_fd < 0)
|
|
goto out;
|
|
|
|
fd_install(task_fd, file);
|
|
|
|
bpf_prog = bpf_prog_get_type_dev(task_fd, BPF_PROG_TYPE_FUSE,
|
|
false);
|
|
|
|
/* Close the fd, which also closes the file */
|
|
__close_fd(current->files, task_fd);
|
|
file = NULL;
|
|
}
|
|
#endif
|
|
|
|
out:
|
|
if (file)
|
|
fput(file);
|
|
return bpf_prog;
|
|
}
|
|
EXPORT_SYMBOL(fuse_get_bpf_prog);
|
|
|
|
|