1
0
mirror of https://github.com/openbsd/src.git synced 2026-06-17 23:03:29 +02:00

Everything needed to bring the FUSE kernel protocol in line with

the Linux implementation is now in place. With this update, the
kernel can support ports that talk directly to /dev/fuse0 rather
than relying on libfuse.

sys/fusebuf.h is retained rather than introducing fuse_kernel.h

OK claudio@
This commit is contained in:
helg
2026-06-17 13:29:01 +00:00
parent 9f1303c651
commit 51cdf7b684
17 changed files with 1130 additions and 550 deletions
+2 -2
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: fuse.c,v 1.59 2026/03/10 16:20:57 deraadt Exp $ */ /* $OpenBSD: fuse.c,v 1.60 2026/06/17 13:29:01 helg Exp $ */
/* /*
* Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com> * Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com>
* *
@@ -82,6 +82,7 @@ static struct fuse_opt fuse_lib_opts[] = {
FUSE_OPT_KEY("fmask=%o", KEY_STUB), FUSE_OPT_KEY("fmask=%o", KEY_STUB),
FUSE_LIB_OPT("umask=", set_mode), FUSE_LIB_OPT("umask=", set_mode),
FUSE_LIB_OPT("umask=%o", umask), FUSE_LIB_OPT("umask=%o", umask),
FUSE_OPT_KEY("max_write=", FUSE_OPT_KEY_KEEP),
FUSE_OPT_END FUSE_OPT_END
}; };
@@ -98,7 +99,6 @@ static struct fuse_opt fuse_mount_opts[] = {
FUSE_MOUNT_OPT("fsname=%s", fsname), FUSE_MOUNT_OPT("fsname=%s", fsname),
FUSE_MOUNT_OPT("max_read=%u", max_read), FUSE_MOUNT_OPT("max_read=%u", max_read),
FUSE_OPT_KEY("max_readahead", KEY_STUB), FUSE_OPT_KEY("max_readahead", KEY_STUB),
FUSE_OPT_KEY("max_write", KEY_STUB),
FUSE_MOUNT_OPT("noatime", noatime), FUSE_MOUNT_OPT("noatime", noatime),
FUSE_MOUNT_OPT("nonempty", nonempty), FUSE_MOUNT_OPT("nonempty", nonempty),
FUSE_MOUNT_OPT("-r", rdonly), FUSE_MOUNT_OPT("-r", rdonly),
+4 -23
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: fuse_chan.c,v 1.2 2026/01/22 11:53:31 helg Exp $ */ /* $OpenBSD: fuse_chan.c,v 1.3 2026/06/17 13:29:01 helg Exp $ */
/* /*
* Copyright (c) 2025 Helg Bredow <helg@openbsd.org> * Copyright (c) 2025 Helg Bredow <helg@openbsd.org>
* *
@@ -16,6 +16,7 @@
*/ */
#include <errno.h> #include <errno.h>
#include <unistd.h>
#include "debug.h" #include "debug.h"
#include "fuse_private.h" #include "fuse_private.h"
@@ -33,27 +34,9 @@ DEF(fuse_chan_fd);
int int
fuse_chan_recv(struct fuse_chan **chp, char *buf, size_t size) fuse_chan_recv(struct fuse_chan **chp, char *buf, size_t size)
{ {
struct fuse_chan *ch = *chp;
struct fusebuf *fbuf = (struct fusebuf *)buf;
struct iovec iov[2];
ssize_t n; ssize_t n;
if (chp == NULL || *chp == NULL || buf == NULL) n = read((*chp)->fd, buf, size);
return (-EINVAL);
/* XXX
* This will change once the kernel protocol is updated to be compatible
* with Linux.
* buf is contiguous memory but our fbuf is separated into the header
* and io structs with a pointer to the data buffer so we need to
* overlay our fbuf with pointer to data buffer.
*/
iov[0].iov_base = fbuf;
iov[0].iov_len = sizeof(fbuf->fb_hdr) + sizeof(fbuf->FD);
iov[1].iov_base = fbuf->fb_dat;
iov[1].iov_len = size - (sizeof(fbuf->fb_hdr) + sizeof(fbuf->FD));
n = readv(ch->fd, iov, 2);
if (n == -1) if (n == -1)
return (-errno); return (-errno);
@@ -67,10 +50,8 @@ fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[], size_t count)
ssize_t n; ssize_t n;
n = writev(ch->fd, iov, count); n = writev(ch->fd, iov, count);
if (n == -1) { if (n == -1)
DPERROR(__func__);
return (-errno); return (-errno);
}
return (0); return (0);
} }
+116 -56
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: fuse_lowlevel.c,v 1.2 2026/01/29 06:04:27 helg Exp $ */ /* $OpenBSD: fuse_lowlevel.c,v 1.3 2026/06/17 13:29:01 helg Exp $ */
/* /*
* Copyright (c) 2025 Helg Bredow <helg@openbsd.org> * Copyright (c) 2025 Helg Bredow <helg@openbsd.org>
* *
@@ -16,6 +16,8 @@
*/ */
#include <sys/uio.h> #include <sys/uio.h>
#include <sys/fusebuf.h>
#include <assert.h>
#include <errno.h> #include <errno.h>
#include <stddef.h> #include <stddef.h>
#include <stdlib.h> #include <stdlib.h>
@@ -25,28 +27,48 @@
#include "debug.h" #include "debug.h"
#include "fuse_private.h" #include "fuse_private.h"
#if defined(__clang__) || __GNUC_PREREQ__(4, 6)
/* confirm that constants in sys/fusebuf.h and fuse_lowlevel.h match */
static_assert(FUSE_FATTR_MODE==FUSE_SET_ATTR_MODE, "definition mismatch");
static_assert(FUSE_FATTR_UID==FUSE_SET_ATTR_UID, "definition mismatch");
static_assert(FUSE_FATTR_GID==FUSE_SET_ATTR_GID, "definition mismatch");
static_assert(FUSE_FATTR_SIZE==FUSE_SET_ATTR_SIZE, "definition mismatch");
static_assert(FUSE_FATTR_ATIME==FUSE_SET_ATTR_ATIME, "definition mismatch");
static_assert(FUSE_FATTR_MTIME==FUSE_SET_ATTR_MTIME, "definition mismatch");
/* TODO: not implemented in kernel yet
static_assert(FUSE_FATTR_ATIME_NOW==FUSE_SET_ATTR_ATIME_NOW,
"definition mismatch");
static_assert(FUSE_FATTR_MTIME_NOW==FUSE_SET_ATTR_MTIME_NOW,
"definition mismatch");
*/
#endif
enum { enum {
KEY_HELP, KEY_HELP,
KEY_VERSION, KEY_VERSION,
KEY_DEBUG KEY_DEBUG
}; };
/* options supported by fuse_lowlevel_new */
#define FUSE_LIB_OPT(o, m) {o, offsetof(struct fuse_session, fci.m), 1}
static const struct fuse_opt fuse_ll_opts[] = { static const struct fuse_opt fuse_ll_opts[] = {
/* core options, also supported by fuse_parse_cmdline(3) */
FUSE_OPT_KEY("debug", KEY_DEBUG), FUSE_OPT_KEY("debug", KEY_DEBUG),
FUSE_OPT_KEY("-d", KEY_DEBUG), FUSE_OPT_KEY("-d", KEY_DEBUG),
FUSE_OPT_KEY("-h", KEY_HELP), FUSE_OPT_KEY("-h", KEY_HELP),
FUSE_OPT_KEY("--help", KEY_HELP), FUSE_OPT_KEY("--help", KEY_HELP),
FUSE_OPT_KEY("-V", KEY_VERSION), FUSE_OPT_KEY("-V", KEY_VERSION),
FUSE_OPT_KEY("--version", KEY_VERSION), FUSE_OPT_KEY("--version", KEY_VERSION),
FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_DISCARD), /* fuse_lowlevel_new(3) options */
FUSE_LIB_OPT("max_write=%u", max_write),
FUSE_OPT_END FUSE_OPT_END
}; };
static void static void
dump_version(void) dump_version(void)
{ {
fprintf(stderr, "FUSE library version: %d.%d\n", FUSE_MAJOR_VERSION, fprintf(stderr, "OpenBSD FUSE library version: %d.%d\n",
FUSE_MINOR_VERSION); FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION);
} }
static void static void
@@ -70,12 +92,12 @@ ifuse_ll_opt_proc(void *data, const char *arg, int key,
break; break;
case KEY_DEBUG: case KEY_DEBUG:
ifuse_debug_init(); ifuse_debug_init();
return (1); return (0);
default: default:
fprintf(stderr, "fuse: unknown option -- %s\n", arg); fprintf(stderr, "fuse: unknown option -- %s\n", arg);
} }
return -1; return (-1);
} }
struct fuse_session * struct fuse_session *
@@ -89,11 +111,23 @@ fuse_lowlevel_new(struct fuse_args *fargs,
if (se == NULL) if (se == NULL)
return (NULL); return (NULL);
if (fuse_opt_parse(fargs, NULL, fuse_ll_opts, ifuse_ll_opt_proc) == -1) { se->fci.proto_major = FUSE_KERNEL_VERSION;
se->fci.proto_minor = FUSE_KERNEL_MINOR_VERSION;
/* default that may be overridden by fargs */
se->fci.max_write = FUSEBUFMAXSIZE;
if (fuse_opt_parse(fargs, se, fuse_ll_opts, ifuse_ll_opt_proc) == -1) {
free(se); free(se);
return (NULL); return (NULL);
} }
/* validate parsed options */
if (se->fci.max_write > FUSEBUFMAXSIZE) {
DPRINTF("libfuse: max_write %u too large, using %u instead.\n",
se->fci.max_write, FUSEBUFMAXSIZE);
se->fci.max_write = FUSEBUFMAXSIZE;
}
if (llops->create && !llops->mknod) if (llops->create && !llops->mknod)
DPRINTF("libfuse: WARNING: filesystem supports creating files " DPRINTF("libfuse: WARNING: filesystem supports creating files "
"but does not implement mknod. No new files can be " "but does not implement mknod. No new files can be "
@@ -113,12 +147,33 @@ fuse_lowlevel_new(struct fuse_args *fargs,
} }
DEF(fuse_lowlevel_new); DEF(fuse_lowlevel_new);
static void
ifuse_stat2attr(const struct stat *stbuf, struct fuse_attr *attr)
{
memset(attr, 0, sizeof(*attr));
attr->ino = stbuf->st_ino;
attr->size = stbuf->st_size;
attr->blocks = stbuf->st_blocks;
attr->atime = stbuf->st_atim.tv_sec;
attr->mtime = stbuf->st_mtim.tv_sec;
attr->ctime = stbuf->st_ctim.tv_sec;
attr->atimensec = stbuf->st_atim.tv_nsec;
attr->mtimensec = stbuf->st_mtim.tv_nsec;
attr->ctimensec = stbuf->st_ctim.tv_nsec;
attr->mode = stbuf->st_mode;
attr->nlink = stbuf->st_nlink;
attr->uid = stbuf->st_uid;
attr->gid = stbuf->st_gid;
attr->rdev = stbuf->st_rdev;
attr->blksize = stbuf->st_blksize;
}
static int static int
ifuse_reply(fuse_req_t req, const char *data, const size_t data_size, int err) ifuse_reply(fuse_req_t req, const void *data, const size_t data_size, int err)
{ {
struct fusebuf *fbuf; struct fusebuf *fbuf;
struct fuse_out_header hdr;
struct iovec iov[2]; struct iovec iov[2];
size_t fbuf_size;
/* check for sanity */ /* check for sanity */
if (data == NULL && data_size > 0) { if (data == NULL && data_size > 0) {
@@ -128,31 +183,28 @@ ifuse_reply(fuse_req_t req, const char *data, const size_t data_size, int err)
} }
fbuf = req->fbuf; fbuf = req->fbuf;
fbuf_size = sizeof(fbuf->fb_hdr) + sizeof(fbuf->FD);
fbuf->fb_err = err; hdr.unique = fbuf->fb_uuid;
fbuf->fb_len = data_size; hdr.len = sizeof(hdr) + data_size;
hdr.error = err;
iov[0].iov_base = fbuf; iov[0].iov_base = &hdr;
iov[0].iov_len = fbuf_size; iov[0].iov_len = sizeof(hdr);
iov[1].iov_base = (void *)data; iov[1].iov_base = (void *)data;
iov[1].iov_len = data_size; iov[1].iov_len = data_size;
DPRINTF("errno: %d", fbuf->fb_err); DPRINTF("errno: %d", err);
return fuse_chan_send(req->ch, iov, 2); return fuse_chan_send(req->ch, iov, 2);
} }
static int /*
ifuse_reply_ok(fuse_req_t req) * fuse_reply_err takes a non-negated errno but kernel expects negated errno.
{ */
return ifuse_reply(req, NULL, 0, 0);
}
int int
fuse_reply_err(fuse_req_t req, int err) fuse_reply_err(fuse_req_t req, int err)
{ {
return ifuse_reply(req, NULL, 0, err); return ifuse_reply(req, NULL, 0, -err);
} }
DEF(fuse_reply_err); DEF(fuse_reply_err);
@@ -173,50 +225,59 @@ DEF(fuse_reply_readlink);
int int
fuse_reply_write(fuse_req_t req, size_t size) fuse_reply_write(fuse_req_t req, size_t size)
{ {
struct fusebuf *fbuf; struct fuse_write_out out;
fbuf = req->fbuf; memset(&out, 0, sizeof(out));
fbuf->fb_io_len = size; out.size = size;
return ifuse_reply_ok(req); return ifuse_reply(req, &out, sizeof(out), 0);
} }
DEF(fuse_reply_write); DEF(fuse_reply_write);
int int
fuse_reply_attr(fuse_req_t req, const struct stat *stbuf, double attr_timeout) fuse_reply_attr(fuse_req_t req, const struct stat *stbuf, double attr_timeout)
{ {
struct fusebuf *fbuf; struct fuse_attr_out out;
fbuf = req->fbuf; memset(&out, 0, sizeof(out));
fbuf->fb_attr = *stbuf; ifuse_stat2attr(stbuf, &out.attr);
return ifuse_reply_ok(req); return ifuse_reply(req, &out, sizeof(out), 0);
} }
DEF(fuse_reply_attr); DEF(fuse_reply_attr);
int int
fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e) fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
{ {
struct fusebuf *fbuf; struct fuse_entry_out out;
memset(&out, 0, sizeof(out));
out.nodeid = e->ino;
out.generation = e->generation;
ifuse_stat2attr(&e->attr, &out.attr);
fbuf = req->fbuf;
fbuf->fb_attr = e->attr;
fbuf->fb_ino = e->ino;
DPRINTF("inode: %llu\t", e->ino); DPRINTF("inode: %llu\t", e->ino);
return ifuse_reply_ok(req); return ifuse_reply(req, &out, sizeof(out), 0);
} }
DEF(fuse_reply_entry); DEF(fuse_reply_entry);
int int
fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf) fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
{ {
struct fusebuf *fbuf; struct fuse_statfs_out out;
fbuf = req->fbuf; memset(&out, 0, sizeof(out));
fbuf->fb_stat = *stbuf; out.st.bsize = stbuf->f_bsize;
out.st.frsize = stbuf->f_frsize;
out.st.blocks = stbuf->f_blocks;
out.st.bfree = stbuf->f_bfree;
out.st.bavail = stbuf->f_bavail;
out.st.files = stbuf->f_files;
out.st.ffree = stbuf->f_ffree;
out.st.namelen = stbuf->f_namemax;
return ifuse_reply_ok(req); return ifuse_reply(req, &out, sizeof(out), 0);
} }
DEF(fuse_reply_statfs); DEF(fuse_reply_statfs);
@@ -240,12 +301,13 @@ DEF(fuse_reply_create);
int int
fuse_reply_open(fuse_req_t req, const struct fuse_file_info *ffi) fuse_reply_open(fuse_req_t req, const struct fuse_file_info *ffi)
{ {
struct fusebuf *fbuf; struct fuse_open_out out;
fbuf = req->fbuf; memset(&out, 0, sizeof(out));
fbuf->fb_io_fd = ffi->fh; out.fh = ffi->fh;
out.open_flags = ffi->flags;
return ifuse_reply_ok(req); return ifuse_reply(req, &out, sizeof(out), 0);
} }
DEF(fuse_reply_open); DEF(fuse_reply_open);
@@ -256,22 +318,19 @@ fuse_reply_none(fuse_req_t req)
} }
DEF(fuse_reply_none); DEF(fuse_reply_none);
#define GENERIC_DIRSIZ(NLEN) \
((sizeof (struct dirent) - (MAXNAMLEN+1)) + ((NLEN+1 + 7) &~ 7))
size_t size_t
fuse_add_direntry(fuse_req_t req, char *buf, const size_t bufsize, fuse_add_direntry(fuse_req_t req, char *buf, const size_t bufsize,
const char *name, const struct stat *stbuf, off_t off) const char *name, const struct stat *stbuf, off_t off)
{ {
struct dirent *dir; struct fuse_dirent *dir;
size_t namelen; size_t namelen;
size_t len; size_t len;
if (name == NULL) if (name == NULL)
return (0); return (0);
namelen = strnlen(name, MAXNAMLEN); namelen = strlen(name);
len = GENERIC_DIRSIZ(namelen); len = FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + namelen);
/* NULL buf is used to request size to be calculated */ /* NULL buf is used to request size to be calculated */
if (buf == NULL || stbuf == NULL || req == NULL) if (buf == NULL || stbuf == NULL || req == NULL)
@@ -281,13 +340,14 @@ fuse_add_direntry(fuse_req_t req, char *buf, const size_t bufsize,
if (bufsize < len) if (bufsize < len)
return (len); return (len);
dir = (struct dirent *)buf; dir = (struct fuse_dirent *)buf;
dir->d_fileno = stbuf->st_ino; memset(dir, 0, len);
dir->d_type = IFTODT(stbuf->st_mode); dir->ino = stbuf->st_ino;
dir->d_reclen = len; dir->type = IFTODT(stbuf->st_mode);
dir->d_off = off; dir->off = off;
strlcpy(dir->d_name, name, sizeof(dir->d_name)); dir->namelen = namelen;
dir->d_namlen = strlen(dir->d_name); /* name is not NUL-terminated */
memcpy(dir->name, name, namelen);
return (len); return (len);
} }
+3 -3
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: fuse_lowlevel.h,v 1.2 2026/01/22 11:53:31 helg Exp $ */ /* $OpenBSD: fuse_lowlevel.h,v 1.3 2026/06/17 13:29:01 helg Exp $ */
/* /*
* Copyright (c) 2025 Helg Bredow <helg@openbsd.org> * Copyright (c) 2025 Helg Bredow <helg@openbsd.org>
* *
@@ -126,7 +126,6 @@ void fuse_session_reset(struct fuse_session *);
void fuse_session_process(struct fuse_session *, const char *, size_t, void fuse_session_process(struct fuse_session *, const char *, size_t,
struct fuse_chan *); struct fuse_chan *);
/* /*
* FUSE Channel API Prototypes * FUSE Channel API Prototypes
*/ */
@@ -161,7 +160,8 @@ const struct fuse_ctx *fuse_req_ctx(fuse_req_t);
void *fuse_req_userdata(fuse_req_t); void *fuse_req_userdata(fuse_req_t);
/* /*
* Bitmasks for setattr to indicate what to set. * Bitmasks for setattr to indicate what to set. These must be the same
* as the FUSE_FATTR_* definitions in <sys/fusebuf.h>
*/ */
#define FUSE_SET_ATTR_MODE (1 << 0) #define FUSE_SET_ATTR_MODE (1 << 0)
#define FUSE_SET_ATTR_UID (1 << 1) #define FUSE_SET_ATTR_UID (1 << 1)
+17 -16
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: fuse_ops.c,v 1.42 2026/01/29 06:04:27 helg Exp $ */ /* $OpenBSD: fuse_ops.c,v 1.43 2026/06/17 13:29:01 helg Exp $ */
/* /*
* Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com> * Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com>
* *
@@ -33,7 +33,7 @@
*/ */
static fuse_req_t ireq; static fuse_req_t ireq;
const fuse_req_t fuse_req_t
ifuse_req(void) ifuse_req(void)
{ {
return (ireq); return (ireq);
@@ -192,9 +192,6 @@ out:
fuse_reply_err(req, -err); fuse_reply_err(req, -err);
} }
#define GENERIC_DIRSIZ(NLEN) \
((sizeof (struct dirent) - (MAXNAMLEN+1)) + ((NLEN+1 + 7) &~ 7))
/* /*
* This function adds one directory entry to the buffer. * This function adds one directory entry to the buffer.
* FUSE file systems can implement readdir in one of two ways. * FUSE file systems can implement readdir in one of two ways.
@@ -265,11 +262,15 @@ ifuse_fill_readdir(void *dh, const char *name, const struct stat *stbuf,
/* advance buf to start of next entry and now add it for real */ /* advance buf to start of next entry and now add it for real */
buf = (char *) fd->buf + fd->len; buf = (char *) fd->buf + fd->len;
resid = fd->size - fd->len; resid = fd->size - fd->len;
len = fuse_add_direntry(ifuse_req(), buf, resid, name, &attr, off);
fd->len += len; fd->len += len;
fd->idx += len; fd->idx += len;
if (off)
fuse_add_direntry(ifuse_req(), buf, resid, name, &attr, off);
else
fuse_add_direntry(ifuse_req(), buf, resid, name, &attr,
fd->idx);
return (0); return (0);
} }
@@ -769,23 +770,23 @@ ifuse_ops_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
} }
ictx_init(req); ictx_init(req);
if (flags & FUSE_FATTR_MODE) { if (flags & FUSE_SET_ATTR_MODE) {
if (f->op.chmod) if (f->op.chmod)
err = f->op.chmod(realname, attr->st_mode); err = f->op.chmod(realname, attr->st_mode);
else else
err = ENOSYS; err = ENOSYS;
} }
if (!err && (flags & FUSE_FATTR_UID || flags & FUSE_FATTR_GID)) { if (!err && (flags & FUSE_SET_ATTR_UID || flags & FUSE_SET_ATTR_GID)) {
uid = (flags & FUSE_FATTR_UID) ? attr->st_uid : (uid_t)-1; uid = (flags & FUSE_SET_ATTR_UID) ? attr->st_uid : (uid_t)-1;
gid = (flags & FUSE_FATTR_GID) ? attr->st_gid : (gid_t)-1; gid = (flags & FUSE_SET_ATTR_GID) ? attr->st_gid : (gid_t)-1;
if (f->op.chown) if (f->op.chown)
err = f->op.chown(realname, uid, gid); err = f->op.chown(realname, uid, gid);
else else
err = ENOSYS; err = ENOSYS;
} }
if (!err && (flags & FUSE_FATTR_MTIME || flags & FUSE_FATTR_ATIME)) { if (!err && (flags & FUSE_SET_ATTR_MTIME || flags & FUSE_SET_ATTR_ATIME)) {
if (f->op.utimens) { if (f->op.utimens) {
ts[0] = attr->st_atim; ts[0] = attr->st_atim;
ts[1] = attr->st_mtim; ts[1] = attr->st_mtim;
@@ -798,7 +799,7 @@ ifuse_ops_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
err = ENOSYS; err = ENOSYS;
} }
if (!err && (flags & FUSE_FATTR_SIZE)) { if (!err && (flags & FUSE_SET_ATTR_SIZE)) {
if (f->op.truncate) if (f->op.truncate)
err = f->op.truncate(realname, attr->st_size); err = f->op.truncate(realname, attr->st_size);
else else
@@ -918,16 +919,16 @@ ifuse_ops_destroy(void *userdata)
} }
static void static void
ifuse_ops_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup /* XXX */) ifuse_ops_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
{ {
struct fuse *f = (struct fuse *)fuse_req_userdata(req); struct fuse *f = (struct fuse *)fuse_req_userdata(req);
struct fuse_vnode *vn; struct fuse_vnode *vn;
vn = tree_get(&f->vnode_tree, ino); vn = tree_get(&f->vnode_tree, ino);
if (vn != NULL) if (vn != NULL)
unref_vn(f, vn); unref_vn(f, vn, nlookup);
fuse_reply_err(req, 0); fuse_reply_none(req);
} }
static void static void
+9 -7
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: fuse_private.h,v 1.28 2026/01/29 06:04:27 helg Exp $ */ /* $OpenBSD: fuse_private.h,v 1.29 2026/06/17 13:29:01 helg Exp $ */
/* /*
* Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com> * Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com>
* *
@@ -58,10 +58,11 @@ SPLAY_HEAD(tree, treeentry);
struct fuse_session { struct fuse_session {
struct fuse_lowlevel_ops llops; struct fuse_lowlevel_ops llops;
struct fuse_chan *chan; struct fuse_conn_info fci;
void *userdata; struct fuse_chan *chan;
int init; void *userdata;
int exit; int init;
int exit;
}; };
struct fuse_chan { struct fuse_chan {
@@ -122,12 +123,13 @@ struct fuse_req {
#define FUSE_ROOT_INO ((ino_t)1) #define FUSE_ROOT_INO ((ino_t)1)
/* fuse_ops.c */ /* fuse_ops.c */
const fuse_req_t ifuse_req(void); fuse_req_t ifuse_req(void);
/* fuse_subr.c */ /* fuse_subr.c */
struct fuse_vnode *alloc_vn(struct fuse *, const char *, ino_t, ino_t); struct fuse_vnode *alloc_vn(struct fuse *, const char *, ino_t, ino_t);
void ref_vn(struct fuse_vnode *); void ref_vn(struct fuse_vnode *);
void unref_vn(struct fuse *, struct fuse_vnode *); void unref_vn(struct fuse *, struct fuse_vnode *,
const uint64_t);
struct fuse_vnode *get_vn_by_name_and_parent(struct fuse *, const char *, struct fuse_vnode *get_vn_by_name_and_parent(struct fuse *, const char *,
ino_t); ino_t);
void remove_vnode_from_name_tree(struct fuse *, void remove_vnode_from_name_tree(struct fuse *,
+150 -115
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: fuse_session.c,v 1.1 2026/01/22 11:53:31 helg Exp $ */ /* $OpenBSD: fuse_session.c,v 1.2 2026/06/17 13:29:01 helg Exp $ */
/* /*
* Copyright (c) 2025 Helg Bredow <helg@openbsd.org> * Copyright (c) 2025 Helg Bredow <helg@openbsd.org>
* *
@@ -16,6 +16,7 @@
*/ */
#include <sys/uio.h> #include <sys/uio.h>
#include <sys/fusebuf.h>
#include <errno.h> #include <errno.h>
#include <err.h> #include <err.h>
#include <stdlib.h> #include <stdlib.h>
@@ -83,8 +84,7 @@ int
fuse_session_loop(struct fuse_session *se) fuse_session_loop(struct fuse_session *se)
{ {
struct fuse_chan *ch; struct fuse_chan *ch;
struct fusebuf fbuf; char *buf;
char *buf = (char *)&fbuf;
size_t bufsize; size_t bufsize;
int err; int err;
@@ -95,15 +95,25 @@ fuse_session_loop(struct fuse_session *se)
if (ch == NULL) if (ch == NULL)
return (-1); return (-1);
/* prepare the read and write data buffer */ /*
fbuf.fb_dat = calloc(1, FUSEBUFMAXSIZE); * Prepare the read data buffer. We need enough space for the header,
if (fbuf.fb_dat == NULL) { * input struct and any additional data, filenames or the buffer for
* write(2). The minimum buffer size must be large enough for the
* name and path parameters for FUSE_SYMLINK.
*/
if (se->fci.max_write > FUSEBUFMAXSIZE)
bufsize = sizeof(struct fusebuf) + FUSEBUFMAXSIZE;
else if (se->fci.max_write < PATH_MAX + NAME_MAX)
bufsize = sizeof(struct fusebuf) + PATH_MAX + NAME_MAX;
else
bufsize = sizeof(struct fusebuf) + se->fci.max_write;
buf = calloc(1, bufsize);
if (buf == NULL) {
DPERROR(__func__); DPERROR(__func__);
return (-1); return (-1);
} }
bufsize = sizeof(fbuf.fb_hdr) + sizeof(fbuf.FD) + FUSEBUFMAXSIZE;
while (!fuse_session_exited(se)) { while (!fuse_session_exited(se)) {
err = fuse_chan_recv(&ch, buf, bufsize); err = fuse_chan_recv(&ch, buf, bufsize);
if (err == -EINTR || err == -ENODEV) { if (err == -EINTR || err == -ENODEV) {
@@ -117,7 +127,7 @@ fuse_session_loop(struct fuse_session *se)
fuse_session_process(se, buf, bufsize, ch); fuse_session_process(se, buf, bufsize, ch);
} }
free(fbuf.fb_dat); free(buf);
fuse_session_reset(se); fuse_session_reset(se);
return (err == 0 ? 0 : -1); return (err == 0 ? 0 : -1);
@@ -127,20 +137,31 @@ DEF(fuse_session_loop);
static void static void
iprocess_init(fuse_req_t req) iprocess_init(fuse_req_t req)
{ {
struct fusebuf *fbuf = req->fbuf;
struct fuse_session *se = req->se; struct fuse_session *se = req->se;
struct fuse_conn_info fci; struct fuse_conn_info *fci = &se->fci;
struct fuse_init_out out;
uint32_t major = fbuf->in.init.major;
uint32_t minor = fbuf->in.init.minor;
DPRINTF("%-11s", "init"); DPRINTF("%-11s", "init");
DPRINTF("Kernel: %d.%d\t", major, minor);
DPRINTF("libfuse: %d.%d\t", fci->proto_major, fci->proto_minor);
if (se->llops.init) { if (major != fci->proto_major && minor != fci->proto_minor)
memset(&fci, 0, sizeof(fci)); errx(1, "FUSE kernel protocol version mismatch");
fci.proto_minor = FUSE_MINOR_VERSION;
fci.proto_major = FUSE_MAJOR_VERSION;
se->llops.init(se->userdata, &fci); if (se->llops.init)
} se->llops.init(se->userdata, fci);
fuse_reply_err(req, 0); memset(&out, 0, sizeof(out));
out.major = fci->proto_major;
out.minor = fci->proto_minor;
out.max_write = fci->max_write;
DPRINTF("max_write: %u\t", out.max_write);
fuse_reply_buf(req, (const char *)&out, sizeof(out));
se->init = 1; se->init = 1;
} }
@@ -162,7 +183,7 @@ iprocess_lookup(fuse_req_t req)
{ {
struct fusebuf *fbuf = req->fbuf; struct fusebuf *fbuf = req->fbuf;
struct fuse_session *se = req->se; struct fuse_session *se = req->se;
const char *name = fbuf->fb_dat; const char *name = fb_dat(fbuf->hdr);
DPRINTF("%-11s", "lookup"); DPRINTF("%-11s", "lookup");
DPRINTF("inode: %llu\t", fbuf->fb_ino); DPRINTF("inode: %llu\t", fbuf->fb_ino);
@@ -179,17 +200,13 @@ iprocess_getattr(fuse_req_t req)
{ {
struct fusebuf *fbuf = req->fbuf; struct fusebuf *fbuf = req->fbuf;
struct fuse_session *se = req->se; struct fuse_session *se = req->se;
struct fuse_file_info ffi;
DPRINTF("%-11s", "getattr"); DPRINTF("%-11s", "getattr");
DPRINTF("inode: %llu\t", fbuf->fb_ino); DPRINTF("inode: %llu\t", fbuf->fb_ino);
if (se->llops.getattr) { if (se->llops.getattr) {
memset(&ffi, 0, sizeof(ffi)); /* fuse_getattr_in is unused */
ffi.fh = fbuf->fb_io_fd; se->llops.getattr(req, fbuf->fb_ino, NULL);
ffi.fh_old = ffi.fh;
se->llops.getattr(req, fbuf->fb_ino, &ffi);
} else } else
fuse_reply_err(req, ENOSYS); fuse_reply_err(req, ENOSYS);
} }
@@ -200,18 +217,28 @@ iprocess_setattr(fuse_req_t req)
struct fusebuf *fbuf = req->fbuf; struct fusebuf *fbuf = req->fbuf;
struct fuse_session *se = req->se; struct fuse_session *se = req->se;
struct fuse_file_info ffi; struct fuse_file_info ffi;
struct stat *stbuf = &fbuf->fb_attr; struct stat stbuf;
struct fb_io *io = fbtod(fbuf, struct fb_io *); const int flags = fbuf->in.setattr.valid;
DPRINTF("%-11s", "setattr"); DPRINTF("%-11s", "setattr");
DPRINTF("inode: %llu\t", fbuf->fb_ino); DPRINTF("inode: %llu\t", fbuf->fb_ino);
if (se->llops.setattr) { if (se->llops.setattr) {
memset(&ffi, 0, sizeof(ffi)); memset(&ffi, 0, sizeof(ffi));
ffi.fh = fbuf->fb_io_fd; ffi.fh = fbuf->in.setattr.fh;
ffi.fh_old = ffi.fh; ffi.fh_old = ffi.fh;
se->llops.setattr(req, fbuf->fb_ino, stbuf, io->fi_flags, &ffi); memset(&stbuf, 0, sizeof(stbuf));
stbuf.st_size = fbuf->in.setattr.size;
stbuf.st_atime = fbuf->in.setattr.atime;
stbuf.st_mtime = fbuf->in.setattr.mtime;
stbuf.st_atim.tv_nsec = fbuf->in.setattr.atimensec;
stbuf.st_mtim.tv_nsec = fbuf->in.setattr.mtimensec;
stbuf.st_mode = fbuf->in.setattr.mode;
stbuf.st_uid = fbuf->in.setattr.uid;
stbuf.st_gid = fbuf->in.setattr.gid;
se->llops.setattr(req, fbuf->fb_ino, &stbuf, flags, &ffi);
} else } else
fuse_reply_err(req, ENOSYS); fuse_reply_err(req, ENOSYS);
} }
@@ -227,7 +254,7 @@ iprocess_opendir(fuse_req_t req)
DPRINTF("inode: %llu\t", fbuf->fb_ino); DPRINTF("inode: %llu\t", fbuf->fb_ino);
memset(&ffi, 0, sizeof(ffi)); memset(&ffi, 0, sizeof(ffi));
ffi.flags = fbuf->fb_io_flags; ffi.flags = fbuf->in.open.flags;
if (se->llops.opendir) if (se->llops.opendir)
se->llops.opendir(req, fbuf->fb_ino, &ffi); se->llops.opendir(req, fbuf->fb_ino, &ffi);
@@ -244,16 +271,16 @@ iprocess_readdir(fuse_req_t req)
DPRINTF("%-11s", "readdir"); DPRINTF("%-11s", "readdir");
DPRINTF("inode: %llu\t", fbuf->fb_ino); DPRINTF("inode: %llu\t", fbuf->fb_ino);
DPRINTF("size: %lu\t", fbuf->fb_io_len); DPRINTF("size: %u\t", fbuf->in.read.size);
DPRINTF("offset: %llu\t", fbuf->fb_io_off); DPRINTF("offset: %llu\t", fbuf->in.read.offset);
if (se->llops.readdir) { if (se->llops.readdir) {
memset(&ffi, 0, sizeof(ffi)); memset(&ffi, 0, sizeof(ffi));
ffi.fh = fbuf->fb_io_fd; ffi.fh = fbuf->in.read.fh;
ffi.fh_old = ffi.fh; ffi.fh_old = ffi.fh;
se->llops.readdir(req, fbuf->fb_ino, fbuf->fb_io_len, se->llops.readdir(req, fbuf->fb_ino, fbuf->in.read.size,
fbuf->fb_io_off, &ffi); fbuf->in.read.offset, &ffi);
} else } else
fuse_reply_err(req, ENOSYS); fuse_reply_err(req, ENOSYS);
} }
@@ -270,9 +297,9 @@ iprocess_releasedir(fuse_req_t req)
if (se->llops.releasedir) { if (se->llops.releasedir) {
memset(&ffi, 0, sizeof(ffi)); memset(&ffi, 0, sizeof(ffi));
ffi.fh = fbuf->fb_io_fd; ffi.fh = fbuf->in.release.fh;
ffi.fh_old = ffi.fh; ffi.fh_old = ffi.fh;
ffi.flags = fbuf->fb_io_flags; ffi.flags = fbuf->in.release.flags;
se->llops.releasedir(req, fbuf->fb_ino, &ffi); se->llops.releasedir(req, fbuf->fb_ino, &ffi);
} else } else
@@ -284,16 +311,17 @@ iprocess_mkdir(fuse_req_t req)
{ {
struct fusebuf *fbuf = req->fbuf; struct fusebuf *fbuf = req->fbuf;
struct fuse_session *se = req->se; struct fuse_session *se = req->se;
const char *name = fb_dat(fbuf->in.mkdir);
DPRINTF("%-11s", "mkdir"); DPRINTF("%-11s", "mkdir");
DPRINTF("inode: %llu\t", fbuf->fb_ino); DPRINTF("inode: %llu\t", fbuf->fb_ino);
DPRINTF("mode: %#6o\t", fbuf->fb_io_mode); DPRINTF("mode: %#6o\t", fbuf->in.mkdir.mode);
DPRINTF("name: %s\t", fbuf->fb_dat); DPRINTF("name: %s\t", name);
if (se->llops.mkdir) if (se->llops.mkdir) {
se->llops.mkdir(req, fbuf->fb_ino, fbuf->fb_dat, req->ctx.umask = fbuf->in.mkdir.umask;
fbuf->fb_io_mode); se->llops.mkdir(req, fbuf->fb_ino, name, fbuf->in.mkdir.mode);
else } else
fuse_reply_err(req, ENOSYS); fuse_reply_err(req, ENOSYS);
} }
@@ -302,13 +330,14 @@ iprocess_rmdir(fuse_req_t req)
{ {
struct fusebuf *fbuf = req->fbuf; struct fusebuf *fbuf = req->fbuf;
struct fuse_session *se = req->se; struct fuse_session *se = req->se;
const char *name = fb_dat(fbuf->hdr);
DPRINTF("%-11s", "rmdir"); DPRINTF("%-11s", "rmdir");
DPRINTF("inode: %llu\t", fbuf->fb_ino); DPRINTF("inode: %llu\t", fbuf->fb_ino);
DPRINTF("name: %s\t", fbuf->fb_dat); DPRINTF("name: %s\t", name);
if (se->llops.rmdir) if (se->llops.rmdir)
se->llops.rmdir(req, fbuf->fb_ino, fbuf->fb_dat); se->llops.rmdir(req, fbuf->fb_ino, name);
else else
fuse_reply_err(req, ENOSYS); fuse_reply_err(req, ENOSYS);
} }
@@ -318,16 +347,18 @@ iprocess_mknod(fuse_req_t req)
{ {
struct fusebuf *fbuf = req->fbuf; struct fusebuf *fbuf = req->fbuf;
struct fuse_session *se = req->se; struct fuse_session *se = req->se;
const char *name = fb_dat(fbuf->in.mknod);
DPRINTF("%-11s", "mknod"); DPRINTF("%-11s", "mknod");
DPRINTF("inode: %llu\t", fbuf->fb_ino); DPRINTF("inode: %llu\t", fbuf->fb_ino);
DPRINTF("mode: %#6o\t", fbuf->fb_io_mode); DPRINTF("mode: %#6o\t", fbuf->in.mknod.mode);
DPRINTF("name: %s\t", fbuf->fb_dat); DPRINTF("name: %s\t", name);
if (se->llops.mknod) if (se->llops.mknod) {
se->llops.mknod(req, fbuf->fb_ino, fbuf->fb_dat, req->ctx.umask = fbuf->in.mknod.umask;
fbuf->fb_io_mode, fbuf->fb_io_rdev); se->llops.mknod(req, fbuf->fb_ino, name, fbuf->in.mknod.mode,
else fbuf->in.mknod.rdev);
} else
fuse_reply_err(req, ENOSYS); fuse_reply_err(req, ENOSYS);
} }
@@ -342,7 +373,7 @@ iprocess_open(fuse_req_t req)
DPRINTF("inode: %llu\t", fbuf->fb_ino); DPRINTF("inode: %llu\t", fbuf->fb_ino);
memset(&ffi, 0, sizeof(ffi)); memset(&ffi, 0, sizeof(ffi));
ffi.flags = fbuf->fb_io_flags; ffi.flags = fbuf->in.open.flags;
if (se->llops.open) if (se->llops.open)
se->llops.open(req, fbuf->fb_ino, &ffi); se->llops.open(req, fbuf->fb_ino, &ffi);
@@ -359,17 +390,17 @@ iprocess_read(fuse_req_t req)
DPRINTF("%-11s", "read"); DPRINTF("%-11s", "read");
DPRINTF("inode: %llu\t", fbuf->fb_ino); DPRINTF("inode: %llu\t", fbuf->fb_ino);
DPRINTF("size: %lu\t", fbuf->fb_io_len); DPRINTF("size: %u\t", fbuf->in.read.size);
DPRINTF("offset: %llu\t", fbuf->fb_io_off); DPRINTF("offset: %llu\t", fbuf->in.read.offset);
if (se->llops.read) { if (se->llops.read) {
memset(&ffi, 0, sizeof(ffi)); memset(&ffi, 0, sizeof(ffi));
ffi.fh = fbuf->fb_io_fd; ffi.fh = fbuf->in.read.fh;
ffi.fh_old = ffi.fh; ffi.fh_old = ffi.fh;
ffi.flags = fbuf->fb_io_flags; ffi.flags = fbuf->in.read.flags;
se->llops.read(req, fbuf->fb_ino, fbuf->fb_io_len, se->llops.read(req, fbuf->fb_ino, fbuf->in.read.size,
fbuf->fb_io_off, &ffi); fbuf->in.read.offset, &ffi);
} else } else
fuse_reply_err(req, ENOSYS); fuse_reply_err(req, ENOSYS);
} }
@@ -383,17 +414,17 @@ iprocess_write(fuse_req_t req)
DPRINTF("%-11s", "write"); DPRINTF("%-11s", "write");
DPRINTF("inode: %llu\t", fbuf->fb_ino); DPRINTF("inode: %llu\t", fbuf->fb_ino);
DPRINTF("size: %lu\t", fbuf->fb_io_len); DPRINTF("size: %u\t", fbuf->in.write.size);
DPRINTF("offset: %llu\t", fbuf->fb_io_off); DPRINTF("offset: %llu\t", fbuf->in.write.offset);
if (se->llops.write) { if (se->llops.write) {
memset(&ffi, 0, sizeof(ffi)); memset(&ffi, 0, sizeof(ffi));
ffi.fh = fbuf->fb_io_fd; ffi.fh = fbuf->in.write.fh;
ffi.fh_old = ffi.fh; ffi.fh_old = ffi.fh;
ffi.writepage = fbuf->fb_io_flags & 1; // XXX ffi.writepage = fbuf->in.write.flags & 1; // XXX
se->llops.write(req, fbuf->fb_ino, fbuf->fb_dat, se->llops.write(req, fbuf->fb_ino, fb_dat(fbuf->in.write),
fbuf->fb_io_len, fbuf->fb_io_off, &ffi); fbuf->in.write.size, fbuf->in.write.offset, &ffi);
} else } else
fuse_reply_err(req, ENOSYS); fuse_reply_err(req, ENOSYS);
} }
@@ -404,20 +435,18 @@ iprocess_fsync(fuse_req_t req)
struct fusebuf *fbuf = req->fbuf; struct fusebuf *fbuf = req->fbuf;
struct fuse_session *se = req->se; struct fuse_session *se = req->se;
struct fuse_file_info ffi; struct fuse_file_info ffi;
const uint32_t fsync_flags = fbuf->in.fsync.fsync_flags & 1;
DPRINTF("%-11s", "fsync"); DPRINTF("%-11s", "fsync");
DPRINTF("inode: %llu\t", fbuf->fb_ino); DPRINTF("inode: %llu\t", fbuf->fb_ino);
DPRINTF("flags: %u", fsync_flags);
if (se->llops.fsync) { if (se->llops.fsync) {
memset(&ffi, 0, sizeof(ffi)); memset(&ffi, 0, sizeof(ffi));
ffi.fh = fbuf->fb_io_fd; ffi.fh = fbuf->in.fsync.fh;
ffi.fh_old = ffi.fh; ffi.fh_old = ffi.fh;
/* se->llops.fsync(req, fbuf->fb_ino, fsync_flags, &ffi);
* fdatasync(2) is just a wrapper around fsync(2) so datasync
* is always false.
*/
se->llops.fsync(req, fbuf->fb_ino, 0 /* datasync */, &ffi);
} else } else
fuse_reply_err(req, ENOSYS); fuse_reply_err(req, ENOSYS);
} }
@@ -434,7 +463,7 @@ iprocess_flush(fuse_req_t req)
if (se->llops.flush) { if (se->llops.flush) {
memset(&ffi, 0, sizeof(ffi)); memset(&ffi, 0, sizeof(ffi));
ffi.fh = fbuf->fb_io_fd; ffi.fh = fbuf->in.flush.fh;
ffi.fh_old = ffi.fh; ffi.fh_old = ffi.fh;
ffi.flush = 1; ffi.flush = 1;
@@ -455,9 +484,9 @@ iprocess_release(fuse_req_t req)
if (se->llops.release) { if (se->llops.release) {
memset(&ffi, 0, sizeof(ffi)); memset(&ffi, 0, sizeof(ffi));
ffi.fh = fbuf->fb_io_fd; ffi.fh = fbuf->in.release.fh;
ffi.fh_old = ffi.fh; ffi.fh_old = ffi.fh;
ffi.flags = fbuf->fb_io_flags; ffi.flags = fbuf->in.release.flags;
se->llops.release(req, fbuf->fb_ino, &ffi); se->llops.release(req, fbuf->fb_ino, &ffi);
} else } else
@@ -469,14 +498,16 @@ iprocess_forget(fuse_req_t req)
{ {
struct fusebuf *fbuf = req->fbuf; struct fusebuf *fbuf = req->fbuf;
struct fuse_session *se = req->se; struct fuse_session *se = req->se;
const uint64_t nlookup = fbuf->in.forget.nlookup;
DPRINTF("%-11s", "forget"); DPRINTF("%-11s", "forget");
DPRINTF("inode: %llu\t", fbuf->fb_ino); DPRINTF("inode: %llu\t", fbuf->fb_ino);
DPRINTF("nlookup: %llu\t", nlookup);
if (se->llops.forget) if (se->llops.forget)
se->llops.forget(req, fbuf->fb_ino, 1 /* TODO */); se->llops.forget(req, fbuf->fb_ino, nlookup);
else else
fuse_reply_err(req, 0); fuse_reply_none(req);
} }
static void static void
@@ -488,9 +519,9 @@ iprocess_symlink(fuse_req_t req)
const char *name; const char *name;
int len; int len;
name = fbuf->fb_dat; name = fb_dat(fbuf->hdr);
len = strnlen(name, fbuf->fb_len); len = strlen(name);
target = &fbuf->fb_dat[len + 1]; target = &name[len + 1];
DPRINTF("%-11s", "symlink"); DPRINTF("%-11s", "symlink");
DPRINTF("inode: %llu\t", fbuf->fb_ino); DPRINTF("inode: %llu\t", fbuf->fb_ino);
@@ -523,15 +554,16 @@ iprocess_link(fuse_req_t req)
{ {
struct fusebuf *fbuf = req->fbuf; struct fusebuf *fbuf = req->fbuf;
struct fuse_session *se = req->se; struct fuse_session *se = req->se;
const char *name = fbuf->fb_dat; const char *name = fb_dat(fbuf->in.link);
const uint64_t oldnodeid = fbuf->in.link.oldnodeid;
DPRINTF("%-11s", "link"); DPRINTF("%-11s", "link");
DPRINTF("inode: %llu\t", fbuf->fb_ino); DPRINTF("inode: %llu\t", fbuf->fb_ino);
DPRINTF("inode: %llu\t", fbuf->fb_io_ino); DPRINTF("inode: %llu\t", oldnodeid);
DPRINTF("name: %s\t", name); DPRINTF("name: %s\t", name);
if (se->llops.link) if (se->llops.link)
se->llops.link(req, fbuf->fb_io_ino, fbuf->fb_ino, name); se->llops.link(req, oldnodeid, fbuf->fb_ino, name);
else else
fuse_reply_err(req, ENOSYS); fuse_reply_err(req, ENOSYS);
} }
@@ -541,13 +573,14 @@ iprocess_unlink(fuse_req_t req)
{ {
struct fusebuf *fbuf = req->fbuf; struct fusebuf *fbuf = req->fbuf;
struct fuse_session *se = req->se; struct fuse_session *se = req->se;
const char *name = fb_dat(fbuf->hdr);
DPRINTF("%-11s", "unlink"); DPRINTF("%-11s", "unlink");
DPRINTF("inode: %llu\t", fbuf->fb_ino); DPRINTF("inode: %llu\t", fbuf->fb_ino);
DPRINTF("name: %s\t", fbuf->fb_dat); DPRINTF("name: %s\t", name);
if (se->llops.unlink) if (se->llops.unlink)
se->llops.unlink(req, fbuf->fb_ino, fbuf->fb_dat); se->llops.unlink(req, fbuf->fb_ino, name);
else else
fuse_reply_err(req, ENOSYS); fuse_reply_err(req, ENOSYS);
} }
@@ -557,23 +590,23 @@ iprocess_rename(fuse_req_t req)
{ {
struct fusebuf *fbuf = req->fbuf; struct fusebuf *fbuf = req->fbuf;
struct fuse_session *se = req->se; struct fuse_session *se = req->se;
const uint64_t newdir = fbuf->in.rename.newdir;
const char *target; const char *target;
const char *name; const char *name;
int len; int len;
name = fbuf->fb_dat; name = fb_dat(fbuf->in.rename);
len = strnlen(name, fbuf->fb_len); len = strlen(name);
target = &fbuf->fb_dat[len + 1]; target = &name[len + 1];
DPRINTF("%-11s", "rename"); DPRINTF("%-11s", "rename");
DPRINTF("inode: %llu\t", fbuf->fb_ino); DPRINTF("inode: %llu\t", fbuf->fb_ino);
DPRINTF("name: %s\t", name); DPRINTF("name: %s\t", name);
DPRINTF("inode: %llu\t", fbuf->fb_io_ino); DPRINTF("newdir: %llu\t", newdir);
DPRINTF("target: %s\t", target); DPRINTF("target: %s\t", target);
if (se->llops.rename) if (se->llops.rename)
se->llops.rename(req, fbuf->fb_ino, name, fbuf->fb_io_ino, se->llops.rename(req, fbuf->fb_ino, name, newdir, target);
target);
else else
fuse_reply_err(req, ENOSYS); fuse_reply_err(req, ENOSYS);
} }
@@ -607,86 +640,88 @@ fuse_session_process(struct fuse_session *se, const char *buf, size_t len,
req.ctx.uid = fbuf->fb_uid; req.ctx.uid = fbuf->fb_uid;
req.ctx.gid = fbuf->fb_gid; req.ctx.gid = fbuf->fb_gid;
req.ctx.pid = fbuf->fb_tid; req.ctx.pid = fbuf->fb_tid;
req.ctx.umask = fbuf->fb_umask;
/* later set in create, mknod, mkdir */
req.ctx.umask = 0;
/* need to at least have the header for the next check */ /* need to at least have the header for the next check */
if (len < sizeof(fbuf->fb_hdr)) if (len < sizeof(fbuf->hdr))
return; return;
if (len < sizeof(fbuf->fb_hdr) + sizeof(fbuf->FD) + fbuf->fb_len) if (len < fbuf->hdr.len)
return; return;
switch (fbuf->fb_type) { switch (fbuf->fb_type) {
case FBT_INIT: case FUSE_INIT:
iprocess_init(&req); iprocess_init(&req);
break; break;
case FBT_DESTROY: case FUSE_DESTROY:
iprocess_destroy(&req); iprocess_destroy(&req);
break; break;
case FBT_LOOKUP: case FUSE_LOOKUP:
iprocess_lookup(&req); iprocess_lookup(&req);
break; break;
case FBT_GETATTR: case FUSE_GETATTR:
iprocess_getattr(&req); iprocess_getattr(&req);
break; break;
case FBT_SETATTR: case FUSE_SETATTR:
iprocess_setattr(&req); iprocess_setattr(&req);
break; break;
case FBT_OPENDIR: case FUSE_OPENDIR:
iprocess_opendir(&req); iprocess_opendir(&req);
break; break;
case FBT_READDIR: case FUSE_READDIR:
iprocess_readdir(&req); iprocess_readdir(&req);
break; break;
case FBT_RELEASEDIR: case FUSE_RELEASEDIR:
iprocess_releasedir(&req); iprocess_releasedir(&req);
break; break;
case FBT_MKDIR: case FUSE_MKDIR:
iprocess_mkdir(&req); iprocess_mkdir(&req);
break; break;
case FBT_RMDIR: case FUSE_RMDIR:
iprocess_rmdir(&req); iprocess_rmdir(&req);
break; break;
case FBT_MKNOD: case FUSE_MKNOD:
iprocess_mknod(&req); iprocess_mknod(&req);
break; break;
case FBT_OPEN: case FUSE_OPEN:
iprocess_open(&req); iprocess_open(&req);
break; break;
case FBT_READ: case FUSE_READ:
iprocess_read(&req); iprocess_read(&req);
break; break;
case FBT_WRITE: case FUSE_WRITE:
iprocess_write(&req); iprocess_write(&req);
break; break;
case FBT_FSYNC: case FUSE_FSYNC:
iprocess_fsync(&req); iprocess_fsync(&req);
break; break;
case FBT_FLUSH: case FUSE_FLUSH:
iprocess_flush(&req); iprocess_flush(&req);
break; break;
case FBT_RELEASE: case FUSE_RELEASE:
iprocess_release(&req); iprocess_release(&req);
break; break;
case FBT_RECLAIM: case FUSE_FORGET:
iprocess_forget(&req); iprocess_forget(&req);
break; break;
case FBT_SYMLINK: case FUSE_SYMLINK:
iprocess_symlink(&req); iprocess_symlink(&req);
break; break;
case FBT_READLINK: case FUSE_READLINK:
iprocess_readlink(&req); iprocess_readlink(&req);
break; break;
case FBT_LINK: case FUSE_LINK:
iprocess_link(&req); iprocess_link(&req);
break; break;
case FBT_UNLINK: case FUSE_UNLINK:
iprocess_unlink(&req); iprocess_unlink(&req);
break; break;
case FBT_RENAME: case FUSE_RENAME:
iprocess_rename(&req); iprocess_rename(&req);
break; break;
case FBT_STATFS: case FUSE_STATFS:
iprocess_statfs(&req); iprocess_statfs(&req);
break; break;
default: default:
+5 -4
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: fuse_subr.c,v 1.13 2026/01/29 06:04:27 helg Exp $ */ /* $OpenBSD: fuse_subr.c,v 1.14 2026/06/17 13:29:01 helg Exp $ */
/* /*
* Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com> * Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com>
* *
@@ -69,13 +69,14 @@ ref_vn(struct fuse_vnode *vn)
} }
void void
unref_vn(struct fuse *f, struct fuse_vnode *vn) unref_vn(struct fuse *f, struct fuse_vnode *vn, const uint64_t nlookup)
{ {
if (--vn->ref == 0) { vn->ref -= nlookup;
if (vn->ref == 0) {
tree_pop(&f->vnode_tree, vn->ino); tree_pop(&f->vnode_tree, vn->ino);
remove_vnode_from_name_tree(f, vn); remove_vnode_from_name_tree(f, vn);
if (vn->parent != NULL) if (vn->parent != NULL)
unref_vn(f, vn->parent); unref_vn(f, vn->parent, 1);
free(vn); free(vn);
} }
} }
+97 -55
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: fuse_device.c,v 1.49 2026/01/22 11:53:31 helg Exp $ */ /* $OpenBSD: fuse_device.c,v 1.50 2026/06/17 13:29:01 helg Exp $ */
/* /*
* Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com> * Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com>
* *
@@ -303,7 +303,6 @@ fuseread(dev_t dev, struct uio *uio, int ioflag)
{ {
struct fuse_d *fd; struct fuse_d *fd;
struct fusebuf *fbuf; struct fusebuf *fbuf;
struct fb_hdr hdr;
int error = 0; int error = 0;
fd = fuse_lookup(minor(dev)); fd = fuse_lookup(minor(dev));
@@ -342,20 +341,16 @@ fuseread(dev_t dev, struct uio *uio, int ioflag)
} }
/* We get the whole fusebuf or nothing */ /* We get the whole fusebuf or nothing */
if (uio->uio_resid < sizeof(fbuf->fb_hdr) + sizeof(fbuf->FD) + if (uio->uio_resid < sizeof(fbuf->hdr) + fbuf->op_in_len +
fbuf->fb_len) { fbuf->fb_len) {
error = EINVAL; error = EINVAL;
goto end; goto end;
} }
/* Do not send kernel pointers */ error = uiomove(&fbuf->hdr, sizeof(fbuf->hdr), uio);
memcpy(&hdr.fh_next, &fbuf->fb_next, sizeof(fbuf->fb_next));
memset(&fbuf->fb_next, 0, sizeof(fbuf->fb_next));
error = uiomove(&fbuf->fb_hdr, sizeof(fbuf->fb_hdr), uio);
if (error) if (error)
goto end; goto end;
error = uiomove(&fbuf->FD, sizeof(fbuf->FD), uio); error = uiomove(&fbuf->op, fbuf->op_in_len, uio);
if (error) if (error)
goto end; goto end;
if (fbuf->fb_len > 0) { if (fbuf->fb_len > 0) {
@@ -367,8 +362,6 @@ fuseread(dev_t dev, struct uio *uio, int ioflag)
#ifdef FUSE_DEBUG #ifdef FUSE_DEBUG
fuse_dump_buff((char *)fbuf, sizeof(struct fusebuf)); fuse_dump_buff((char *)fbuf, sizeof(struct fusebuf));
#endif #endif
/* Restore kernel pointers */
memcpy(&fbuf->fb_next, &hdr.fh_next, sizeof(fbuf->fb_next));
free(fbuf->fb_dat, M_FUSEFS, fbuf->fb_len); free(fbuf->fb_dat, M_FUSEFS, fbuf->fb_len);
fbuf->fb_dat = NULL; fbuf->fb_dat = NULL;
@@ -376,6 +369,13 @@ fuseread(dev_t dev, struct uio *uio, int ioflag)
/* Move the fbuf to the wait queue */ /* Move the fbuf to the wait queue */
SIMPLEQ_REMOVE_HEAD(&fd->fd_fbufs_in, fb_next); SIMPLEQ_REMOVE_HEAD(&fd->fd_fbufs_in, fb_next);
stat_fbufs_in--; stat_fbufs_in--;
/* FUSE_FORGET has no response */
if (fbuf->fb_type == FUSE_FORGET) {
fb_delete(fbuf);
goto end;
}
SIMPLEQ_INSERT_TAIL(&fd->fd_fbufs_wait, fbuf, fb_next); SIMPLEQ_INSERT_TAIL(&fd->fd_fbufs_wait, fbuf, fb_next);
stat_fbufs_wait++; stat_fbufs_wait++;
@@ -391,37 +391,35 @@ fusewrite(dev_t dev, struct uio *uio, int ioflag)
struct fusebuf *lastfbuf; struct fusebuf *lastfbuf;
struct fuse_d *fd; struct fuse_d *fd;
struct fusebuf *fbuf; struct fusebuf *fbuf;
struct fb_hdr hdr; struct fuse_out_header hdr;
int error = 0; int error = 0;
fd = fuse_lookup(minor(dev)); fd = fuse_lookup(minor(dev));
if (fd == NULL) if (fd == NULL)
return (ENXIO); return (ENODEV);
/* Check for sanity - must receive more than just the header */ /* Check for sanity - must receive at least the header */
if (uio->uio_resid <= sizeof(hdr)) { if (uio->uio_resid < sizeof(hdr)) {
error = EINVAL; error = EINVAL;
goto out; goto out;
} }
/* Read the header */
if ((error = uiomove(&hdr, sizeof(hdr), uio)) != 0) if ((error = uiomove(&hdr, sizeof(hdr), uio)) != 0)
goto out; goto out;
/* Check for sanity */ /*
if (hdr.fh_len > FUSEBUFMAXSIZE) { * A unique value of zero means daemon is notifying us and hdr.error
error = EINVAL; * contains notification type. Currently unsupported.
goto out; */
} if (hdr.unique == 0) {
error = 0;
/* We get the whole fusebuf or nothing */
if (uio->uio_resid != sizeof(fbuf->FD) + hdr.fh_len) {
error = EINVAL;
goto out; goto out;
} }
/* looking for uuid in fd_fbufs_wait */ /* looking for uuid in fd_fbufs_wait */
SIMPLEQ_FOREACH(fbuf, &fd->fd_fbufs_wait, fb_next) { SIMPLEQ_FOREACH(fbuf, &fd->fd_fbufs_wait, fb_next) {
if (fbuf->fb_uuid == hdr.fh_uuid) if (fbuf->fb_uuid == hdr.unique)
break; break;
lastfbuf = fbuf; lastfbuf = fbuf;
@@ -432,49 +430,93 @@ fusewrite(dev_t dev, struct uio *uio, int ioflag)
} }
/* Update fb_hdr */ /* Update fb_hdr */
fbuf->fb_len = hdr.fh_len; fbuf->fb_err = -hdr.error;
fbuf->fb_err = hdr.fh_err;
fbuf->fb_ino = hdr.fh_ino;
/* Check for corrupted fbufs */ /* Don't expect out struct or data if there was an error */
if ((fbuf->fb_len && fbuf->fb_err) || fbuf->fb_len > fbuf->fb_io_len || if (fbuf->fb_err) {
SIMPLEQ_EMPTY(&fd->fd_fbufs_wait)) { if (uio->uio_resid > 0) {
printf("fuse: dropping corrupted fusebuf: %zu: %zu: %d\n", error = EINVAL;
fbuf->fb_io_len, fbuf->fb_len, fbuf->fb_err); fbuf->fb_err = EIO;
}
goto end;
}
/* get operation output */
if (fbuf->op_out_len > 0) {
if ((error = uiomove(&fbuf->op, fbuf->op_out_len, uio)) != 0) {
fbuf->fb_err = error;
goto end;
}
}
/* Calculate the length of the data buffer to expect */
if (fbuf->op_out_buf) {
fbuf->fb_len = hdr.len - sizeof(hdr) - fbuf->op_out_len;
if (fbuf->fb_len > fd->fd_fmp->max_read || fbuf->fb_len < 0) {
printf("fuse: invalid fusebuf read size: %llu "
"opcode=%d\n", fbuf->fb_len, fbuf->fb_type);
error = EINVAL;
fbuf->fb_err = EIO;
goto end;
}
} else
fbuf->fb_len = 0;
/* validate remaining data */
if (uio->uio_resid != fbuf->fb_len) {
error = EINVAL; error = EINVAL;
fbuf->fb_err = EIO; fbuf->fb_err = EIO;
goto end; goto end;
} }
/* Get the missing data from the fbuf */
error = uiomove(&fbuf->FD, sizeof(fbuf->FD), uio);
if (error)
return error;
fbuf->fb_dat = NULL;
if (fbuf->fb_len > 0) { if (fbuf->fb_len > 0) {
fbuf->fb_dat = malloc(fbuf->fb_len, M_FUSEFS, fbuf->fb_dat = malloc(fbuf->fb_len, M_FUSEFS, M_WAITOK);
M_WAITOK | M_ZERO); if ((error = uiomove(fbuf->fb_dat, fbuf->fb_len, uio)) != 0) {
error = uiomove(fbuf->fb_dat, fbuf->fb_len, uio);
if (error) {
free(fbuf->fb_dat, M_FUSEFS, fbuf->fb_len); free(fbuf->fb_dat, M_FUSEFS, fbuf->fb_len);
fbuf->fb_dat = NULL; fbuf->fb_dat = NULL;
fbuf->fb_err = error;
goto end; goto end;
} }
} }
#ifdef FUSE_DEBUG if (fbuf->fb_type == FUSE_INIT && fbuf->fb_err == 0) {
fuse_dump_buff((char *)fbuf, sizeof(struct fusebuf)); /*
#endif * We don't support userspace with a smaller major version and
* it's up to userspace implementations to fall back to our
switch (fbuf->fb_type) { * version if they are capable of a later version.
case FBT_INIT: */
if (fbuf->op.out.init.major != FUSE_KERNEL_VERSION) {
printf("fuse: unsupported major version: %d.%d\n",
fbuf->op.out.init.major, fbuf->op.out.init.minor);
error = EINVAL;
goto end;
}
/*
* If the major versions match then both shall use the smallest
* of the two minor versions for communication. 7.9 is the
* smallest version less than what we support where the ABI has
* not changed. Supporting an earlier version would require
* conditional handling of some FUSE input arguments. If the
* daemon supports a later version then it must fall back to
* ours.
*/
if (fbuf->op.out.init.minor < 9) {
printf("fuse: unsupported minor version: %d.%d\n",
fbuf->op.out.init.major, fbuf->op.out.init.minor);
error = EINVAL;
goto end;
}
/*
* max_write determines the size of buffer to send to the file
* system daemon when writing so ensure that it's sane.
*/
fd->fd_fmp->max_write = MIN(fbuf->op.out.init.max_write,
FUSEBUFMAXSIZE);
if (fd->fd_fmp->max_write == 0)
fd->fd_fmp->max_write = FUSEBUFMAXSIZE;
fd->fd_fmp->sess_init = 1; fd->fd_fmp->sess_init = 1;
break ; }
case FBT_DESTROY:
fd->fd_fmp = NULL;
break ;
}
end: end:
/* Remove the fbuf from the wait queue */ /* Remove the fbuf from the wait queue */
if (fbuf == SIMPLEQ_FIRST(&fd->fd_fbufs_wait)) if (fbuf == SIMPLEQ_FIRST(&fd->fd_fbufs_wait))
@@ -488,7 +530,7 @@ end:
* FBT_INIT doesn't expect a response. Otherwise let the VFS * FBT_INIT doesn't expect a response. Otherwise let the VFS
* syscall that is waiting on this fbuf know the reponse is ready. * syscall that is waiting on this fbuf know the reponse is ready.
*/ */
if (fbuf->fb_type == FBT_INIT) if (fbuf->fb_type == FUSE_INIT)
fb_delete(fbuf); fb_delete(fbuf);
else else
wakeup(fbuf); wakeup(fbuf);
+8 -8
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: fuse_file.c,v 1.10 2024/10/31 13:55:21 claudio Exp $ */ /* $OpenBSD: fuse_file.c,v 1.11 2026/06/17 13:29:01 helg Exp $ */
/* /*
* Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com> * Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com>
* *
@@ -36,8 +36,8 @@ fusefs_file_open(struct fusefs_mnt *fmp, struct fusefs_node *ip,
return (0); return (0);
fbuf = fb_setup(0, ip->i_number, fbuf = fb_setup(0, ip->i_number,
((isdir) ? FBT_OPENDIR : FBT_OPEN), p); ((isdir) ? FUSE_OPENDIR : FUSE_OPEN), p);
fbuf->fb_io_flags = flags; fbuf->op.in.open.flags = flags;
error = fb_queue(fmp->dev, fbuf); error = fb_queue(fmp->dev, fbuf);
if (error) { if (error) {
@@ -45,7 +45,7 @@ fusefs_file_open(struct fusefs_mnt *fmp, struct fusefs_node *ip,
return (error); return (error);
} }
ip->fufh[fufh_type].fh_id = fbuf->fb_io_fd; ip->fufh[fufh_type].fh_id = fbuf->op.out.open.fh;
ip->fufh[fufh_type].fh_type = fufh_type; ip->fufh[fufh_type].fh_type = fufh_type;
fb_delete(fbuf); fb_delete(fbuf);
@@ -61,13 +61,13 @@ fusefs_file_close(struct fusefs_mnt *fmp, struct fusefs_node * ip,
if (fmp->sess_init) { if (fmp->sess_init) {
fbuf = fb_setup(0, ip->i_number, fbuf = fb_setup(0, ip->i_number,
((isdir) ? FBT_RELEASEDIR : FBT_RELEASE), p); ((isdir) ? FUSE_RELEASEDIR : FUSE_RELEASE), p);
fbuf->fb_io_fd = ip->fufh[fufh_type].fh_id; fbuf->op.in.release.fh = ip->fufh[fufh_type].fh_id;
fbuf->fb_io_flags = flags; fbuf->op.in.release.flags = flags;
error = fb_queue(fmp->dev, fbuf); error = fb_queue(fmp->dev, fbuf);
if (error && (error != ENOSYS)) if (error && (error != ENOSYS))
printf("fusefs: file error %d\n", error); printf("fusefs: file close error: %d\n", error);
fb_delete(fbuf); fb_delete(fbuf);
} }
+13 -9
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: fuse_lookup.c,v 1.23 2026/01/22 11:53:31 helg Exp $ */ /* $OpenBSD: fuse_lookup.c,v 1.24 2026/06/17 13:29:01 helg Exp $ */
/* /*
* Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com> * Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com>
* *
@@ -82,7 +82,7 @@ fusefs_lookup(void *v)
/* got a real entry */ /* got a real entry */
fbuf = fb_setup(cnp->cn_namelen + 1, dp->i_number, fbuf = fb_setup(cnp->cn_namelen + 1, dp->i_number,
FBT_LOOKUP, p); FUSE_LOOKUP, p);
memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen); memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen);
fbuf->fb_dat[cnp->cn_namelen] = '\0'; fbuf->fb_dat[cnp->cn_namelen] = '\0';
@@ -119,8 +119,8 @@ fusefs_lookup(void *v)
return (ENOENT); return (ENOENT);
} }
nid = fbuf->fb_ino; nid = fbuf->op.out.entry.nodeid;
nvtype = IFTOVT(fbuf->fb_attr.st_mode); nvtype = IFTOVT(fbuf->op.out.entry.attr.mode);
fb_delete(fbuf); fb_delete(fbuf);
/* /*
@@ -159,6 +159,7 @@ fusefs_lookup(void *v)
goto reclaim; goto reclaim;
tdp->v_type = nvtype; tdp->v_type = nvtype;
VTOI(tdp)->i_parent_cache = dp->i_number;
*vpp = tdp; *vpp = tdp;
cnp->cn_flags |= SAVENAME; cnp->cn_flags |= SAVENAME;
@@ -193,6 +194,9 @@ fusefs_lookup(void *v)
} }
*vpp = tdp; *vpp = tdp;
/* Didn't actually make a call but vget increments lookup */
VTOI(tdp)->nlookup--;
} else if (nid == dp->i_number) { } else if (nid == dp->i_number) {
vref(vdp); vref(vdp);
*vpp = vdp; *vpp = vdp;
@@ -203,6 +207,7 @@ fusefs_lookup(void *v)
goto reclaim; goto reclaim;
tdp->v_type = nvtype; tdp->v_type = nvtype;
VTOI(tdp)->i_parent_cache = dp->i_number;
/* /*
* Cache the parent if it's a directory so that we can resolve * Cache the parent if it's a directory so that we can resolve
@@ -222,11 +227,10 @@ fusefs_lookup(void *v)
return (error); return (error);
reclaim: reclaim:
if (nid != dp->i_number && nid != FUSE_ROOTINO) { if (nid != dp->i_number && nid != FUSE_ROOT_ID) {
fbuf = fb_setup(0, nid, FBT_RECLAIM, p); fbuf = fb_setup(0, nid, FUSE_FORGET, p);
if (fb_queue(fmp->dev, fbuf)) fbuf->op.in.forget.nlookup = 1;
printf("fusefs: libfuse vnode reclaim failed\n"); fuse_device_queue_fbuf(fmp->dev, fbuf); /* no response */
fb_delete(fbuf);
} }
return (error); return (error);
} }
+42 -19
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: fuse_vfsops.c,v 1.49 2025/09/20 13:53:36 mpi Exp $ */ /* $OpenBSD: fuse_vfsops.c,v 1.50 2026/06/17 13:29:01 helg Exp $ */
/* /*
* Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com> * Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com>
* *
@@ -68,7 +68,7 @@ const struct vfsops fusefs_vfsops = {
struct pool fusefs_fbuf_pool; struct pool fusefs_fbuf_pool;
#define PENDING 2 /* FBT_INIT reply not yet received */ #define PENDING 2 /* FUSE_INIT reply not yet received */
int int
fusefs_mount(struct mount *mp, const char *path, void *data, fusefs_mount(struct mount *mp, const char *path, void *data,
@@ -111,6 +111,12 @@ fusefs_mount(struct mount *mp, const char *path, void *data,
else else
fmp->max_read = FUSEBUFMAXSIZE; fmp->max_read = FUSEBUFMAXSIZE;
/*
* Initialise to a safe value just to be sure. This will be
* overwritten when the file system responds to FUSE_INIT.
*/
fmp->max_write = BLKDEV_IOSIZE;
fmp->allow_other = args->allow_other; fmp->allow_other = args->allow_other;
mp->mnt_data = fmp; mp->mnt_data = fmp;
@@ -124,7 +130,11 @@ fusefs_mount(struct mount *mp, const char *path, void *data,
strlcpy(mp->mnt_stat.f_mntfromspec, "fusefs", MNAMELEN); strlcpy(mp->mnt_stat.f_mntfromspec, "fusefs", MNAMELEN);
fuse_device_set_fmp(fmp, 1); fuse_device_set_fmp(fmp, 1);
fbuf = fb_setup(0, 0, FBT_INIT, p); fbuf = fb_setup(0, 0, FUSE_INIT, p);
fbuf->op.in.init.major = FUSE_KERNEL_VERSION;
fbuf->op.in.init.minor = FUSE_KERNEL_MINOR_VERSION;
fbuf->op.in.init.max_readahead = 0;
fbuf->op.in.init.flags = 0; /* OpenBSD supports nothing... */
/* cannot tsleep on mount */ /* cannot tsleep on mount */
fuse_device_queue_fbuf(fmp->dev, fbuf); fuse_device_queue_fbuf(fmp->dev, fbuf);
@@ -157,7 +167,7 @@ fusefs_unmount(struct mount *mp, int mntflags, struct proc *p)
return (error); return (error);
if (fmp->sess_init && fmp->sess_init != PENDING) { if (fmp->sess_init && fmp->sess_init != PENDING) {
fbuf = fb_setup(0, 0, FBT_DESTROY, p); fbuf = fb_setup(0, 0, FUSE_DESTROY, p);
error = fb_queue(fmp->dev, fbuf); error = fb_queue(fmp->dev, fbuf);
@@ -182,7 +192,7 @@ fusefs_root(struct mount *mp, struct vnode **vpp)
struct vnode *nvp; struct vnode *nvp;
int error; int error;
if ((error = VFS_VGET(mp, FUSE_ROOTINO, &nvp)) != 0) if ((error = VFS_VGET(mp, FUSE_ROOT_ID, &nvp)) != 0)
return (error); return (error);
nvp->v_type = VDIR; nvp->v_type = VDIR;
@@ -214,10 +224,10 @@ fusefs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p)
copy_statfs_info(sbp, mp); copy_statfs_info(sbp, mp);
/* /*
* Both FBT_INIT and FBT_STATFS are sent to the FUSE file system * Both FUSE_INIT and FUSE_STATFS are sent to the FUSE file system
* daemon when it is mounted. However, the daemon is the process * daemon when it is mounted. However, the daemon is the process
* that called mount(2) so to prevent a deadlock return dummy * that called mount(2) so to prevent a deadlock return dummy
* values until the response to FBT_INIT init is received. All * values until the response to FUSE_INIT init is received. All
* other VFS syscalls are queued. * other VFS syscalls are queued.
*/ */
if (!fmp->sess_init || fmp->sess_init == PENDING) { if (!fmp->sess_init || fmp->sess_init == PENDING) {
@@ -231,7 +241,7 @@ fusefs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p)
sbp->f_iosize = 0; sbp->f_iosize = 0;
sbp->f_namemax = 0; sbp->f_namemax = 0;
} else { } else {
fbuf = fb_setup(0, FUSE_ROOTINO, FBT_STATFS, p); fbuf = fb_setup(0, FUSE_ROOT_ID, FUSE_STATFS, p);
error = fb_queue(fmp->dev, fbuf); error = fb_queue(fmp->dev, fbuf);
@@ -240,15 +250,25 @@ fusefs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p)
return (error); return (error);
} }
sbp->f_bavail = fbuf->fb_stat.f_bavail; sbp->f_blocks = fbuf->op.out.statfs.st.blocks;
sbp->f_bfree = fbuf->fb_stat.f_bfree; sbp->f_bfree = fbuf->op.out.statfs.st.bfree;
sbp->f_blocks = fbuf->fb_stat.f_blocks; sbp->f_bavail = fbuf->op.out.statfs.st.bavail;
sbp->f_files = fbuf->fb_stat.f_files; sbp->f_files = fbuf->op.out.statfs.st.files;
sbp->f_ffree = fbuf->fb_stat.f_ffree; sbp->f_ffree = fbuf->op.out.statfs.st.ffree;
sbp->f_favail = fbuf->fb_stat.f_favail; sbp->f_favail = fbuf->op.out.statfs.st.ffree;
sbp->f_bsize = fbuf->fb_stat.f_frsize; sbp->f_bsize = fbuf->op.out.statfs.st.frsize;
sbp->f_iosize = fbuf->fb_stat.f_bsize; sbp->f_namemax = fbuf->op.out.statfs.st.namelen;
sbp->f_namemax = fbuf->fb_stat.f_namemax; sbp->f_iosize = fbuf->op.out.statfs.st.bsize;
/*
* Programs use this to allocate an i/o buffer so ensure it's
* sane.
*
* XXX Should this be larger?
*/
if (sbp->f_iosize > BLKDEV_IOSIZE)
sbp->f_iosize = BLKDEV_IOSIZE;
fb_delete(fbuf); fb_delete(fbuf);
} }
@@ -276,8 +296,10 @@ retry:
/* /*
* check if vnode is in hash. * check if vnode is in hash.
*/ */
if ((*vpp = fuse_ihashget(fmp->dev, ino)) != NULL) if ((*vpp = fuse_ihashget(fmp->dev, ino)) != NULL) {
VTOI(*vpp)->nlookup++;
return (0); return (0);
}
/* /*
* if not create it * if not create it
@@ -295,6 +317,7 @@ retry:
ip->i_vnode = nvp; ip->i_vnode = nvp;
ip->i_dev = fmp->dev; ip->i_dev = fmp->dev;
ip->i_number = ino; ip->i_number = ino;
ip->nlookup = 1;
for (i = 0; i < FUFH_MAXTYPE; i++) for (i = 0; i < FUFH_MAXTYPE; i++)
ip->fufh[i].fh_type = FUFH_INVALID; ip->fufh[i].fh_type = FUFH_INVALID;
@@ -311,7 +334,7 @@ retry:
ip->i_ump = fmp; ip->i_ump = fmp;
if (ino == FUSE_ROOTINO) if (ino == FUSE_ROOT_ID)
nvp->v_flag |= VROOT; nvp->v_flag |= VROOT;
else { else {
/* /*
+215 -135
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: fuse_vnops.c,v 1.76 2026/01/29 06:04:27 helg Exp $ */ /* $OpenBSD: fuse_vnops.c,v 1.77 2026/06/17 13:29:01 helg Exp $ */
/* /*
* Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com> * Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com>
* *
@@ -20,6 +20,7 @@
#include <sys/dirent.h> #include <sys/dirent.h>
#include <sys/fcntl.h> #include <sys/fcntl.h>
#include <sys/file.h> #include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/lockf.h> #include <sys/lockf.h>
#include <sys/malloc.h> #include <sys/malloc.h>
#include <sys/mount.h> #include <sys/mount.h>
@@ -329,8 +330,8 @@ fusefs_close(void *v)
if (ip->fufh[fufh_type].fh_type == FUFH_INVALID) if (ip->fufh[fufh_type].fh_type == FUFH_INVALID)
return (EBADF); return (EBADF);
fbuf = fb_setup(0, ip->i_number, FBT_FLUSH, ap->a_p); fbuf = fb_setup(0, ip->i_number, FUSE_FLUSH, ap->a_p);
fbuf->fb_io_fd = ip->fufh[fufh_type].fh_id; fbuf->op.in.flush.fh = ip->fufh[fufh_type].fh_id;
error = fb_queue(fmp->dev, fbuf); error = fb_queue(fmp->dev, fbuf);
fb_delete(fbuf); fb_delete(fbuf);
if (error == ENOSYS) { if (error == ENOSYS) {
@@ -405,7 +406,7 @@ fusefs_getattr(void *v)
struct ucred *cred = p->p_ucred; struct ucred *cred = p->p_ucred;
struct fusefs_node *ip; struct fusefs_node *ip;
struct fusebuf *fbuf; struct fusebuf *fbuf;
struct stat *st; struct fuse_attr *st;
int error = 0; int error = 0;
ip = VTOI(vp); ip = VTOI(vp);
@@ -423,7 +424,7 @@ fusefs_getattr(void *v)
vap->va_mode = S_IRUSR | S_IXUSR; vap->va_mode = S_IRUSR | S_IXUSR;
else else
vap->va_mode = S_IRWXU; vap->va_mode = S_IRWXU;
vap->va_nlink = 1; vap->va_nlink = 2;
vap->va_uid = fmp->mp->mnt_stat.f_owner; vap->va_uid = fmp->mp->mnt_stat.f_owner;
vap->va_gid = fmp->mp->mnt_stat.f_owner; vap->va_gid = fmp->mp->mnt_stat.f_owner;
vap->va_fsid = fmp->mp->mnt_stat.f_fsid.val[0]; vap->va_fsid = fmp->mp->mnt_stat.f_fsid.val[0];
@@ -441,7 +442,7 @@ fusefs_getattr(void *v)
if (!fmp->sess_init) if (!fmp->sess_init)
return (ENXIO); return (ENXIO);
fbuf = fb_setup(0, ip->i_number, FBT_GETATTR, p); fbuf = fb_setup(0, ip->i_number, FUSE_GETATTR, p);
error = fb_queue(fmp->dev, fbuf); error = fb_queue(fmp->dev, fbuf);
if (error) { if (error) {
@@ -449,31 +450,36 @@ fusefs_getattr(void *v)
return (error); return (error);
} }
st = &fbuf->fb_attr; st = &fbuf->op.out.attr.attr;
/* opendir(3) expects blocksize to be greater than zero. */ /* opendir(3) expects blocksize to be greater than zero. */
if (st->st_blksize == 0) if (st->blksize == 0)
st->st_blksize = S_BLKSIZE; st->blksize = S_BLKSIZE;
else if (st->blksize > BLKDEV_IOSIZE)
st->blksize = BLKDEV_IOSIZE;
/* calculate blocks of held disk space if fs didn't do it */ /* calculate blocks of held disk space if fs didn't do it */
if (st->st_blocks == 0 && st->st_size > 0) if (st->blocks == 0 && st->size > 0)
st->st_blocks = (st->st_size + S_BLKSIZE - 1) / S_BLKSIZE; st->blocks = (st->size + S_BLKSIZE - 1) / S_BLKSIZE;
memset(vap, 0, sizeof(*vap)); memset(vap, 0, sizeof(*vap));
vap->va_type = IFTOVT(st->st_mode); vap->va_type = IFTOVT(st->mode);
vap->va_mode = st->st_mode & ~S_IFMT; vap->va_mode = st->mode & ~S_IFMT;
vap->va_nlink = st->st_nlink; vap->va_nlink = st->nlink;
vap->va_uid = st->st_uid; vap->va_uid = st->uid;
vap->va_gid = st->st_gid; vap->va_gid = st->gid;
vap->va_fsid = fmp->mp->mnt_stat.f_fsid.val[0]; vap->va_fsid = fmp->mp->mnt_stat.f_fsid.val[0];
vap->va_fileid = st->st_ino; vap->va_fileid = st->ino;
vap->va_size = st->st_size; vap->va_size = st->size;
vap->va_blocksize = st->st_blksize; vap->va_blocksize = st->blksize;
vap->va_atime = st->st_atim; vap->va_atime.tv_sec = st->atime;
vap->va_mtime = st->st_mtim; vap->va_atime.tv_nsec = st->atimensec;
vap->va_ctime = st->st_ctim; vap->va_mtime.tv_sec = st->mtime;
vap->va_rdev = st->st_rdev; vap->va_mtime.tv_nsec = st->mtimensec;
vap->va_bytes = st->st_blocks * S_BLKSIZE; vap->va_ctime.tv_sec = st->ctime;
vap->va_ctime.tv_nsec = st->ctimensec;
vap->va_rdev = st->rdev;
vap->va_bytes = st->blocks * S_BLKSIZE;
fb_delete(fbuf); fb_delete(fbuf);
return (error); return (error);
@@ -490,7 +496,6 @@ fusefs_setattr(void *v)
struct proc *p = ap->a_p; struct proc *p = ap->a_p;
struct fusefs_mnt *fmp; struct fusefs_mnt *fmp;
struct fusebuf *fbuf; struct fusebuf *fbuf;
struct fb_io *io;
int error = 0; int error = 0;
fmp = (struct fusefs_mnt *)ip->i_ump; fmp = (struct fusefs_mnt *)ip->i_ump;
@@ -516,17 +521,16 @@ fusefs_setattr(void *v)
if (fmp->undef_op & UNDEF_SETATTR) if (fmp->undef_op & UNDEF_SETATTR)
return (ENOSYS); return (ENOSYS);
fbuf = fb_setup(sizeof(*io), ip->i_number, FBT_SETATTR, p); fbuf = fb_setup(0, ip->i_number, FUSE_SETATTR, p);
io = fbtod(fbuf, struct fb_io *); fbuf->op.in.setattr.valid = 0;
io->fi_flags = 0;
if (vap->va_uid != (uid_t)VNOVAL) { if (vap->va_uid != (uid_t)VNOVAL) {
if (vp->v_mount->mnt_flag & MNT_RDONLY) { if (vp->v_mount->mnt_flag & MNT_RDONLY) {
error = EROFS; error = EROFS;
goto out; goto out;
} }
fbuf->fb_attr.st_uid = vap->va_uid; fbuf->op.in.setattr.uid |= vap->va_uid;
io->fi_flags |= FUSE_FATTR_UID; fbuf->op.in.setattr.valid |= FUSE_FATTR_UID;
} }
if (vap->va_gid != (gid_t)VNOVAL) { if (vap->va_gid != (gid_t)VNOVAL) {
@@ -534,8 +538,8 @@ fusefs_setattr(void *v)
error = EROFS; error = EROFS;
goto out; goto out;
} }
fbuf->fb_attr.st_gid = vap->va_gid; fbuf->op.in.setattr.gid |= vap->va_gid;
io->fi_flags |= FUSE_FATTR_GID; fbuf->op.in.setattr.valid |= FUSE_FATTR_GID;
} }
if (vap->va_size != VNOVAL) { if (vap->va_size != VNOVAL) {
@@ -559,8 +563,8 @@ fusefs_setattr(void *v)
break; break;
} }
fbuf->fb_attr.st_size = vap->va_size; fbuf->op.in.setattr.size |= vap->va_size;
io->fi_flags |= FUSE_FATTR_SIZE; fbuf->op.in.setattr.valid |= FUSE_FATTR_SIZE;
} }
if (vap->va_atime.tv_nsec != VNOVAL) { if (vap->va_atime.tv_nsec != VNOVAL) {
@@ -568,8 +572,9 @@ fusefs_setattr(void *v)
error = EROFS; error = EROFS;
goto out; goto out;
} }
fbuf->fb_attr.st_atim = vap->va_atime; fbuf->op.in.setattr.atime = vap->va_atime.tv_sec;
io->fi_flags |= FUSE_FATTR_ATIME; fbuf->op.in.setattr.atimensec = vap->va_atime.tv_nsec;
fbuf->op.in.setattr.valid |= FUSE_FATTR_ATIME;
} }
if (vap->va_mtime.tv_nsec != VNOVAL) { if (vap->va_mtime.tv_nsec != VNOVAL) {
@@ -577,8 +582,9 @@ fusefs_setattr(void *v)
error = EROFS; error = EROFS;
goto out; goto out;
} }
fbuf->fb_attr.st_mtim = vap->va_mtime; fbuf->op.in.setattr.mtime = vap->va_mtime.tv_sec;
io->fi_flags |= FUSE_FATTR_MTIME; fbuf->op.in.setattr.mtimensec = vap->va_mtime.tv_nsec;
fbuf->op.in.setattr.valid |= FUSE_FATTR_MTIME;
} }
/* XXX should set a flag if (vap->va_vaflags & VA_UTIMES_CHANGE) */ /* XXX should set a flag if (vap->va_vaflags & VA_UTIMES_CHANGE) */
@@ -599,11 +605,11 @@ fusefs_setattr(void *v)
goto out; goto out;
} }
fbuf->fb_attr.st_mode = vap->va_mode & ALLPERMS; fbuf->op.in.setattr.mode = vap->va_mode & ALLPERMS;
io->fi_flags |= FUSE_FATTR_MODE; fbuf->op.in.setattr.valid |= FUSE_FATTR_MODE;
} }
if (!io->fi_flags) { if (!fbuf->op.in.setattr.valid) {
goto out; goto out;
} }
@@ -667,9 +673,9 @@ fusefs_link(void *v)
} }
fbuf = fb_setup(cnp->cn_namelen + 1, dip->i_number, fbuf = fb_setup(cnp->cn_namelen + 1, dip->i_number,
FBT_LINK, p); FUSE_LINK, p);
fbuf->fb_io_ino = ip->i_number; fbuf->op.in.link.oldnodeid = ip->i_number;
memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen); memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen);
fbuf->fb_dat[cnp->cn_namelen] = '\0'; fbuf->fb_dat[cnp->cn_namelen] = '\0';
@@ -728,7 +734,7 @@ fusefs_symlink(void *v)
len = strlen(target) + 1; len = strlen(target) + 1;
fbuf = fb_setup(len + cnp->cn_namelen + 1, dp->i_number, fbuf = fb_setup(len + cnp->cn_namelen + 1, dp->i_number,
FBT_SYMLINK, p); FUSE_SYMLINK, p);
memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen); memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen);
fbuf->fb_dat[cnp->cn_namelen] = '\0'; fbuf->fb_dat[cnp->cn_namelen] = '\0';
@@ -743,7 +749,15 @@ fusefs_symlink(void *v)
goto bad; goto bad;
} }
if ((error = VFS_VGET(fmp->mp, fbuf->fb_ino, &tdp))) { /* symlink returns a fuse_entry_out with the ino of the new link */
if (fbuf->op.out.entry.nodeid == 0 ||
fbuf->op.out.entry.nodeid == FUSE_ROOT_ID) {
error = EIO;
fb_delete(fbuf);
goto bad;
}
if ((error = VFS_VGET(fmp->mp, fbuf->op.out.entry.nodeid, &tdp))) {
fb_delete(fbuf); fb_delete(fbuf);
goto bad; goto bad;
} }
@@ -767,16 +781,20 @@ fusefs_readdir(void *v)
struct fusefs_node *ip; struct fusefs_node *ip;
struct fusefs_mnt *fmp; struct fusefs_mnt *fmp;
struct fusebuf *fbuf; struct fusebuf *fbuf;
struct dirent *dp; struct fuse_dirent *fdp;
char *edp; struct dirent de;
struct vnode *vp; struct vnode *vp;
struct proc *p; struct proc *p;
struct uio *uio; struct uio *uio;
size_t read_size;
uint32_t fresid;
off_t foffset, freclen;
int error = 0, eofflag = 0, diropen = 0; int error = 0, eofflag = 0, diropen = 0;
vp = ap->a_vp; vp = ap->a_vp;
uio = ap->a_uio; uio = ap->a_uio;
p = uio->uio_procp; p = uio->uio_procp;
foffset = uio->uio_offset;
ip = VTOI(vp); ip = VTOI(vp);
fmp = (struct fusefs_mnt *)ip->i_ump; fmp = (struct fusefs_mnt *)ip->i_ump;
@@ -784,31 +802,35 @@ fusefs_readdir(void *v)
if (!fmp->sess_init) if (!fmp->sess_init)
return (ENXIO); return (ENXIO);
/*
* Some file systems expect that the kernel always uses the same
* read buffer size. In the event that uio_resid is a multiple of
* max_read, we need to ensure that we use a consistent buffer size
* in the loop.
*/
read_size = MIN(uio->uio_resid, fmp->max_read);
/* /*
* Basic check to ensure buffer is large enough for at least one * Basic check to ensure buffer is large enough for at least one
* dirent with maximum allowed name length. * dirent with maximum allowed name length.
*/ */
if (uio->uio_resid < sizeof(struct dirent)) if (read_size < sizeof(struct dirent))
return (EINVAL); return (EINVAL);
if (ip->fufh[FUFH_RDONLY].fh_type == FUFH_INVALID) { if (ip->fufh[FUFH_RDONLY].fh_type == FUFH_INVALID) {
error = fusefs_file_open(fmp, ip, FUFH_RDONLY, O_RDONLY, 1, p); error = fusefs_file_open(fmp, ip, FUFH_RDONLY, O_RDONLY, 1, p);
if (error) if (error)
return (error); return (error);
else
diropen = 1; diropen = 1;
} }
while (uio->uio_resid > 0) { /* loop until we run out of buffer space */
fbuf = fb_setup(0, ip->i_number, FBT_READDIR, p); while (!error && uio->uio_resid >= read_size) {
fbuf = fb_setup(0, ip->i_number, FUSE_READDIR, p);
fbuf->fb_io_fd = ip->fufh[FUFH_RDONLY].fh_id; fbuf->op.in.read.fh = ip->fufh[FUFH_RDONLY].fh_id;
fbuf->fb_io_off = uio->uio_offset; fbuf->op.in.read.offset = foffset;
fbuf->fb_io_len = MIN(uio->uio_resid, fmp->max_read); fbuf->op.in.read.size = read_size;
/* might not have enough space for another dirent */
if (fbuf->fb_io_len < sizeof(struct dirent))
break;
error = fb_queue(fmp->dev, fbuf); error = fb_queue(fmp->dev, fbuf);
if (error) { if (error) {
@@ -816,58 +838,78 @@ fusefs_readdir(void *v)
break; break;
} }
/* ack end of readdir */ /*
* Ack end of readdir. Only used by getcwd(3), getdents(3)
* relies on a repeat call that returns no entries.
*/
if (fbuf->fb_len == 0) { if (fbuf->fb_len == 0) {
eofflag = 1; eofflag = 1;
fb_delete(fbuf); fb_delete(fbuf);
break; break;
} }
/* validate the returned dirents */ /* validate and convert the returned dirents */
dp = (struct dirent *)fbuf->fb_dat; fresid = fbuf->fb_len;
edp = fbuf->fb_dat + fbuf->fb_len; fdp = (struct fuse_dirent *)fbuf->fb_dat;
while ((char *)dp < edp) {
while (uio->uio_resid > 0 && fresid > FUSE_NAME_OFFSET) {
/* get the size of the FUSE dirent */
freclen = FUSE_DIRENT_SIZE(fdp);
/* check for partial dirent */ /* check for partial dirent */
if ((char *)dp + offsetof(struct dirent, d_name) >= edp if (fresid < freclen)
|| dp->d_reclen <= offsetof(struct dirent, d_name) break;
|| (char *)dp + dp->d_reclen > edp) {
error = EINVAL; /* check for sane name length */
if (fdp->namelen == 0 || fdp->namelen > MAXNAMLEN) {
error = EIO;
break; break;
} }
/* check name doesn't extend past end of dirent */
if (dp->d_namlen + offsetof(struct dirent, d_name) >=
dp->d_reclen) {
error = EINVAL;
break;
}
/*
* If name doesn't extend to end of dirent then set
* remaining space to NULL. This is not usually the
* case but we can't trust userspace.
*/
memset(dp->d_name + dp->d_namlen, 0, dp->d_reclen -
dp->d_namlen - offsetof(struct dirent, d_name));
/* check for illegal character in file name */ /* check for illegal character in file name */
if (memchr(dp->d_name, '/', dp->d_namlen) != NULL) { if (memchr(fdp->name, '/', fdp->namelen) != NULL) {
error = EINVAL; error = EIO;
break; break;
} }
dp = (struct dirent *)((char *)dp + dp->d_reclen);
}
if (error) {
fb_delete(fbuf);
break;
}
if ((error = uiomove(fbuf->fb_dat, fbuf->fb_len, uio))) { /* copy FUSE dirent into struct dirent */
fb_delete(fbuf); memset(&de, 0, sizeof(de));
break; de.d_namlen = fdp->namelen;
de.d_reclen = DIRENT_RECSIZE(de.d_namlen);
if (uio->uio_resid < de.d_reclen)
goto out;
/*
* d_off is used by telldir, seekdir, readdir libc
* functions and expect the file system's offset. This
* will be passed back to this function so needs to be
* the FUSE offset.
*/
de.d_off = fdp->off;
de.d_fileno = fdp->ino;
de.d_type = fdp->type;
memcpy(de.d_name, fdp->name, de.d_namlen);
/* pad with NUL */
/* XXX necessary if we memset 0 above?
* or is this better? */
memset(de.d_name + de.d_namlen, 0, de.d_reclen
- de.d_namlen - offsetof(struct dirent, d_name));
if ((error = uiomove(&de, de.d_reclen, uio)))
break;
/* advance to next FUSE dirent */
fresid -= freclen;
foffset = fdp->off;
fdp = (struct fuse_dirent *)((char *)fdp + freclen);
} }
fb_delete(fbuf); fb_delete(fbuf);
} }
out:
uio->uio_offset = foffset;
if (!error && ap->a_eofflag != NULL) if (!error && ap->a_eofflag != NULL)
*ap->a_eofflag = eofflag; *ap->a_eofflag = eofflag;
@@ -949,11 +991,7 @@ fusefs_readlink(void *v)
if (fmp->undef_op & UNDEF_READLINK) if (fmp->undef_op & UNDEF_READLINK)
return (ENOSYS); return (ENOSYS);
fbuf = fb_setup(0, ip->i_number, FBT_READLINK, p); fbuf = fb_setup(0, ip->i_number, FUSE_READLINK, p);
fbuf->fb_io_off = uio->uio_offset;
fbuf->fb_io_len = MIN(uio->uio_resid, fmp->max_read);
error = fb_queue(fmp->dev, fbuf); error = fb_queue(fmp->dev, fbuf);
if (error) { if (error) {
@@ -991,7 +1029,7 @@ fusefs_reclaim(void *v)
struct fusefs_filehandle *fufh = NULL; struct fusefs_filehandle *fufh = NULL;
struct fusefs_mnt *fmp; struct fusefs_mnt *fmp;
struct fusebuf *fbuf; struct fusebuf *fbuf;
int type, error = 0; int type;
fmp = (struct fusefs_mnt *)ip->i_ump; fmp = (struct fusefs_mnt *)ip->i_ump;
@@ -1008,12 +1046,11 @@ fusefs_reclaim(void *v)
/* /*
* If the fuse connection is opened ask libfuse to free the vnodes. * If the fuse connection is opened ask libfuse to free the vnodes.
*/ */
if (fmp->sess_init && ip->i_number != FUSE_ROOTINO) { if (fmp->sess_init && ip->i_number != FUSE_ROOT_ID) {
fbuf = fb_setup(0, ip->i_number, FBT_RECLAIM, p); fbuf = fb_setup(0, ip->i_number, FUSE_FORGET, p);
error = fb_queue(fmp->dev, fbuf); fbuf->op.in.forget.nlookup = ip->nlookup;
if (error) fuse_device_queue_fbuf(fmp->dev, fbuf);
printf("fusefs: vnode reclaim failed: %d\n", error); /* FUSE_FORGET has no response */
fb_delete(fbuf);
} }
/* /*
@@ -1074,9 +1111,10 @@ fusefs_create(void *v)
} }
fbuf = fb_setup(cnp->cn_namelen + 1, ip->i_number, fbuf = fb_setup(cnp->cn_namelen + 1, ip->i_number,
FBT_MKNOD, p); FUSE_MKNOD, p);
fbuf->fb_io_mode = mode; fbuf->op.in.mknod.mode = mode;
fbuf->op.in.mknod.umask = p->p_p->ps_fd->fd_cmask;
memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen); memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen);
fbuf->fb_dat[cnp->cn_namelen] = '\0'; fbuf->fb_dat[cnp->cn_namelen] = '\0';
@@ -1089,10 +1127,17 @@ fusefs_create(void *v)
goto out; goto out;
} }
if ((error = VFS_VGET(fmp->mp, fbuf->fb_ino, &tdp))) /* mknod returns a fuse_entry_out with the ino of the new file */
if (fbuf->op.out.entry.nodeid == 0 ||
fbuf->op.out.entry.nodeid == FUSE_ROOT_ID) {
error = EIO;
goto out;
}
if ((error = VFS_VGET(fmp->mp, fbuf->op.out.entry.nodeid, &tdp)))
goto out; goto out;
tdp->v_type = IFTOVT(fbuf->fb_attr.st_mode); tdp->v_type = VREG;
*vpp = tdp; *vpp = tdp;
VN_KNOTE(ap->a_dvp, NOTE_WRITE); VN_KNOTE(ap->a_dvp, NOTE_WRITE);
@@ -1131,11 +1176,12 @@ fusefs_mknod(void *v)
} }
fbuf = fb_setup(cnp->cn_namelen + 1, ip->i_number, fbuf = fb_setup(cnp->cn_namelen + 1, ip->i_number,
FBT_MKNOD, p); FUSE_MKNOD, p);
fbuf->fb_io_mode = MAKEIMODE(vap->va_type, vap->va_mode); fbuf->op.in.mknod.mode = MAKEIMODE(vap->va_type, vap->va_mode);
fbuf->op.in.mknod.umask = p->p_p->ps_fd->fd_cmask;
if (vap->va_rdev != VNOVAL) if (vap->va_rdev != VNOVAL)
fbuf->fb_io_rdev = vap->va_rdev; fbuf->op.in.mknod.rdev = vap->va_rdev;
memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen); memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen);
fbuf->fb_dat[cnp->cn_namelen] = '\0'; fbuf->fb_dat[cnp->cn_namelen] = '\0';
@@ -1148,10 +1194,21 @@ fusefs_mknod(void *v)
goto out; goto out;
} }
if ((error = VFS_VGET(fmp->mp, fbuf->fb_ino, &tdp))) /* mknod returns a fuse_entry_out with the ino of the new file */
if (fbuf->op.out.entry.nodeid == 0 ||
fbuf->op.out.entry.nodeid == FUSE_ROOT_ID) {
error = EIO;
goto out;
}
if ((error = VFS_VGET(fmp->mp, fbuf->op.out.entry.nodeid, &tdp)))
goto out; goto out;
tdp->v_type = IFTOVT(fbuf->fb_attr.st_mode); /*
* Don't trust the type returned by the file system and assume it
* created what it was asked.
*/
tdp->v_type = vap->va_type;
*vpp = tdp; *vpp = tdp;
VN_KNOTE(ap->a_dvp, NOTE_WRITE); VN_KNOTE(ap->a_dvp, NOTE_WRITE);
@@ -1194,12 +1251,12 @@ fusefs_read(void *v)
return (EINVAL); return (EINVAL);
while (uio->uio_resid > 0) { while (uio->uio_resid > 0) {
fbuf = fb_setup(0, ip->i_number, FBT_READ, p); fbuf = fb_setup(0, ip->i_number, FUSE_READ, p);
size = MIN(uio->uio_resid, fmp->max_read); size = MIN(uio->uio_resid, fmp->max_read);
fbuf->fb_io_fd = fusefs_fd_get(ip, FUFH_RDONLY); fbuf->op.in.read.fh = fusefs_fd_get(ip, FUFH_RDONLY);
fbuf->fb_io_off = uio->uio_offset; fbuf->op.in.read.offset = uio->uio_offset;
fbuf->fb_io_len = size; fbuf->op.in.read.size = size;
error = fb_queue(fmp->dev, fbuf); error = fb_queue(fmp->dev, fbuf);
@@ -1240,6 +1297,15 @@ fusefs_write(void *v)
ip = VTOI(vp); ip = VTOI(vp);
fmp = (struct fusefs_mnt *)ip->i_ump; fmp = (struct fusefs_mnt *)ip->i_ump;
/*
* XXX
* fmp->max_write will not be set to the value wanted by the file
* system if sess_init == PENDING. It is not currently possible for
* this to be the case since a write operation cannot take place on
* a vnode until after VOP_LOOKUP(9) has successfully returned. At
* which point, the file system must have responded to FUSE_INIT and
* sess_init will be 1 and max_write will be correctly set.
*/
if (!fmp->sess_init) if (!fmp->sess_init)
return (ENXIO); return (ENXIO);
if (uio->uio_resid == 0) if (uio->uio_resid == 0)
@@ -1253,12 +1319,12 @@ fusefs_write(void *v)
} }
while (uio->uio_resid > 0) { while (uio->uio_resid > 0) {
len = MIN(uio->uio_resid, fmp->max_read); len = MIN(uio->uio_resid, fmp->max_write);
fbuf = fb_setup(len, ip->i_number, FBT_WRITE, p); fbuf = fb_setup(len, ip->i_number, FUSE_WRITE, p);
fbuf->fb_io_fd = fusefs_fd_get(ip, FUFH_WRONLY); fbuf->op.in.write.fh = fusefs_fd_get(ip, FUFH_WRONLY);
fbuf->fb_io_off = uio->uio_offset; fbuf->op.in.write.offset = uio->uio_offset;
fbuf->fb_io_len = len; fbuf->op.in.write.size = len;
if ((error = uiomove(fbuf->fb_dat, len, uio))) { if ((error = uiomove(fbuf->fb_dat, len, uio))) {
printf("fusefs: uio error %i\n", error); printf("fusefs: uio error %i\n", error);
@@ -1270,8 +1336,8 @@ fusefs_write(void *v)
if (error) if (error)
break; break;
diff = len - fbuf->fb_io_len; diff = len - fbuf->op.out.write.size;
if (fbuf->fb_io_len > len) { if (fbuf->op.out.write.size > len) {
error = EINVAL; error = EINVAL;
break; break;
} }
@@ -1382,14 +1448,14 @@ abortit:
} }
fbuf = fb_setup(fcnp->cn_namelen + tcnp->cn_namelen + 2, fbuf = fb_setup(fcnp->cn_namelen + tcnp->cn_namelen + 2,
dp->i_number, FBT_RENAME, p); dp->i_number, FUSE_RENAME, p);
memcpy(fbuf->fb_dat, fcnp->cn_nameptr, fcnp->cn_namelen); memcpy(fbuf->fb_dat, fcnp->cn_nameptr, fcnp->cn_namelen);
fbuf->fb_dat[fcnp->cn_namelen] = '\0'; fbuf->fb_dat[fcnp->cn_namelen] = '\0';
memcpy(fbuf->fb_dat + fcnp->cn_namelen + 1, tcnp->cn_nameptr, memcpy(fbuf->fb_dat + fcnp->cn_namelen + 1, tcnp->cn_nameptr,
tcnp->cn_namelen); tcnp->cn_namelen);
fbuf->fb_dat[fcnp->cn_namelen + tcnp->cn_namelen + 1] = '\0'; fbuf->fb_dat[fcnp->cn_namelen + tcnp->cn_namelen + 1] = '\0';
fbuf->fb_io_ino = VTOI(tdvp)->i_number; fbuf->op.in.rename.newdir = VTOI(tdvp)->i_number;
error = fb_queue(fmp->dev, fbuf); error = fb_queue(fmp->dev, fbuf);
@@ -1449,9 +1515,10 @@ fusefs_mkdir(void *v)
} }
fbuf = fb_setup(cnp->cn_namelen + 1, ip->i_number, fbuf = fb_setup(cnp->cn_namelen + 1, ip->i_number,
FBT_MKDIR, p); FUSE_MKDIR, p);
fbuf->fb_io_mode = MAKEIMODE(vap->va_type, vap->va_mode); fbuf->op.in.mkdir.mode = MAKEIMODE(vap->va_type, vap->va_mode);
fbuf->op.in.mkdir.umask = p->p_p->ps_fd->fd_cmask;
memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen); memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen);
fbuf->fb_dat[cnp->cn_namelen] = '\0'; fbuf->fb_dat[cnp->cn_namelen] = '\0';
@@ -1464,12 +1531,19 @@ fusefs_mkdir(void *v)
goto out; goto out;
} }
if ((error = VFS_VGET(fmp->mp, fbuf->fb_ino, &tdp))) { /* mkdir returns a fuse_entry_out with the ino of the new directory */
if (fbuf->op.out.entry.nodeid == 0 ||
fbuf->op.out.entry.nodeid == FUSE_ROOT_ID) {
error = EIO;
goto out;
}
if ((error = VFS_VGET(fmp->mp, fbuf->op.out.entry.nodeid, &tdp))) {
fb_delete(fbuf); fb_delete(fbuf);
goto out; goto out;
} }
tdp->v_type = IFTOVT(fbuf->fb_attr.st_mode); tdp->v_type = VDIR;
*vpp = tdp; *vpp = tdp;
VN_KNOTE(ap->a_dvp, NOTE_WRITE | NOTE_LINK); VN_KNOTE(ap->a_dvp, NOTE_WRITE | NOTE_LINK);
@@ -1517,7 +1591,7 @@ fusefs_rmdir(void *v)
VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK); VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
fbuf = fb_setup(cnp->cn_namelen + 1, dp->i_number, fbuf = fb_setup(cnp->cn_namelen + 1, dp->i_number,
FBT_RMDIR, p); FUSE_RMDIR, p);
memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen); memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen);
fbuf->fb_dat[cnp->cn_namelen] = '\0'; fbuf->fb_dat[cnp->cn_namelen] = '\0';
@@ -1575,7 +1649,7 @@ fusefs_remove(void *v)
} }
fbuf = fb_setup(cnp->cn_namelen + 1, dp->i_number, fbuf = fb_setup(cnp->cn_namelen + 1, dp->i_number,
FBT_UNLINK, p); FUSE_UNLINK, p);
memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen); memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen);
fbuf->fb_dat[cnp->cn_namelen] = '\0'; fbuf->fb_dat[cnp->cn_namelen] = '\0';
@@ -1674,8 +1748,14 @@ fusefs_fsync(void *v)
if (fufh->fh_type == FUFH_WRONLY || if (fufh->fh_type == FUFH_WRONLY ||
fufh->fh_type == FUFH_RDWR) { fufh->fh_type == FUFH_RDWR) {
fbuf = fb_setup(0, ip->i_number, FBT_FSYNC, p); fbuf = fb_setup(0, ip->i_number, FUSE_FSYNC, p);
fbuf->fb_io_fd = fufh->fh_id; fbuf->op.in.fsync.fh = fufh->fh_id;
/*
* fdatasync(2) is just a wrapper around fsync(2) so
* datasync is always false.
*/
fbuf->op.in.fsync.fsync_flags = 0;
/* Always behave as if ap->a_waitfor = MNT_WAIT. */ /* Always behave as if ap->a_waitfor = MNT_WAIT. */
error = fb_queue(fmp->dev, fbuf); error = fb_queue(fmp->dev, fbuf);
+77 -3
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: fusebuf.c,v 1.18 2021/03/11 13:31:35 jsg Exp $ */ /* $OpenBSD: fusebuf.c,v 1.19 2026/06/17 13:29:01 helg Exp $ */
/* /*
* Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com> * Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com>
* *
@@ -35,7 +35,82 @@ fb_setup(size_t len, ino_t ino, int op, struct proc *p)
{ {
struct fusebuf *fbuf; struct fusebuf *fbuf;
fbuf = pool_get(&fusefs_fbuf_pool, PR_WAITOK | PR_ZERO); KASSERT(len <= FUSEBUFMAXSIZE);
fbuf = pool_get(&fusefs_fbuf_pool, PR_WAITOK | PR_ZERO);
fbuf->op_in_len = 0;
fbuf->op_out_len = 0;
fbuf->op_out_buf = 0;
switch(op) {
case FUSE_GETATTR:
fbuf->op_in_len = sizeof(struct fuse_getattr_in);
fbuf->op_out_len = sizeof(struct fuse_attr_out);
break;
case FUSE_SETATTR:
fbuf->op_in_len = sizeof(struct fuse_setattr_in);
fbuf->op_out_len = sizeof(struct fuse_attr_out);
break;
case FUSE_READLINK:
fbuf->op_out_buf = 1;
break;
case FUSE_FLUSH:
fbuf->op_in_len = sizeof(struct fuse_flush_in);
break;
case FUSE_INIT:
fbuf->op_in_len = sizeof(struct fuse_init_in);
fbuf->op_out_len = sizeof(struct fuse_init_out);
break;
case FUSE_OPEN:
case FUSE_OPENDIR:
fbuf->op_in_len = sizeof(struct fuse_open_in);
fbuf->op_out_len = sizeof(struct fuse_open_out);
break;
case FUSE_READ:
case FUSE_READDIR:
fbuf->op_in_len = sizeof(struct fuse_read_in);
fbuf->op_out_buf = 1;
break;
case FUSE_MKDIR:
fbuf->op_in_len = sizeof(struct fuse_mkdir_in);
fbuf->op_out_len = sizeof(struct fuse_entry_out);
break;
case FUSE_MKNOD:
fbuf->op_in_len = sizeof(struct fuse_mknod_in);
fbuf->op_out_len = sizeof(struct fuse_entry_out);
break;
case FUSE_FSYNC:
fbuf->op_in_len = sizeof(struct fuse_fsync_in);
break;
case FUSE_RENAME:
fbuf->op_in_len = sizeof(struct fuse_rename_in);
break;
case FUSE_LINK:
fbuf->op_in_len = sizeof(struct fuse_link_in);
fbuf->op_out_len = sizeof(struct fuse_entry_out);
break;
case FUSE_SYMLINK:
fbuf->op_out_len = sizeof(struct fuse_entry_out);
break;
case FUSE_RELEASE:
case FUSE_RELEASEDIR:
fbuf->op_in_len = sizeof(struct fuse_release_in);
break;
case FUSE_WRITE:
fbuf->op_in_len = sizeof(struct fuse_write_in);
fbuf->op_out_len = sizeof(struct fuse_write_out);
break;
case FUSE_FORGET:
fbuf->op_in_len = sizeof(struct fuse_forget_in);
break;
case FUSE_LOOKUP:
fbuf->op_out_len = sizeof(struct fuse_entry_out);
break;
case FUSE_STATFS:
fbuf->op_out_len = sizeof(struct fuse_statfs_out);
break;
}
fbuf->hdr.len = sizeof(fbuf->hdr) + fbuf->op_in_len + len;
fbuf->fb_len = len; fbuf->fb_len = len;
fbuf->fb_err = 0; fbuf->fb_err = 0;
arc4random_buf(&fbuf->fb_uuid, sizeof fbuf->fb_uuid); arc4random_buf(&fbuf->fb_uuid, sizeof fbuf->fb_uuid);
@@ -48,7 +123,6 @@ fb_setup(size_t len, ino_t ino, int op, struct proc *p)
fbuf->fb_tid = p->p_tid + THREAD_PID_OFFSET; fbuf->fb_tid = p->p_tid + THREAD_PID_OFFSET;
fbuf->fb_uid = p->p_ucred->cr_uid; fbuf->fb_uid = p->p_ucred->cr_uid;
fbuf->fb_gid = p->p_ucred->cr_gid; fbuf->fb_gid = p->p_ucred->cr_gid;
fbuf->fb_umask = p->p_p->ps_fd->fd_cmask;
if (len == 0) if (len == 0)
fbuf->fb_dat = NULL; fbuf->fb_dat = NULL;
else else
+2 -6
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: fusefs.h,v 1.15 2025/09/08 17:25:46 helg Exp $ */ /* $OpenBSD: fusefs.h,v 1.16 2026/06/17 13:29:01 helg Exp $ */
/* /*
* Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com> * Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com>
* *
@@ -41,6 +41,7 @@ struct fusefs_mnt {
struct mount *mp; struct mount *mp;
uint32_t undef_op; uint32_t undef_op;
int max_read; int max_read;
int max_write;
int sess_init; int sess_init;
int allow_other; int allow_other;
dev_t dev; dev_t dev;
@@ -74,11 +75,6 @@ void fuse_device_cleanup(dev_t);
void fuse_device_queue_fbuf(dev_t, struct fusebuf *); void fuse_device_queue_fbuf(dev_t, struct fusebuf *);
void fuse_device_set_fmp(struct fusefs_mnt *, int); void fuse_device_set_fmp(struct fusefs_mnt *, int);
/*
* The root inode is the root of the file system. Inode 0 can't be used for
* normal purposes.
*/
#define FUSE_ROOTINO ((ino_t)1)
#define VFSTOFUSEFS(mp) ((struct fusefs_mnt *)((mp)->mnt_data)) #define VFSTOFUSEFS(mp) ((struct fusefs_mnt *)((mp)->mnt_data))
#endif /* _KERNEL */ #endif /* _KERNEL */
+4 -1
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: fusefs_node.h,v 1.6 2026/01/22 11:53:31 helg Exp $ */ /* $OpenBSD: fusefs_node.h,v 1.7 2026/06/17 13:29:01 helg Exp $ */
/* /*
* Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com> * Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com>
* *
@@ -49,6 +49,9 @@ struct fusefs_node {
/** meta **/ /** meta **/
off_t filesize; off_t filesize;
/** lookup count needed by FUSE_FORGET **/
uint64_t nlookup;
}; };
#ifdef ITOV #ifdef ITOV
+366 -88
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: fusebuf.h,v 1.16 2025/09/06 06:15:52 helg Exp $ */ /* $OpenBSD: fusebuf.h,v 1.17 2026/06/17 13:29:01 helg Exp $ */
/* /*
* Copyright (c) 2013 Sylvestre Gallon * Copyright (c) 2013 Sylvestre Gallon
* Copyright (c) 2013 Martin Pieuchot * Copyright (c) 2013 Martin Pieuchot
@@ -19,36 +19,25 @@
#ifndef _SYS_FUSEBUF_H_ #ifndef _SYS_FUSEBUF_H_
#define _SYS_FUSEBUF_H_ #define _SYS_FUSEBUF_H_
#include <sys/stdint.h>
/* /*
* Maximum size of the read or write buffer sent from the kernel for VFS * Maximum size of the read or write buffer sent from the kernel for VFS
* syscalls: read, readdir, readlink, write. * syscalls: read, readdir, readlink, write.
*/ */
#define FUSEBUFMAXSIZE (4096*1024) #define FUSEBUFMAXSIZE (4096*1024)
/* header at beginning of each fusebuf: */ /** Linux FUSE kernel interface major version we somewhat emulate. */
struct fb_hdr { #define FUSE_KERNEL_VERSION 7
SIMPLEQ_ENTRY(fusebuf) fh_next; /* next buffer in chain */
size_t fh_len; /* Amount of data */
int fh_err; /* errno to pass back */
int fh_type; /* type of data */
ino_t fh_ino; /* Inode of this fusebuf */
uint64_t fh_uuid; /* Uuid to track the answer */
pid_t fh_tid; /* calling proc thread id */
uid_t fh_uid; /* calling proc uid */
gid_t fh_gid; /* calling proc gid */
mode_t fh_umask; /* calling proc umask */
};
/* header for fuse file operations (like read/write/mkdir): */ /** Linux FUSE kernel interface minor version we somewhat emulate. */
struct fb_io { #define FUSE_KERNEL_MINOR_VERSION 19
uint64_t fi_fd; /* fd where the io is performed */
ino_t fi_ino; /* ino for the io */ /*
off_t fi_off; /* offset for the io */ * The root inode is the root of the mounted FUSE file system.
size_t fi_len; /* Length of data */ * Also note that inode 0 can't be used for normal purposes.
mode_t fi_mode; /* mode for fd */ */
uint32_t fi_flags; /* flags on transfer */ #define FUSE_ROOT_ID ((ino_t)1)
dev_t fi_rdev; /* dev for mknod */
};
/* /*
* An operation is issued by the kernel through fuse(4) when the * An operation is issued by the kernel through fuse(4) when the
@@ -62,43 +51,6 @@ struct fb_io {
* When the userland file system answers to this operation it uses * When the userland file system answers to this operation it uses
* the same ID (fh_uuid). * the same ID (fh_uuid).
*/ */
struct fusebuf {
struct fb_hdr fb_hdr;
union {
struct statvfs FD_stat; /* vfs statfs */
struct stat FD_attr; /* for attr vnops */
struct fb_io FD_io; /* for file io vnops */
} FD;
uint8_t *fb_dat; /* data's */
};
#define fb_next fb_hdr.fh_next
#define fb_len fb_hdr.fh_len
#define fb_err fb_hdr.fh_err
#define fb_type fb_hdr.fh_type
#define fb_ino fb_hdr.fh_ino
#define fb_uuid fb_hdr.fh_uuid
#define fb_tid fb_hdr.fh_tid
#define fb_uid fb_hdr.fh_uid
#define fb_gid fb_hdr.fh_gid
#define fb_umask fb_hdr.fh_umask
#define fb_stat FD.FD_stat
#define fb_attr FD.FD_attr
#define fb_io_fd FD.FD_io.fi_fd
#define fb_io_ino FD.FD_io.fi_ino
#define fb_io_off FD.FD_io.fi_off
#define fb_io_len FD.FD_io.fi_len
#define fb_io_mode FD.FD_io.fi_mode
#define fb_io_flags FD.FD_io.fi_flags
#define fb_io_rdev FD.FD_io.fi_rdev
/*
* Macros for type conversion
* fbtod(fb,t) - convert fusebuf pointer to data pointer of correct
* type
*/
#define fbtod(fb,t) ((t)((fb)->fb_dat))
/* flags needed by setattr */ /* flags needed by setattr */
#define FUSE_FATTR_MODE (1 << 0) #define FUSE_FATTR_MODE (1 << 0)
@@ -107,42 +59,368 @@ struct fusebuf {
#define FUSE_FATTR_SIZE (1 << 3) #define FUSE_FATTR_SIZE (1 << 3)
#define FUSE_FATTR_ATIME (1 << 4) #define FUSE_FATTR_ATIME (1 << 4)
#define FUSE_FATTR_MTIME (1 << 5) #define FUSE_FATTR_MTIME (1 << 5)
#define FUSE_FATTR_FH (1 << 6)
/* fusebuf types */ /* fusebuf types */
#define FBT_LOOKUP 0 #define FUSE_LOOKUP 1
#define FBT_GETATTR 1 #define FUSE_GETATTR 3
#define FBT_SETATTR 2 #define FUSE_SETATTR 4
#define FBT_READLINK 3 #define FUSE_READLINK 5
#define FBT_SYMLINK 4 #define FUSE_SYMLINK 6
#define FBT_MKNOD 5 #define FUSE_MKNOD 8
#define FBT_MKDIR 6 #define FUSE_MKDIR 9
#define FBT_UNLINK 7 #define FUSE_UNLINK 10
#define FBT_RMDIR 8 #define FUSE_RMDIR 11
#define FBT_RENAME 9 #define FUSE_RENAME 12
#define FBT_LINK 10 #define FUSE_LINK 13
#define FBT_OPEN 11 #define FUSE_OPEN 14
#define FBT_READ 12 #define FUSE_READ 15
#define FBT_WRITE 13 #define FUSE_WRITE 16
#define FBT_STATFS 14 #define FUSE_STATFS 17
#define FBT_RELEASE 16 #define FUSE_RELEASE 18
#define FBT_FSYNC 17 #define FUSE_FSYNC 20
#define FBT_FLUSH 18 #define FUSE_FLUSH 25
#define FBT_INIT 19 #define FUSE_INIT 26
#define FBT_OPENDIR 20 #define FUSE_OPENDIR 27
#define FBT_READDIR 21 #define FUSE_READDIR 28
#define FBT_RELEASEDIR 22 #define FUSE_RELEASEDIR 29
#define FBT_FSYNCDIR 23 #define FUSE_DESTROY 38
#define FBT_ACCESS 24 #define FUSE_FORGET 2
#define FBT_DESTROY 26
#define FBT_RECLAIM 27 struct fuse_attr {
uint64_t ino;
uint64_t size;
uint64_t blocks;
uint64_t atime;
uint64_t mtime;
uint64_t ctime;
uint32_t atimensec;
uint32_t mtimensec;
uint32_t ctimensec;
uint32_t mode;
uint32_t nlink;
uint32_t uid;
uint32_t gid;
uint32_t rdev;
uint32_t blksize;
uint32_t padding;
};
struct fuse_entry_out {
uint64_t nodeid; /* Inode number */
uint64_t generation; /* Not implemented */
uint64_t entry_valid; /* Not implemented */
uint64_t attr_valid; /* Not implemented */
uint32_t entry_valid_nsec; /* Not implemented */
uint32_t attr_valid_nsec; /* Not implemented */
struct fuse_attr attr;
};
struct fuse_forget_in {
uint64_t nlookup;
};
struct fuse_getattr_in {
uint32_t getattr_flags; /* Not implemented */
uint32_t dummy;
uint64_t fh; /* Not implemented */
};
struct fuse_attr_out {
uint64_t attr_valid; /* Not implemented */
uint32_t attr_valid_nsec; /* Not implemented */
uint32_t dummy;
struct fuse_attr attr;
};
struct fuse_mknod_in {
uint32_t mode;
uint32_t rdev;
uint32_t umask;
uint32_t padding;
};
struct fuse_mkdir_in {
uint32_t mode;
uint32_t umask;
};
struct fuse_rename_in {
uint64_t newdir;
};
struct fuse_link_in {
uint64_t oldnodeid;
};
struct fuse_setattr_in {
uint32_t valid;
uint32_t padding;
uint64_t fh;
uint64_t size;
uint64_t lock_owner;
uint64_t atime;
uint64_t mtime;
uint64_t unused2;
uint32_t atimensec;
uint32_t mtimensec;
uint32_t unused3;
uint32_t mode;
uint32_t unused4;
uint32_t uid;
uint32_t gid;
uint32_t unused5;
};
struct fuse_open_in {
uint32_t flags;
uint32_t unused;
};
struct fuse_open_out {
uint64_t fh;
uint32_t open_flags;
uint32_t padding;
};
struct fuse_release_in {
uint64_t fh;
uint32_t flags;
uint32_t release_flags;
uint64_t lock_owner;
};
struct fuse_flush_in {
uint64_t fh;
uint32_t unused;
uint32_t padding;
uint64_t lock_owner;
};
struct fuse_read_in {
uint64_t fh;
uint64_t offset;
uint32_t size;
uint32_t read_flags;
uint64_t lock_owner;
uint32_t flags;
uint32_t padding;
};
struct fuse_write_in {
uint64_t fh;
uint64_t offset;
uint32_t size;
uint32_t write_flags;
uint64_t lock_owner;
uint32_t flags;
uint32_t padding;
};
struct fuse_write_out {
uint32_t size;
uint32_t padding;
};
struct fuse_kstatfs {
uint64_t blocks;
uint64_t bfree;
uint64_t bavail;
uint64_t files;
uint64_t ffree;
uint32_t bsize;
uint32_t namelen;
uint32_t frsize;
uint32_t padding;
uint32_t spare[6];
};
struct fuse_statfs_out {
struct fuse_kstatfs st;
};
struct fuse_fsync_in {
uint64_t fh;
uint32_t fsync_flags; //XXX
uint32_t padding;
};
struct fuse_init_in {
uint32_t major;
uint32_t minor;
uint32_t max_readahead;
uint32_t flags;
};
struct fuse_init_out {
uint32_t major;
uint32_t minor;
uint32_t max_readahead; /* Not implemented */
uint32_t flags; /* Not implemented */
uint16_t max_background; /* Not implemented */
uint16_t congestion_threshold; /* Not implemented */
uint32_t max_write;
};
struct fuse_in_header {
uint32_t len;
uint32_t opcode;
uint64_t unique;
uint64_t nodeid;
uint32_t uid;
uint32_t gid;
uint32_t pid;
uint32_t padding;
};
struct fuse_out_header {
uint32_t len;
int32_t error;
uint64_t unique;
};
struct fuse_dirent {
uint64_t ino;
uint64_t off;
uint32_t namelen;
uint32_t type;
char name[]; /* unterminated string */
};
#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
#define FUSE_DIRENT_ALIGN(x) \
(((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1))
#define FUSE_DIRENT_SIZE(d) \
FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
#ifdef _KERNEL #ifdef _KERNEL
/*
* An operation is issued by the kernel through fuse(4) when the
* userland file system needs to execute an action (mkdir(2),
* link(2), etc).
*
* When the userland file system answers to this operation it uses
* the same ID (fb_uuid).
*/
struct fusebuf {
SIMPLEQ_ENTRY(fusebuf) next; /* next buffer in chain */
struct fuse_in_header hdr;
int32_t error; /* error returned by daemon */
size_t op_in_len; /* size of input */
size_t op_out_len; /* size of output */
uint8_t op_out_buf; /* whether to expect data */
union {
union {
struct fuse_forget_in forget;
struct fuse_getattr_in getattr;
struct fuse_setattr_in setattr;
struct fuse_mknod_in mknod;
struct fuse_mkdir_in mkdir;
struct fuse_rename_in rename;
struct fuse_link_in link;
struct fuse_open_in open;
struct fuse_read_in read;
struct fuse_write_in write;
struct fuse_release_in release;
struct fuse_fsync_in fsync;
struct fuse_flush_in flush;
struct fuse_init_in init;
} in;
union {
struct fuse_entry_out entry;
struct fuse_attr_out attr;
struct fuse_open_out open;
struct fuse_write_out write;
struct fuse_statfs_out statfs;
struct fuse_init_out init;
} out;
} op;
uint64_t dat_len;
uint8_t *dat;
};
#define fb_dat dat
#define fb_next next
#define fb_err error
#define fb_len dat_len
#define fb_type hdr.opcode
#define fb_uuid hdr.unique
#define fb_ino hdr.nodeid
#define fb_tid hdr.pid
#define fb_uid hdr.uid
#define fb_gid hdr.gid
/* fusebuf prototypes */ /* fusebuf prototypes */
struct fusebuf *fb_setup(size_t, ino_t, int, struct proc *); struct fusebuf *fb_setup(size_t, ino_t, int, struct proc *);
int fb_queue(dev_t, struct fusebuf *); int fb_queue(dev_t, struct fusebuf *);
void fb_delete(struct fusebuf *); void fb_delete(struct fusebuf *);
#else /* _KERNEL */
/*
* Userland fusebuf more directly maps to the protocol binary layout for each
* file operation request (input). This consists of a header followed by an
* optional input struct with data immediately following either the header or
* input struct.
*
* FUSE_LOOKUP: hdr, dat (filename)
* FUSE_GETATTR: hdr, getattr
* FUSE_SETATTR: hdr, setattr
* FUSE_READLINK: hdr
* FUSE_SYMLINK: hdr, dat (source\0target)
* FUSE_MKNOD: hdr, mknod, dat (filename)
* FUSE_MKDIR: hdr, mkdir, dat (dirname)
* FUSE_UNLINK: hdr, dat (filename)
* FUSE_RMDIR: hdr, dat (dirname)
* FUSE_RENAME: hdr, rename, dat (oldname\0newname)
* FUSE_LINK: hdr, dat (source\0target)
* FUSE_OPEN: hdr, open
* FUSE_READ: hdr, read
* FUSE_WRITE: hdr, write, dat
* FUSE_STATFS: hdr, statfs
* FUSE_RELEASE: hdr, release
* FUSE_FSYNC: hdr, fsync
* FUSE_FLUSH: hdr, flush
* FUSE_INIT: hdr, init
* FUSE_OPENDIR: hdr, open
* FUSE_READDIR: hdr, read
* FUSE_RELEASEDIR: hdr, release
* FUSE_DESTROY: hdr
* FUSE_FORGET: hdr, forget
*/
struct fusebuf {
struct fuse_in_header hdr;
union {
struct fuse_forget_in forget;
struct fuse_getattr_in getattr;
struct fuse_setattr_in setattr;
struct fuse_mknod_in mknod;
struct fuse_mkdir_in mkdir;
struct fuse_rename_in rename;
struct fuse_link_in link;
struct fuse_open_in open;
struct fuse_read_in read;
struct fuse_write_in write;
struct fuse_release_in release;
struct fuse_fsync_in fsync;
struct fuse_flush_in flush;
struct fuse_init_in init;
} in;
};
#define fb_type hdr.opcode
#define fb_uuid hdr.unique
#define fb_ino hdr.nodeid
#define fb_tid hdr.pid
#define fb_uid hdr.uid
#define fb_gid hdr.gid
/*
* Macro to get the additional data for operations that have it.
* The data starts immediately after the related input struct.
*/
#define fb_dat(in) (((char *)&(in)) + sizeof(in))
#endif /* _KERNEL */ #endif /* _KERNEL */
#endif /* _SYS_FUSEBUF_H_ */ #endif /* _SYS_FUSEBUF_H_ */