This allows altering input or output parameters to fuse calls that will be handled directly by the backing filesystems. BPF programs can signal whether the entire operation should instead go through regular fuse, or if a postfilter call is needed. Signed-off-by: Daniel Rosenberg <drosen@xxxxxxxxxx> Signed-off-by: Paul Lawrence <paullawrence@xxxxxxxxxx> --- fs/fuse/fuse_i.h | 72 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 6fb5c7a1ff11..07b50be2c6e4 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1936,6 +1936,46 @@ static inline void convert_fuse_statfs(struct kstatfs *stbuf, struct fuse_kstatf int __init fuse_bpf_init(void); void __exit fuse_bpf_cleanup(void); +static inline void fuse_bpf_set_in_ends(struct bpf_fuse_args *fa) +{ + int i; + + for (i = 0; i < FUSE_MAX_ARGS_IN; i++) + fa->in_args[i].end_offset = (void *) + ((char *)fa->in_args[i].value + + fa->in_args[i].size); +} + +static inline void fuse_bpf_set_in_immutable(struct bpf_fuse_args *fa) +{ + int i; + + for (i = 0; i < FUSE_MAX_ARGS_IN; i++) + fa->in_args[i].flags |= BPF_FUSE_IMMUTABLE; +} + +static inline void fuse_bpf_set_out_ends(struct bpf_fuse_args *fa) +{ + int i; + + for (i = 0; i < FUSE_MAX_ARGS_OUT; i++) + fa->out_args[i].end_offset = (void *) + ((char *)fa->out_args[i].value + + fa->out_args[i].size); +} + +static inline void fuse_bpf_free_alloced(struct bpf_fuse_args *fa) +{ + int i; + + for (i = 0; i < FUSE_MAX_ARGS_IN; i++) + if (fa->in_args[i].flags & BPF_FUSE_ALLOCATED) + kfree(fa->in_args[i].value); + for (i = 0; i < FUSE_MAX_ARGS_OUT; i++) + if (fa->out_args[i].flags & BPF_FUSE_ALLOCATED) + kfree(fa->out_args[i].value); +} + /* * expression statement to wrap the backing filter logic * struct inode *inode: inode with bpf and backing inode @@ -1958,6 +1998,7 @@ void __exit fuse_bpf_cleanup(void); bool initialized = false; \ bool handled = false; \ ssize_t res; \ + int bpf_next; \ io feo = { 0 }; \ int error = 0; \ \ @@ -1969,17 +2010,47 @@ void __exit fuse_bpf_cleanup(void); error = initialize_in(&fa, &feo, args); \ if (error) \ break; \ + fuse_bpf_set_in_ends(&fa); \ + \ + fa.opcode |= FUSE_PREFILTER; \ + bpf_next = fuse_inode->bpf ? \ + bpf_prog_run(fuse_inode->bpf, &fa) : \ + BPF_FUSE_CONTINUE; \ + if (bpf_next < 0) { \ + error = bpf_next; \ + break; \ + } \ + \ + fuse_bpf_set_in_immutable(&fa); \ \ error = initialize_out(&fa, &feo, args); \ if (error) \ break; \ + fuse_bpf_set_out_ends(&fa); \ \ initialized = true; \ + if (bpf_next == BPF_FUSE_USER) { \ + handled = false; \ + break; \ + } \ + \ + fa.opcode &= ~FUSE_PREFILTER; \ \ error = backing(&fa, &out, args); \ if (error < 0) \ fa.error_in = error; \ \ + if (bpf_next == BPF_FUSE_CONTINUE) \ + break; \ + \ + fa.opcode |= FUSE_POSTFILTER; \ + if (bpf_next == BPF_FUSE_POSTFILTER) \ + bpf_next = bpf_prog_run(fuse_inode->bpf, &fa); \ + if (bpf_next < 0) { \ + error = bpf_next; \ + break; \ + } \ + \ } while (false); \ \ if (initialized && handled) { \ @@ -1987,6 +2058,7 @@ void __exit fuse_bpf_cleanup(void); if (res) \ error = res; \ } \ + fuse_bpf_free_alloced(&fa); \ \ out = error ? _Generic((out), \ default : \ -- 2.37.3.998.g577e59143f-goog