diff options
author | Paul Lawrence <paullawrence@google.com> | 2022-08-15 10:11:05 -0700 |
---|---|---|
committer | Paul Lawrence <paullawrence@google.com> | 2022-10-06 11:45:19 -0700 |
commit | 8b3bc907fc44199253080bf2b96ed70e89d9e802 (patch) | |
tree | 38c5576da95f3933e02ecde2e9d8c37fdda5dfde | |
parent | 2454156fad07ded1eaaa95d78badd1b6500e975a (diff) | |
download | libfuse-8b3bc907fc44199253080bf2b96ed70e89d9e802.tar.gz |
Add support for readdir and lookup postfilters
Bug: 219958836
Test: Client code is called correctly
Change-Id: Ia3d6f8487cc5e9f220c46236f5cd2d0a1d145206
-rw-r--r-- | include/fuse_kernel.h | 8 | ||||
-rw-r--r-- | include/fuse_lowlevel.h | 46 | ||||
-rw-r--r-- | lib/fuse_lowlevel.c | 70 |
3 files changed, 121 insertions, 3 deletions
diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h index 7adace0..7928a2f 100644 --- a/include/fuse_kernel.h +++ b/include/fuse_kernel.h @@ -716,6 +716,12 @@ struct fuse_read_in { uint32_t padding; }; +struct fuse_read_out { + uint64_t offset; + uint32_t size; + uint32_t again; +}; + struct fuse_passthrough_out_v0 { uint32_t fd; /* For future implementation */ @@ -903,7 +909,7 @@ struct fuse_in_header { uint32_t uid; uint32_t gid; uint32_t pid; - uint32_t padding; + uint32_t error_in; }; struct fuse_out_header { diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h index c591f71..92370ea 100644 --- a/include/fuse_lowlevel.h +++ b/include/fuse_lowlevel.h @@ -143,6 +143,12 @@ struct fuse_forget_data { #define FUSE_SET_ATTR_CTIME (1 << 10) /* ----------------------------------------------------------- * + * structs from fuse_kernel.h * + * ----------------------------------------------------------- */ +struct fuse_entry_out; +struct fuse_entry_bpf_out; + +/* ----------------------------------------------------------- * * Request methods and replies * * ----------------------------------------------------------- */ @@ -219,6 +225,25 @@ struct fuse_lowlevel_ops { void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name); /** + * post filter a lookup + * + * Valid replies: + * fuse_reply_entry + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param error_in the error, or 0, of the lookup + * @param name the name that was looked up + * @param feo the fuse entry out struct from the lookup + * @param febo the fuse entry bpf out struct from the lookup + */ + void (*lookup_postfilter)(fuse_req_t req, fuse_ino_t parent, + uint32_t error_in, const char *name, + struct fuse_entry_out *feo, + struct fuse_entry_bpf_out *febo); + + /** * Forget about an inode * * This function is called when the kernel removes an inode @@ -743,6 +768,27 @@ struct fuse_lowlevel_ops { struct fuse_file_info *fi); /** + * Read directory postfilter + * + * Valid replies: + * fuse_reply_buf + * fuse_reply_data + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param error_in the error from the readdir + * @param off_in offset to continue reading the directory stream before backing + * @param off_out offset to continue reading the directory stream after backing + * @param size_out length in bytes of dirents + * @param dirents array of dirents read by backing + * @param fi file information + */ + void (*readdirpostfilter)(fuse_req_t req, fuse_ino_t ino, uint32_t error_in, + off_t off_in, off_t off_out, size_t size_out, + const void *dirents, struct fuse_file_info *fi); + + /** * Release an open directory * * For every opendir call there will be exactly one releasedir diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index 964462f..e5004be 100644 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -1187,6 +1187,28 @@ static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) fuse_reply_err(req, ENOSYS); } +static void do_lookup_postfilter(fuse_req_t req, fuse_ino_t nodeid, uint32_t error_in, + const void *inarg, size_t size) +{ + if (req->se->op.lookup_postfilter) { + char *name = (char *) inarg; + size_t namelen = strlen(name); + + if (size != namelen + 1 + sizeof(struct fuse_entry_out) + + sizeof(struct fuse_entry_bpf_out)) { + fuse_log(FUSE_LOG_ERR, "%s: Bad size", __func__); + fuse_reply_err(req, EIO); + } else { + struct fuse_entry_out *feo = (void *) (name + namelen + 1); + struct fuse_entry_bpf_out *febo = (char *) feo + sizeof(*feo); + + req->se->op.lookup_postfilter(req, nodeid, error_in, name, feo, + febo); + } + } else + fuse_reply_err(req, ENOSYS); +} + static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_forget_in *arg = (struct fuse_forget_in *) inarg; @@ -1629,6 +1651,26 @@ static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) fuse_reply_err(req, ENOSYS); } +static void do_readdir_postfilter(fuse_req_t req, fuse_ino_t nodeid, + uint32_t error_in, const void *inarg, + size_t size) { + struct fuse_read_in *fri = (struct fuse_read_in *) inarg; + struct fuse_read_out *fro = (struct fuse_read_out *) (fri + 1); + struct fuse_dirent *dirents = (struct fuse_dirent *) (fro + 1); + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.fh = fri->fh; + + if (req->se->op.readdirpostfilter) + req->se->op.readdirpostfilter(req, nodeid, error_in, fri->offset, + fro->offset, + size - sizeof(*fri) - sizeof(*fro), + dirents, &fi); + else + fuse_reply_err(req, ENOSYS); +} + static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_release_in *arg = (struct fuse_release_in *) inarg; @@ -2597,7 +2639,7 @@ static struct { [FUSE_GETATTR] = { do_getattr, "GETATTR" }, [FUSE_SETATTR] = { do_setattr, "SETATTR" }, [FUSE_READLINK] = { do_readlink, "READLINK" }, - [FUSE_CANONICAL_PATH] = { do_canonical_path, "CANONICAL_PATH" }, + [FUSE_CANONICAL_PATH] = { do_canonical_path, "CANONICAL_PATH" }, [FUSE_SYMLINK] = { do_symlink, "SYMLINK" }, [FUSE_MKNOD] = { do_mknod, "MKNOD" }, [FUSE_MKDIR] = { do_mkdir, "MKDIR" }, @@ -2641,6 +2683,18 @@ static struct { [CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" }, }; +static struct { + void (*func)( fuse_req_t, fuse_ino_t, const void *); + const char *name; +} fuse_ll_prefilter_ops[] = {}; + +static struct { + void (*func)( fuse_req_t, fuse_ino_t, uint32_t, const void *, size_t size); +} fuse_ll_postfilter_ops[] = { + [FUSE_LOOKUP] = {do_lookup_postfilter}, + [FUSE_READDIR] = {do_readdir_postfilter}, +}; + #define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0])) static const char *opname(enum fuse_opcode opcode) @@ -2818,8 +2872,20 @@ void fuse_session_process_buf_int(struct fuse_session *se, do_write_buf(req, in->nodeid, inarg, buf); else if (in->opcode == FUSE_NOTIFY_REPLY) do_notify_reply(req, in->nodeid, inarg, buf); - else + else if (!opcode_filter) fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg); + else if (opcode_filter == FUSE_PREFILTER && fuse_ll_prefilter_ops[in->opcode].func) + fuse_ll_prefilter_ops[in->opcode].func(req, in->nodeid, inarg); + else if (opcode_filter == FUSE_POSTFILTER + && fuse_ll_postfilter_ops[in->opcode].func) + fuse_ll_postfilter_ops[in->opcode].func( + req, in->nodeid, in->error_in, inarg, + buf->size - sizeof(struct fuse_in_header)); + else { + fuse_log(FUSE_LOG_ERR, "Bad opcode"); + err = ENOSYS; + goto reply_err; + } out_free: free(mbuf); |