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:
+2
-2
@@ -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>
|
||||
*
|
||||
@@ -82,6 +82,7 @@ static struct fuse_opt fuse_lib_opts[] = {
|
||||
FUSE_OPT_KEY("fmask=%o", KEY_STUB),
|
||||
FUSE_LIB_OPT("umask=", set_mode),
|
||||
FUSE_LIB_OPT("umask=%o", umask),
|
||||
FUSE_OPT_KEY("max_write=", FUSE_OPT_KEY_KEEP),
|
||||
FUSE_OPT_END
|
||||
};
|
||||
|
||||
@@ -98,7 +99,6 @@ static struct fuse_opt fuse_mount_opts[] = {
|
||||
FUSE_MOUNT_OPT("fsname=%s", fsname),
|
||||
FUSE_MOUNT_OPT("max_read=%u", max_read),
|
||||
FUSE_OPT_KEY("max_readahead", KEY_STUB),
|
||||
FUSE_OPT_KEY("max_write", KEY_STUB),
|
||||
FUSE_MOUNT_OPT("noatime", noatime),
|
||||
FUSE_MOUNT_OPT("nonempty", nonempty),
|
||||
FUSE_MOUNT_OPT("-r", rdonly),
|
||||
|
||||
+4
-23
@@ -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>
|
||||
*
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "fuse_private.h"
|
||||
@@ -33,27 +34,9 @@ DEF(fuse_chan_fd);
|
||||
int
|
||||
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;
|
||||
|
||||
if (chp == NULL || *chp == NULL || buf == NULL)
|
||||
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);
|
||||
n = read((*chp)->fd, buf, size);
|
||||
if (n == -1)
|
||||
return (-errno);
|
||||
|
||||
@@ -67,10 +50,8 @@ fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[], size_t count)
|
||||
ssize_t n;
|
||||
|
||||
n = writev(ch->fd, iov, count);
|
||||
if (n == -1) {
|
||||
DPERROR(__func__);
|
||||
if (n == -1)
|
||||
return (-errno);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
+116
-56
@@ -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>
|
||||
*
|
||||
@@ -16,6 +16,8 @@
|
||||
*/
|
||||
|
||||
#include <sys/uio.h>
|
||||
#include <sys/fusebuf.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
@@ -25,28 +27,48 @@
|
||||
#include "debug.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 {
|
||||
KEY_HELP,
|
||||
KEY_VERSION,
|
||||
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[] = {
|
||||
/* core options, also supported by fuse_parse_cmdline(3) */
|
||||
FUSE_OPT_KEY("debug", KEY_DEBUG),
|
||||
FUSE_OPT_KEY("-d", KEY_DEBUG),
|
||||
FUSE_OPT_KEY("-h", KEY_HELP),
|
||||
FUSE_OPT_KEY("--help", KEY_HELP),
|
||||
FUSE_OPT_KEY("-V", 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
|
||||
};
|
||||
|
||||
static void
|
||||
dump_version(void)
|
||||
{
|
||||
fprintf(stderr, "FUSE library version: %d.%d\n", FUSE_MAJOR_VERSION,
|
||||
FUSE_MINOR_VERSION);
|
||||
fprintf(stderr, "OpenBSD FUSE library version: %d.%d\n",
|
||||
FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -70,12 +92,12 @@ ifuse_ll_opt_proc(void *data, const char *arg, int key,
|
||||
break;
|
||||
case KEY_DEBUG:
|
||||
ifuse_debug_init();
|
||||
return (1);
|
||||
return (0);
|
||||
default:
|
||||
fprintf(stderr, "fuse: unknown option -- %s\n", arg);
|
||||
}
|
||||
|
||||
return -1;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
struct fuse_session *
|
||||
@@ -89,11 +111,23 @@ fuse_lowlevel_new(struct fuse_args *fargs,
|
||||
if (se == 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);
|
||||
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)
|
||||
DPRINTF("libfuse: WARNING: filesystem supports creating files "
|
||||
"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);
|
||||
|
||||
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
|
||||
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 fuse_out_header hdr;
|
||||
struct iovec iov[2];
|
||||
size_t fbuf_size;
|
||||
|
||||
/* check for sanity */
|
||||
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_size = sizeof(fbuf->fb_hdr) + sizeof(fbuf->FD);
|
||||
|
||||
fbuf->fb_err = err;
|
||||
fbuf->fb_len = data_size;
|
||||
hdr.unique = fbuf->fb_uuid;
|
||||
hdr.len = sizeof(hdr) + data_size;
|
||||
hdr.error = err;
|
||||
|
||||
iov[0].iov_base = fbuf;
|
||||
iov[0].iov_len = fbuf_size;
|
||||
iov[0].iov_base = &hdr;
|
||||
iov[0].iov_len = sizeof(hdr);
|
||||
iov[1].iov_base = (void *)data;
|
||||
iov[1].iov_len = data_size;
|
||||
|
||||
DPRINTF("errno: %d", fbuf->fb_err);
|
||||
DPRINTF("errno: %d", err);
|
||||
|
||||
return fuse_chan_send(req->ch, iov, 2);
|
||||
}
|
||||
|
||||
static int
|
||||
ifuse_reply_ok(fuse_req_t req)
|
||||
{
|
||||
return ifuse_reply(req, NULL, 0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* fuse_reply_err takes a non-negated errno but kernel expects negated errno.
|
||||
*/
|
||||
int
|
||||
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);
|
||||
|
||||
@@ -173,50 +225,59 @@ DEF(fuse_reply_readlink);
|
||||
int
|
||||
fuse_reply_write(fuse_req_t req, size_t size)
|
||||
{
|
||||
struct fusebuf *fbuf;
|
||||
struct fuse_write_out out;
|
||||
|
||||
fbuf = req->fbuf;
|
||||
fbuf->fb_io_len = size;
|
||||
memset(&out, 0, sizeof(out));
|
||||
out.size = size;
|
||||
|
||||
return ifuse_reply_ok(req);
|
||||
return ifuse_reply(req, &out, sizeof(out), 0);
|
||||
}
|
||||
DEF(fuse_reply_write);
|
||||
|
||||
int
|
||||
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;
|
||||
fbuf->fb_attr = *stbuf;
|
||||
memset(&out, 0, sizeof(out));
|
||||
ifuse_stat2attr(stbuf, &out.attr);
|
||||
|
||||
return ifuse_reply_ok(req);
|
||||
return ifuse_reply(req, &out, sizeof(out), 0);
|
||||
}
|
||||
DEF(fuse_reply_attr);
|
||||
|
||||
int
|
||||
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);
|
||||
|
||||
return ifuse_reply_ok(req);
|
||||
return ifuse_reply(req, &out, sizeof(out), 0);
|
||||
}
|
||||
DEF(fuse_reply_entry);
|
||||
|
||||
int
|
||||
fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
|
||||
{
|
||||
struct fusebuf *fbuf;
|
||||
struct fuse_statfs_out out;
|
||||
|
||||
fbuf = req->fbuf;
|
||||
fbuf->fb_stat = *stbuf;
|
||||
memset(&out, 0, sizeof(out));
|
||||
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);
|
||||
|
||||
@@ -240,12 +301,13 @@ DEF(fuse_reply_create);
|
||||
int
|
||||
fuse_reply_open(fuse_req_t req, const struct fuse_file_info *ffi)
|
||||
{
|
||||
struct fusebuf *fbuf;
|
||||
struct fuse_open_out out;
|
||||
|
||||
fbuf = req->fbuf;
|
||||
fbuf->fb_io_fd = ffi->fh;
|
||||
memset(&out, 0, sizeof(out));
|
||||
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);
|
||||
|
||||
@@ -256,22 +318,19 @@ fuse_reply_none(fuse_req_t req)
|
||||
}
|
||||
DEF(fuse_reply_none);
|
||||
|
||||
#define GENERIC_DIRSIZ(NLEN) \
|
||||
((sizeof (struct dirent) - (MAXNAMLEN+1)) + ((NLEN+1 + 7) &~ 7))
|
||||
|
||||
size_t
|
||||
fuse_add_direntry(fuse_req_t req, char *buf, const size_t bufsize,
|
||||
const char *name, const struct stat *stbuf, off_t off)
|
||||
{
|
||||
struct dirent *dir;
|
||||
struct fuse_dirent *dir;
|
||||
size_t namelen;
|
||||
size_t len;
|
||||
|
||||
if (name == NULL)
|
||||
return (0);
|
||||
|
||||
namelen = strnlen(name, MAXNAMLEN);
|
||||
len = GENERIC_DIRSIZ(namelen);
|
||||
namelen = strlen(name);
|
||||
len = FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + namelen);
|
||||
|
||||
/* NULL buf is used to request size to be calculated */
|
||||
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)
|
||||
return (len);
|
||||
|
||||
dir = (struct dirent *)buf;
|
||||
dir->d_fileno = stbuf->st_ino;
|
||||
dir->d_type = IFTODT(stbuf->st_mode);
|
||||
dir->d_reclen = len;
|
||||
dir->d_off = off;
|
||||
strlcpy(dir->d_name, name, sizeof(dir->d_name));
|
||||
dir->d_namlen = strlen(dir->d_name);
|
||||
dir = (struct fuse_dirent *)buf;
|
||||
memset(dir, 0, len);
|
||||
dir->ino = stbuf->st_ino;
|
||||
dir->type = IFTODT(stbuf->st_mode);
|
||||
dir->off = off;
|
||||
dir->namelen = namelen;
|
||||
/* name is not NUL-terminated */
|
||||
memcpy(dir->name, name, namelen);
|
||||
|
||||
return (len);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
*
|
||||
@@ -126,7 +126,6 @@ void fuse_session_reset(struct fuse_session *);
|
||||
void fuse_session_process(struct fuse_session *, const char *, size_t,
|
||||
struct fuse_chan *);
|
||||
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
/*
|
||||
* 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_UID (1 << 1)
|
||||
|
||||
+17
-16
@@ -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>
|
||||
*
|
||||
@@ -33,7 +33,7 @@
|
||||
*/
|
||||
static fuse_req_t ireq;
|
||||
|
||||
const fuse_req_t
|
||||
fuse_req_t
|
||||
ifuse_req(void)
|
||||
{
|
||||
return (ireq);
|
||||
@@ -192,9 +192,6 @@ out:
|
||||
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.
|
||||
* 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 */
|
||||
buf = (char *) fd->buf + fd->len;
|
||||
resid = fd->size - fd->len;
|
||||
len = fuse_add_direntry(ifuse_req(), buf, resid, name, &attr, off);
|
||||
|
||||
fd->len += 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);
|
||||
}
|
||||
|
||||
@@ -769,23 +770,23 @@ ifuse_ops_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
|
||||
}
|
||||
|
||||
ictx_init(req);
|
||||
if (flags & FUSE_FATTR_MODE) {
|
||||
if (flags & FUSE_SET_ATTR_MODE) {
|
||||
if (f->op.chmod)
|
||||
err = f->op.chmod(realname, attr->st_mode);
|
||||
else
|
||||
err = ENOSYS;
|
||||
}
|
||||
|
||||
if (!err && (flags & FUSE_FATTR_UID || flags & FUSE_FATTR_GID)) {
|
||||
uid = (flags & FUSE_FATTR_UID) ? attr->st_uid : (uid_t)-1;
|
||||
gid = (flags & FUSE_FATTR_GID) ? attr->st_gid : (gid_t)-1;
|
||||
if (!err && (flags & FUSE_SET_ATTR_UID || flags & FUSE_SET_ATTR_GID)) {
|
||||
uid = (flags & FUSE_SET_ATTR_UID) ? attr->st_uid : (uid_t)-1;
|
||||
gid = (flags & FUSE_SET_ATTR_GID) ? attr->st_gid : (gid_t)-1;
|
||||
if (f->op.chown)
|
||||
err = f->op.chown(realname, uid, gid);
|
||||
else
|
||||
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) {
|
||||
ts[0] = attr->st_atim;
|
||||
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;
|
||||
}
|
||||
|
||||
if (!err && (flags & FUSE_FATTR_SIZE)) {
|
||||
if (!err && (flags & FUSE_SET_ATTR_SIZE)) {
|
||||
if (f->op.truncate)
|
||||
err = f->op.truncate(realname, attr->st_size);
|
||||
else
|
||||
@@ -918,16 +919,16 @@ ifuse_ops_destroy(void *userdata)
|
||||
}
|
||||
|
||||
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_vnode *vn;
|
||||
|
||||
vn = tree_get(&f->vnode_tree, ino);
|
||||
if (vn != NULL)
|
||||
unref_vn(f, vn);
|
||||
unref_vn(f, vn, nlookup);
|
||||
|
||||
fuse_reply_err(req, 0);
|
||||
fuse_reply_none(req);
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -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>
|
||||
*
|
||||
@@ -58,10 +58,11 @@ SPLAY_HEAD(tree, treeentry);
|
||||
|
||||
struct fuse_session {
|
||||
struct fuse_lowlevel_ops llops;
|
||||
struct fuse_chan *chan;
|
||||
void *userdata;
|
||||
int init;
|
||||
int exit;
|
||||
struct fuse_conn_info fci;
|
||||
struct fuse_chan *chan;
|
||||
void *userdata;
|
||||
int init;
|
||||
int exit;
|
||||
};
|
||||
|
||||
struct fuse_chan {
|
||||
@@ -122,12 +123,13 @@ struct fuse_req {
|
||||
#define FUSE_ROOT_INO ((ino_t)1)
|
||||
|
||||
/* fuse_ops.c */
|
||||
const fuse_req_t ifuse_req(void);
|
||||
fuse_req_t ifuse_req(void);
|
||||
|
||||
/* fuse_subr.c */
|
||||
struct fuse_vnode *alloc_vn(struct fuse *, const char *, ino_t, ino_t);
|
||||
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 *,
|
||||
ino_t);
|
||||
void remove_vnode_from_name_tree(struct fuse *,
|
||||
|
||||
+150
-115
@@ -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>
|
||||
*
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
#include <sys/uio.h>
|
||||
#include <sys/fusebuf.h>
|
||||
#include <errno.h>
|
||||
#include <err.h>
|
||||
#include <stdlib.h>
|
||||
@@ -83,8 +84,7 @@ int
|
||||
fuse_session_loop(struct fuse_session *se)
|
||||
{
|
||||
struct fuse_chan *ch;
|
||||
struct fusebuf fbuf;
|
||||
char *buf = (char *)&fbuf;
|
||||
char *buf;
|
||||
size_t bufsize;
|
||||
int err;
|
||||
|
||||
@@ -95,15 +95,25 @@ fuse_session_loop(struct fuse_session *se)
|
||||
if (ch == NULL)
|
||||
return (-1);
|
||||
|
||||
/* prepare the read and write data buffer */
|
||||
fbuf.fb_dat = calloc(1, FUSEBUFMAXSIZE);
|
||||
if (fbuf.fb_dat == NULL) {
|
||||
/*
|
||||
* Prepare the read data buffer. We need enough space for the header,
|
||||
* 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__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
bufsize = sizeof(fbuf.fb_hdr) + sizeof(fbuf.FD) + FUSEBUFMAXSIZE;
|
||||
|
||||
while (!fuse_session_exited(se)) {
|
||||
err = fuse_chan_recv(&ch, buf, bufsize);
|
||||
if (err == -EINTR || err == -ENODEV) {
|
||||
@@ -117,7 +127,7 @@ fuse_session_loop(struct fuse_session *se)
|
||||
fuse_session_process(se, buf, bufsize, ch);
|
||||
}
|
||||
|
||||
free(fbuf.fb_dat);
|
||||
free(buf);
|
||||
fuse_session_reset(se);
|
||||
|
||||
return (err == 0 ? 0 : -1);
|
||||
@@ -127,20 +137,31 @@ DEF(fuse_session_loop);
|
||||
static void
|
||||
iprocess_init(fuse_req_t req)
|
||||
{
|
||||
struct fusebuf *fbuf = req->fbuf;
|
||||
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("Kernel: %d.%d\t", major, minor);
|
||||
DPRINTF("libfuse: %d.%d\t", fci->proto_major, fci->proto_minor);
|
||||
|
||||
if (se->llops.init) {
|
||||
memset(&fci, 0, sizeof(fci));
|
||||
fci.proto_minor = FUSE_MINOR_VERSION;
|
||||
fci.proto_major = FUSE_MAJOR_VERSION;
|
||||
if (major != fci->proto_major && minor != fci->proto_minor)
|
||||
errx(1, "FUSE kernel protocol version mismatch");
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -162,7 +183,7 @@ iprocess_lookup(fuse_req_t req)
|
||||
{
|
||||
struct fusebuf *fbuf = req->fbuf;
|
||||
struct fuse_session *se = req->se;
|
||||
const char *name = fbuf->fb_dat;
|
||||
const char *name = fb_dat(fbuf->hdr);
|
||||
|
||||
DPRINTF("%-11s", "lookup");
|
||||
DPRINTF("inode: %llu\t", fbuf->fb_ino);
|
||||
@@ -179,17 +200,13 @@ iprocess_getattr(fuse_req_t req)
|
||||
{
|
||||
struct fusebuf *fbuf = req->fbuf;
|
||||
struct fuse_session *se = req->se;
|
||||
struct fuse_file_info ffi;
|
||||
|
||||
DPRINTF("%-11s", "getattr");
|
||||
DPRINTF("inode: %llu\t", fbuf->fb_ino);
|
||||
|
||||
if (se->llops.getattr) {
|
||||
memset(&ffi, 0, sizeof(ffi));
|
||||
ffi.fh = fbuf->fb_io_fd;
|
||||
ffi.fh_old = ffi.fh;
|
||||
|
||||
se->llops.getattr(req, fbuf->fb_ino, &ffi);
|
||||
/* fuse_getattr_in is unused */
|
||||
se->llops.getattr(req, fbuf->fb_ino, NULL);
|
||||
} else
|
||||
fuse_reply_err(req, ENOSYS);
|
||||
}
|
||||
@@ -200,18 +217,28 @@ iprocess_setattr(fuse_req_t req)
|
||||
struct fusebuf *fbuf = req->fbuf;
|
||||
struct fuse_session *se = req->se;
|
||||
struct fuse_file_info ffi;
|
||||
struct stat *stbuf = &fbuf->fb_attr;
|
||||
struct fb_io *io = fbtod(fbuf, struct fb_io *);
|
||||
struct stat stbuf;
|
||||
const int flags = fbuf->in.setattr.valid;
|
||||
|
||||
DPRINTF("%-11s", "setattr");
|
||||
DPRINTF("inode: %llu\t", fbuf->fb_ino);
|
||||
|
||||
if (se->llops.setattr) {
|
||||
memset(&ffi, 0, sizeof(ffi));
|
||||
ffi.fh = fbuf->fb_io_fd;
|
||||
ffi.fh = fbuf->in.setattr.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
|
||||
fuse_reply_err(req, ENOSYS);
|
||||
}
|
||||
@@ -227,7 +254,7 @@ iprocess_opendir(fuse_req_t req)
|
||||
DPRINTF("inode: %llu\t", fbuf->fb_ino);
|
||||
|
||||
memset(&ffi, 0, sizeof(ffi));
|
||||
ffi.flags = fbuf->fb_io_flags;
|
||||
ffi.flags = fbuf->in.open.flags;
|
||||
|
||||
if (se->llops.opendir)
|
||||
se->llops.opendir(req, fbuf->fb_ino, &ffi);
|
||||
@@ -244,16 +271,16 @@ iprocess_readdir(fuse_req_t req)
|
||||
|
||||
DPRINTF("%-11s", "readdir");
|
||||
DPRINTF("inode: %llu\t", fbuf->fb_ino);
|
||||
DPRINTF("size: %lu\t", fbuf->fb_io_len);
|
||||
DPRINTF("offset: %llu\t", fbuf->fb_io_off);
|
||||
DPRINTF("size: %u\t", fbuf->in.read.size);
|
||||
DPRINTF("offset: %llu\t", fbuf->in.read.offset);
|
||||
|
||||
if (se->llops.readdir) {
|
||||
memset(&ffi, 0, sizeof(ffi));
|
||||
ffi.fh = fbuf->fb_io_fd;
|
||||
ffi.fh = fbuf->in.read.fh;
|
||||
ffi.fh_old = ffi.fh;
|
||||
|
||||
se->llops.readdir(req, fbuf->fb_ino, fbuf->fb_io_len,
|
||||
fbuf->fb_io_off, &ffi);
|
||||
se->llops.readdir(req, fbuf->fb_ino, fbuf->in.read.size,
|
||||
fbuf->in.read.offset, &ffi);
|
||||
} else
|
||||
fuse_reply_err(req, ENOSYS);
|
||||
}
|
||||
@@ -270,9 +297,9 @@ iprocess_releasedir(fuse_req_t req)
|
||||
|
||||
if (se->llops.releasedir) {
|
||||
memset(&ffi, 0, sizeof(ffi));
|
||||
ffi.fh = fbuf->fb_io_fd;
|
||||
ffi.fh = fbuf->in.release.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);
|
||||
} else
|
||||
@@ -284,16 +311,17 @@ iprocess_mkdir(fuse_req_t req)
|
||||
{
|
||||
struct fusebuf *fbuf = req->fbuf;
|
||||
struct fuse_session *se = req->se;
|
||||
const char *name = fb_dat(fbuf->in.mkdir);
|
||||
|
||||
DPRINTF("%-11s", "mkdir");
|
||||
DPRINTF("inode: %llu\t", fbuf->fb_ino);
|
||||
DPRINTF("mode: %#6o\t", fbuf->fb_io_mode);
|
||||
DPRINTF("name: %s\t", fbuf->fb_dat);
|
||||
DPRINTF("mode: %#6o\t", fbuf->in.mkdir.mode);
|
||||
DPRINTF("name: %s\t", name);
|
||||
|
||||
if (se->llops.mkdir)
|
||||
se->llops.mkdir(req, fbuf->fb_ino, fbuf->fb_dat,
|
||||
fbuf->fb_io_mode);
|
||||
else
|
||||
if (se->llops.mkdir) {
|
||||
req->ctx.umask = fbuf->in.mkdir.umask;
|
||||
se->llops.mkdir(req, fbuf->fb_ino, name, fbuf->in.mkdir.mode);
|
||||
} else
|
||||
fuse_reply_err(req, ENOSYS);
|
||||
}
|
||||
|
||||
@@ -302,13 +330,14 @@ iprocess_rmdir(fuse_req_t req)
|
||||
{
|
||||
struct fusebuf *fbuf = req->fbuf;
|
||||
struct fuse_session *se = req->se;
|
||||
const char *name = fb_dat(fbuf->hdr);
|
||||
|
||||
DPRINTF("%-11s", "rmdir");
|
||||
DPRINTF("inode: %llu\t", fbuf->fb_ino);
|
||||
DPRINTF("name: %s\t", fbuf->fb_dat);
|
||||
DPRINTF("name: %s\t", name);
|
||||
|
||||
if (se->llops.rmdir)
|
||||
se->llops.rmdir(req, fbuf->fb_ino, fbuf->fb_dat);
|
||||
se->llops.rmdir(req, fbuf->fb_ino, name);
|
||||
else
|
||||
fuse_reply_err(req, ENOSYS);
|
||||
}
|
||||
@@ -318,16 +347,18 @@ iprocess_mknod(fuse_req_t req)
|
||||
{
|
||||
struct fusebuf *fbuf = req->fbuf;
|
||||
struct fuse_session *se = req->se;
|
||||
const char *name = fb_dat(fbuf->in.mknod);
|
||||
|
||||
DPRINTF("%-11s", "mknod");
|
||||
DPRINTF("inode: %llu\t", fbuf->fb_ino);
|
||||
DPRINTF("mode: %#6o\t", fbuf->fb_io_mode);
|
||||
DPRINTF("name: %s\t", fbuf->fb_dat);
|
||||
DPRINTF("mode: %#6o\t", fbuf->in.mknod.mode);
|
||||
DPRINTF("name: %s\t", name);
|
||||
|
||||
if (se->llops.mknod)
|
||||
se->llops.mknod(req, fbuf->fb_ino, fbuf->fb_dat,
|
||||
fbuf->fb_io_mode, fbuf->fb_io_rdev);
|
||||
else
|
||||
if (se->llops.mknod) {
|
||||
req->ctx.umask = fbuf->in.mknod.umask;
|
||||
se->llops.mknod(req, fbuf->fb_ino, name, fbuf->in.mknod.mode,
|
||||
fbuf->in.mknod.rdev);
|
||||
} else
|
||||
fuse_reply_err(req, ENOSYS);
|
||||
}
|
||||
|
||||
@@ -342,7 +373,7 @@ iprocess_open(fuse_req_t req)
|
||||
DPRINTF("inode: %llu\t", fbuf->fb_ino);
|
||||
|
||||
memset(&ffi, 0, sizeof(ffi));
|
||||
ffi.flags = fbuf->fb_io_flags;
|
||||
ffi.flags = fbuf->in.open.flags;
|
||||
|
||||
if (se->llops.open)
|
||||
se->llops.open(req, fbuf->fb_ino, &ffi);
|
||||
@@ -359,17 +390,17 @@ iprocess_read(fuse_req_t req)
|
||||
|
||||
DPRINTF("%-11s", "read");
|
||||
DPRINTF("inode: %llu\t", fbuf->fb_ino);
|
||||
DPRINTF("size: %lu\t", fbuf->fb_io_len);
|
||||
DPRINTF("offset: %llu\t", fbuf->fb_io_off);
|
||||
DPRINTF("size: %u\t", fbuf->in.read.size);
|
||||
DPRINTF("offset: %llu\t", fbuf->in.read.offset);
|
||||
|
||||
if (se->llops.read) {
|
||||
memset(&ffi, 0, sizeof(ffi));
|
||||
ffi.fh = fbuf->fb_io_fd;
|
||||
ffi.fh = fbuf->in.read.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,
|
||||
fbuf->fb_io_off, &ffi);
|
||||
se->llops.read(req, fbuf->fb_ino, fbuf->in.read.size,
|
||||
fbuf->in.read.offset, &ffi);
|
||||
} else
|
||||
fuse_reply_err(req, ENOSYS);
|
||||
}
|
||||
@@ -383,17 +414,17 @@ iprocess_write(fuse_req_t req)
|
||||
|
||||
DPRINTF("%-11s", "write");
|
||||
DPRINTF("inode: %llu\t", fbuf->fb_ino);
|
||||
DPRINTF("size: %lu\t", fbuf->fb_io_len);
|
||||
DPRINTF("offset: %llu\t", fbuf->fb_io_off);
|
||||
DPRINTF("size: %u\t", fbuf->in.write.size);
|
||||
DPRINTF("offset: %llu\t", fbuf->in.write.offset);
|
||||
|
||||
if (se->llops.write) {
|
||||
memset(&ffi, 0, sizeof(ffi));
|
||||
ffi.fh = fbuf->fb_io_fd;
|
||||
ffi.fh = fbuf->in.write.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,
|
||||
fbuf->fb_io_len, fbuf->fb_io_off, &ffi);
|
||||
se->llops.write(req, fbuf->fb_ino, fb_dat(fbuf->in.write),
|
||||
fbuf->in.write.size, fbuf->in.write.offset, &ffi);
|
||||
} else
|
||||
fuse_reply_err(req, ENOSYS);
|
||||
}
|
||||
@@ -404,20 +435,18 @@ iprocess_fsync(fuse_req_t req)
|
||||
struct fusebuf *fbuf = req->fbuf;
|
||||
struct fuse_session *se = req->se;
|
||||
struct fuse_file_info ffi;
|
||||
const uint32_t fsync_flags = fbuf->in.fsync.fsync_flags & 1;
|
||||
|
||||
DPRINTF("%-11s", "fsync");
|
||||
DPRINTF("inode: %llu\t", fbuf->fb_ino);
|
||||
DPRINTF("flags: %u", fsync_flags);
|
||||
|
||||
if (se->llops.fsync) {
|
||||
memset(&ffi, 0, sizeof(ffi));
|
||||
ffi.fh = fbuf->fb_io_fd;
|
||||
ffi.fh = fbuf->in.fsync.fh;
|
||||
ffi.fh_old = ffi.fh;
|
||||
|
||||
/*
|
||||
* fdatasync(2) is just a wrapper around fsync(2) so datasync
|
||||
* is always false.
|
||||
*/
|
||||
se->llops.fsync(req, fbuf->fb_ino, 0 /* datasync */, &ffi);
|
||||
se->llops.fsync(req, fbuf->fb_ino, fsync_flags, &ffi);
|
||||
} else
|
||||
fuse_reply_err(req, ENOSYS);
|
||||
}
|
||||
@@ -434,7 +463,7 @@ iprocess_flush(fuse_req_t req)
|
||||
|
||||
if (se->llops.flush) {
|
||||
memset(&ffi, 0, sizeof(ffi));
|
||||
ffi.fh = fbuf->fb_io_fd;
|
||||
ffi.fh = fbuf->in.flush.fh;
|
||||
ffi.fh_old = ffi.fh;
|
||||
ffi.flush = 1;
|
||||
|
||||
@@ -455,9 +484,9 @@ iprocess_release(fuse_req_t req)
|
||||
|
||||
if (se->llops.release) {
|
||||
memset(&ffi, 0, sizeof(ffi));
|
||||
ffi.fh = fbuf->fb_io_fd;
|
||||
ffi.fh = fbuf->in.release.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);
|
||||
} else
|
||||
@@ -469,14 +498,16 @@ iprocess_forget(fuse_req_t req)
|
||||
{
|
||||
struct fusebuf *fbuf = req->fbuf;
|
||||
struct fuse_session *se = req->se;
|
||||
const uint64_t nlookup = fbuf->in.forget.nlookup;
|
||||
|
||||
DPRINTF("%-11s", "forget");
|
||||
DPRINTF("inode: %llu\t", fbuf->fb_ino);
|
||||
DPRINTF("nlookup: %llu\t", nlookup);
|
||||
|
||||
if (se->llops.forget)
|
||||
se->llops.forget(req, fbuf->fb_ino, 1 /* TODO */);
|
||||
se->llops.forget(req, fbuf->fb_ino, nlookup);
|
||||
else
|
||||
fuse_reply_err(req, 0);
|
||||
fuse_reply_none(req);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -488,9 +519,9 @@ iprocess_symlink(fuse_req_t req)
|
||||
const char *name;
|
||||
int len;
|
||||
|
||||
name = fbuf->fb_dat;
|
||||
len = strnlen(name, fbuf->fb_len);
|
||||
target = &fbuf->fb_dat[len + 1];
|
||||
name = fb_dat(fbuf->hdr);
|
||||
len = strlen(name);
|
||||
target = &name[len + 1];
|
||||
|
||||
DPRINTF("%-11s", "symlink");
|
||||
DPRINTF("inode: %llu\t", fbuf->fb_ino);
|
||||
@@ -523,15 +554,16 @@ iprocess_link(fuse_req_t req)
|
||||
{
|
||||
struct fusebuf *fbuf = req->fbuf;
|
||||
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("inode: %llu\t", fbuf->fb_ino);
|
||||
DPRINTF("inode: %llu\t", fbuf->fb_io_ino);
|
||||
DPRINTF("inode: %llu\t", oldnodeid);
|
||||
DPRINTF("name: %s\t", name);
|
||||
|
||||
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
|
||||
fuse_reply_err(req, ENOSYS);
|
||||
}
|
||||
@@ -541,13 +573,14 @@ iprocess_unlink(fuse_req_t req)
|
||||
{
|
||||
struct fusebuf *fbuf = req->fbuf;
|
||||
struct fuse_session *se = req->se;
|
||||
const char *name = fb_dat(fbuf->hdr);
|
||||
|
||||
DPRINTF("%-11s", "unlink");
|
||||
DPRINTF("inode: %llu\t", fbuf->fb_ino);
|
||||
DPRINTF("name: %s\t", fbuf->fb_dat);
|
||||
DPRINTF("name: %s\t", name);
|
||||
|
||||
if (se->llops.unlink)
|
||||
se->llops.unlink(req, fbuf->fb_ino, fbuf->fb_dat);
|
||||
se->llops.unlink(req, fbuf->fb_ino, name);
|
||||
else
|
||||
fuse_reply_err(req, ENOSYS);
|
||||
}
|
||||
@@ -557,23 +590,23 @@ iprocess_rename(fuse_req_t req)
|
||||
{
|
||||
struct fusebuf *fbuf = req->fbuf;
|
||||
struct fuse_session *se = req->se;
|
||||
const uint64_t newdir = fbuf->in.rename.newdir;
|
||||
const char *target;
|
||||
const char *name;
|
||||
int len;
|
||||
|
||||
name = fbuf->fb_dat;
|
||||
len = strnlen(name, fbuf->fb_len);
|
||||
target = &fbuf->fb_dat[len + 1];
|
||||
name = fb_dat(fbuf->in.rename);
|
||||
len = strlen(name);
|
||||
target = &name[len + 1];
|
||||
|
||||
DPRINTF("%-11s", "rename");
|
||||
DPRINTF("inode: %llu\t", fbuf->fb_ino);
|
||||
DPRINTF("name: %s\t", name);
|
||||
DPRINTF("inode: %llu\t", fbuf->fb_io_ino);
|
||||
DPRINTF("newdir: %llu\t", newdir);
|
||||
DPRINTF("target: %s\t", target);
|
||||
|
||||
if (se->llops.rename)
|
||||
se->llops.rename(req, fbuf->fb_ino, name, fbuf->fb_io_ino,
|
||||
target);
|
||||
se->llops.rename(req, fbuf->fb_ino, name, newdir, target);
|
||||
else
|
||||
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.gid = fbuf->fb_gid;
|
||||
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 */
|
||||
if (len < sizeof(fbuf->fb_hdr))
|
||||
if (len < sizeof(fbuf->hdr))
|
||||
return;
|
||||
|
||||
if (len < sizeof(fbuf->fb_hdr) + sizeof(fbuf->FD) + fbuf->fb_len)
|
||||
if (len < fbuf->hdr.len)
|
||||
return;
|
||||
|
||||
switch (fbuf->fb_type) {
|
||||
case FBT_INIT:
|
||||
case FUSE_INIT:
|
||||
iprocess_init(&req);
|
||||
break;
|
||||
case FBT_DESTROY:
|
||||
case FUSE_DESTROY:
|
||||
iprocess_destroy(&req);
|
||||
break;
|
||||
case FBT_LOOKUP:
|
||||
case FUSE_LOOKUP:
|
||||
iprocess_lookup(&req);
|
||||
break;
|
||||
case FBT_GETATTR:
|
||||
case FUSE_GETATTR:
|
||||
iprocess_getattr(&req);
|
||||
break;
|
||||
case FBT_SETATTR:
|
||||
case FUSE_SETATTR:
|
||||
iprocess_setattr(&req);
|
||||
break;
|
||||
case FBT_OPENDIR:
|
||||
case FUSE_OPENDIR:
|
||||
iprocess_opendir(&req);
|
||||
break;
|
||||
case FBT_READDIR:
|
||||
case FUSE_READDIR:
|
||||
iprocess_readdir(&req);
|
||||
break;
|
||||
case FBT_RELEASEDIR:
|
||||
case FUSE_RELEASEDIR:
|
||||
iprocess_releasedir(&req);
|
||||
break;
|
||||
case FBT_MKDIR:
|
||||
case FUSE_MKDIR:
|
||||
iprocess_mkdir(&req);
|
||||
break;
|
||||
case FBT_RMDIR:
|
||||
case FUSE_RMDIR:
|
||||
iprocess_rmdir(&req);
|
||||
break;
|
||||
case FBT_MKNOD:
|
||||
case FUSE_MKNOD:
|
||||
iprocess_mknod(&req);
|
||||
break;
|
||||
case FBT_OPEN:
|
||||
case FUSE_OPEN:
|
||||
iprocess_open(&req);
|
||||
break;
|
||||
case FBT_READ:
|
||||
case FUSE_READ:
|
||||
iprocess_read(&req);
|
||||
break;
|
||||
case FBT_WRITE:
|
||||
case FUSE_WRITE:
|
||||
iprocess_write(&req);
|
||||
break;
|
||||
case FBT_FSYNC:
|
||||
case FUSE_FSYNC:
|
||||
iprocess_fsync(&req);
|
||||
break;
|
||||
case FBT_FLUSH:
|
||||
case FUSE_FLUSH:
|
||||
iprocess_flush(&req);
|
||||
break;
|
||||
case FBT_RELEASE:
|
||||
case FUSE_RELEASE:
|
||||
iprocess_release(&req);
|
||||
break;
|
||||
case FBT_RECLAIM:
|
||||
case FUSE_FORGET:
|
||||
iprocess_forget(&req);
|
||||
break;
|
||||
case FBT_SYMLINK:
|
||||
case FUSE_SYMLINK:
|
||||
iprocess_symlink(&req);
|
||||
break;
|
||||
case FBT_READLINK:
|
||||
case FUSE_READLINK:
|
||||
iprocess_readlink(&req);
|
||||
break;
|
||||
case FBT_LINK:
|
||||
case FUSE_LINK:
|
||||
iprocess_link(&req);
|
||||
break;
|
||||
case FBT_UNLINK:
|
||||
case FUSE_UNLINK:
|
||||
iprocess_unlink(&req);
|
||||
break;
|
||||
case FBT_RENAME:
|
||||
case FUSE_RENAME:
|
||||
iprocess_rename(&req);
|
||||
break;
|
||||
case FBT_STATFS:
|
||||
case FUSE_STATFS:
|
||||
iprocess_statfs(&req);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -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>
|
||||
*
|
||||
@@ -69,13 +69,14 @@ ref_vn(struct fuse_vnode *vn)
|
||||
}
|
||||
|
||||
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);
|
||||
remove_vnode_from_name_tree(f, vn);
|
||||
if (vn->parent != NULL)
|
||||
unref_vn(f, vn->parent);
|
||||
unref_vn(f, vn->parent, 1);
|
||||
free(vn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
*
|
||||
@@ -303,7 +303,6 @@ fuseread(dev_t dev, struct uio *uio, int ioflag)
|
||||
{
|
||||
struct fuse_d *fd;
|
||||
struct fusebuf *fbuf;
|
||||
struct fb_hdr hdr;
|
||||
int error = 0;
|
||||
|
||||
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 */
|
||||
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) {
|
||||
error = EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Do not send kernel pointers */
|
||||
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);
|
||||
error = uiomove(&fbuf->hdr, sizeof(fbuf->hdr), uio);
|
||||
if (error)
|
||||
goto end;
|
||||
error = uiomove(&fbuf->FD, sizeof(fbuf->FD), uio);
|
||||
error = uiomove(&fbuf->op, fbuf->op_in_len, uio);
|
||||
if (error)
|
||||
goto end;
|
||||
if (fbuf->fb_len > 0) {
|
||||
@@ -367,8 +362,6 @@ fuseread(dev_t dev, struct uio *uio, int ioflag)
|
||||
#ifdef FUSE_DEBUG
|
||||
fuse_dump_buff((char *)fbuf, sizeof(struct fusebuf));
|
||||
#endif
|
||||
/* Restore kernel pointers */
|
||||
memcpy(&fbuf->fb_next, &hdr.fh_next, sizeof(fbuf->fb_next));
|
||||
|
||||
free(fbuf->fb_dat, M_FUSEFS, fbuf->fb_len);
|
||||
fbuf->fb_dat = NULL;
|
||||
@@ -376,6 +369,13 @@ fuseread(dev_t dev, struct uio *uio, int ioflag)
|
||||
/* Move the fbuf to the wait queue */
|
||||
SIMPLEQ_REMOVE_HEAD(&fd->fd_fbufs_in, fb_next);
|
||||
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);
|
||||
stat_fbufs_wait++;
|
||||
|
||||
@@ -391,37 +391,35 @@ fusewrite(dev_t dev, struct uio *uio, int ioflag)
|
||||
struct fusebuf *lastfbuf;
|
||||
struct fuse_d *fd;
|
||||
struct fusebuf *fbuf;
|
||||
struct fb_hdr hdr;
|
||||
struct fuse_out_header hdr;
|
||||
int error = 0;
|
||||
|
||||
fd = fuse_lookup(minor(dev));
|
||||
if (fd == NULL)
|
||||
return (ENXIO);
|
||||
return (ENODEV);
|
||||
|
||||
/* Check for sanity - must receive more than just the header */
|
||||
if (uio->uio_resid <= sizeof(hdr)) {
|
||||
/* Check for sanity - must receive at least the header */
|
||||
if (uio->uio_resid < sizeof(hdr)) {
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Read the header */
|
||||
if ((error = uiomove(&hdr, sizeof(hdr), uio)) != 0)
|
||||
goto out;
|
||||
|
||||
/* Check for sanity */
|
||||
if (hdr.fh_len > FUSEBUFMAXSIZE) {
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* We get the whole fusebuf or nothing */
|
||||
if (uio->uio_resid != sizeof(fbuf->FD) + hdr.fh_len) {
|
||||
error = EINVAL;
|
||||
/*
|
||||
* A unique value of zero means daemon is notifying us and hdr.error
|
||||
* contains notification type. Currently unsupported.
|
||||
*/
|
||||
if (hdr.unique == 0) {
|
||||
error = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* looking for uuid in fd_fbufs_wait */
|
||||
SIMPLEQ_FOREACH(fbuf, &fd->fd_fbufs_wait, fb_next) {
|
||||
if (fbuf->fb_uuid == hdr.fh_uuid)
|
||||
if (fbuf->fb_uuid == hdr.unique)
|
||||
break;
|
||||
|
||||
lastfbuf = fbuf;
|
||||
@@ -432,49 +430,93 @@ fusewrite(dev_t dev, struct uio *uio, int ioflag)
|
||||
}
|
||||
|
||||
/* Update fb_hdr */
|
||||
fbuf->fb_len = hdr.fh_len;
|
||||
fbuf->fb_err = hdr.fh_err;
|
||||
fbuf->fb_ino = hdr.fh_ino;
|
||||
fbuf->fb_err = -hdr.error;
|
||||
|
||||
/* Check for corrupted fbufs */
|
||||
if ((fbuf->fb_len && fbuf->fb_err) || fbuf->fb_len > fbuf->fb_io_len ||
|
||||
SIMPLEQ_EMPTY(&fd->fd_fbufs_wait)) {
|
||||
printf("fuse: dropping corrupted fusebuf: %zu: %zu: %d\n",
|
||||
fbuf->fb_io_len, fbuf->fb_len, fbuf->fb_err);
|
||||
/* Don't expect out struct or data if there was an error */
|
||||
if (fbuf->fb_err) {
|
||||
if (uio->uio_resid > 0) {
|
||||
error = EINVAL;
|
||||
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;
|
||||
fbuf->fb_err = EIO;
|
||||
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) {
|
||||
fbuf->fb_dat = malloc(fbuf->fb_len, M_FUSEFS,
|
||||
M_WAITOK | M_ZERO);
|
||||
error = uiomove(fbuf->fb_dat, fbuf->fb_len, uio);
|
||||
if (error) {
|
||||
fbuf->fb_dat = malloc(fbuf->fb_len, M_FUSEFS, M_WAITOK);
|
||||
if ((error = uiomove(fbuf->fb_dat, fbuf->fb_len, uio)) != 0) {
|
||||
free(fbuf->fb_dat, M_FUSEFS, fbuf->fb_len);
|
||||
fbuf->fb_dat = NULL;
|
||||
fbuf->fb_err = error;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef FUSE_DEBUG
|
||||
fuse_dump_buff((char *)fbuf, sizeof(struct fusebuf));
|
||||
#endif
|
||||
|
||||
switch (fbuf->fb_type) {
|
||||
case FBT_INIT:
|
||||
if (fbuf->fb_type == FUSE_INIT && fbuf->fb_err == 0) {
|
||||
/*
|
||||
* We don't support userspace with a smaller major version and
|
||||
* it's up to userspace implementations to fall back to our
|
||||
* version if they are capable of a later version.
|
||||
*/
|
||||
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;
|
||||
break ;
|
||||
case FBT_DESTROY:
|
||||
fd->fd_fmp = NULL;
|
||||
break ;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
/* Remove the fbuf from the wait queue */
|
||||
if (fbuf == SIMPLEQ_FIRST(&fd->fd_fbufs_wait))
|
||||
@@ -488,7 +530,7 @@ end:
|
||||
* FBT_INIT doesn't expect a response. Otherwise let the VFS
|
||||
* 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);
|
||||
else
|
||||
wakeup(fbuf);
|
||||
|
||||
@@ -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>
|
||||
*
|
||||
@@ -36,8 +36,8 @@ fusefs_file_open(struct fusefs_mnt *fmp, struct fusefs_node *ip,
|
||||
return (0);
|
||||
|
||||
fbuf = fb_setup(0, ip->i_number,
|
||||
((isdir) ? FBT_OPENDIR : FBT_OPEN), p);
|
||||
fbuf->fb_io_flags = flags;
|
||||
((isdir) ? FUSE_OPENDIR : FUSE_OPEN), p);
|
||||
fbuf->op.in.open.flags = flags;
|
||||
|
||||
error = fb_queue(fmp->dev, fbuf);
|
||||
if (error) {
|
||||
@@ -45,7 +45,7 @@ fusefs_file_open(struct fusefs_mnt *fmp, struct fusefs_node *ip,
|
||||
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;
|
||||
|
||||
fb_delete(fbuf);
|
||||
@@ -61,13 +61,13 @@ fusefs_file_close(struct fusefs_mnt *fmp, struct fusefs_node * ip,
|
||||
|
||||
if (fmp->sess_init) {
|
||||
fbuf = fb_setup(0, ip->i_number,
|
||||
((isdir) ? FBT_RELEASEDIR : FBT_RELEASE), p);
|
||||
fbuf->fb_io_fd = ip->fufh[fufh_type].fh_id;
|
||||
fbuf->fb_io_flags = flags;
|
||||
((isdir) ? FUSE_RELEASEDIR : FUSE_RELEASE), p);
|
||||
fbuf->op.in.release.fh = ip->fufh[fufh_type].fh_id;
|
||||
fbuf->op.in.release.flags = flags;
|
||||
|
||||
error = fb_queue(fmp->dev, fbuf);
|
||||
if (error && (error != ENOSYS))
|
||||
printf("fusefs: file error %d\n", error);
|
||||
printf("fusefs: file close error: %d\n", error);
|
||||
|
||||
fb_delete(fbuf);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
*
|
||||
@@ -82,7 +82,7 @@ fusefs_lookup(void *v)
|
||||
|
||||
/* got a real entry */
|
||||
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);
|
||||
fbuf->fb_dat[cnp->cn_namelen] = '\0';
|
||||
@@ -119,8 +119,8 @@ fusefs_lookup(void *v)
|
||||
return (ENOENT);
|
||||
}
|
||||
|
||||
nid = fbuf->fb_ino;
|
||||
nvtype = IFTOVT(fbuf->fb_attr.st_mode);
|
||||
nid = fbuf->op.out.entry.nodeid;
|
||||
nvtype = IFTOVT(fbuf->op.out.entry.attr.mode);
|
||||
fb_delete(fbuf);
|
||||
|
||||
/*
|
||||
@@ -159,6 +159,7 @@ fusefs_lookup(void *v)
|
||||
goto reclaim;
|
||||
|
||||
tdp->v_type = nvtype;
|
||||
VTOI(tdp)->i_parent_cache = dp->i_number;
|
||||
*vpp = tdp;
|
||||
cnp->cn_flags |= SAVENAME;
|
||||
|
||||
@@ -193,6 +194,9 @@ fusefs_lookup(void *v)
|
||||
}
|
||||
*vpp = tdp;
|
||||
|
||||
/* Didn't actually make a call but vget increments lookup */
|
||||
VTOI(tdp)->nlookup--;
|
||||
|
||||
} else if (nid == dp->i_number) {
|
||||
vref(vdp);
|
||||
*vpp = vdp;
|
||||
@@ -203,6 +207,7 @@ fusefs_lookup(void *v)
|
||||
goto reclaim;
|
||||
|
||||
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
|
||||
@@ -222,11 +227,10 @@ fusefs_lookup(void *v)
|
||||
return (error);
|
||||
|
||||
reclaim:
|
||||
if (nid != dp->i_number && nid != FUSE_ROOTINO) {
|
||||
fbuf = fb_setup(0, nid, FBT_RECLAIM, p);
|
||||
if (fb_queue(fmp->dev, fbuf))
|
||||
printf("fusefs: libfuse vnode reclaim failed\n");
|
||||
fb_delete(fbuf);
|
||||
if (nid != dp->i_number && nid != FUSE_ROOT_ID) {
|
||||
fbuf = fb_setup(0, nid, FUSE_FORGET, p);
|
||||
fbuf->op.in.forget.nlookup = 1;
|
||||
fuse_device_queue_fbuf(fmp->dev, fbuf); /* no response */
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
*
|
||||
@@ -68,7 +68,7 @@ const struct vfsops fusefs_vfsops = {
|
||||
|
||||
struct pool fusefs_fbuf_pool;
|
||||
|
||||
#define PENDING 2 /* FBT_INIT reply not yet received */
|
||||
#define PENDING 2 /* FUSE_INIT reply not yet received */
|
||||
|
||||
int
|
||||
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
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
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 */
|
||||
fuse_device_queue_fbuf(fmp->dev, fbuf);
|
||||
@@ -157,7 +167,7 @@ fusefs_unmount(struct mount *mp, int mntflags, struct proc *p)
|
||||
return (error);
|
||||
|
||||
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);
|
||||
|
||||
@@ -182,7 +192,7 @@ fusefs_root(struct mount *mp, struct vnode **vpp)
|
||||
struct vnode *nvp;
|
||||
int error;
|
||||
|
||||
if ((error = VFS_VGET(mp, FUSE_ROOTINO, &nvp)) != 0)
|
||||
if ((error = VFS_VGET(mp, FUSE_ROOT_ID, &nvp)) != 0)
|
||||
return (error);
|
||||
|
||||
nvp->v_type = VDIR;
|
||||
@@ -214,10 +224,10 @@ fusefs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p)
|
||||
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
|
||||
* 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.
|
||||
*/
|
||||
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_namemax = 0;
|
||||
} 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);
|
||||
|
||||
@@ -240,15 +250,25 @@ fusefs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p)
|
||||
return (error);
|
||||
}
|
||||
|
||||
sbp->f_bavail = fbuf->fb_stat.f_bavail;
|
||||
sbp->f_bfree = fbuf->fb_stat.f_bfree;
|
||||
sbp->f_blocks = fbuf->fb_stat.f_blocks;
|
||||
sbp->f_files = fbuf->fb_stat.f_files;
|
||||
sbp->f_ffree = fbuf->fb_stat.f_ffree;
|
||||
sbp->f_favail = fbuf->fb_stat.f_favail;
|
||||
sbp->f_bsize = fbuf->fb_stat.f_frsize;
|
||||
sbp->f_iosize = fbuf->fb_stat.f_bsize;
|
||||
sbp->f_namemax = fbuf->fb_stat.f_namemax;
|
||||
sbp->f_blocks = fbuf->op.out.statfs.st.blocks;
|
||||
sbp->f_bfree = fbuf->op.out.statfs.st.bfree;
|
||||
sbp->f_bavail = fbuf->op.out.statfs.st.bavail;
|
||||
sbp->f_files = fbuf->op.out.statfs.st.files;
|
||||
sbp->f_ffree = fbuf->op.out.statfs.st.ffree;
|
||||
sbp->f_favail = fbuf->op.out.statfs.st.ffree;
|
||||
sbp->f_bsize = fbuf->op.out.statfs.st.frsize;
|
||||
sbp->f_namemax = fbuf->op.out.statfs.st.namelen;
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -276,8 +296,10 @@ retry:
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* if not create it
|
||||
@@ -295,6 +317,7 @@ retry:
|
||||
ip->i_vnode = nvp;
|
||||
ip->i_dev = fmp->dev;
|
||||
ip->i_number = ino;
|
||||
ip->nlookup = 1;
|
||||
|
||||
for (i = 0; i < FUFH_MAXTYPE; i++)
|
||||
ip->fufh[i].fh_type = FUFH_INVALID;
|
||||
@@ -311,7 +334,7 @@ retry:
|
||||
|
||||
ip->i_ump = fmp;
|
||||
|
||||
if (ino == FUSE_ROOTINO)
|
||||
if (ino == FUSE_ROOT_ID)
|
||||
nvp->v_flag |= VROOT;
|
||||
else {
|
||||
/*
|
||||
|
||||
+215
-135
@@ -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>
|
||||
*
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <sys/dirent.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/filedesc.h>
|
||||
#include <sys/lockf.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mount.h>
|
||||
@@ -329,8 +330,8 @@ fusefs_close(void *v)
|
||||
if (ip->fufh[fufh_type].fh_type == FUFH_INVALID)
|
||||
return (EBADF);
|
||||
|
||||
fbuf = fb_setup(0, ip->i_number, FBT_FLUSH, ap->a_p);
|
||||
fbuf->fb_io_fd = ip->fufh[fufh_type].fh_id;
|
||||
fbuf = fb_setup(0, ip->i_number, FUSE_FLUSH, ap->a_p);
|
||||
fbuf->op.in.flush.fh = ip->fufh[fufh_type].fh_id;
|
||||
error = fb_queue(fmp->dev, fbuf);
|
||||
fb_delete(fbuf);
|
||||
if (error == ENOSYS) {
|
||||
@@ -405,7 +406,7 @@ fusefs_getattr(void *v)
|
||||
struct ucred *cred = p->p_ucred;
|
||||
struct fusefs_node *ip;
|
||||
struct fusebuf *fbuf;
|
||||
struct stat *st;
|
||||
struct fuse_attr *st;
|
||||
int error = 0;
|
||||
|
||||
ip = VTOI(vp);
|
||||
@@ -423,7 +424,7 @@ fusefs_getattr(void *v)
|
||||
vap->va_mode = S_IRUSR | S_IXUSR;
|
||||
else
|
||||
vap->va_mode = S_IRWXU;
|
||||
vap->va_nlink = 1;
|
||||
vap->va_nlink = 2;
|
||||
vap->va_uid = 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];
|
||||
@@ -441,7 +442,7 @@ fusefs_getattr(void *v)
|
||||
if (!fmp->sess_init)
|
||||
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);
|
||||
if (error) {
|
||||
@@ -449,31 +450,36 @@ fusefs_getattr(void *v)
|
||||
return (error);
|
||||
}
|
||||
|
||||
st = &fbuf->fb_attr;
|
||||
st = &fbuf->op.out.attr.attr;
|
||||
|
||||
/* opendir(3) expects blocksize to be greater than zero. */
|
||||
if (st->st_blksize == 0)
|
||||
st->st_blksize = S_BLKSIZE;
|
||||
if (st->blksize == 0)
|
||||
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 */
|
||||
if (st->st_blocks == 0 && st->st_size > 0)
|
||||
st->st_blocks = (st->st_size + S_BLKSIZE - 1) / S_BLKSIZE;
|
||||
if (st->blocks == 0 && st->size > 0)
|
||||
st->blocks = (st->size + S_BLKSIZE - 1) / S_BLKSIZE;
|
||||
|
||||
memset(vap, 0, sizeof(*vap));
|
||||
vap->va_type = IFTOVT(st->st_mode);
|
||||
vap->va_mode = st->st_mode & ~S_IFMT;
|
||||
vap->va_nlink = st->st_nlink;
|
||||
vap->va_uid = st->st_uid;
|
||||
vap->va_gid = st->st_gid;
|
||||
vap->va_type = IFTOVT(st->mode);
|
||||
vap->va_mode = st->mode & ~S_IFMT;
|
||||
vap->va_nlink = st->nlink;
|
||||
vap->va_uid = st->uid;
|
||||
vap->va_gid = st->gid;
|
||||
vap->va_fsid = fmp->mp->mnt_stat.f_fsid.val[0];
|
||||
vap->va_fileid = st->st_ino;
|
||||
vap->va_size = st->st_size;
|
||||
vap->va_blocksize = st->st_blksize;
|
||||
vap->va_atime = st->st_atim;
|
||||
vap->va_mtime = st->st_mtim;
|
||||
vap->va_ctime = st->st_ctim;
|
||||
vap->va_rdev = st->st_rdev;
|
||||
vap->va_bytes = st->st_blocks * S_BLKSIZE;
|
||||
vap->va_fileid = st->ino;
|
||||
vap->va_size = st->size;
|
||||
vap->va_blocksize = st->blksize;
|
||||
vap->va_atime.tv_sec = st->atime;
|
||||
vap->va_atime.tv_nsec = st->atimensec;
|
||||
vap->va_mtime.tv_sec = st->mtime;
|
||||
vap->va_mtime.tv_nsec = st->mtimensec;
|
||||
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);
|
||||
return (error);
|
||||
@@ -490,7 +496,6 @@ fusefs_setattr(void *v)
|
||||
struct proc *p = ap->a_p;
|
||||
struct fusefs_mnt *fmp;
|
||||
struct fusebuf *fbuf;
|
||||
struct fb_io *io;
|
||||
int error = 0;
|
||||
|
||||
fmp = (struct fusefs_mnt *)ip->i_ump;
|
||||
@@ -516,17 +521,16 @@ fusefs_setattr(void *v)
|
||||
if (fmp->undef_op & UNDEF_SETATTR)
|
||||
return (ENOSYS);
|
||||
|
||||
fbuf = fb_setup(sizeof(*io), ip->i_number, FBT_SETATTR, p);
|
||||
io = fbtod(fbuf, struct fb_io *);
|
||||
io->fi_flags = 0;
|
||||
fbuf = fb_setup(0, ip->i_number, FUSE_SETATTR, p);
|
||||
fbuf->op.in.setattr.valid = 0;
|
||||
|
||||
if (vap->va_uid != (uid_t)VNOVAL) {
|
||||
if (vp->v_mount->mnt_flag & MNT_RDONLY) {
|
||||
error = EROFS;
|
||||
goto out;
|
||||
}
|
||||
fbuf->fb_attr.st_uid = vap->va_uid;
|
||||
io->fi_flags |= FUSE_FATTR_UID;
|
||||
fbuf->op.in.setattr.uid |= vap->va_uid;
|
||||
fbuf->op.in.setattr.valid |= FUSE_FATTR_UID;
|
||||
}
|
||||
|
||||
if (vap->va_gid != (gid_t)VNOVAL) {
|
||||
@@ -534,8 +538,8 @@ fusefs_setattr(void *v)
|
||||
error = EROFS;
|
||||
goto out;
|
||||
}
|
||||
fbuf->fb_attr.st_gid = vap->va_gid;
|
||||
io->fi_flags |= FUSE_FATTR_GID;
|
||||
fbuf->op.in.setattr.gid |= vap->va_gid;
|
||||
fbuf->op.in.setattr.valid |= FUSE_FATTR_GID;
|
||||
}
|
||||
|
||||
if (vap->va_size != VNOVAL) {
|
||||
@@ -559,8 +563,8 @@ fusefs_setattr(void *v)
|
||||
break;
|
||||
}
|
||||
|
||||
fbuf->fb_attr.st_size = vap->va_size;
|
||||
io->fi_flags |= FUSE_FATTR_SIZE;
|
||||
fbuf->op.in.setattr.size |= vap->va_size;
|
||||
fbuf->op.in.setattr.valid |= FUSE_FATTR_SIZE;
|
||||
}
|
||||
|
||||
if (vap->va_atime.tv_nsec != VNOVAL) {
|
||||
@@ -568,8 +572,9 @@ fusefs_setattr(void *v)
|
||||
error = EROFS;
|
||||
goto out;
|
||||
}
|
||||
fbuf->fb_attr.st_atim = vap->va_atime;
|
||||
io->fi_flags |= FUSE_FATTR_ATIME;
|
||||
fbuf->op.in.setattr.atime = vap->va_atime.tv_sec;
|
||||
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) {
|
||||
@@ -577,8 +582,9 @@ fusefs_setattr(void *v)
|
||||
error = EROFS;
|
||||
goto out;
|
||||
}
|
||||
fbuf->fb_attr.st_mtim = vap->va_mtime;
|
||||
io->fi_flags |= FUSE_FATTR_MTIME;
|
||||
fbuf->op.in.setattr.mtime = vap->va_mtime.tv_sec;
|
||||
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) */
|
||||
|
||||
@@ -599,11 +605,11 @@ fusefs_setattr(void *v)
|
||||
goto out;
|
||||
}
|
||||
|
||||
fbuf->fb_attr.st_mode = vap->va_mode & ALLPERMS;
|
||||
io->fi_flags |= FUSE_FATTR_MODE;
|
||||
fbuf->op.in.setattr.mode = vap->va_mode & ALLPERMS;
|
||||
fbuf->op.in.setattr.valid |= FUSE_FATTR_MODE;
|
||||
}
|
||||
|
||||
if (!io->fi_flags) {
|
||||
if (!fbuf->op.in.setattr.valid) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -667,9 +673,9 @@ fusefs_link(void *v)
|
||||
}
|
||||
|
||||
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);
|
||||
fbuf->fb_dat[cnp->cn_namelen] = '\0';
|
||||
|
||||
@@ -728,7 +734,7 @@ fusefs_symlink(void *v)
|
||||
len = strlen(target) + 1;
|
||||
|
||||
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);
|
||||
fbuf->fb_dat[cnp->cn_namelen] = '\0';
|
||||
@@ -743,7 +749,15 @@ fusefs_symlink(void *v)
|
||||
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);
|
||||
goto bad;
|
||||
}
|
||||
@@ -767,16 +781,20 @@ fusefs_readdir(void *v)
|
||||
struct fusefs_node *ip;
|
||||
struct fusefs_mnt *fmp;
|
||||
struct fusebuf *fbuf;
|
||||
struct dirent *dp;
|
||||
char *edp;
|
||||
struct fuse_dirent *fdp;
|
||||
struct dirent de;
|
||||
struct vnode *vp;
|
||||
struct proc *p;
|
||||
struct uio *uio;
|
||||
size_t read_size;
|
||||
uint32_t fresid;
|
||||
off_t foffset, freclen;
|
||||
int error = 0, eofflag = 0, diropen = 0;
|
||||
|
||||
vp = ap->a_vp;
|
||||
uio = ap->a_uio;
|
||||
p = uio->uio_procp;
|
||||
foffset = uio->uio_offset;
|
||||
|
||||
ip = VTOI(vp);
|
||||
fmp = (struct fusefs_mnt *)ip->i_ump;
|
||||
@@ -784,31 +802,35 @@ fusefs_readdir(void *v)
|
||||
if (!fmp->sess_init)
|
||||
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
|
||||
* dirent with maximum allowed name length.
|
||||
*/
|
||||
if (uio->uio_resid < sizeof(struct dirent))
|
||||
if (read_size < sizeof(struct dirent))
|
||||
return (EINVAL);
|
||||
|
||||
if (ip->fufh[FUFH_RDONLY].fh_type == FUFH_INVALID) {
|
||||
error = fusefs_file_open(fmp, ip, FUFH_RDONLY, O_RDONLY, 1, p);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
diropen = 1;
|
||||
else
|
||||
diropen = 1;
|
||||
}
|
||||
|
||||
while (uio->uio_resid > 0) {
|
||||
fbuf = fb_setup(0, ip->i_number, FBT_READDIR, p);
|
||||
|
||||
fbuf->fb_io_fd = ip->fufh[FUFH_RDONLY].fh_id;
|
||||
fbuf->fb_io_off = uio->uio_offset;
|
||||
fbuf->fb_io_len = MIN(uio->uio_resid, fmp->max_read);
|
||||
|
||||
/* might not have enough space for another dirent */
|
||||
if (fbuf->fb_io_len < sizeof(struct dirent))
|
||||
break;
|
||||
/* loop until we run out of buffer space */
|
||||
while (!error && uio->uio_resid >= read_size) {
|
||||
fbuf = fb_setup(0, ip->i_number, FUSE_READDIR, p);
|
||||
fbuf->op.in.read.fh = ip->fufh[FUFH_RDONLY].fh_id;
|
||||
fbuf->op.in.read.offset = foffset;
|
||||
fbuf->op.in.read.size = read_size;
|
||||
|
||||
error = fb_queue(fmp->dev, fbuf);
|
||||
if (error) {
|
||||
@@ -816,58 +838,78 @@ fusefs_readdir(void *v)
|
||||
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) {
|
||||
eofflag = 1;
|
||||
fb_delete(fbuf);
|
||||
break;
|
||||
}
|
||||
|
||||
/* validate the returned dirents */
|
||||
dp = (struct dirent *)fbuf->fb_dat;
|
||||
edp = fbuf->fb_dat + fbuf->fb_len;
|
||||
while ((char *)dp < edp) {
|
||||
/* validate and convert the returned dirents */
|
||||
fresid = fbuf->fb_len;
|
||||
fdp = (struct fuse_dirent *)fbuf->fb_dat;
|
||||
|
||||
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 */
|
||||
if ((char *)dp + offsetof(struct dirent, d_name) >= edp
|
||||
|| dp->d_reclen <= offsetof(struct dirent, d_name)
|
||||
|| (char *)dp + dp->d_reclen > edp) {
|
||||
error = EINVAL;
|
||||
if (fresid < freclen)
|
||||
break;
|
||||
|
||||
/* check for sane name length */
|
||||
if (fdp->namelen == 0 || fdp->namelen > MAXNAMLEN) {
|
||||
error = EIO;
|
||||
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 */
|
||||
if (memchr(dp->d_name, '/', dp->d_namlen) != NULL) {
|
||||
error = EINVAL;
|
||||
if (memchr(fdp->name, '/', fdp->namelen) != NULL) {
|
||||
error = EIO;
|
||||
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))) {
|
||||
fb_delete(fbuf);
|
||||
break;
|
||||
/* copy FUSE dirent into struct dirent */
|
||||
memset(&de, 0, sizeof(de));
|
||||
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);
|
||||
}
|
||||
|
||||
out:
|
||||
uio->uio_offset = foffset;
|
||||
|
||||
if (!error && ap->a_eofflag != NULL)
|
||||
*ap->a_eofflag = eofflag;
|
||||
|
||||
@@ -949,11 +991,7 @@ fusefs_readlink(void *v)
|
||||
if (fmp->undef_op & UNDEF_READLINK)
|
||||
return (ENOSYS);
|
||||
|
||||
fbuf = fb_setup(0, ip->i_number, FBT_READLINK, p);
|
||||
|
||||
fbuf->fb_io_off = uio->uio_offset;
|
||||
fbuf->fb_io_len = MIN(uio->uio_resid, fmp->max_read);
|
||||
|
||||
fbuf = fb_setup(0, ip->i_number, FUSE_READLINK, p);
|
||||
error = fb_queue(fmp->dev, fbuf);
|
||||
|
||||
if (error) {
|
||||
@@ -991,7 +1029,7 @@ fusefs_reclaim(void *v)
|
||||
struct fusefs_filehandle *fufh = NULL;
|
||||
struct fusefs_mnt *fmp;
|
||||
struct fusebuf *fbuf;
|
||||
int type, error = 0;
|
||||
int type;
|
||||
|
||||
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 (fmp->sess_init && ip->i_number != FUSE_ROOTINO) {
|
||||
fbuf = fb_setup(0, ip->i_number, FBT_RECLAIM, p);
|
||||
error = fb_queue(fmp->dev, fbuf);
|
||||
if (error)
|
||||
printf("fusefs: vnode reclaim failed: %d\n", error);
|
||||
fb_delete(fbuf);
|
||||
if (fmp->sess_init && ip->i_number != FUSE_ROOT_ID) {
|
||||
fbuf = fb_setup(0, ip->i_number, FUSE_FORGET, p);
|
||||
fbuf->op.in.forget.nlookup = ip->nlookup;
|
||||
fuse_device_queue_fbuf(fmp->dev, fbuf);
|
||||
/* FUSE_FORGET has no response */
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1074,9 +1111,10 @@ fusefs_create(void *v)
|
||||
}
|
||||
|
||||
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);
|
||||
fbuf->fb_dat[cnp->cn_namelen] = '\0';
|
||||
@@ -1089,10 +1127,17 @@ fusefs_create(void *v)
|
||||
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;
|
||||
|
||||
tdp->v_type = IFTOVT(fbuf->fb_attr.st_mode);
|
||||
tdp->v_type = VREG;
|
||||
|
||||
*vpp = tdp;
|
||||
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,
|
||||
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)
|
||||
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);
|
||||
fbuf->fb_dat[cnp->cn_namelen] = '\0';
|
||||
@@ -1148,10 +1194,21 @@ fusefs_mknod(void *v)
|
||||
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;
|
||||
|
||||
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;
|
||||
VN_KNOTE(ap->a_dvp, NOTE_WRITE);
|
||||
@@ -1194,12 +1251,12 @@ fusefs_read(void *v)
|
||||
return (EINVAL);
|
||||
|
||||
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);
|
||||
fbuf->fb_io_fd = fusefs_fd_get(ip, FUFH_RDONLY);
|
||||
fbuf->fb_io_off = uio->uio_offset;
|
||||
fbuf->fb_io_len = size;
|
||||
fbuf->op.in.read.fh = fusefs_fd_get(ip, FUFH_RDONLY);
|
||||
fbuf->op.in.read.offset = uio->uio_offset;
|
||||
fbuf->op.in.read.size = size;
|
||||
|
||||
error = fb_queue(fmp->dev, fbuf);
|
||||
|
||||
@@ -1240,6 +1297,15 @@ fusefs_write(void *v)
|
||||
ip = VTOI(vp);
|
||||
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)
|
||||
return (ENXIO);
|
||||
if (uio->uio_resid == 0)
|
||||
@@ -1253,12 +1319,12 @@ fusefs_write(void *v)
|
||||
}
|
||||
|
||||
while (uio->uio_resid > 0) {
|
||||
len = MIN(uio->uio_resid, fmp->max_read);
|
||||
fbuf = fb_setup(len, ip->i_number, FBT_WRITE, p);
|
||||
len = MIN(uio->uio_resid, fmp->max_write);
|
||||
fbuf = fb_setup(len, ip->i_number, FUSE_WRITE, p);
|
||||
|
||||
fbuf->fb_io_fd = fusefs_fd_get(ip, FUFH_WRONLY);
|
||||
fbuf->fb_io_off = uio->uio_offset;
|
||||
fbuf->fb_io_len = len;
|
||||
fbuf->op.in.write.fh = fusefs_fd_get(ip, FUFH_WRONLY);
|
||||
fbuf->op.in.write.offset = uio->uio_offset;
|
||||
fbuf->op.in.write.size = len;
|
||||
|
||||
if ((error = uiomove(fbuf->fb_dat, len, uio))) {
|
||||
printf("fusefs: uio error %i\n", error);
|
||||
@@ -1270,8 +1336,8 @@ fusefs_write(void *v)
|
||||
if (error)
|
||||
break;
|
||||
|
||||
diff = len - fbuf->fb_io_len;
|
||||
if (fbuf->fb_io_len > len) {
|
||||
diff = len - fbuf->op.out.write.size;
|
||||
if (fbuf->op.out.write.size > len) {
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
@@ -1382,14 +1448,14 @@ abortit:
|
||||
}
|
||||
|
||||
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);
|
||||
fbuf->fb_dat[fcnp->cn_namelen] = '\0';
|
||||
memcpy(fbuf->fb_dat + fcnp->cn_namelen + 1, tcnp->cn_nameptr,
|
||||
tcnp->cn_namelen);
|
||||
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);
|
||||
|
||||
@@ -1449,9 +1515,10 @@ fusefs_mkdir(void *v)
|
||||
}
|
||||
|
||||
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);
|
||||
fbuf->fb_dat[cnp->cn_namelen] = '\0';
|
||||
|
||||
@@ -1464,12 +1531,19 @@ fusefs_mkdir(void *v)
|
||||
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);
|
||||
goto out;
|
||||
}
|
||||
|
||||
tdp->v_type = IFTOVT(fbuf->fb_attr.st_mode);
|
||||
tdp->v_type = VDIR;
|
||||
|
||||
*vpp = tdp;
|
||||
VN_KNOTE(ap->a_dvp, NOTE_WRITE | NOTE_LINK);
|
||||
@@ -1517,7 +1591,7 @@ fusefs_rmdir(void *v)
|
||||
VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
|
||||
|
||||
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);
|
||||
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,
|
||||
FBT_UNLINK, p);
|
||||
FUSE_UNLINK, p);
|
||||
memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen);
|
||||
fbuf->fb_dat[cnp->cn_namelen] = '\0';
|
||||
|
||||
@@ -1674,8 +1748,14 @@ fusefs_fsync(void *v)
|
||||
if (fufh->fh_type == FUFH_WRONLY ||
|
||||
fufh->fh_type == FUFH_RDWR) {
|
||||
|
||||
fbuf = fb_setup(0, ip->i_number, FBT_FSYNC, p);
|
||||
fbuf->fb_io_fd = fufh->fh_id;
|
||||
fbuf = fb_setup(0, ip->i_number, FUSE_FSYNC, p);
|
||||
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. */
|
||||
error = fb_queue(fmp->dev, fbuf);
|
||||
|
||||
@@ -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>
|
||||
*
|
||||
@@ -35,7 +35,82 @@ fb_setup(size_t len, ino_t ino, int op, struct proc *p)
|
||||
{
|
||||
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_err = 0;
|
||||
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_uid = p->p_ucred->cr_uid;
|
||||
fbuf->fb_gid = p->p_ucred->cr_gid;
|
||||
fbuf->fb_umask = p->p_p->ps_fd->fd_cmask;
|
||||
if (len == 0)
|
||||
fbuf->fb_dat = NULL;
|
||||
else
|
||||
|
||||
@@ -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>
|
||||
*
|
||||
@@ -41,6 +41,7 @@ struct fusefs_mnt {
|
||||
struct mount *mp;
|
||||
uint32_t undef_op;
|
||||
int max_read;
|
||||
int max_write;
|
||||
int sess_init;
|
||||
int allow_other;
|
||||
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_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))
|
||||
|
||||
#endif /* _KERNEL */
|
||||
|
||||
@@ -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>
|
||||
*
|
||||
@@ -49,6 +49,9 @@ struct fusefs_node {
|
||||
|
||||
/** meta **/
|
||||
off_t filesize;
|
||||
|
||||
/** lookup count needed by FUSE_FORGET **/
|
||||
uint64_t nlookup;
|
||||
};
|
||||
|
||||
#ifdef ITOV
|
||||
|
||||
+366
-88
@@ -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 Martin Pieuchot
|
||||
@@ -19,36 +19,25 @@
|
||||
#ifndef _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
|
||||
* syscalls: read, readdir, readlink, write.
|
||||
*/
|
||||
#define FUSEBUFMAXSIZE (4096*1024)
|
||||
|
||||
/* header at beginning of each fusebuf: */
|
||||
struct fb_hdr {
|
||||
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 */
|
||||
};
|
||||
/** Linux FUSE kernel interface major version we somewhat emulate. */
|
||||
#define FUSE_KERNEL_VERSION 7
|
||||
|
||||
/* header for fuse file operations (like read/write/mkdir): */
|
||||
struct fb_io {
|
||||
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 */
|
||||
size_t fi_len; /* Length of data */
|
||||
mode_t fi_mode; /* mode for fd */
|
||||
uint32_t fi_flags; /* flags on transfer */
|
||||
dev_t fi_rdev; /* dev for mknod */
|
||||
};
|
||||
/** Linux FUSE kernel interface minor version we somewhat emulate. */
|
||||
#define FUSE_KERNEL_MINOR_VERSION 19
|
||||
|
||||
/*
|
||||
* The root inode is the root of the mounted FUSE file system.
|
||||
* Also note that inode 0 can't be used for normal purposes.
|
||||
*/
|
||||
#define FUSE_ROOT_ID ((ino_t)1)
|
||||
|
||||
/*
|
||||
* 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
|
||||
* 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 */
|
||||
#define FUSE_FATTR_MODE (1 << 0)
|
||||
@@ -107,42 +59,368 @@ struct fusebuf {
|
||||
#define FUSE_FATTR_SIZE (1 << 3)
|
||||
#define FUSE_FATTR_ATIME (1 << 4)
|
||||
#define FUSE_FATTR_MTIME (1 << 5)
|
||||
#define FUSE_FATTR_FH (1 << 6)
|
||||
|
||||
/* fusebuf types */
|
||||
#define FBT_LOOKUP 0
|
||||
#define FBT_GETATTR 1
|
||||
#define FBT_SETATTR 2
|
||||
#define FBT_READLINK 3
|
||||
#define FBT_SYMLINK 4
|
||||
#define FBT_MKNOD 5
|
||||
#define FBT_MKDIR 6
|
||||
#define FBT_UNLINK 7
|
||||
#define FBT_RMDIR 8
|
||||
#define FBT_RENAME 9
|
||||
#define FBT_LINK 10
|
||||
#define FBT_OPEN 11
|
||||
#define FBT_READ 12
|
||||
#define FBT_WRITE 13
|
||||
#define FBT_STATFS 14
|
||||
#define FBT_RELEASE 16
|
||||
#define FBT_FSYNC 17
|
||||
#define FBT_FLUSH 18
|
||||
#define FBT_INIT 19
|
||||
#define FBT_OPENDIR 20
|
||||
#define FBT_READDIR 21
|
||||
#define FBT_RELEASEDIR 22
|
||||
#define FBT_FSYNCDIR 23
|
||||
#define FBT_ACCESS 24
|
||||
#define FBT_DESTROY 26
|
||||
#define FBT_RECLAIM 27
|
||||
#define FUSE_LOOKUP 1
|
||||
#define FUSE_GETATTR 3
|
||||
#define FUSE_SETATTR 4
|
||||
#define FUSE_READLINK 5
|
||||
#define FUSE_SYMLINK 6
|
||||
#define FUSE_MKNOD 8
|
||||
#define FUSE_MKDIR 9
|
||||
#define FUSE_UNLINK 10
|
||||
#define FUSE_RMDIR 11
|
||||
#define FUSE_RENAME 12
|
||||
#define FUSE_LINK 13
|
||||
#define FUSE_OPEN 14
|
||||
#define FUSE_READ 15
|
||||
#define FUSE_WRITE 16
|
||||
#define FUSE_STATFS 17
|
||||
#define FUSE_RELEASE 18
|
||||
#define FUSE_FSYNC 20
|
||||
#define FUSE_FLUSH 25
|
||||
#define FUSE_INIT 26
|
||||
#define FUSE_OPENDIR 27
|
||||
#define FUSE_READDIR 28
|
||||
#define FUSE_RELEASEDIR 29
|
||||
#define FUSE_DESTROY 38
|
||||
#define FUSE_FORGET 2
|
||||
|
||||
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
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
struct fusebuf *fb_setup(size_t, ino_t, int, struct proc *);
|
||||
int fb_queue(dev_t, 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 /* _SYS_FUSEBUF_H_ */
|
||||
|
||||
Reference in New Issue
Block a user