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