/*
* 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.
*/
/*
* 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)
{
struct puffs_fctrl_io *fio;
LIST_FOREACH(fio, &pu->pu_ios, fio_entries) {
if (fio->stat & FIO_WRGONE)
continue;
psize = sysconf(_SC_PAGESIZE);
minsize = 4*psize;
if (ss < (size_t)minsize || ss == PUFFS_STACKSIZE_MIN) {
if (ss != PUFFS_STACKSIZE_MIN)
warnx("%s: adjusting " "stacksize to minimum %ld",
__func__, minsize);
ss = 4*psize;
}
stackshift = -1;
bonus = 0;
while (ss) {
if (ss & 0x1)
bonus++;
ss >>= 1;
stackshift++;
}
if (bonus > 1) {
stackshift++;
warnx("%s: using next power of two: %d", __func__,
1 << stackshift);
}
int
puffs_mount(struct puffs_usermount *pu, const char *dir, int mntflags,
puffs_cookie_t cookie)
{
int rv, fd, sverrno;
char *comfd;
pu->pu_kargp->pa_root_cookie = cookie;
/* 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 (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
/* no sigset_t static initializer */
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;
}
/*
* 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;
struct puffs_framectrl *pfctrl;
struct puffs_fctrl_io *fio;
struct kevent *curev;
size_t nchanges;
int ndone;
while (puffs_getstate(pu) != PUFFS_STATE_UNMOUNTED) {
/* 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);
/* 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;
/* 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;
switch (curev->filter) {
case EVFILT_READ:
puffs__framev_input(pu, pfctrl, fio);
what |= PUFFS_FBIO_READ;
break;
case EVFILT_WRITE:
puffs__framev_output(pu, pfctrl, fio);
what |= PUFFS_FBIO_WRITE;
break;
case EVFILT_SIGNAL:
if ((pu->pu_state & PU_DONEXIT) == 0) {
PU_SETSFLAG(pu, PU_DONEXIT);
puffs_exit(pu, 0);
}
break;
default:
warn("unhandled filter %d", curev->filter);
}
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);
}
}
if (puffs__cc_restoremain(pu) == -1)
warn("cannot restore main context. impending doom");
}
int
puffs_mainloop(struct puffs_usermount *pu)
{
struct puffs_fctrl_io *fio;
struct puffs_cc *pcc;
struct kevent *curev;
size_t nevs;
int sverrno, i;
/*
* 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;