843 lines
18 KiB
C
843 lines
18 KiB
C
/* $NetBSD: puffs_portal.c,v 1.10 2019/05/23 11:13:17 kre Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2007 Antti Kantee. All Rights Reserved.
|
|
* Development was supported by the Finnish Cultural Foundation.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
#ifndef lint
|
|
__RCSID("$NetBSD: puffs_portal.c,v 1.10 2019/05/23 11:13:17 kre Exp $");
|
|
#endif /* !lint */
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/socket.h>
|
|
|
|
#include <assert.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <mntopts.h>
|
|
#include <paths.h>
|
|
#include <poll.h>
|
|
#include <puffs.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <util.h>
|
|
|
|
#include "portald.h"
|
|
|
|
struct portal_node {
|
|
char *path;
|
|
int fd;
|
|
};
|
|
|
|
__dead static void usage(void);
|
|
|
|
PUFFSOP_PROTOS(portal);
|
|
|
|
#define PORTAL_ROOT NULL
|
|
#define METADATASIZE (sizeof(int) + sizeof(size_t))
|
|
|
|
qelem q;
|
|
int readcfg, sigchild;
|
|
const char *cfg;
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
|
|
errx(1, "usage: %s [-o options] /path/portal.conf mount_point",
|
|
getprogname());
|
|
}
|
|
|
|
static void
|
|
sighup(int sig)
|
|
{
|
|
|
|
readcfg = 1;
|
|
}
|
|
|
|
static void
|
|
sigcry(int sig)
|
|
{
|
|
|
|
sigchild = 1;
|
|
}
|
|
|
|
static void
|
|
portal_loopfn(struct puffs_usermount *pu)
|
|
{
|
|
|
|
if (readcfg)
|
|
conf_read(&q, cfg);
|
|
readcfg = 0;
|
|
|
|
if (sigchild) {
|
|
sigchild = 0;
|
|
while (waitpid(-1, NULL, WNOHANG) != -1)
|
|
continue;
|
|
}
|
|
}
|
|
|
|
#define PUFBUF_FD 1
|
|
#define PUFBUF_DATA 2
|
|
|
|
#define CMSIZE (sizeof(struct cmsghdr) + sizeof(int))
|
|
|
|
/* receive file descriptor produced by our child process */
|
|
static int
|
|
readfd(struct puffs_framebuf *pufbuf, int fd, int *done)
|
|
{
|
|
struct cmsghdr *cmp;
|
|
struct msghdr msg;
|
|
struct iovec iov;
|
|
ssize_t n;
|
|
int error, rv;
|
|
|
|
rv = 0;
|
|
cmp = emalloc(CMSG_SPACE(sizeof(int)));
|
|
|
|
iov.iov_base = &error;
|
|
iov.iov_len = sizeof(int);
|
|
msg.msg_iov = &iov;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_name = NULL;
|
|
msg.msg_namelen = 0;
|
|
msg.msg_control = cmp;
|
|
msg.msg_controllen = CMSG_SPACE(sizeof(int));
|
|
|
|
n = recvmsg(fd, &msg, 0);
|
|
if (n == -1) {
|
|
rv = errno;
|
|
goto out;
|
|
}
|
|
if (n == 0) {
|
|
rv = ECONNRESET;
|
|
goto out;
|
|
}
|
|
|
|
/* the data for the server */
|
|
puffs_framebuf_putdata_atoff(pufbuf, 0, &error, sizeof(int));
|
|
if (error) {
|
|
rv = error;
|
|
goto out;
|
|
}
|
|
puffs_framebuf_putdata_atoff(pufbuf, sizeof(int),
|
|
CMSG_DATA(cmp), sizeof(int));
|
|
*done = 1;
|
|
|
|
out:
|
|
free(cmp);
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* receive data from provider
|
|
*
|
|
* XXX: should read directly into the buffer and adjust offsets
|
|
* instead of doing memcpy
|
|
*/
|
|
static int
|
|
readdata(struct puffs_framebuf *pufbuf, int fd, int *done)
|
|
{
|
|
char buf[1024];
|
|
size_t max;
|
|
ssize_t n;
|
|
size_t moved;
|
|
|
|
/* don't override metadata */
|
|
if (puffs_framebuf_telloff(pufbuf) == 0)
|
|
puffs_framebuf_seekset(pufbuf, METADATASIZE);
|
|
puffs_framebuf_getdata_atoff(pufbuf, sizeof(int), &max, sizeof(size_t));
|
|
moved = puffs_framebuf_tellsize(pufbuf) - METADATASIZE;
|
|
assert(max >= moved);
|
|
max -= moved;
|
|
|
|
do {
|
|
n = read(fd, buf, MIN(sizeof(buf), max));
|
|
if (n == 0) {
|
|
/*
|
|
* Deal with EOF here by closing the file descriptor
|
|
* and thus causing an error on subsequent accesses.
|
|
* This is the last kevent notification we are going
|
|
* to be getting for regular files.
|
|
*/
|
|
close(fd);
|
|
if (moved)
|
|
break;
|
|
else
|
|
return -1; /* caught by read */
|
|
}
|
|
if (n < 0) {
|
|
if (moved)
|
|
return 0;
|
|
|
|
if (errno != EAGAIN)
|
|
return errno;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
puffs_framebuf_putdata(pufbuf, buf, n);
|
|
moved += n;
|
|
max -= n;
|
|
} while (max > 0);
|
|
|
|
*done = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
portal_frame_rf(struct puffs_usermount *pu, struct puffs_framebuf *pufbuf,
|
|
int fd, int *done)
|
|
{
|
|
int type;
|
|
|
|
if (puffs_framebuf_getdata_atoff(pufbuf, 0, &type, sizeof(int)) == -1)
|
|
return EINVAL;
|
|
|
|
if (type == PUFBUF_FD)
|
|
return readfd(pufbuf, fd, done);
|
|
else if (type == PUFBUF_DATA)
|
|
return readdata(pufbuf, fd, done);
|
|
else
|
|
abort();
|
|
}
|
|
|
|
static int
|
|
portal_frame_wf(struct puffs_usermount *pu, struct puffs_framebuf *pufbuf,
|
|
int fd, int *done)
|
|
{
|
|
void *win;
|
|
size_t pbsize, pboff, winlen;
|
|
ssize_t n;
|
|
int error;
|
|
|
|
pboff = puffs_framebuf_telloff(pufbuf);
|
|
pbsize = puffs_framebuf_tellsize(pufbuf);
|
|
error = 0;
|
|
|
|
do {
|
|
assert(pbsize > pboff);
|
|
winlen = pbsize - pboff;
|
|
if (puffs_framebuf_getwindow(pufbuf, pboff, &win, &winlen)==-1)
|
|
return errno;
|
|
n = write(fd, win, winlen);
|
|
if (n == 0) {
|
|
if (pboff != 0)
|
|
break;
|
|
else
|
|
return -1; /* caught by node_write */
|
|
}
|
|
if (n < 0) {
|
|
if (pboff != 0)
|
|
break;
|
|
|
|
if (errno != EAGAIN)
|
|
return errno;
|
|
return 0;
|
|
}
|
|
|
|
pboff += n;
|
|
puffs_framebuf_seekset(pufbuf, pboff);
|
|
} while (pboff != pbsize);
|
|
|
|
*done = 1;
|
|
puffs_framebuf_putdata_atoff(pufbuf, 0, &pboff, sizeof(size_t));
|
|
return error;
|
|
}
|
|
|
|
/* transfer file descriptor to master file server */
|
|
static int
|
|
sendfd(int s, int fd, int error)
|
|
{
|
|
struct cmsghdr *cmp;
|
|
struct msghdr msg;
|
|
struct iovec iov;
|
|
ssize_t n;
|
|
int rv;
|
|
|
|
rv = 0;
|
|
cmp = emalloc(CMSG_LEN(sizeof(int)));
|
|
|
|
iov.iov_base = &error;
|
|
iov.iov_len = sizeof(int);
|
|
|
|
msg.msg_iov = &iov;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_name = NULL;
|
|
msg.msg_namelen = 0;
|
|
if (error == 0) {
|
|
cmp->cmsg_level = SOL_SOCKET;
|
|
cmp->cmsg_type = SCM_RIGHTS;
|
|
cmp->cmsg_len = CMSG_LEN(sizeof(int));
|
|
|
|
msg.msg_control = cmp;
|
|
msg.msg_controllen = CMSG_LEN(sizeof(int));
|
|
*(int *)CMSG_DATA(cmp) = fd;
|
|
} else {
|
|
msg.msg_control = NULL;
|
|
msg.msg_controllen = 0;
|
|
}
|
|
|
|
n = sendmsg(s, &msg, 0);
|
|
if (n == -1)
|
|
rv = errno;
|
|
else if (n < (ssize_t)sizeof(int))
|
|
rv = EPROTO;
|
|
|
|
free(cmp);
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Produce I/O file descriptor by forking (like original portald).
|
|
*
|
|
* child: run provider and transfer produced fd to parent
|
|
* parent: yield until child produces fd. receive it and store it.
|
|
*/
|
|
static int
|
|
provide(struct puffs_usermount *pu, struct portal_node *portn,
|
|
struct portal_cred *portc, char **v)
|
|
{
|
|
struct puffs_cc *pcc = puffs_cc_getcc(pu);
|
|
struct puffs_framebuf *pufbuf;
|
|
int s[2];
|
|
int fd, error;
|
|
int data;
|
|
|
|
pufbuf = puffs_framebuf_make();
|
|
if (pufbuf == NULL)
|
|
return ENOMEM;
|
|
|
|
data = PUFBUF_FD;
|
|
if (puffs_framebuf_putdata(pufbuf, &data, sizeof(int)) == -1)
|
|
goto bad;
|
|
|
|
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, s) == -1)
|
|
goto bad;
|
|
|
|
switch (fork()) {
|
|
case -1:
|
|
close(s[0]); close(s[1]);
|
|
goto bad;
|
|
case 0:
|
|
close(s[0]);
|
|
error = activate_argv(portc, portn->path, v, &fd);
|
|
sendfd(s[1], fd, error);
|
|
exit(0);
|
|
default:
|
|
close(s[1]);
|
|
puffs_framev_addfd(pu, s[0], PUFFS_FBIO_READ);
|
|
puffs_framev_enqueue_directreceive(pcc, s[0], pufbuf, 0);
|
|
puffs_framev_removefd(pu, s[0], 0);
|
|
close(s[0]);
|
|
|
|
if (puffs_framebuf_tellsize(pufbuf) < sizeof(int)) {
|
|
errno = EIO;
|
|
goto bad;
|
|
}
|
|
puffs_framebuf_getdata_atoff(pufbuf, 0, &error, sizeof(int));
|
|
if (error) {
|
|
errno = error;
|
|
goto bad;
|
|
}
|
|
|
|
if (puffs_framebuf_tellsize(pufbuf) != 2*sizeof(int)) {
|
|
errno = EIO;
|
|
goto bad;
|
|
}
|
|
|
|
puffs_framebuf_getdata_atoff(pufbuf, sizeof(int),
|
|
&fd, sizeof(int));
|
|
puffs_framebuf_destroy(pufbuf);
|
|
|
|
data = 1;
|
|
if (ioctl(fd, FIONBIO, &data) == -1)
|
|
return errno;
|
|
|
|
if (puffs_framev_addfd(pu, fd, PUFFS_FBIO_WRITE) == -1)
|
|
return errno;
|
|
|
|
portn->fd = fd;
|
|
return 0;
|
|
}
|
|
|
|
bad:
|
|
puffs_framebuf_destroy(pufbuf);
|
|
return errno;
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
extern char *optarg;
|
|
extern int optind;
|
|
struct puffs_usermount *pu;
|
|
struct puffs_ops *pops;
|
|
mntoptparse_t mp;
|
|
int pflags, mntflags;
|
|
int detach;
|
|
int ch;
|
|
|
|
setprogname(argv[0]);
|
|
|
|
mntflags = pflags = 0;
|
|
detach = 1;
|
|
while ((ch = getopt(argc, argv, "o:s")) != -1) {
|
|
switch (ch) {
|
|
case 'o':
|
|
mp = getmntopts(optarg, puffsmopts, &mntflags, &pflags);
|
|
if (mp == NULL)
|
|
err(1, "getmntopts");
|
|
freemntopts(mp);
|
|
break;
|
|
case 's': /* stay on top */
|
|
detach = 0;
|
|
break;
|
|
default:
|
|
usage();
|
|
/*NOTREACHED*/
|
|
}
|
|
}
|
|
pflags |= PUFFS_KFLAG_NOCACHE | PUFFS_KFLAG_LOOKUP_FULLPNBUF;
|
|
if (pflags & PUFFS_FLAG_OPDUMP)
|
|
detach = 0;
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (argc != 2)
|
|
usage();
|
|
|
|
PUFFSOP_INIT(pops);
|
|
|
|
PUFFSOP_SETFSNOP(pops, unmount);
|
|
PUFFSOP_SETFSNOP(pops, sync);
|
|
PUFFSOP_SETFSNOP(pops, statvfs);
|
|
|
|
PUFFSOP_SET(pops, portal, node, lookup);
|
|
PUFFSOP_SET(pops, portal, node, getattr);
|
|
PUFFSOP_SET(pops, portal, node, setattr);
|
|
PUFFSOP_SET(pops, portal, node, open);
|
|
PUFFSOP_SET(pops, portal, node, read);
|
|
PUFFSOP_SET(pops, portal, node, write);
|
|
PUFFSOP_SET(pops, portal, node, seek);
|
|
PUFFSOP_SET(pops, portal, node, poll);
|
|
PUFFSOP_SET(pops, portal, node, inactive);
|
|
PUFFSOP_SET(pops, portal, node, reclaim);
|
|
|
|
pu = puffs_init(pops, _PATH_PUFFS, "portal", NULL, pflags);
|
|
if (pu == NULL)
|
|
err(1, "init");
|
|
|
|
if (signal(SIGHUP, sighup) == SIG_ERR)
|
|
warn("cannot set sighup handler");
|
|
if (signal(SIGCHLD, sigcry) == SIG_ERR)
|
|
err(1, "cannot set sigchild handler");
|
|
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
|
|
err(1, "cannot ignore sigpipe");
|
|
|
|
readcfg = 0;
|
|
cfg = argv[0];
|
|
if (*cfg != '/')
|
|
errx(1, "need absolute path for config");
|
|
q.q_forw = q.q_back = &q;
|
|
if (conf_read(&q, cfg) == -1)
|
|
err(1, "cannot read cfg \"%s\"", cfg);
|
|
|
|
puffs_ml_setloopfn(pu, portal_loopfn);
|
|
puffs_framev_init(pu, portal_frame_rf, portal_frame_wf, NULL,NULL,NULL);
|
|
|
|
if (detach)
|
|
if (puffs_daemon(pu, 1, 1) == -1)
|
|
err(1, "puffs_daemon");
|
|
|
|
if (puffs_mount(pu, argv[1], mntflags, PORTAL_ROOT) == -1)
|
|
err(1, "mount");
|
|
if (puffs_mainloop(pu) == -1)
|
|
err(1, "mainloop");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct portal_node *
|
|
makenode(const char *path)
|
|
{
|
|
struct portal_node *portn;
|
|
|
|
portn = emalloc(sizeof(struct portal_node));
|
|
portn->path = estrdup(path);
|
|
portn->fd = -1;
|
|
|
|
return portn;
|
|
}
|
|
|
|
static void
|
|
credtr(struct portal_cred *portc, const struct puffs_cred *puffc, int mode)
|
|
{
|
|
memset(portc, 0, sizeof(struct portal_cred));
|
|
|
|
portc->pcr_flag = mode;
|
|
puffs_cred_getuid(puffc, &portc->pcr_uid);
|
|
puffs_cred_getgid(puffc, &portc->pcr_gid);
|
|
puffs_cred_getgroups(puffc, portc->pcr_groups,
|
|
(short *)&portc->pcr_ngroups);
|
|
}
|
|
|
|
/*
|
|
* XXX: we could also simply already resolve the name at this stage
|
|
* instead of deferring it to open. But doing it in open is how the
|
|
* original portald does it, and I don't want to introduce any funny
|
|
* incompatibilities.
|
|
*/
|
|
int
|
|
portal_node_lookup(struct puffs_usermount *pu, puffs_cookie_t opc,
|
|
struct puffs_newinfo *pni, const struct puffs_cn *pcn)
|
|
{
|
|
struct portal_node *portn;
|
|
|
|
assert(opc == PORTAL_ROOT);
|
|
|
|
if (pcn->pcn_nameiop != NAMEI_LOOKUP
|
|
&& pcn->pcn_nameiop != NAMEI_CREATE)
|
|
return EOPNOTSUPP;
|
|
|
|
portn = makenode(pcn->pcn_name);
|
|
puffs_newinfo_setcookie(pni, portn);
|
|
puffs_newinfo_setvtype(pni, VREG);
|
|
|
|
pcn->pcn_flags &= ~NAMEI_REQUIREDIR;
|
|
pcn->pcn_consume = strlen(pcn->pcn_name) - pcn->pcn_namelen;
|
|
|
|
return 0;
|
|
}
|
|
|
|
unsigned int fakeid = 3;
|
|
|
|
/* XXX: libpuffs'ize */
|
|
int
|
|
portal_node_getattr(struct puffs_usermount *pu, puffs_cookie_t opc,
|
|
struct vattr *va, const struct puffs_cred *pcr)
|
|
{
|
|
struct timeval tv;
|
|
struct timespec ts;
|
|
int res = 0;
|
|
|
|
puffs_vattr_null(va);
|
|
if (opc == PORTAL_ROOT) {
|
|
va->va_type = VDIR;
|
|
va->va_mode = 0777;
|
|
va->va_nlink = 2;
|
|
va->va_uid = va->va_gid = 0;
|
|
#if 0 /* XXX Huh? */
|
|
va->va_fileid = fakeid++;
|
|
#else
|
|
va->va_fileid = 2; /*XXX; ROOTINO*/
|
|
#endif
|
|
va->va_size = va->va_bytes = 0;
|
|
va->va_gen = 0;
|
|
va->va_rdev = PUFFS_VNOVAL;
|
|
va->va_blocksize = DEV_BSIZE;
|
|
|
|
gettimeofday(&tv, NULL);
|
|
TIMEVAL_TO_TIMESPEC(&tv, &ts);
|
|
va->va_atime = va->va_ctime = va->va_mtime =
|
|
va->va_birthtime = ts;
|
|
} else {
|
|
/* cheat for now */
|
|
int error;
|
|
int newfd;
|
|
struct stat st;
|
|
struct portal_node *portn = opc;
|
|
struct portal_cred portc;
|
|
char **v = conf_match(&q, portn->path);
|
|
|
|
if (v == NULL)
|
|
return ENOENT;
|
|
if (portn->fd == -1) {
|
|
credtr(&portc, pcr, 0777);
|
|
error = provide(pu, portn, &portc, v);
|
|
if (error)
|
|
return error;
|
|
newfd = 1;
|
|
} else
|
|
newfd = 0;
|
|
|
|
if (fstat(portn->fd, &st) == -1)
|
|
res = errno;
|
|
else {
|
|
#if 0
|
|
va->va_type = S_ISDIR(st.st_mode) ? VDIR : VREG; /* XXX */
|
|
#else
|
|
va->va_type = puffs_mode2vt(st.st_mode);
|
|
#endif
|
|
/* puffs supplies S_IFMT bits later */
|
|
va->va_mode = (st.st_mode & ~S_IFMT);
|
|
|
|
va->va_nlink = st.st_nlink ? st.st_nlink : 1;
|
|
va->va_uid = st.st_uid;
|
|
va->va_gid = st.st_gid;
|
|
va->va_fileid = st.st_ino ? st.st_ino : fakeid++;
|
|
va->va_size = va->va_bytes = st.st_size;
|
|
va->va_gen = 0;
|
|
va->va_rdev = st.st_rdev;
|
|
va->va_blocksize = st.st_blksize;
|
|
va->va_atime = st.st_atim;
|
|
va->va_ctime = st.st_ctim;
|
|
va->va_mtime = st.st_mtim;
|
|
va->va_birthtime = st.st_birthtim;
|
|
}
|
|
if (newfd) {
|
|
puffs_framev_removefd(pu, portn->fd, 0);
|
|
close(portn->fd);
|
|
portn->fd = -1;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/* for writing, just pretend we care */
|
|
int
|
|
portal_node_setattr(struct puffs_usermount *pu, puffs_cookie_t opc,
|
|
const struct vattr *va, const struct puffs_cred *pcr)
|
|
{
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
portal_node_open(struct puffs_usermount *pu, puffs_cookie_t opc, int mode,
|
|
const struct puffs_cred *pcr)
|
|
{
|
|
struct portal_node *portn = opc;
|
|
struct portal_cred portc;
|
|
char **v;
|
|
|
|
if (opc == PORTAL_ROOT)
|
|
return 0;
|
|
|
|
if (mode & O_NONBLOCK)
|
|
return EOPNOTSUPP;
|
|
|
|
v = conf_match(&q, portn->path);
|
|
if (v == NULL)
|
|
return ENOENT;
|
|
|
|
credtr(&portc, pcr, mode);
|
|
return provide(pu, portn, &portc, v);
|
|
}
|
|
|
|
int
|
|
portal_node_read(struct puffs_usermount *pu, puffs_cookie_t opc,
|
|
uint8_t *buf, off_t offset, size_t *resid,
|
|
const struct puffs_cred *pcr, int ioflag)
|
|
{
|
|
struct puffs_cc *pcc = puffs_cc_getcc(pu);
|
|
struct portal_node *portn = opc;
|
|
struct puffs_framebuf *pufbuf;
|
|
size_t xfersize, winsize, boff;
|
|
void *win;
|
|
int rv, error;
|
|
int data, dummy;
|
|
|
|
assert(opc != PORTAL_ROOT);
|
|
error = 0;
|
|
|
|
/* if we can't (re-)enable it, treat it as EOF */
|
|
rv = puffs_framev_enablefd(pu, portn->fd, PUFFS_FBIO_READ);
|
|
if (rv == -1)
|
|
return 0;
|
|
|
|
pufbuf = puffs_framebuf_make();
|
|
data = PUFBUF_DATA;
|
|
puffs_framebuf_putdata(pufbuf, &data, sizeof(int));
|
|
puffs_framebuf_putdata(pufbuf, resid, sizeof(size_t));
|
|
|
|
/* if we are doing nodelay, do read directly */
|
|
if (ioflag & PUFFS_IO_NDELAY) {
|
|
rv = readdata(pufbuf, portn->fd, &dummy);
|
|
if (rv != 0) {
|
|
error = rv;
|
|
goto out;
|
|
}
|
|
} else {
|
|
rv = puffs_framev_enqueue_directreceive(pcc,
|
|
portn->fd, pufbuf, 0);
|
|
|
|
if (rv == -1) {
|
|
error = errno;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
xfersize = puffs_framebuf_tellsize(pufbuf) - METADATASIZE;
|
|
if (xfersize == 0) {
|
|
assert(ioflag & PUFFS_IO_NDELAY);
|
|
error = EAGAIN;
|
|
goto out;
|
|
}
|
|
|
|
*resid -= xfersize;
|
|
boff = 0;
|
|
while (xfersize > 0) {
|
|
winsize = xfersize;
|
|
rv = puffs_framebuf_getwindow(pufbuf, METADATASIZE,
|
|
&win, &winsize);
|
|
assert(rv == 0);
|
|
assert(winsize > 0);
|
|
|
|
memcpy(buf + boff, win, winsize);
|
|
xfersize -= winsize;
|
|
boff += winsize;
|
|
}
|
|
|
|
out:
|
|
puffs_framev_disablefd(pu, portn->fd, PUFFS_FBIO_READ);
|
|
puffs_framebuf_destroy(pufbuf);
|
|
|
|
/* a trickery, from readdata() */
|
|
if (error == -1)
|
|
return 0;
|
|
return error;
|
|
}
|
|
|
|
int
|
|
portal_node_write(struct puffs_usermount *pu, puffs_cookie_t opc,
|
|
uint8_t *buf, off_t offset, size_t *resid,
|
|
const struct puffs_cred *pcr, int ioflag)
|
|
{
|
|
struct puffs_cc *pcc = puffs_cc_getcc(pu);
|
|
struct portal_node *portn = opc;
|
|
struct puffs_framebuf *pufbuf;
|
|
size_t written;
|
|
int error, rv, dummy;
|
|
|
|
assert(opc != PORTAL_ROOT);
|
|
|
|
pufbuf = puffs_framebuf_make();
|
|
puffs_framebuf_putdata(pufbuf, buf, *resid);
|
|
|
|
error = 0;
|
|
if (ioflag & PUFFS_IO_NDELAY) {
|
|
rv = portal_frame_wf(pu, pufbuf, portn->fd, &dummy);
|
|
if (rv) {
|
|
error = rv;
|
|
goto out;
|
|
}
|
|
} else {
|
|
rv = puffs_framev_enqueue_directsend(pcc, portn->fd, pufbuf, 0);
|
|
if (rv == -1) {
|
|
error = errno;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
rv = puffs_framebuf_getdata_atoff(pufbuf, 0, &written, sizeof(size_t));
|
|
assert(rv == 0);
|
|
assert(written <= *resid);
|
|
*resid -= written;
|
|
|
|
out:
|
|
puffs_framebuf_destroy(pufbuf);
|
|
if (error == -1)
|
|
error = 0;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
portal_node_seek(struct puffs_usermount *pu, puffs_cookie_t opc,
|
|
off_t oldoff, off_t newoff, const struct puffs_cred *pcr)
|
|
{
|
|
struct portal_node *portn = opc;
|
|
|
|
if (opc == PORTAL_ROOT || portn->fd == -1)
|
|
return EOPNOTSUPP;
|
|
|
|
if (lseek(portn->fd, newoff, SEEK_SET) == -1)
|
|
return errno;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
portal_node_poll(struct puffs_usermount *pu, puffs_cookie_t opc, int *events)
|
|
{
|
|
struct puffs_cc *pcc = puffs_cc_getcc(pu);
|
|
struct portal_node *portn = opc;
|
|
int what;
|
|
|
|
what = 0;
|
|
if (*events & POLLIN)
|
|
what |= PUFFS_FBIO_READ;
|
|
if (*events & POLLOUT)
|
|
what |= PUFFS_FBIO_WRITE;
|
|
if (*events & POLLERR)
|
|
what |= PUFFS_FBIO_ERROR;
|
|
|
|
if (puffs_framev_enqueue_waitevent(pcc, portn->fd, &what) == -1) {
|
|
*events = POLLERR;
|
|
return errno;
|
|
}
|
|
|
|
*events = 0;
|
|
if (what & PUFFS_FBIO_READ)
|
|
*events |= POLLIN;
|
|
if (what & PUFFS_FBIO_WRITE)
|
|
*events |= POLLOUT;
|
|
if (what & PUFFS_FBIO_ERROR)
|
|
*events |= POLLERR;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
portal_node_inactive(struct puffs_usermount *pu, puffs_cookie_t opc)
|
|
{
|
|
|
|
if (opc == PORTAL_ROOT)
|
|
return 0;
|
|
|
|
puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N1);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
portal_node_reclaim(struct puffs_usermount *pu, puffs_cookie_t opc)
|
|
{
|
|
struct portal_node *portn = opc;
|
|
|
|
if (portn->fd != -1) {
|
|
puffs_framev_removefd(pu, portn->fd, 0);
|
|
close(portn->fd);
|
|
}
|
|
free(portn->path);
|
|
free(portn);
|
|
|
|
return 0;
|
|
}
|