puffs.c revision 1.112
1/*	$NetBSD: puffs.c,v 1.112 2010/07/06 18:01:14 pooka Exp $	*/
2
3/*
4 * Copyright (c) 2005, 2006, 2007  Antti Kantee.  All Rights Reserved.
5 *
6 * Development of this software was supported by the
7 * Google Summer of Code program and the Ulla Tuominen Foundation.
8 * The Google SoC project was mentored by Bill Studenmund.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
20 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33#if !defined(lint)
34__RCSID("$NetBSD: puffs.c,v 1.112 2010/07/06 18:01:14 pooka Exp $");
35#endif /* !lint */
36
37#include <sys/param.h>
38#include <sys/mount.h>
39
40#include <assert.h>
41#include <err.h>
42#include <errno.h>
43#include <fcntl.h>
44#include <mntopts.h>
45#include <paths.h>
46#include <puffs.h>
47#include <stdio.h>
48#include <stdlib.h>
49#include <string.h>
50#include <syslog.h>
51#include <unistd.h>
52
53#include "puffs_priv.h"
54
55/* Most file systems want this for opts, so just give it to them */
56const struct mntopt puffsmopts[] = {
57	MOPT_STDOPTS,
58	PUFFSMOPT_STD,
59	MOPT_NULL,
60};
61
62#ifdef PUFFS_WITH_THREADS
63#include <pthread.h>
64pthread_mutex_t pu_lock = PTHREAD_MUTEX_INITIALIZER;
65#endif
66
67#define FILLOP(lower, upper)						\
68do {									\
69	if (pops->puffs_node_##lower)					\
70		opmask[PUFFS_VN_##upper] = 1;				\
71} while (/*CONSTCOND*/0)
72static void
73fillvnopmask(struct puffs_ops *pops, struct puffs_kargs *pa)
74{
75	uint8_t *opmask = pa->pa_vnopmask;
76
77	memset(opmask, 0, sizeof(pa->pa_vnopmask));
78
79	FILLOP(create,   CREATE);
80	FILLOP(mknod,    MKNOD);
81	FILLOP(open,     OPEN);
82	FILLOP(close,    CLOSE);
83	FILLOP(access,   ACCESS);
84	FILLOP(getattr,  GETATTR);
85	FILLOP(setattr,  SETATTR);
86	FILLOP(poll,     POLL);
87	FILLOP(mmap,     MMAP);
88	FILLOP(fsync,    FSYNC);
89	FILLOP(seek,     SEEK);
90	FILLOP(remove,   REMOVE);
91	FILLOP(link,     LINK);
92	FILLOP(rename,   RENAME);
93	FILLOP(mkdir,    MKDIR);
94	FILLOP(rmdir,    RMDIR);
95	FILLOP(symlink,  SYMLINK);
96	FILLOP(readdir,  READDIR);
97	FILLOP(readlink, READLINK);
98	FILLOP(reclaim,  RECLAIM);
99	FILLOP(inactive, INACTIVE);
100	FILLOP(print,    PRINT);
101	FILLOP(read,     READ);
102	FILLOP(write,    WRITE);
103	FILLOP(abortop,  ABORTOP);
104	FILLOP(pathconf, PATHCONF);
105
106	FILLOP(getextattr,  GETEXTATTR);
107	FILLOP(setextattr,  SETEXTATTR);
108	FILLOP(listextattr, LISTEXTATTR);
109	FILLOP(deleteextattr, DELETEEXTATTR);
110}
111#undef FILLOP
112
113/*
114 * Go over all framev entries and write everything we can.  This is
115 * mostly for the benefit of delivering "unmount" to the kernel.
116 */
117static void
118finalpush(struct puffs_usermount *pu)
119{
120	struct puffs_fctrl_io *fio;
121
122	LIST_FOREACH(fio, &pu->pu_ios, fio_entries) {
123		if (fio->stat & FIO_WRGONE)
124			continue;
125
126		puffs__framev_output(pu, fio->fctrl, fio);
127	}
128}
129
130/*ARGSUSED*/
131void
132puffs_kernerr_abort(struct puffs_usermount *pu, uint8_t type,
133	int error, const char *str, puffs_cookie_t cookie)
134{
135
136	fprintf(stderr, "abort: type %d, error %d, cookie %p (%s)\n",
137	    type, error, cookie, str);
138	abort();
139}
140
141/*ARGSUSED*/
142void
143puffs_kernerr_log(struct puffs_usermount *pu, uint8_t type,
144	int error, const char *str, puffs_cookie_t cookie)
145{
146
147	syslog(LOG_WARNING, "kernel: type %d, error %d, cookie %p (%s)\n",
148	    type, error, cookie, str);
149}
150
151int
152puffs_getselectable(struct puffs_usermount *pu)
153{
154
155	return pu->pu_fd;
156}
157
158uint64_t
159puffs__nextreq(struct puffs_usermount *pu)
160{
161	uint64_t rv;
162
163	PU_LOCK();
164	rv = pu->pu_nextreq++ | (uint64_t)1<<63;
165	PU_UNLOCK();
166
167	return rv;
168}
169
170int
171puffs_setblockingmode(struct puffs_usermount *pu, int mode)
172{
173	int rv, x;
174
175	assert(puffs_getstate(pu) == PUFFS_STATE_RUNNING);
176
177	if (mode != PUFFSDEV_BLOCK && mode != PUFFSDEV_NONBLOCK) {
178		errno = EINVAL;
179		return -1;
180	}
181
182	x = mode;
183	rv = ioctl(pu->pu_fd, FIONBIO, &x);
184
185	if (rv == 0) {
186		if (mode == PUFFSDEV_BLOCK)
187			pu->pu_state &= ~PU_ASYNCFD;
188		else
189			pu->pu_state |= PU_ASYNCFD;
190	}
191
192	return rv;
193}
194
195int
196puffs_getstate(struct puffs_usermount *pu)
197{
198
199	return pu->pu_state & PU_STATEMASK;
200}
201
202void
203puffs_setstacksize(struct puffs_usermount *pu, size_t ss)
204{
205	long psize, minsize;
206	int stackshift;
207	int bonus;
208
209	assert(puffs_getstate(pu) == PUFFS_STATE_BEFOREMOUNT);
210
211	psize = sysconf(_SC_PAGESIZE);
212	minsize = 4*psize;
213	if (ss < (size_t)minsize || ss == PUFFS_STACKSIZE_MIN) {
214		if (ss != PUFFS_STACKSIZE_MIN)
215			fprintf(stderr, "puffs_setstacksize: adjusting "
216			    "stacksize to minimum %ld\n", minsize);
217		ss = 4*psize;
218	}
219
220	stackshift = -1;
221	bonus = 0;
222	while (ss) {
223		if (ss & 0x1)
224			bonus++;
225		ss >>= 1;
226		stackshift++;
227	}
228	if (bonus > 1) {
229		stackshift++;
230		fprintf(stderr, "puffs_setstacksize: using next power of two: "
231		    "%d\n", 1<<stackshift);
232	}
233
234	pu->pu_cc_stackshift = stackshift;
235}
236
237struct puffs_pathobj *
238puffs_getrootpathobj(struct puffs_usermount *pu)
239{
240	struct puffs_node *pnr;
241
242	pnr = pu->pu_pn_root;
243	if (pnr == NULL) {
244		errno = ENOENT;
245		return NULL;
246	}
247
248	return &pnr->pn_po;
249}
250
251void
252puffs_setroot(struct puffs_usermount *pu, struct puffs_node *pn)
253{
254
255	pu->pu_pn_root = pn;
256}
257
258struct puffs_node *
259puffs_getroot(struct puffs_usermount *pu)
260{
261
262	return pu->pu_pn_root;
263}
264
265void
266puffs_setrootinfo(struct puffs_usermount *pu, enum vtype vt,
267	vsize_t vsize, dev_t rdev)
268{
269	struct puffs_kargs *pargs = pu->pu_kargp;
270
271	if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT) {
272		warnx("puffs_setrootinfo: call has effect only "
273		    "before mount\n");
274		return;
275	}
276
277	pargs->pa_root_vtype = vt;
278	pargs->pa_root_vsize = vsize;
279	pargs->pa_root_rdev = rdev;
280}
281
282void *
283puffs_getspecific(struct puffs_usermount *pu)
284{
285
286	return pu->pu_privdata;
287}
288
289void
290puffs_setspecific(struct puffs_usermount *pu, void *privdata)
291{
292
293	pu->pu_privdata = privdata;
294}
295
296void
297puffs_setmntinfo(struct puffs_usermount *pu,
298	const char *mntfromname, const char *puffsname)
299{
300	struct puffs_kargs *pargs = pu->pu_kargp;
301
302	(void)strlcpy(pargs->pa_mntfromname, mntfromname,
303	    sizeof(pargs->pa_mntfromname));
304	(void)strlcpy(pargs->pa_typename, puffsname,
305	    sizeof(pargs->pa_typename));
306}
307
308size_t
309puffs_getmaxreqlen(struct puffs_usermount *pu)
310{
311
312	return pu->pu_maxreqlen;
313}
314
315void
316puffs_setmaxreqlen(struct puffs_usermount *pu, size_t reqlen)
317{
318
319	if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT)
320		warnx("puffs_setmaxreqlen: call has effect only "
321		    "before mount\n");
322
323	pu->pu_kargp->pa_maxmsglen = reqlen;
324}
325
326void
327puffs_setfhsize(struct puffs_usermount *pu, size_t fhsize, int flags)
328{
329
330	if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT)
331		warnx("puffs_setfhsize: call has effect only before mount\n");
332
333	pu->pu_kargp->pa_fhsize = fhsize;
334	pu->pu_kargp->pa_fhflags = flags;
335}
336
337void
338puffs_setncookiehash(struct puffs_usermount *pu, int nhash)
339{
340
341	if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT)
342		warnx("puffs_setfhsize: call has effect only before mount\n");
343
344	pu->pu_kargp->pa_nhashbuckets = nhash;
345}
346
347void
348puffs_set_pathbuild(struct puffs_usermount *pu, pu_pathbuild_fn fn)
349{
350
351	pu->pu_pathbuild = fn;
352}
353
354void
355puffs_set_pathtransform(struct puffs_usermount *pu, pu_pathtransform_fn fn)
356{
357
358	pu->pu_pathtransform = fn;
359}
360
361void
362puffs_set_pathcmp(struct puffs_usermount *pu, pu_pathcmp_fn fn)
363{
364
365	pu->pu_pathcmp = fn;
366}
367
368void
369puffs_set_pathfree(struct puffs_usermount *pu, pu_pathfree_fn fn)
370{
371
372	pu->pu_pathfree = fn;
373}
374
375void
376puffs_set_namemod(struct puffs_usermount *pu, pu_namemod_fn fn)
377{
378
379	pu->pu_namemod = fn;
380}
381
382void
383puffs_set_errnotify(struct puffs_usermount *pu, pu_errnotify_fn fn)
384{
385
386	pu->pu_errnotify = fn;
387}
388
389void
390puffs_set_cmap(struct puffs_usermount *pu, pu_cmap_fn fn)
391{
392
393	pu->pu_cmap = fn;
394}
395
396void
397puffs_ml_setloopfn(struct puffs_usermount *pu, puffs_ml_loop_fn lfn)
398{
399
400	pu->pu_ml_lfn = lfn;
401}
402
403void
404puffs_ml_settimeout(struct puffs_usermount *pu, struct timespec *ts)
405{
406
407	if (ts == NULL) {
408		pu->pu_ml_timep = NULL;
409	} else {
410		pu->pu_ml_timeout = *ts;
411		pu->pu_ml_timep = &pu->pu_ml_timeout;
412	}
413}
414
415void
416puffs_set_prepost(struct puffs_usermount *pu,
417	pu_prepost_fn pre, pu_prepost_fn pst)
418{
419
420	pu->pu_oppre = pre;
421	pu->pu_oppost = pst;
422}
423
424void
425puffs_setback(struct puffs_cc *pcc, int whatback)
426{
427	struct puffs_req *preq = puffs__framebuf_getdataptr(pcc->pcc_pb);
428
429	assert(PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN && (
430	    preq->preq_optype == PUFFS_VN_OPEN ||
431	    preq->preq_optype == PUFFS_VN_MMAP ||
432	    preq->preq_optype == PUFFS_VN_REMOVE ||
433	    preq->preq_optype == PUFFS_VN_RMDIR ||
434	    preq->preq_optype == PUFFS_VN_INACTIVE));
435
436	preq->preq_setbacks |= whatback & PUFFS_SETBACK_MASK;
437}
438
439int
440puffs_daemon(struct puffs_usermount *pu, int nochdir, int noclose)
441{
442	long int n;
443	int parent, value, fd;
444
445	if (pipe(pu->pu_dpipe) == -1)
446		return -1;
447
448	switch (fork()) {
449	case -1:
450		return -1;
451	case 0:
452		parent = 0;
453		break;
454	default:
455		parent = 1;
456		break;
457	}
458	pu->pu_state |= PU_PUFFSDAEMON;
459
460	if (parent) {
461		close(pu->pu_dpipe[1]);
462		n = read(pu->pu_dpipe[0], &value, sizeof(int));
463		if (n == -1)
464			err(1, "puffs_daemon");
465		if (n != sizeof(value))
466			errx(1, "puffs_daemon got %ld bytes", n);
467		if (value) {
468			errno = value;
469			err(1, "puffs_daemon");
470		}
471		exit(0);
472	} else {
473		if (setsid() == -1)
474			goto fail;
475
476		if (!nochdir)
477			chdir("/");
478
479		if (!noclose) {
480			fd = open(_PATH_DEVNULL, O_RDWR, 0);
481			if (fd == -1)
482				goto fail;
483			dup2(fd, STDIN_FILENO);
484			dup2(fd, STDOUT_FILENO);
485			dup2(fd, STDERR_FILENO);
486			if (fd > STDERR_FILENO)
487				close(fd);
488		}
489		return 0;
490	}
491
492 fail:
493	n = write(pu->pu_dpipe[1], &errno, sizeof(int));
494	assert(n == 4);
495	return -1;
496}
497
498static void
499shutdaemon(struct puffs_usermount *pu, int error)
500{
501	ssize_t n;
502
503	n = write(pu->pu_dpipe[1], &error, sizeof(int));
504	assert(n == 4);
505	close(pu->pu_dpipe[0]);
506	close(pu->pu_dpipe[1]);
507	pu->pu_state &= ~PU_PUFFSDAEMON;
508}
509
510int
511puffs_mount(struct puffs_usermount *pu, const char *dir, int mntflags,
512	puffs_cookie_t cookie)
513{
514	char rp[MAXPATHLEN];
515	int rv, fd, sverrno;
516	char *comfd;
517
518	pu->pu_kargp->pa_root_cookie = cookie;
519
520	/* XXXkludgehere */
521	/* kauth doesn't provide this service any longer */
522	if (geteuid() != 0)
523		mntflags |= MNT_NOSUID | MNT_NODEV;
524
525	if (realpath(dir, rp) == NULL) {
526		rv = -1;
527		goto out;
528	}
529
530	if (strcmp(dir, rp) != 0) {
531		warnx("puffs_mount: \"%s\" is a relative path.", dir);
532		warnx("puffs_mount: using \"%s\" instead.", rp);
533	}
534
535	/*
536	 * Undocumented...  Well, documented only here.
537	 *
538	 * This is used for imaginative purposes.  If the env variable is
539	 * set, puffs_mount() doesn't do the regular mount procedure.
540	 * Rather, it crams the mount data down the comfd and sets comfd as
541	 * the puffs descriptor.
542	 *
543	 * This shouldn't be used unless you can read my mind ( ... or write
544	 * it, not to mention execute it, but that's starting to get silly).
545	 */
546	if ((comfd = getenv("PUFFS_COMFD")) != NULL) {
547		size_t len;
548
549		if (sscanf(comfd, "%d", &pu->pu_fd) != 1) {
550			errno = EINVAL;
551			rv = -1;
552			goto out;
553		}
554		/* check that what we got at least resembles an fd */
555		if (fcntl(pu->pu_fd, F_GETFL) == -1) {
556			rv = -1;
557			goto out;
558		}
559
560#define allwrite(buf, len)						\
561do {									\
562	ssize_t al_rv;							\
563	al_rv = write(pu->pu_fd, buf, len);				\
564	if ((size_t)al_rv != len) {					\
565		if (al_rv != -1)					\
566			errno = EIO;					\
567		rv = -1;						\
568		goto out;						\
569	}								\
570} while (/*CONSTCOND*/0)
571		len = strlen(dir)+1;
572		allwrite(&len, sizeof(len));
573		allwrite(dir, len);
574		len = strlen(pu->pu_kargp->pa_mntfromname)+1;
575		allwrite(&len, sizeof(len));
576		allwrite(pu->pu_kargp->pa_mntfromname, len);
577		allwrite(&mntflags, sizeof(mntflags));
578		len = sizeof(*pu->pu_kargp);
579		allwrite(&len, sizeof(len));
580		allwrite(pu->pu_kargp, sizeof(*pu->pu_kargp));
581		allwrite(&pu->pu_flags, sizeof(pu->pu_flags));
582#undef allwrite
583
584		rv = 0;
585	} else {
586		fd = open(_PATH_PUFFS, O_RDWR);
587		if (fd == -1) {
588			warnx("puffs_mount: cannot open %s", _PATH_PUFFS);
589			rv = -1;
590			goto out;
591		}
592		if (fd <= 2)
593			warnx("puffs_mount: device fd %d (<= 2), sure this is "
594			    "what you want?", fd);
595
596		pu->pu_kargp->pa_fd = pu->pu_fd = fd;
597		if ((rv = mount(MOUNT_PUFFS, rp, mntflags,
598		    pu->pu_kargp, sizeof(struct puffs_kargs))) == -1)
599			goto out;
600	}
601
602	PU_SETSTATE(pu, PUFFS_STATE_RUNNING);
603
604 out:
605	if (rv != 0)
606		sverrno = errno;
607	else
608		sverrno = 0;
609	free(pu->pu_kargp);
610	pu->pu_kargp = NULL;
611
612	if (pu->pu_state & PU_PUFFSDAEMON)
613		shutdaemon(pu, sverrno);
614
615	errno = sverrno;
616	return rv;
617}
618
619struct puffs_usermount *
620puffs_init(struct puffs_ops *pops, const char *mntfromname,
621	const char *puffsname, void *priv, uint32_t pflags)
622{
623	struct puffs_usermount *pu;
624	struct puffs_kargs *pargs;
625	int sverrno;
626
627	if (puffsname == PUFFS_DEFER)
628		puffsname = "n/a";
629	if (mntfromname == PUFFS_DEFER)
630		mntfromname = "n/a";
631	if (priv == PUFFS_DEFER)
632		priv = NULL;
633
634	pu = malloc(sizeof(struct puffs_usermount));
635	if (pu == NULL)
636		goto failfree;
637	memset(pu, 0, sizeof(struct puffs_usermount));
638
639	pargs = pu->pu_kargp = malloc(sizeof(struct puffs_kargs));
640	if (pargs == NULL)
641		goto failfree;
642	memset(pargs, 0, sizeof(struct puffs_kargs));
643
644	pargs->pa_vers = PUFFSVERSION;
645	pargs->pa_flags = PUFFS_FLAG_KERN(pflags);
646	fillvnopmask(pops, pargs);
647	puffs_setmntinfo(pu, mntfromname, puffsname);
648
649	puffs_zerostatvfs(&pargs->pa_svfsb);
650	pargs->pa_root_cookie = NULL;
651	pargs->pa_root_vtype = VDIR;
652	pargs->pa_root_vsize = 0;
653	pargs->pa_root_rdev = 0;
654	pargs->pa_maxmsglen = 0;
655	if (sizeof(time_t) == 4)
656		pargs->pa_time32 = 1;
657	else
658		pargs->pa_time32 = 0;
659
660	pu->pu_flags = pflags;
661	pu->pu_ops = *pops;
662	free(pops); /* XXX */
663
664	pu->pu_privdata = priv;
665	pu->pu_cc_stackshift = PUFFS_CC_STACKSHIFT_DEFAULT;
666	LIST_INIT(&pu->pu_pnodelst);
667	LIST_INIT(&pu->pu_ios);
668	LIST_INIT(&pu->pu_ios_rmlist);
669	LIST_INIT(&pu->pu_ccmagazin);
670	TAILQ_INIT(&pu->pu_sched);
671
672	pu->pu_framectrl[PU_FRAMECTRL_FS].rfb = puffs__fsframe_read;
673	pu->pu_framectrl[PU_FRAMECTRL_FS].wfb = puffs__fsframe_write;
674	pu->pu_framectrl[PU_FRAMECTRL_FS].cmpfb = puffs__fsframe_cmp;
675	pu->pu_framectrl[PU_FRAMECTRL_FS].gotfb = puffs__fsframe_gotframe;
676	pu->pu_framectrl[PU_FRAMECTRL_FS].fdnotfn = puffs_framev_unmountonclose;
677
678	/* defaults for some user-settable translation functions */
679	pu->pu_cmap = NULL; /* identity translation */
680
681	pu->pu_pathbuild = puffs_stdpath_buildpath;
682	pu->pu_pathfree = puffs_stdpath_freepath;
683	pu->pu_pathcmp = puffs_stdpath_cmppath;
684	pu->pu_pathtransform = NULL;
685	pu->pu_namemod = NULL;
686
687	pu->pu_errnotify = puffs_kernerr_log;
688
689	PU_SETSTATE(pu, PUFFS_STATE_BEFOREMOUNT);
690
691	return pu;
692
693 failfree:
694	/* can't unmount() from here for obvious reasons */
695	sverrno = errno;
696	free(pu);
697	errno = sverrno;
698	return NULL;
699}
700
701void
702puffs_cancel(struct puffs_usermount *pu, int error)
703{
704
705	assert(puffs_getstate(pu) < PUFFS_STATE_RUNNING);
706	shutdaemon(pu, error);
707	free(pu);
708}
709
710/*ARGSUSED1*/
711int
712puffs_exit(struct puffs_usermount *pu, int unused /* strict compat */)
713{
714	struct puffs_framebuf *pb;
715	struct puffs_req *preq;
716	void *winp;
717	size_t winlen;
718	int sverrno;
719
720	pb = puffs_framebuf_make();
721	if (pb == NULL) {
722		errno = ENOMEM;
723		return -1;
724	}
725
726	winlen = sizeof(struct puffs_req);
727	if (puffs_framebuf_getwindow(pb, 0, &winp, &winlen) == -1) {
728		sverrno = errno;
729		puffs_framebuf_destroy(pb);
730		errno = sverrno;
731		return -1;
732	}
733	preq = winp;
734
735	preq->preq_buflen = sizeof(struct puffs_req);
736	preq->preq_opclass = PUFFSOP_UNMOUNT;
737	preq->preq_id = puffs__nextreq(pu);
738
739	puffs_framev_enqueue_justsend(pu, puffs_getselectable(pu), pb, 1, 0);
740
741	return 0;
742}
743
744/* no sigset_t static intializer */
745static int sigs[NSIG] = { 0, };
746static int sigcatch = 0;
747
748int
749puffs_unmountonsignal(int sig, bool sigignore)
750{
751
752	if (sig < 0 || sig >= (int)NSIG) {
753		errno = EINVAL;
754		return -1;
755	}
756	if (sigignore)
757		if (signal(sig, SIG_IGN) == SIG_ERR)
758			return -1;
759
760	if (!sigs[sig])
761		sigcatch++;
762	sigs[sig] = 1;
763
764	return 0;
765}
766
767/*
768 * Actual mainloop.  This is called from a context which can block.
769 * It is called either from puffs_mainloop (indirectly, via
770 * puffs_cc_continue() or from puffs_cc_yield()).
771 */
772void
773puffs__theloop(struct puffs_cc *pcc)
774{
775	struct puffs_usermount *pu = pcc->pcc_pu;
776	struct puffs_framectrl *pfctrl;
777	struct puffs_fctrl_io *fio;
778	struct kevent *curev;
779	size_t nchanges;
780	int ndone;
781
782	while (puffs_getstate(pu) != PUFFS_STATE_UNMOUNTED) {
783
784		/*
785		 * Schedule existing requests.
786		 */
787		while ((pcc = TAILQ_FIRST(&pu->pu_sched)) != NULL) {
788			TAILQ_REMOVE(&pu->pu_sched, pcc, pcc_schedent);
789			puffs__goto(pcc);
790		}
791
792		if (pu->pu_ml_lfn)
793			pu->pu_ml_lfn(pu);
794
795		/* XXX: can we still do these optimizations? */
796#if 0
797		/*
798		 * Do this here, because:
799		 *  a) loopfunc might generate some results
800		 *  b) it's still "after" event handling (except for round 1)
801		 */
802		if (puffs_req_putput(ppr) == -1)
803			goto out;
804		puffs_req_resetput(ppr);
805
806		/* micro optimization: skip kevent syscall if possible */
807		if (pu->pu_nfds == 1 && pu->pu_ml_timep == NULL
808		    && (pu->pu_state & PU_ASYNCFD) == 0) {
809			pfctrl = XXX->fctrl;
810			puffs_framev_input(pu, pfctrl, XXX);
811			continue;
812		}
813#endif
814
815		/* else: do full processing */
816		/* Don't bother worrying about O(n) for now */
817		LIST_FOREACH(fio, &pu->pu_ios, fio_entries) {
818			if (fio->stat & FIO_WRGONE)
819				continue;
820
821			pfctrl = fio->fctrl;
822
823			/*
824			 * Try to write out everything to avoid the
825			 * need for enabling EVFILT_WRITE.  The likely
826			 * case is that we can fit everything into the
827			 * socket buffer.
828			 */
829			puffs__framev_output(pu, pfctrl, fio);
830		}
831
832		/*
833		 * Build list of which to enable/disable in writecheck.
834		 */
835		nchanges = 0;
836		LIST_FOREACH(fio, &pu->pu_ios, fio_entries) {
837			if (fio->stat & FIO_WRGONE)
838				continue;
839
840			/* en/disable write checks for kqueue as needed */
841			assert((FIO_EN_WRITE(fio) && FIO_RM_WRITE(fio)) == 0);
842			if (FIO_EN_WRITE(fio)) {
843				EV_SET(&pu->pu_evs[nchanges], fio->io_fd,
844				    EVFILT_WRITE, EV_ENABLE, 0, 0,
845				    (uintptr_t)fio);
846				fio->stat |= FIO_WR;
847				nchanges++;
848			}
849			if (FIO_RM_WRITE(fio)) {
850				EV_SET(&pu->pu_evs[nchanges], fio->io_fd,
851				    EVFILT_WRITE, EV_DISABLE, 0, 0,
852				    (uintptr_t)fio);
853				fio->stat &= ~FIO_WR;
854				nchanges++;
855			}
856		}
857
858		ndone = kevent(pu->pu_kq, pu->pu_evs, nchanges,
859		    pu->pu_evs, pu->pu_nevs, pu->pu_ml_timep);
860
861		if (ndone == -1) {
862			if (errno != EINTR)
863				break;
864			else
865				continue;
866		}
867
868		/* uoptimize */
869		if (ndone == 0)
870			continue;
871
872		/* iterate over the results */
873		for (curev = pu->pu_evs; ndone--; curev++) {
874			int what;
875
876#if 0
877			/* get & possibly dispatch events from kernel */
878			if (curev->ident == puffsfd) {
879				if (puffs_req_handle(pgr, ppr, 0) == -1)
880					goto out;
881				continue;
882			}
883#endif
884
885			fio = (void *)curev->udata;
886			if (__predict_true(fio))
887				pfctrl = fio->fctrl;
888			else
889				pfctrl = NULL;
890			if (curev->flags & EV_ERROR) {
891				assert(curev->filter == EVFILT_WRITE);
892				fio->stat &= ~FIO_WR;
893
894				/* XXX: how to know if it's a transient error */
895				puffs__framev_writeclose(pu, fio,
896				    (int)curev->data);
897				puffs__framev_notify(fio, PUFFS_FBIO_ERROR);
898				continue;
899			}
900
901			what = 0;
902			if (curev->filter == EVFILT_READ) {
903				puffs__framev_input(pu, pfctrl, fio);
904				what |= PUFFS_FBIO_READ;
905			}
906
907			else if (curev->filter == EVFILT_WRITE) {
908				puffs__framev_output(pu, pfctrl, fio);
909				what |= PUFFS_FBIO_WRITE;
910			}
911
912			else if (__predict_false(curev->filter==EVFILT_SIGNAL)){
913				if ((pu->pu_state & PU_DONEXIT) == 0) {
914					PU_SETSFLAG(pu, PU_DONEXIT);
915					puffs_exit(pu, 0);
916				}
917			}
918			if (what)
919				puffs__framev_notify(fio, what);
920		}
921
922		/*
923		 * Really free fd's now that we don't have references
924		 * to them.
925		 */
926		while ((fio = LIST_FIRST(&pu->pu_ios_rmlist)) != NULL) {
927			LIST_REMOVE(fio, fio_entries);
928			free(fio);
929		}
930	}
931
932	if (puffs__cc_restoremain(pu) == -1)
933		warn("cannot restore main context.  impending doom");
934}
935int
936puffs_mainloop(struct puffs_usermount *pu)
937{
938	struct puffs_fctrl_io *fio;
939	struct puffs_cc *pcc;
940	struct kevent *curev;
941	size_t nevs;
942	int sverrno, i;
943
944	assert(puffs_getstate(pu) >= PUFFS_STATE_RUNNING);
945
946	pu->pu_kq = kqueue();
947	if (pu->pu_kq == -1)
948		goto out;
949	pu->pu_state |= PU_HASKQ;
950
951	puffs_setblockingmode(pu, PUFFSDEV_NONBLOCK);
952	if (puffs__framev_addfd_ctrl(pu, puffs_getselectable(pu),
953	    PUFFS_FBIO_READ | PUFFS_FBIO_WRITE,
954	    &pu->pu_framectrl[PU_FRAMECTRL_FS]) == -1)
955		goto out;
956
957	nevs = pu->pu_nevs + sigcatch;
958	curev = realloc(pu->pu_evs, nevs * sizeof(struct kevent));
959	if (curev == NULL)
960		goto out;
961	pu->pu_evs = curev;
962	pu->pu_nevs = nevs;
963
964	LIST_FOREACH(fio, &pu->pu_ios, fio_entries) {
965		EV_SET(curev, fio->io_fd, EVFILT_READ, EV_ADD,
966		    0, 0, (uintptr_t)fio);
967		curev++;
968		EV_SET(curev, fio->io_fd, EVFILT_WRITE, EV_ADD | EV_DISABLE,
969		    0, 0, (uintptr_t)fio);
970		curev++;
971	}
972	for (i = 0; i < NSIG; i++) {
973		if (sigs[i]) {
974			EV_SET(curev, i, EVFILT_SIGNAL, EV_ADD | EV_ENABLE,
975			    0, 0, 0);
976			curev++;
977		}
978	}
979	assert(curev - pu->pu_evs == (ssize_t)pu->pu_nevs);
980	if (kevent(pu->pu_kq, pu->pu_evs, pu->pu_nevs, NULL, 0, NULL) == -1)
981		goto out;
982
983	pu->pu_state |= PU_INLOOP;
984
985	/*
986	 * Create alternate execution context and jump to it.  Note
987	 * that we come "out" of savemain twice.  Where we come out
988	 * of it depends on the architecture.  If the return address is
989	 * stored on the stack, we jump out from puffs_cc_continue(),
990	 * for a register return address from puffs__cc_savemain().
991	 * PU_MAINRESTORE makes sure we DTRT in both cases.
992	 */
993	if (puffs__cc_create(pu, puffs__theloop, &pcc) == -1) {
994		goto out;
995	}
996	if (puffs__cc_savemain(pu) == -1) {
997		goto out;
998	}
999	if ((pu->pu_state & PU_MAINRESTORE) == 0)
1000		puffs_cc_continue(pcc);
1001
1002	finalpush(pu);
1003	errno = 0;
1004
1005 out:
1006	/* store the real error for a while */
1007	sverrno = errno;
1008
1009	errno = sverrno;
1010	if (errno)
1011		return -1;
1012	else
1013		return 0;
1014}
1015