aboutsummaryrefslogtreecommitdiff
path: root/lib/fuse_lowlevel.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/fuse_lowlevel.c')
-rw-r--r--lib/fuse_lowlevel.c104
1 files changed, 96 insertions, 8 deletions
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index aee22b4..4c75e3b 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -421,6 +421,7 @@ int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param* e) {
if (!e->ino && req->se->conn.proto_minor < 4) return fuse_reply_err(req, ENOENT);
memset(&arg, 0, sizeof(arg));
+ fill_entry(&arg, e);
if (extended_args) {
memset(&bpf_arg, 0, sizeof(bpf_arg));
@@ -435,7 +436,6 @@ int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param* e) {
return send_reply_ok(req, &arg_ext, size);
} else {
- fill_entry(&arg, e);
return send_reply_ok(req, &arg, size);
}
}
@@ -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;
@@ -1677,10 +1719,14 @@ static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
{
+ struct fuse_session *se = req->se;
+ unsigned int xattr_ext = !!(se->conn.want & FUSE_CAP_SETXATTR_EXT);
struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg;
- char *name = PARAM(arg);
+ char *name = xattr_ext ? PARAM(arg) :
+ (char *)arg + FUSE_COMPAT_SETXATTR_IN_SIZE;
char *value = name + strlen(name) + 1;
+ /* XXX:The API should be extended to support extra_flags/setxattr_flags */
if (req->se->op.setxattr)
req->se->op.setxattr(req, nodeid, name, value, arg->size,
arg->flags);
@@ -2013,6 +2059,7 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
struct fuse_session *se = req->se;
size_t bufsize = se->bufsize;
size_t outargsize = sizeof(outarg);
+ int extended_flags;
(void) nodeid;
if (se->debug) {
@@ -2032,6 +2079,10 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
outarg.major = FUSE_KERNEL_VERSION;
outarg.minor = FUSE_KERNEL_MINOR_VERSION;
+ extended_flags = arg->major > 7 || (arg->major == 7 && arg->minor >= 36);
+ fuse_log(FUSE_LOG_DEBUG, "fuse: protocol version: %u.%u, extended flags: %d\n",
+ arg->major, arg->minor, extended_flags);
+
if (arg->major < 7) {
fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n",
arg->major, arg->minor);
@@ -2084,6 +2135,8 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
se->conn.capable |= FUSE_CAP_NO_OPENDIR_SUPPORT;
if (arg->flags & FUSE_EXPLICIT_INVAL_DATA)
se->conn.capable |= FUSE_CAP_EXPLICIT_INVAL_DATA;
+ if (arg->flags & FUSE_SETXATTR_EXT)
+ se->conn.capable |= FUSE_CAP_SETXATTR_EXT;
if (!(arg->flags & FUSE_MAX_PAGES)) {
size_t max_bufsize =
FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize()
@@ -2092,8 +2145,13 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
bufsize = max_bufsize;
}
}
- if (arg->flags & FUSE_PASSTHROUGH)
- se->conn.capable |= FUSE_PASSTHROUGH;
+ if (extended_flags) {
+ if (arg->flags2 & (1 << 31))
+ se->conn.capable |= FUSE_CAP_PASSTHROUGH;
+ } else {
+ if (arg->flags & (1 << 31))
+ se->conn.capable |= FUSE_CAP_PASSTHROUGH;
+ }
} else {
se->conn.max_readahead = 0;
}
@@ -2206,12 +2264,18 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
outarg.flags |= FUSE_WRITEBACK_CACHE;
if (se->conn.want & FUSE_CAP_POSIX_ACL)
outarg.flags |= FUSE_POSIX_ACL;
- if (se->conn.want & FUSE_CAP_PASSTHROUGH)
- outarg.flags |= FUSE_PASSTHROUGH;
+ if (se->conn.want & FUSE_CAP_PASSTHROUGH) {
+ if (extended_flags)
+ outarg.flags2 |= (1 << 31);
+ else
+ outarg.flags |= (1 << 31);
+ }
if (se->conn.want & FUSE_CAP_CACHE_SYMLINKS)
outarg.flags |= FUSE_CACHE_SYMLINKS;
if (se->conn.want & FUSE_CAP_EXPLICIT_INVAL_DATA)
outarg.flags |= FUSE_EXPLICIT_INVAL_DATA;
+ if (se->conn.want & FUSE_CAP_SETXATTR_EXT)
+ outarg.flags |= FUSE_SETXATTR_EXT;
outarg.max_readahead = se->conn.max_readahead;
outarg.max_write = se->conn.max_write;
if (se->conn.proto_minor >= 13) {
@@ -2583,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" },
@@ -2627,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)
@@ -2804,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);