minix/lib/libpuffs/puffs.c
David van Moolenbroek ba736c7968 libpuffs: clean up, unbreak
- move MINIX3-specific files into minix/lib/libpuffs;
- resynchronize the remaining files with NetBSD code;
- remove a few unnecessary changes;
- put remaining MINIX3-specific changes in __minix blocks;
- sort out the source files being linked at all.

The result is that libpuffs now successfully links against FUSE
file system programs again.  It can successfully mount, perform
some of the most basic operations, and unmount the file system.

Change-Id: Ieac220f7ad8c4d1fa293abda81967e8045be0bb4
2015-09-23 12:05:03 +00:00

1120 lines
24 KiB
C

/* $NetBSD: puffs.c,v 1.117 2011/11/14 01:27:42 chs Exp $ */
/*
* Copyright (c) 2005, 2006, 2007 Antti Kantee. All Rights Reserved.
*
* Development of this software was supported by the
* Google Summer of Code program and the Ulla Tuominen Foundation.
* The Google SoC project was mentored by Bill Studenmund.
*
* 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>
#if !defined(lint)
__RCSID("$NetBSD: puffs.c,v 1.117 2011/11/14 01:27:42 chs Exp $");
#endif /* !lint */
#include <sys/param.h>
#include <sys/mount.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <mntopts.h>
#include <paths.h>
#ifndef __minix
#include <pthread.h>
#endif /* !__minix */
#include <puffs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include "puffs_priv.h"
/* Most file systems want this for opts, so just give it to them */
const struct mntopt puffsmopts[] = {
MOPT_STDOPTS,
PUFFSMOPT_STD,
MOPT_NULL,
};
#ifndef __minix
pthread_mutex_t pu_lock = PTHREAD_MUTEX_INITIALIZER;
#endif /* !__minix */
#define FILLOP(lower, upper) \
do { \
if (pops->puffs_node_##lower) \
opmask[PUFFS_VN_##upper] = 1; \
} while (/*CONSTCOND*/0)
static void
fillvnopmask(struct puffs_ops *pops, struct puffs_kargs *pa)
{
uint8_t *opmask = pa->pa_vnopmask;
memset(opmask, 0, sizeof(pa->pa_vnopmask));
FILLOP(create, CREATE);
FILLOP(mknod, MKNOD);
FILLOP(open, OPEN);
FILLOP(close, CLOSE);
FILLOP(access, ACCESS);
FILLOP(getattr, GETATTR);
FILLOP(setattr, SETATTR);
FILLOP(poll, POLL);
FILLOP(mmap, MMAP);
FILLOP(fsync, FSYNC);
FILLOP(seek, SEEK);
FILLOP(remove, REMOVE);
FILLOP(link, LINK);
FILLOP(rename, RENAME);
FILLOP(mkdir, MKDIR);
FILLOP(rmdir, RMDIR);
FILLOP(symlink, SYMLINK);
FILLOP(readdir, READDIR);
FILLOP(readlink, READLINK);
FILLOP(reclaim, RECLAIM);
FILLOP(inactive, INACTIVE);
FILLOP(print, PRINT);
FILLOP(read, READ);
FILLOP(write, WRITE);
FILLOP(advlock, ADVLOCK);
FILLOP(abortop, ABORTOP);
FILLOP(pathconf, PATHCONF);
FILLOP(getextattr, GETEXTATTR);
FILLOP(setextattr, SETEXTATTR);
FILLOP(listextattr, LISTEXTATTR);
FILLOP(deleteextattr, DELETEEXTATTR);
}
#undef FILLOP
/*
* Go over all framev entries and write everything we can. This is
* mostly for the benefit of delivering "unmount" to the kernel.
*/
static void
finalpush(struct puffs_usermount *pu)
{
#ifndef __minix
struct puffs_fctrl_io *fio;
LIST_FOREACH(fio, &pu->pu_ios, fio_entries) {
if (fio->stat & FIO_WRGONE)
continue;
puffs__framev_output(pu, fio->fctrl, fio);
}
#endif /* !__minix */
}
/*ARGSUSED*/
void
puffs_kernerr_abort(struct puffs_usermount *pu, uint8_t type,
int error, const char *str, puffs_cookie_t cookie)
{
#ifndef __minix
fprintf(stderr, "abort: type %d, error %d, cookie %p (%s)\n",
#else /* __minix */
lpuffs_debug("abort: type %d, error %d, cookie %p (%s)\n",
#endif /* __minix */
type, error, cookie, str);
abort();
}
/*ARGSUSED*/
void
puffs_kernerr_log(struct puffs_usermount *pu, uint8_t type,
int error, const char *str, puffs_cookie_t cookie)
{
syslog(LOG_WARNING, "kernel: type %d, error %d, cookie %p (%s)\n",
type, error, cookie, str);
}
#ifndef __minix
int
puffs_getselectable(struct puffs_usermount *pu)
{
return pu->pu_fd;
}
uint64_t
puffs__nextreq(struct puffs_usermount *pu)
{
uint64_t rv;
PU_LOCK();
rv = pu->pu_nextreq++ | (uint64_t)1<<63;
PU_UNLOCK();
return rv;
}
int
puffs_setblockingmode(struct puffs_usermount *pu, int mode)
{
int rv, x;
assert(puffs_getstate(pu) == PUFFS_STATE_RUNNING);
if (mode != PUFFSDEV_BLOCK && mode != PUFFSDEV_NONBLOCK) {
errno = EINVAL;
return -1;
}
x = mode;
rv = ioctl(pu->pu_fd, FIONBIO, &x);
if (rv == 0) {
if (mode == PUFFSDEV_BLOCK)
pu->pu_state &= ~PU_ASYNCFD;
else
pu->pu_state |= PU_ASYNCFD;
}
return rv;
}
#endif /* !__minix */
int
puffs_getstate(struct puffs_usermount *pu)
{
return pu->pu_state & PU_STATEMASK;
}
void
puffs_setstacksize(struct puffs_usermount *pu, size_t ss)
{
long psize, minsize;
int stackshift;
int bonus;
assert(puffs_getstate(pu) == PUFFS_STATE_BEFOREMOUNT);
psize = sysconf(_SC_PAGESIZE);
minsize = 4*psize;
if (ss < (size_t)minsize || ss == PUFFS_STACKSIZE_MIN) {
if (ss != PUFFS_STACKSIZE_MIN)
#ifndef __minix
fprintf(stderr, "puffs_setstacksize: adjusting "
"stacksize to minimum %ld\n", minsize);
#endif /* !__minix */
ss = 4*psize;
}
stackshift = -1;
bonus = 0;
while (ss) {
if (ss & 0x1)
bonus++;
ss >>= 1;
stackshift++;
}
if (bonus > 1) {
stackshift++;
#ifndef __minix
fprintf(stderr, "puffs_setstacksize: using next power of two: "
"%d\n", 1<<stackshift);
#endif /* !__minix */
}
pu->pu_cc_stackshift = stackshift;
}
struct puffs_pathobj *
puffs_getrootpathobj(struct puffs_usermount *pu)
{
struct puffs_node *pnr;
pnr = pu->pu_pn_root;
if (pnr == NULL) {
errno = ENOENT;
return NULL;
}
return &pnr->pn_po;
}
void
puffs_setroot(struct puffs_usermount *pu, struct puffs_node *pn)
{
pu->pu_pn_root = pn;
}
struct puffs_node *
puffs_getroot(struct puffs_usermount *pu)
{
return pu->pu_pn_root;
}
void
puffs_setrootinfo(struct puffs_usermount *pu, enum vtype vt,
vsize_t vsize, dev_t rdev)
{
struct puffs_kargs *pargs = pu->pu_kargp;
if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT) {
warnx("puffs_setrootinfo: call has effect only "
"before mount\n");
return;
}
pargs->pa_root_vtype = vt;
pargs->pa_root_vsize = vsize;
pargs->pa_root_rdev = rdev;
}
void *
puffs_getspecific(struct puffs_usermount *pu)
{
return pu->pu_privdata;
}
void
puffs_setspecific(struct puffs_usermount *pu, void *privdata)
{
pu->pu_privdata = privdata;
}
void
puffs_setmntinfo(struct puffs_usermount *pu,
const char *mntfromname, const char *puffsname)
{
struct puffs_kargs *pargs = pu->pu_kargp;
(void)strlcpy(pargs->pa_mntfromname, mntfromname,
sizeof(pargs->pa_mntfromname));
(void)strlcpy(pargs->pa_typename, puffsname,
sizeof(pargs->pa_typename));
}
size_t
puffs_getmaxreqlen(struct puffs_usermount *pu)
{
return pu->pu_maxreqlen;
}
void
puffs_setmaxreqlen(struct puffs_usermount *pu, size_t reqlen)
{
if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT)
warnx("puffs_setmaxreqlen: call has effect only "
"before mount\n");
pu->pu_kargp->pa_maxmsglen = reqlen;
}
void
puffs_setfhsize(struct puffs_usermount *pu, size_t fhsize, int flags)
{
if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT)
warnx("puffs_setfhsize: call has effect only before mount\n");
pu->pu_kargp->pa_fhsize = fhsize;
pu->pu_kargp->pa_fhflags = flags;
}
void
puffs_setncookiehash(struct puffs_usermount *pu, int nhash)
{
if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT)
warnx("puffs_setfhsize: call has effect only before mount\n");
pu->pu_kargp->pa_nhashbuckets = nhash;
}
void
puffs_set_pathbuild(struct puffs_usermount *pu, pu_pathbuild_fn fn)
{
pu->pu_pathbuild = fn;
}
void
puffs_set_pathtransform(struct puffs_usermount *pu, pu_pathtransform_fn fn)
{
pu->pu_pathtransform = fn;
}
void
puffs_set_pathcmp(struct puffs_usermount *pu, pu_pathcmp_fn fn)
{
pu->pu_pathcmp = fn;
}
void
puffs_set_pathfree(struct puffs_usermount *pu, pu_pathfree_fn fn)
{
pu->pu_pathfree = fn;
}
void
puffs_set_namemod(struct puffs_usermount *pu, pu_namemod_fn fn)
{
pu->pu_namemod = fn;
}
void
puffs_set_errnotify(struct puffs_usermount *pu, pu_errnotify_fn fn)
{
pu->pu_errnotify = fn;
}
void
puffs_set_cmap(struct puffs_usermount *pu, pu_cmap_fn fn)
{
pu->pu_cmap = fn;
}
void
puffs_ml_setloopfn(struct puffs_usermount *pu, puffs_ml_loop_fn lfn)
{
pu->pu_ml_lfn = lfn;
}
void
puffs_ml_settimeout(struct puffs_usermount *pu, struct timespec *ts)
{
if (ts == NULL) {
pu->pu_ml_timep = NULL;
} else {
pu->pu_ml_timeout = *ts;
pu->pu_ml_timep = &pu->pu_ml_timeout;
}
}
void
puffs_set_prepost(struct puffs_usermount *pu,
pu_prepost_fn pre, pu_prepost_fn pst)
{
pu->pu_oppre = pre;
pu->pu_oppost = pst;
}
#ifndef __minix
void
puffs_setback(struct puffs_cc *pcc, int whatback)
{
struct puffs_req *preq = puffs__framebuf_getdataptr(pcc->pcc_pb);
assert(PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN && (
preq->preq_optype == PUFFS_VN_OPEN ||
preq->preq_optype == PUFFS_VN_MMAP ||
preq->preq_optype == PUFFS_VN_REMOVE ||
preq->preq_optype == PUFFS_VN_RMDIR ||
preq->preq_optype == PUFFS_VN_INACTIVE));
preq->preq_setbacks |= whatback & PUFFS_SETBACK_MASK;
}
int
puffs_daemon(struct puffs_usermount *pu, int nochdir, int noclose)
{
long int n;
int parent, value, fd;
if (pipe(pu->pu_dpipe) == -1)
return -1;
switch (fork()) {
case -1:
return -1;
case 0:
parent = 0;
break;
default:
parent = 1;
break;
}
pu->pu_state |= PU_PUFFSDAEMON;
if (parent) {
close(pu->pu_dpipe[1]);
n = read(pu->pu_dpipe[0], &value, sizeof(int));
if (n == -1)
err(1, "puffs_daemon");
if (n != sizeof(value))
errx(1, "puffs_daemon got %ld bytes", n);
if (value) {
errno = value;
err(1, "puffs_daemon");
}
exit(0);
} else {
if (setsid() == -1)
goto fail;
if (!nochdir)
chdir("/");
if (!noclose) {
fd = open(_PATH_DEVNULL, O_RDWR, 0);
if (fd == -1)
goto fail;
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
if (fd > STDERR_FILENO)
close(fd);
}
return 0;
}
fail:
n = write(pu->pu_dpipe[1], &errno, sizeof(int));
assert(n == 4);
return -1;
}
#endif /* !__minix */
static void
shutdaemon(struct puffs_usermount *pu, int error)
{
#ifndef __minix
ssize_t n;
n = write(pu->pu_dpipe[1], &error, sizeof(int));
assert(n == 4);
close(pu->pu_dpipe[0]);
close(pu->pu_dpipe[1]);
#endif /* !__minix */
pu->pu_state &= ~PU_PUFFSDAEMON;
}
int
puffs_mount(struct puffs_usermount *pu, const char *dir, int mntflags,
puffs_cookie_t cookie)
{
#ifndef __minix
int rv, fd, sverrno;
char *comfd;
#endif /* !__minix */
pu->pu_kargp->pa_root_cookie = cookie;
#ifndef __minix
/* XXXkludgehere */
/* kauth doesn't provide this service any longer */
if (geteuid() != 0)
mntflags |= MNT_NOSUID | MNT_NODEV;
/*
* Undocumented... Well, documented only here.
*
* This is used for imaginative purposes. If the env variable is
* set, puffs_mount() doesn't do the regular mount procedure.
* Rather, it crams the mount data down the comfd and sets comfd as
* the puffs descriptor.
*
* This shouldn't be used unless you can read my mind ( ... or write
* it, not to mention execute it, but that's starting to get silly).
*/
if ((comfd = getenv("PUFFS_COMFD")) != NULL) {
size_t len;
if (sscanf(comfd, "%d", &pu->pu_fd) != 1) {
errno = EINVAL;
rv = -1;
goto out;
}
/* check that what we got at least resembles an fd */
if (fcntl(pu->pu_fd, F_GETFL) == -1) {
rv = -1;
goto out;
}
#define allwrite(buf, len) \
do { \
ssize_t al_rv; \
al_rv = write(pu->pu_fd, buf, len); \
if ((size_t)al_rv != len) { \
if (al_rv != -1) \
errno = EIO; \
rv = -1; \
goto out; \
} \
} while (/*CONSTCOND*/0)
len = strlen(dir)+1;
allwrite(&len, sizeof(len));
allwrite(dir, len);
len = strlen(pu->pu_kargp->pa_mntfromname)+1;
allwrite(&len, sizeof(len));
allwrite(pu->pu_kargp->pa_mntfromname, len);
allwrite(&mntflags, sizeof(mntflags));
len = sizeof(*pu->pu_kargp);
allwrite(&len, sizeof(len));
allwrite(pu->pu_kargp, sizeof(*pu->pu_kargp));
allwrite(&pu->pu_flags, sizeof(pu->pu_flags));
#undef allwrite
rv = 0;
} else {
char rp[MAXPATHLEN];
if (realpath(dir, rp) == NULL) {
rv = -1;
goto out;
}
if (strcmp(dir, rp) != 0) {
warnx("puffs_mount: \"%s\" is a relative path.", dir);
warnx("puffs_mount: using \"%s\" instead.", rp);
}
fd = open(_PATH_PUFFS, O_RDWR);
if (fd == -1) {
warnx("puffs_mount: cannot open %s", _PATH_PUFFS);
rv = -1;
goto out;
}
if (fd <= 2)
warnx("puffs_mount: device fd %d (<= 2), sure this is "
"what you want?", fd);
pu->pu_kargp->pa_fd = pu->pu_fd = fd;
if ((rv = mount(MOUNT_PUFFS, rp, mntflags,
pu->pu_kargp, sizeof(struct puffs_kargs))) == -1)
goto out;
}
#else /* __minix */
/* Process the already-received mount request. */
if (!lpuffs_pump()) {
/* Not mounted? This should never happen.. */
free(pu->pu_kargp);
pu->pu_kargp = NULL;
errno = EINVAL;
return -1;
}
#endif /* __minix */
PU_SETSTATE(pu, PUFFS_STATE_RUNNING);
#ifndef __minix
out:
if (rv != 0)
sverrno = errno;
else
sverrno = 0;
free(pu->pu_kargp);
pu->pu_kargp = NULL;
if (pu->pu_state & PU_PUFFSDAEMON)
shutdaemon(pu, sverrno);
errno = sverrno;
return rv;
#else /* __minix */
return 0;
#endif /* __minix */
}
struct puffs_usermount *
puffs_init(struct puffs_ops *pops, const char *mntfromname,
const char *puffsname, void *priv, uint32_t pflags)
{
struct puffs_usermount *pu;
struct puffs_kargs *pargs;
int sverrno;
if (puffsname == PUFFS_DEFER)
puffsname = "n/a";
if (mntfromname == PUFFS_DEFER)
mntfromname = "n/a";
if (priv == PUFFS_DEFER)
priv = NULL;
pu = malloc(sizeof(struct puffs_usermount));
if (pu == NULL)
goto failfree;
memset(pu, 0, sizeof(struct puffs_usermount));
pargs = pu->pu_kargp = malloc(sizeof(struct puffs_kargs));
if (pargs == NULL)
goto failfree;
memset(pargs, 0, sizeof(struct puffs_kargs));
pargs->pa_vers = PUFFSVERSION;
pargs->pa_flags = PUFFS_FLAG_KERN(pflags);
fillvnopmask(pops, pargs);
puffs_setmntinfo(pu, mntfromname, puffsname);
puffs_zerostatvfs(&pargs->pa_svfsb);
pargs->pa_root_cookie = NULL;
pargs->pa_root_vtype = VDIR;
pargs->pa_root_vsize = 0;
pargs->pa_root_rdev = 0;
pargs->pa_maxmsglen = 0;
if (/*CONSTCOND*/ sizeof(time_t) == 4)
pargs->pa_time32 = 1;
else
pargs->pa_time32 = 0;
pu->pu_flags = pflags;
pu->pu_ops = *pops;
free(pops); /* XXX */
pu->pu_privdata = priv;
pu->pu_cc_stackshift = PUFFS_CC_STACKSHIFT_DEFAULT;
LIST_INIT(&pu->pu_pnodelst);
LIST_INIT(&pu->pu_ios);
LIST_INIT(&pu->pu_ios_rmlist);
LIST_INIT(&pu->pu_ccmagazin);
TAILQ_INIT(&pu->pu_sched);
#ifndef __minix
pu->pu_framectrl[PU_FRAMECTRL_FS].rfb = puffs__fsframe_read;
pu->pu_framectrl[PU_FRAMECTRL_FS].wfb = puffs__fsframe_write;
pu->pu_framectrl[PU_FRAMECTRL_FS].cmpfb = puffs__fsframe_cmp;
pu->pu_framectrl[PU_FRAMECTRL_FS].gotfb = puffs__fsframe_gotframe;
pu->pu_framectrl[PU_FRAMECTRL_FS].fdnotfn = puffs_framev_unmountonclose;
#endif /* !__minix */
/* defaults for some user-settable translation functions */
pu->pu_cmap = NULL; /* identity translation */
pu->pu_pathbuild = puffs_stdpath_buildpath;
pu->pu_pathfree = puffs_stdpath_freepath;
pu->pu_pathcmp = puffs_stdpath_cmppath;
pu->pu_pathtransform = NULL;
pu->pu_namemod = NULL;
pu->pu_errnotify = puffs_kernerr_log;
PU_SETSTATE(pu, PUFFS_STATE_BEFOREMOUNT);
#ifdef __minix
/* Do the MINIX3-specific side of the initialization. */
lpuffs_init(pu);
#endif /* __minix */
return pu;
failfree:
/* can't unmount() from here for obvious reasons */
sverrno = errno;
free(pu);
errno = sverrno;
return NULL;
}
void
puffs_cancel(struct puffs_usermount *pu, int error)
{
assert(puffs_getstate(pu) < PUFFS_STATE_RUNNING);
shutdaemon(pu, error);
free(pu);
}
/*ARGSUSED1*/
int
puffs_exit(struct puffs_usermount *pu, int unused /* strict compat */)
{
#ifndef __minix
struct puffs_framebuf *pb;
struct puffs_req *preq;
void *winp;
size_t winlen;
int sverrno;
pb = puffs_framebuf_make();
if (pb == NULL) {
errno = ENOMEM;
return -1;
}
winlen = sizeof(struct puffs_req);
if (puffs_framebuf_getwindow(pb, 0, &winp, &winlen) == -1) {
sverrno = errno;
puffs_framebuf_destroy(pb);
errno = sverrno;
return -1;
}
preq = winp;
preq->preq_buflen = sizeof(struct puffs_req);
preq->preq_opclass = PUFFSOP_UNMOUNT;
preq->preq_id = puffs__nextreq(pu);
puffs_framev_enqueue_justsend(pu, puffs_getselectable(pu), pb, 1, 0);
#else /* __minix */
struct puffs_node *pn;
lpuffs_debug("puffs_exit\n");
while ((pn = LIST_FIRST(&pu->pu_pnodelst)) != NULL)
puffs_pn_put(pn);
while ((pn = LIST_FIRST(&pu->pu_pnode_removed_lst)) != NULL)
puffs_pn_put(pn);
puffs__cc_exit(pu);
if (pu->pu_state & PU_HASKQ)
close(pu->pu_kq);
free(pu);
#endif /* __minix */
return 0;
}
#ifndef __minix
/* no sigset_t static intializer */
static int sigs[NSIG] = { 0, };
static int sigcatch = 0;
int
puffs_unmountonsignal(int sig, bool sigignore)
{
if (sig < 0 || sig >= (int)NSIG) {
errno = EINVAL;
return -1;
}
if (sigignore)
if (signal(sig, SIG_IGN) == SIG_ERR)
return -1;
if (!sigs[sig])
sigcatch++;
sigs[sig] = 1;
return 0;
}
#endif /* !__minix */
/*
* Actual mainloop. This is called from a context which can block.
* It is called either from puffs_mainloop (indirectly, via
* puffs_cc_continue() or from puffs_cc_yield()).
*/
void
puffs__theloop(struct puffs_cc *pcc)
{
struct puffs_usermount *pu = pcc->pcc_pu;
#ifndef __minix
struct puffs_framectrl *pfctrl;
struct puffs_fctrl_io *fio;
struct kevent *curev;
size_t nchanges;
int ndone;
#endif /* !__minix */
#ifndef __minix
while (puffs_getstate(pu) != PUFFS_STATE_UNMOUNTED) {
#else /* __minix */
do {
#endif /* __minix */
/*
* Schedule existing requests.
*/
while ((pcc = TAILQ_FIRST(&pu->pu_sched)) != NULL) {
TAILQ_REMOVE(&pu->pu_sched, pcc, pcc_schedent);
puffs__goto(pcc);
}
if (pu->pu_ml_lfn)
pu->pu_ml_lfn(pu);
#ifndef __minix
/* XXX: can we still do these optimizations? */
#if 0
/*
* Do this here, because:
* a) loopfunc might generate some results
* b) it's still "after" event handling (except for round 1)
*/
if (puffs_req_putput(ppr) == -1)
goto out;
puffs_req_resetput(ppr);
/* micro optimization: skip kevent syscall if possible */
if (pu->pu_nfds == 1 && pu->pu_ml_timep == NULL
&& (pu->pu_state & PU_ASYNCFD) == 0) {
pfctrl = XXX->fctrl;
puffs_framev_input(pu, pfctrl, XXX);
continue;
}
#endif
/* else: do full processing */
/* Don't bother worrying about O(n) for now */
LIST_FOREACH(fio, &pu->pu_ios, fio_entries) {
if (fio->stat & FIO_WRGONE)
continue;
pfctrl = fio->fctrl;
/*
* Try to write out everything to avoid the
* need for enabling EVFILT_WRITE. The likely
* case is that we can fit everything into the
* socket buffer.
*/
puffs__framev_output(pu, pfctrl, fio);
}
/*
* Build list of which to enable/disable in writecheck.
*/
nchanges = 0;
LIST_FOREACH(fio, &pu->pu_ios, fio_entries) {
if (fio->stat & FIO_WRGONE)
continue;
/* en/disable write checks for kqueue as needed */
assert((FIO_EN_WRITE(fio) && FIO_RM_WRITE(fio)) == 0);
if (FIO_EN_WRITE(fio)) {
EV_SET(&pu->pu_evs[nchanges], fio->io_fd,
EVFILT_WRITE, EV_ENABLE, 0, 0,
(uintptr_t)fio);
fio->stat |= FIO_WR;
nchanges++;
}
if (FIO_RM_WRITE(fio)) {
EV_SET(&pu->pu_evs[nchanges], fio->io_fd,
EVFILT_WRITE, EV_DISABLE, 0, 0,
(uintptr_t)fio);
fio->stat &= ~FIO_WR;
nchanges++;
}
}
ndone = kevent(pu->pu_kq, pu->pu_evs, nchanges,
pu->pu_evs, pu->pu_nevs, pu->pu_ml_timep);
if (ndone == -1) {
if (errno != EINTR)
break;
else
continue;
}
/* uoptimize */
if (ndone == 0)
continue;
/* iterate over the results */
for (curev = pu->pu_evs; ndone--; curev++) {
int what;
#if 0
/* get & possibly dispatch events from kernel */
if (curev->ident == puffsfd) {
if (puffs_req_handle(pgr, ppr, 0) == -1)
goto out;
continue;
}
#endif
fio = (void *)curev->udata;
if (__predict_true(fio))
pfctrl = fio->fctrl;
else
pfctrl = NULL;
if (curev->flags & EV_ERROR) {
assert(curev->filter == EVFILT_WRITE);
fio->stat &= ~FIO_WR;
/* XXX: how to know if it's a transient error */
puffs__framev_writeclose(pu, fio,
(int)curev->data);
puffs__framev_notify(fio, PUFFS_FBIO_ERROR);
continue;
}
what = 0;
if (curev->filter == EVFILT_READ) {
puffs__framev_input(pu, pfctrl, fio);
what |= PUFFS_FBIO_READ;
}
else if (curev->filter == EVFILT_WRITE) {
puffs__framev_output(pu, pfctrl, fio);
what |= PUFFS_FBIO_WRITE;
}
else if (__predict_false(curev->filter==EVFILT_SIGNAL)){
if ((pu->pu_state & PU_DONEXIT) == 0) {
PU_SETSFLAG(pu, PU_DONEXIT);
puffs_exit(pu, 0);
}
}
if (what)
puffs__framev_notify(fio, what);
}
/*
* Really free fd's now that we don't have references
* to them.
*/
while ((fio = LIST_FIRST(&pu->pu_ios_rmlist)) != NULL) {
LIST_REMOVE(fio, fio_entries);
free(fio);
}
#endif /* !__minix */
}
#ifdef __minix
while (lpuffs_pump());
#endif /* __minix */
if (puffs__cc_restoremain(pu) == -1)
warn("cannot restore main context. impending doom");
}
int
puffs_mainloop(struct puffs_usermount *pu)
{
#ifndef __minix
struct puffs_fctrl_io *fio;
#endif /* !__minix */
struct puffs_cc *pcc;
#ifndef __minix
struct kevent *curev;
size_t nevs;
int sverrno, i;
#else /* __minix */
int sverrno;
#endif /* !__minix */
assert(puffs_getstate(pu) >= PUFFS_STATE_RUNNING);
#ifndef __minix
pu->pu_kq = kqueue();
if (pu->pu_kq == -1)
goto out;
#endif /* !__minix */
pu->pu_state |= PU_HASKQ;
#ifndef __minix
puffs_setblockingmode(pu, PUFFSDEV_NONBLOCK);
if (puffs__framev_addfd_ctrl(pu, puffs_getselectable(pu),
PUFFS_FBIO_READ | PUFFS_FBIO_WRITE,
&pu->pu_framectrl[PU_FRAMECTRL_FS]) == -1)
goto out;
nevs = pu->pu_nevs + sigcatch;
curev = realloc(pu->pu_evs, nevs * sizeof(struct kevent));
if (curev == NULL)
goto out;
pu->pu_evs = curev;
pu->pu_nevs = nevs;
LIST_FOREACH(fio, &pu->pu_ios, fio_entries) {
EV_SET(curev, fio->io_fd, EVFILT_READ, EV_ADD,
0, 0, (uintptr_t)fio);
curev++;
EV_SET(curev, fio->io_fd, EVFILT_WRITE, EV_ADD | EV_DISABLE,
0, 0, (uintptr_t)fio);
curev++;
}
for (i = 0; i < NSIG; i++) {
if (sigs[i]) {
EV_SET(curev, i, EVFILT_SIGNAL, EV_ADD | EV_ENABLE,
0, 0, 0);
curev++;
}
}
assert(curev - pu->pu_evs == (ssize_t)pu->pu_nevs);
if (kevent(pu->pu_kq, pu->pu_evs, pu->pu_nevs, NULL, 0, NULL) == -1)
goto out;
#endif /* !__minix */
pu->pu_state |= PU_INLOOP;
/*
* Create alternate execution context and jump to it. Note
* that we come "out" of savemain twice. Where we come out
* of it depends on the architecture. If the return address is
* stored on the stack, we jump out from puffs_cc_continue(),
* for a register return address from puffs__cc_savemain().
* PU_MAINRESTORE makes sure we DTRT in both cases.
*/
if (puffs__cc_create(pu, puffs__theloop, &pcc) == -1) {
goto out;
}
#if 0
if (puffs__cc_savemain(pu) == -1) {
goto out;
}
#else
/*
* XXX
* puffs__cc_savemain() uses getcontext() and then returns.
* the caller (this function) may overwrite the stack frame
* of puffs__cc_savemain(), so when we call setcontext() later and
* return from puffs__cc_savemain() again, the return address or
* saved stack pointer can be garbage.
* avoid this by calling getcontext() directly here.
*/
extern int puffs_fakecc;
if (!puffs_fakecc) {
PU_CLRSFLAG(pu, PU_MAINRESTORE);
if (getcontext(&pu->pu_mainctx) == -1) {
goto out;
}
}
#endif
if ((pu->pu_state & PU_MAINRESTORE) == 0)
puffs_cc_continue(pcc);
finalpush(pu);
errno = 0;
out:
/* store the real error for a while */
sverrno = errno;
errno = sverrno;
if (errno)
return -1;
else
return 0;
}