aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlessio Balsini <balsini@google.com>2021-10-27 21:11:25 +0100
committerPaul Lawrence <paullawrence@google.com>2021-11-16 19:47:04 +0000
commit7e5a12d6de30f111d8eff8c669c9b6ed074fa583 (patch)
tree0cab7dc8faceb97b5ac2ffa27ef996ec5a9118af
parent7a5210792c659ac565d1853d21eb1b6f00f2094d (diff)
downloadlibfuse-7e5a12d6de30f111d8eff8c669c9b6ed074fa583.tar.gz
Support fuse-bpf
Add struct fuse_args Introduce bpf_arg in fuse_reply_entry Currently, the opcode filters are zeroed to preserve the libfuse compatibility. Bug: 202785178 Test: Along with other changes, file /sys/fs/bpf/prog_fuse_media_fuse_media appears. Signed-off-by: Alessio Balsini <balsini@google.com> Signed-off-by: Paul Lawrence <paullawrence@google.com> Change-Id: I38cd80d702813f4e2b45d69af2ca451fca0984c6
-rw-r--r--include/fuse_kernel.h58
-rw-r--r--include/fuse_lowlevel.h4
-rw-r--r--lib/fuse_lowlevel.c73
3 files changed, 122 insertions, 13 deletions
diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h
index bd8794e..2c8e141 100644
--- a/include/fuse_kernel.h
+++ b/include/fuse_kernel.h
@@ -457,6 +457,17 @@ struct fuse_entry_out {
struct fuse_attr attr;
};
+#define FUSE_ACTION_KEEP 0
+#define FUSE_ACTION_REMOVE 1
+#define FUSE_ACTION_REPLACE 2
+
+struct fuse_entry_bpf_out {
+ uint64_t backing_action;
+ uint64_t backing_fd;
+ uint64_t bpf_action;
+ uint64_t bpf_fd;
+};
+
struct fuse_forget_in {
uint64_t nlookup;
};
@@ -859,4 +870,51 @@ struct fuse_copy_file_range_in {
uint64_t flags;
};
+/** Export fuse_args only for bpf */
+#ifdef __KERNEL__
+struct fuse_mount;
+
+/** One input argument of a request */
+struct fuse_in_arg {
+ unsigned size;
+ const void *value;
+};
+
+/** One output argument of a request */
+struct fuse_arg {
+ unsigned size;
+ void *value;
+};
+
+struct fuse_args {
+ uint64_t nodeid;
+ uint32_t opcode;
+ unsigned short in_numargs;
+ unsigned short out_numargs;
+ int force:1;
+ int noreply:1;
+ int nocreds:1;
+ int in_pages:1;
+ int out_pages:1;
+ int out_argvar:1;
+ int page_zeroing:1;
+ int page_replace:1;
+ int may_block:1;
+ struct fuse_in_arg in_args[5];
+ struct fuse_arg out_args[3];
+ void (*end)(struct fuse_mount *fm, struct fuse_args *args, int error);
+
+ /* Path used for completing d_canonical_path */
+ struct path *canonical_path;
+};
+#endif
+
+#define FUSE_BPF_USER_FILTER 1
+#define FUSE_BPF_BACKING 2
+#define FUSE_BPF_POST_FILTER 4
+
+#define FUSE_OPCODE_FILTER 0x0ffff
+#define FUSE_PREFILTER 0x10000
+#define FUSE_POSTFILTER 0x20000
+
#endif /* _LINUX_FUSE_H */
diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h
index e916112..d203157 100644
--- a/include/fuse_lowlevel.h
+++ b/include/fuse_lowlevel.h
@@ -98,6 +98,10 @@ struct fuse_entry_param {
that come through the kernel, this should be set to a very
large value. */
double entry_timeout;
+ uint64_t backing_action;
+ uint64_t backing_fd;
+ uint64_t bpf_action;
+ uint64_t bpf_fd;
};
/**
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index a4a4df0..459fca5 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -400,20 +400,45 @@ static void fill_open(struct fuse_open_out *arg,
arg->open_flags |= FOPEN_NONSEEKABLE;
}
-int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
-{
- struct fuse_entry_out arg;
- size_t size = req->se->conn.proto_minor < 9 ?
- FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg);
+int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param* e) {
+ struct {
+ struct fuse_entry_out arg;
+ struct fuse_entry_bpf_out bpf_arg;
+ } __attribute__((packed)) arg_ext = {0};
- /* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant
- negative entry */
- if (!e->ino && req->se->conn.proto_minor < 4)
- return fuse_reply_err(req, ENOENT);
+ struct fuse_entry_out arg;
+ struct fuse_entry_bpf_out bpf_arg;
+ size_t size;
+ int extended_args = e->bpf_action || bpf_arg.bpf_fd || e->backing_action || e->backing_fd;
- memset(&arg, 0, sizeof(arg));
- fill_entry(&arg, e);
- return send_reply_ok(req, &arg, size);
+ if (extended_args) {
+ size = req->se->conn.proto_minor < 9 ? FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg_ext);
+ } else {
+ size = req->se->conn.proto_minor < 9 ? FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg);
+ }
+
+ /* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant
+ negative entry */
+ if (!e->ino && req->se->conn.proto_minor < 4) return fuse_reply_err(req, ENOENT);
+
+ memset(&arg, 0, sizeof(arg));
+
+ if (extended_args) {
+ memset(&bpf_arg, 0, sizeof(bpf_arg));
+
+ bpf_arg.bpf_action = e->bpf_action;
+ bpf_arg.bpf_fd = e->bpf_fd;
+ bpf_arg.backing_action = e->backing_action;
+ bpf_arg.backing_fd = e->backing_fd;
+
+ arg_ext.arg = arg;
+ arg_ext.bpf_arg = bpf_arg;
+
+ return send_reply_ok(req, &arg_ext, size);
+ } else {
+ fill_entry(&arg, e);
+ return send_reply_ok(req, &arg, size);
+ }
}
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
@@ -2604,6 +2629,22 @@ static const char *opname(enum fuse_opcode opcode)
return fuse_ll_ops[opcode].name;
}
+static const char *opfiltername(int filter)
+{
+ switch (filter) {
+ case 0:
+ return "NONE";
+ case FUSE_PREFILTER:
+ return "FUSE_PREFILTER";
+ case FUSE_POSTFILTER:
+ return "FUSE_POSTFILTER";
+ case FUSE_PREFILTER | FUSE_POSTFILTER:
+ return "FUSE_PREFILTER | FUSE_POSTFILTER";
+ default:
+ return "???";
+ }
+}
+
static int fuse_ll_copy_from_pipe(struct fuse_bufvec *dst,
struct fuse_bufvec *src)
{
@@ -2638,6 +2679,7 @@ void fuse_session_process_buf_int(struct fuse_session *se,
void *mbuf = NULL;
int err;
int res;
+ int opcode_filter;
if (buf->flags & FUSE_BUF_IS_FD) {
if (buf->size < tmpbuf.buf[0].size)
@@ -2659,11 +2701,16 @@ void fuse_session_process_buf_int(struct fuse_session *se,
in = buf->mem;
}
+ /* Cleanup opcode most significant bits used by FUSE BPF */
+ opcode_filter = in->opcode & ~FUSE_OPCODE_FILTER;
+ in->opcode &= FUSE_OPCODE_FILTER;
+
if (se->debug) {
fuse_log(FUSE_LOG_DEBUG,
- "unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n",
+ "unique: %llu, opcode: %s (%i), opcode filter: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n",
(unsigned long long) in->unique,
opname((enum fuse_opcode) in->opcode), in->opcode,
+ opfiltername((enum fuse_opcode) opcode_filter), opcode_filter,
(unsigned long long) in->nodeid, buf->size, in->pid);
}