aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2023-10-05 15:39:13 -0700
committerXin Li <delphij@google.com>2023-10-05 15:39:13 -0700
commit443233e75a2ea4784008e51ba9b8671eb349b5d8 (patch)
treec2ff0f50dd51293b5a8073bf01d2848fc7504746
parent202f02f4758512a6edd1ef7d31cef05bb659b026 (diff)
parent0911652d2c79d981c9c8353f8b400a6869881716 (diff)
downloadlibfuse-443233e75a2ea4784008e51ba9b8671eb349b5d8.tar.gz
Bug: 298295554 Merged-In: Iefaa6682729600f8804b454bb14f9d1611c6d7a7 Change-Id: I84d5fdd1700f18e2596bab4c0a2f123c7b2385a0
-rw-r--r--include/fuse_kernel.h8
-rw-r--r--include/fuse_lowlevel.h46
-rw-r--r--lib/fuse_lowlevel.c70
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 51cd412..4c75e3b 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;
@@ -2605,7 +2647,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" },
@@ -2649,6 +2691,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)
@@ -2826,8 +2880,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);