Add support for non-blocking I/O. Also fixes problems the providers
and popen() caused with concurrent access. This should match the original portalfs in functionality now.
This commit is contained in:
parent
0bc323f5cd
commit
e395775675
@ -1,4 +1,4 @@
|
|||||||
/* $NetBSD: puffs_portal.c,v 1.1 2007/07/02 18:35:14 pooka Exp $ */
|
/* $NetBSD: puffs_portal.c,v 1.2 2007/07/08 10:43:03 pooka Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Antti Kantee. All Rights Reserved.
|
* Copyright (c) 2007 Antti Kantee. All Rights Reserved.
|
||||||
@ -27,6 +27,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
@ -51,9 +52,10 @@ static void usage(void);
|
|||||||
PUFFSOP_PROTOS(portal);
|
PUFFSOP_PROTOS(portal);
|
||||||
|
|
||||||
#define PORTAL_ROOT NULL
|
#define PORTAL_ROOT NULL
|
||||||
|
#define METADATASIZE (sizeof(int) + sizeof(size_t))
|
||||||
|
|
||||||
qelem q;
|
qelem q;
|
||||||
int readcfg;
|
int readcfg, sigchild;
|
||||||
const char *cfg;
|
const char *cfg;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -71,6 +73,13 @@ sighup(int sig)
|
|||||||
readcfg = 1;
|
readcfg = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sigcry(int sig)
|
||||||
|
{
|
||||||
|
|
||||||
|
sigchild = 1;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
portal_loopfn(struct puffs_usermount *pu)
|
portal_loopfn(struct puffs_usermount *pu)
|
||||||
{
|
{
|
||||||
@ -78,6 +87,273 @@ portal_loopfn(struct puffs_usermount *pu)
|
|||||||
if (readcfg)
|
if (readcfg)
|
||||||
conf_read(&q, cfg);
|
conf_read(&q, cfg);
|
||||||
readcfg = 0;
|
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_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;
|
||||||
|
msg.msg_control = cmp;
|
||||||
|
msg.msg_controllen = CMSG_LEN(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));
|
||||||
|
puffs_framebuf_putdata_atoff(pufbuf, sizeof(int),
|
||||||
|
CMSG_DATA(cmp), sizeof(int));
|
||||||
|
*done = 1;
|
||||||
|
|
||||||
|
out:
|
||||||
|
free(cmp);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* receive data from provider */
|
||||||
|
static int
|
||||||
|
readdata(struct puffs_framebuf *pufbuf, int fd, int *done)
|
||||||
|
{
|
||||||
|
char buf[1024];
|
||||||
|
size_t max;
|
||||||
|
ssize_t n;
|
||||||
|
int moved = 0;
|
||||||
|
|
||||||
|
/* we're always done */
|
||||||
|
*done = 1;
|
||||||
|
|
||||||
|
/* 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));
|
||||||
|
|
||||||
|
do {
|
||||||
|
n = read(fd, buf, MIN(sizeof(buf), max));
|
||||||
|
if (n == 0) {
|
||||||
|
if (moved)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return -1; /* caught by read */
|
||||||
|
}
|
||||||
|
if (n < 0) {
|
||||||
|
if (errno != EAGAIN)
|
||||||
|
return errno;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
puffs_framebuf_putdata(pufbuf, buf, n);
|
||||||
|
moved = 1;
|
||||||
|
max -= n;
|
||||||
|
} while (n == sizeof(buf) || max == 0);
|
||||||
|
|
||||||
|
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 moved, 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 (moved)
|
||||||
|
return 0;
|
||||||
|
else {
|
||||||
|
error = -1; /* caught by node_write */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (n < 0) {
|
||||||
|
if (errno != EAGAIN) {
|
||||||
|
error = errno;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pboff += n;
|
||||||
|
puffs_framebuf_seekset(pufbuf, pboff);
|
||||||
|
moved = 1;
|
||||||
|
} while (pboff != pbsize);
|
||||||
|
|
||||||
|
*done = 1;
|
||||||
|
puffs_framebuf_putdata_atoff(pufbuf, 0, &pboff, sizeof(size_t));
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
portal_frame_notify(struct puffs_usermount *pu, int fd, int what)
|
||||||
|
{
|
||||||
|
|
||||||
|
/* nada */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
cmp->cmsg_level = SOL_SOCKET;
|
||||||
|
cmp->cmsg_type = SCM_RIGHTS;
|
||||||
|
cmp->cmsg_len = CMSG_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_LEN(sizeof(int));
|
||||||
|
*(int *)CMSG_DATA(cmp) = fd;
|
||||||
|
|
||||||
|
n = sendmsg(s, &msg, 0);
|
||||||
|
if (n == -1)
|
||||||
|
rv = errno;
|
||||||
|
else if (n < 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_cc *pcc, struct portal_node *portn,
|
||||||
|
struct portal_cred *portc, char **v)
|
||||||
|
{
|
||||||
|
struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
|
||||||
|
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)
|
||||||
|
return errno;
|
||||||
|
|
||||||
|
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, s) == -1) {
|
||||||
|
puffs_framebuf_destroy(pufbuf);
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (fork()) {
|
||||||
|
case -1:
|
||||||
|
return errno;
|
||||||
|
case 0:
|
||||||
|
error = activate_argv(portc, portn->path, v, &fd);
|
||||||
|
sendfd(s[1], fd, error);
|
||||||
|
exit(0);
|
||||||
|
default:
|
||||||
|
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]);
|
||||||
|
close(s[1]);
|
||||||
|
if (puffs_framebuf_tellsize(pufbuf) != 2*sizeof(int)) {
|
||||||
|
puffs_framebuf_destroy(pufbuf);
|
||||||
|
return EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
puffs_framebuf_getdata_atoff(pufbuf, 0, &error, sizeof(int));
|
||||||
|
puffs_framebuf_getdata_atoff(pufbuf, sizeof(int),
|
||||||
|
&fd, sizeof(int));
|
||||||
|
puffs_framebuf_destroy(pufbuf);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
@ -140,6 +416,9 @@ main(int argc, char *argv[])
|
|||||||
|
|
||||||
if (signal(SIGHUP, sighup) == SIG_ERR)
|
if (signal(SIGHUP, sighup) == SIG_ERR)
|
||||||
warn("cannot set sighup handler");
|
warn("cannot set sighup handler");
|
||||||
|
if (signal(SIGCHLD, sigcry) == SIG_ERR)
|
||||||
|
err(1, "cannot set sigchild handler");
|
||||||
|
|
||||||
readcfg = 0;
|
readcfg = 0;
|
||||||
cfg = argv[0];
|
cfg = argv[0];
|
||||||
if (*cfg != '/')
|
if (*cfg != '/')
|
||||||
@ -149,6 +428,8 @@ main(int argc, char *argv[])
|
|||||||
err(1, "cannot read cfg \"%s\"", cfg);
|
err(1, "cannot read cfg \"%s\"", cfg);
|
||||||
|
|
||||||
puffs_ml_setloopfn(pu, portal_loopfn);
|
puffs_ml_setloopfn(pu, portal_loopfn);
|
||||||
|
puffs_framev_init(pu, portal_frame_rf, portal_frame_wf,
|
||||||
|
NULL, portal_frame_notify);
|
||||||
if (puffs_mount(pu, argv[1], mntflags, PORTAL_ROOT) == -1)
|
if (puffs_mount(pu, argv[1], mntflags, PORTAL_ROOT) == -1)
|
||||||
err(1, "mount");
|
err(1, "mount");
|
||||||
if (puffs_mainloop(pu, lflags) == -1)
|
if (puffs_mainloop(pu, lflags) == -1)
|
||||||
@ -259,7 +540,6 @@ portal_node_open(struct puffs_cc *pcc, void *opc, int mode,
|
|||||||
struct portal_node *portn = opc;
|
struct portal_node *portn = opc;
|
||||||
struct portal_cred portc;
|
struct portal_cred portc;
|
||||||
char **v;
|
char **v;
|
||||||
int fd;
|
|
||||||
|
|
||||||
if (opc == PORTAL_ROOT)
|
if (opc == PORTAL_ROOT)
|
||||||
return 0;
|
return 0;
|
||||||
@ -269,29 +549,63 @@ portal_node_open(struct puffs_cc *pcc, void *opc, int mode,
|
|||||||
return ENOENT;
|
return ENOENT;
|
||||||
|
|
||||||
credtr(&portc, pcr, mode);
|
credtr(&portc, pcr, mode);
|
||||||
if (activate_argv(&portc, portn->path, v, &fd) != 0)
|
return provide(pcc, portn, &portc, v);
|
||||||
return ENOENT;
|
|
||||||
|
|
||||||
portn->fd = fd;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
portal_node_read(struct puffs_cc *pcc, void *opc, uint8_t *buf, off_t offset,
|
portal_node_read(struct puffs_cc *pcc, void *opc, uint8_t *buf, off_t offset,
|
||||||
size_t *resid, const struct puffs_cred *pcr, int ioflag)
|
size_t *resid, const struct puffs_cred *pcr, int ioflag)
|
||||||
{
|
{
|
||||||
|
struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
|
||||||
struct portal_node *portn = opc;
|
struct portal_node *portn = opc;
|
||||||
ssize_t n;
|
struct puffs_framebuf *pufbuf;
|
||||||
|
size_t xfersize, winsize, boff;
|
||||||
|
void *win;
|
||||||
|
int rv, error;
|
||||||
|
int data;
|
||||||
|
|
||||||
assert(opc != PORTAL_ROOT);
|
assert(opc != PORTAL_ROOT);
|
||||||
|
error = 0;
|
||||||
|
|
||||||
n = read(portn->fd, buf, *resid);
|
/* if we can't (re-)enable it, treat it as EOF */
|
||||||
if (n == -1)
|
rv = puffs_framev_enablefd(pu, portn->fd, PUFFS_FBIO_READ);
|
||||||
return errno;
|
if (rv == -1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
*resid -= n;
|
pufbuf = puffs_framebuf_make();
|
||||||
return 0;
|
data = PUFBUF_DATA;
|
||||||
|
puffs_framebuf_putdata(pufbuf, &data, sizeof(int));
|
||||||
|
puffs_framebuf_putdata(pufbuf, resid, sizeof(size_t));
|
||||||
|
|
||||||
|
rv = puffs_framev_enqueue_directreceive(pcc, portn->fd, pufbuf, 0);
|
||||||
|
if (rv == -1) {
|
||||||
|
error = errno;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
xfersize = puffs_framebuf_tellsize(pufbuf) - METADATASIZE;
|
||||||
|
*resid -= xfersize;
|
||||||
|
boff = 0;
|
||||||
|
do {
|
||||||
|
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;
|
||||||
|
} while (xfersize);
|
||||||
|
|
||||||
|
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
|
int
|
||||||
@ -299,15 +613,30 @@ portal_node_write(struct puffs_cc *pcc, void *opc, uint8_t *buf, off_t offset,
|
|||||||
size_t *resid, const struct puffs_cred *pcr, int ioflag)
|
size_t *resid, const struct puffs_cred *pcr, int ioflag)
|
||||||
{
|
{
|
||||||
struct portal_node *portn = opc;
|
struct portal_node *portn = opc;
|
||||||
ssize_t n;
|
struct puffs_framebuf *pufbuf;
|
||||||
|
size_t written;
|
||||||
|
int error, rv;
|
||||||
|
|
||||||
assert(opc != PORTAL_ROOT);
|
assert(opc != PORTAL_ROOT);
|
||||||
|
|
||||||
n = write(portn->fd, buf, *resid);
|
pufbuf = puffs_framebuf_make();
|
||||||
if (n == -1)
|
puffs_framebuf_putdata(pufbuf, buf, *resid);
|
||||||
return errno;
|
|
||||||
|
|
||||||
*resid -= n;
|
error = 0;
|
||||||
|
if (puffs_framev_enqueue_directsend(pcc, portn->fd, pufbuf, 0) == -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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,6 +645,9 @@ portal_node_inactive(struct puffs_cc *pcc, void *opc,
|
|||||||
const struct puffs_cid *pcid)
|
const struct puffs_cid *pcid)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (opc == PORTAL_ROOT)
|
||||||
|
return 0;
|
||||||
|
|
||||||
puffs_setback(pcc, PUFFS_SETBACK_NOREF_N1);
|
puffs_setback(pcc, PUFFS_SETBACK_NOREF_N1);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user