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:
pooka 2007-07-08 10:43:03 +00:00
parent 0bc323f5cd
commit e395775675

View File

@ -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.
@ -27,6 +27,7 @@
*/
#include <sys/types.h>
#include <sys/wait.h>
#include <assert.h>
#include <err.h>
@ -51,9 +52,10 @@ static void usage(void);
PUFFSOP_PROTOS(portal);
#define PORTAL_ROOT NULL
#define METADATASIZE (sizeof(int) + sizeof(size_t))
qelem q;
int readcfg;
int readcfg, sigchild;
const char *cfg;
static void
@ -71,6 +73,13 @@ sighup(int sig)
readcfg = 1;
}
static void
sigcry(int sig)
{
sigchild = 1;
}
static void
portal_loopfn(struct puffs_usermount *pu)
{
@ -78,6 +87,273 @@ 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_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
@ -140,6 +416,9 @@ main(int argc, char *argv[])
if (signal(SIGHUP, sighup) == SIG_ERR)
warn("cannot set sighup handler");
if (signal(SIGCHLD, sigcry) == SIG_ERR)
err(1, "cannot set sigchild handler");
readcfg = 0;
cfg = argv[0];
if (*cfg != '/')
@ -149,6 +428,8 @@ main(int argc, char *argv[])
err(1, "cannot read cfg \"%s\"", cfg);
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)
err(1, "mount");
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_cred portc;
char **v;
int fd;
if (opc == PORTAL_ROOT)
return 0;
@ -269,29 +549,63 @@ portal_node_open(struct puffs_cc *pcc, void *opc, int mode,
return ENOENT;
credtr(&portc, pcr, mode);
if (activate_argv(&portc, portn->path, v, &fd) != 0)
return ENOENT;
portn->fd = fd;
return 0;
return provide(pcc, portn, &portc, v);
}
int
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)
{
struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
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);
error = 0;
n = read(portn->fd, buf, *resid);
if (n == -1)
return errno;
/* 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;
*resid -= n;
return 0;
pufbuf = puffs_framebuf_make();
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
@ -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)
{
struct portal_node *portn = opc;
ssize_t n;
struct puffs_framebuf *pufbuf;
size_t written;
int error, rv;
assert(opc != PORTAL_ROOT);
n = write(portn->fd, buf, *resid);
if (n == -1)
return errno;
pufbuf = puffs_framebuf_make();
puffs_framebuf_putdata(pufbuf, buf, *resid);
*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;
}
@ -316,6 +645,9 @@ portal_node_inactive(struct puffs_cc *pcc, void *opc,
const struct puffs_cid *pcid)
{
if (opc == PORTAL_ROOT)
return 0;
puffs_setback(pcc, PUFFS_SETBACK_NOREF_N1);
return 0;
}