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