xref: /netbsd/sbin/mount_portal/puffs_portal.c (revision 6550d01e)
1 /*	$NetBSD: puffs_portal.c,v 1.3 2009/12/19 16:01:25 pooka Exp $	*/
2 
3 /*
4  * Copyright (c) 2007  Antti Kantee.  All Rights Reserved.
5  * Development was supported by the Finnish Cultural Foundation.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
17  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 #ifndef lint
31 __RCSID("$NetBSD: puffs_portal.c,v 1.3 2009/12/19 16:01:25 pooka Exp $");
32 #endif /* !lint */
33 
34 #include <sys/types.h>
35 #include <sys/wait.h>
36 
37 #include <assert.h>
38 #include <err.h>
39 #include <errno.h>
40 #include <mntopts.h>
41 #include <paths.h>
42 #include <poll.h>
43 #include <puffs.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <util.h>
49 
50 #include "portald.h"
51 
52 struct portal_node {
53 	char *path;
54 	int fd;
55 };
56 
57 static void usage(void);
58 
59 PUFFSOP_PROTOS(portal);
60 
61 #define PORTAL_ROOT NULL
62 #define METADATASIZE (sizeof(int) + sizeof(size_t))
63 
64 qelem q;
65 int readcfg, sigchild;
66 const char *cfg;
67 
68 static void
69 usage()
70 {
71 
72 	errx(1, "usage: %s [-o options] /path/portal.conf mount_point",
73 	    getprogname());
74 }
75 
76 static void
77 sighup(int sig)
78 {
79 
80 	readcfg = 1;
81 }
82 
83 static void
84 sigcry(int sig)
85 {
86 
87 	sigchild = 1;
88 }
89 
90 static void
91 portal_loopfn(struct puffs_usermount *pu)
92 {
93 
94 	if (readcfg)
95 		conf_read(&q, cfg);
96 	readcfg = 0;
97 
98 	if (sigchild) {
99 		sigchild = 0;
100 		while (waitpid(-1, NULL, WNOHANG) != -1)
101 			continue;
102 	}
103 }
104 
105 #define PUFBUF_FD	1
106 #define PUFBUF_DATA	2
107 
108 #define CMSIZE (sizeof(struct cmsghdr) + sizeof(int))
109 
110 /* receive file descriptor produced by our child process */
111 static int
112 readfd(struct puffs_framebuf *pufbuf, int fd, int *done)
113 {
114 	struct cmsghdr *cmp;
115 	struct msghdr msg;
116 	struct iovec iov;
117 	ssize_t n;
118 	int error, rv;
119 
120 	rv = 0;
121 	cmp = emalloc(CMSG_SPACE(sizeof(int)));
122 
123 	iov.iov_base = &error;
124 	iov.iov_len = sizeof(int);
125 	msg.msg_iov = &iov;
126 	msg.msg_iovlen = 1;
127 	msg.msg_name = NULL;
128 	msg.msg_namelen = 0;
129 	msg.msg_control = cmp;
130 	msg.msg_controllen = CMSG_SPACE(sizeof(int));
131 
132 	n = recvmsg(fd, &msg, 0);
133 	if (n == -1) {
134 		rv = errno;
135 		goto out;
136 	}
137 	if (n == 0) {
138 		rv = ECONNRESET;
139 		goto out;
140 	}
141 
142 	/* the data for the server */
143 	puffs_framebuf_putdata_atoff(pufbuf, 0, &error, sizeof(int));
144 	if (error) {
145 		rv = error;
146 		goto out;
147 	}
148 	puffs_framebuf_putdata_atoff(pufbuf, sizeof(int),
149 	    CMSG_DATA(cmp), sizeof(int));
150 	*done = 1;
151 
152  out:
153 	free(cmp);
154 	return rv;
155 }
156 
157 /*
158  * receive data from provider
159  *
160  * XXX: should read directly into the buffer and adjust offsets
161  * instead of doing memcpy
162  */
163 static int
164 readdata(struct puffs_framebuf *pufbuf, int fd, int *done)
165 {
166 	char buf[1024];
167 	size_t max;
168 	ssize_t n;
169 	size_t moved;
170 
171 	/* don't override metadata */
172 	if (puffs_framebuf_telloff(pufbuf) == 0)
173 		puffs_framebuf_seekset(pufbuf, METADATASIZE);
174 	puffs_framebuf_getdata_atoff(pufbuf, sizeof(int), &max, sizeof(size_t));
175 	moved = puffs_framebuf_tellsize(pufbuf) - METADATASIZE;
176 	assert(max >= moved);
177 	max -= moved;
178 
179 	do {
180 		n = read(fd, buf, MIN(sizeof(buf), max));
181 		if (n == 0) {
182 			if (moved)
183 				break;
184 			else
185 				return -1; /* caught by read */
186 		}
187 		if (n < 0) {
188 			if (moved)
189 				return 0;
190 
191 			if (errno != EAGAIN)
192 				return errno;
193 			else
194 				return 0;
195 		}
196 
197 		puffs_framebuf_putdata(pufbuf, buf, n);
198 		moved += n;
199 		max -= n;
200 	} while (max > 0);
201 
202 	*done = 1;
203 
204 	return 0;
205 }
206 
207 static int
208 portal_frame_rf(struct puffs_usermount *pu, struct puffs_framebuf *pufbuf,
209 	int fd, int *done)
210 {
211 	int type;
212 
213 	if (puffs_framebuf_getdata_atoff(pufbuf, 0, &type, sizeof(int)) == -1)
214 		return EINVAL;
215 
216 	if (type == PUFBUF_FD)
217 		return readfd(pufbuf, fd, done);
218 	else if (type == PUFBUF_DATA)
219 		return readdata(pufbuf, fd, done);
220 	else
221 		abort();
222 }
223 
224 static int
225 portal_frame_wf(struct puffs_usermount *pu, struct puffs_framebuf *pufbuf,
226 	int fd, int *done)
227 {
228 	void *win;
229 	size_t pbsize, pboff, winlen;
230 	ssize_t n;
231 	int error;
232 
233 	pboff = puffs_framebuf_telloff(pufbuf);
234 	pbsize = puffs_framebuf_tellsize(pufbuf);
235 	error = 0;
236 
237 	do {
238 		assert(pbsize > pboff);
239 		winlen = pbsize - pboff;
240 		if (puffs_framebuf_getwindow(pufbuf, pboff, &win, &winlen)==-1)
241 			return errno;
242 		n = write(fd, win, winlen);
243 		if (n == 0) {
244 			if (pboff != 0)
245 				break;
246 			else
247 				return -1; /* caught by node_write */
248 		}
249 		if (n < 0) {
250 			if (pboff != 0)
251 				break;
252 
253 			if (errno != EAGAIN)
254 				return errno;
255 			return 0;
256 		}
257 
258 		pboff += n;
259 		puffs_framebuf_seekset(pufbuf, pboff);
260 	} while (pboff != pbsize);
261 
262 	*done = 1;
263 	puffs_framebuf_putdata_atoff(pufbuf, 0, &pboff, sizeof(size_t));
264 	return error;
265 }
266 
267 /* transfer file descriptor to master file server */
268 static int
269 sendfd(int s, int fd, int error)
270 {
271 	struct cmsghdr *cmp;
272 	struct msghdr msg;
273 	struct iovec iov;
274 	ssize_t n;
275 	int rv;
276 
277 	rv = 0;
278 	cmp = emalloc(CMSG_LEN(sizeof(int)));
279 
280 	iov.iov_base = &error;
281 	iov.iov_len = sizeof(int);
282 
283 	msg.msg_iov = &iov;
284 	msg.msg_iovlen = 1;
285 	msg.msg_name = NULL;
286 	msg.msg_namelen = 0;
287 	if (error == 0) {
288 		cmp->cmsg_level = SOL_SOCKET;
289 		cmp->cmsg_type = SCM_RIGHTS;
290 		cmp->cmsg_len = CMSG_LEN(sizeof(int));
291 
292 		msg.msg_control = cmp;
293 		msg.msg_controllen = CMSG_LEN(sizeof(int));
294 		*(int *)CMSG_DATA(cmp) = fd;
295 	} else {
296 		msg.msg_control = NULL;
297 		msg.msg_controllen = 0;
298 	}
299 
300 	n = sendmsg(s, &msg, 0);
301 	if (n == -1)
302 		rv = errno;
303 	else if (n < (ssize_t)sizeof(int))
304 		rv = EPROTO;
305 
306 	free(cmp);
307 	return rv;
308 }
309 
310 /*
311  * Produce I/O file descriptor by forking (like original portald).
312  *
313  * child: run provider and transfer produced fd to parent
314  * parent: yield until child produces fd.  receive it and store it.
315  */
316 static int
317 provide(struct puffs_usermount *pu, struct portal_node *portn,
318 	struct portal_cred *portc, char **v)
319 {
320 	struct puffs_cc *pcc = puffs_cc_getcc(pu);
321 	struct puffs_framebuf *pufbuf;
322 	int s[2];
323 	int fd, error;
324 	int data;
325 
326 	pufbuf = puffs_framebuf_make();
327 	if (pufbuf == NULL)
328 		return ENOMEM;
329 
330 	data = PUFBUF_FD;
331 	if (puffs_framebuf_putdata(pufbuf, &data, sizeof(int)) == -1)
332 		goto bad;
333 
334 	if (socketpair(AF_LOCAL, SOCK_STREAM, 0, s) == -1)
335 		goto bad;
336 
337 	switch (fork()) {
338 	case -1:
339 		goto bad;
340 	case 0:
341 		error = activate_argv(portc, portn->path, v, &fd);
342 		sendfd(s[1], fd, error);
343 		exit(0);
344 	default:
345 		puffs_framev_addfd(pu, s[0], PUFFS_FBIO_READ);
346 		puffs_framev_enqueue_directreceive(pcc, s[0], pufbuf, 0);
347 		puffs_framev_removefd(pu, s[0], 0);
348 		close(s[0]);
349 		close(s[1]);
350 
351 		if (puffs_framebuf_tellsize(pufbuf) < sizeof(int)) {
352 			errno = EIO;
353 			goto bad;
354 		}
355 		puffs_framebuf_getdata_atoff(pufbuf, 0, &error, sizeof(int));
356 		if (error) {
357 			errno = error;
358 			goto bad;
359 		}
360 
361 		if (puffs_framebuf_tellsize(pufbuf) != 2*sizeof(int)) {
362 			errno = EIO;
363 			goto bad;
364 		}
365 
366 		puffs_framebuf_getdata_atoff(pufbuf, sizeof(int),
367 		    &fd, sizeof(int));
368 		puffs_framebuf_destroy(pufbuf);
369 
370 		data = 1;
371 		if (ioctl(fd, FIONBIO, &data) == -1)
372 			return errno;
373 
374 		if (puffs_framev_addfd(pu, fd, PUFFS_FBIO_WRITE) == -1)
375 			return errno;
376 
377 		portn->fd = fd;
378 		return 0;
379 	}
380 
381  bad:
382 	puffs_framebuf_destroy(pufbuf);
383 	return errno;
384 }
385 
386 int
387 main(int argc, char *argv[])
388 {
389 	extern char *optarg;
390 	extern int optind;
391 	struct puffs_usermount *pu;
392 	struct puffs_ops *pops;
393 	mntoptparse_t mp;
394 	int pflags, mntflags;
395 	int detach;
396 	int ch;
397 
398 	setprogname(argv[0]);
399 
400 	mntflags = pflags = 0;
401 	detach = 1;
402 	while ((ch = getopt(argc, argv, "o:s")) != -1) {
403 		switch (ch) {
404 		case 'o':
405 			mp = getmntopts(optarg, puffsmopts, &mntflags, &pflags);
406 			if (mp == NULL)
407 				err(1, "getmntopts");
408 			freemntopts(mp);
409 			break;
410 		case 's': /* stay on top */
411 			detach = 0;
412 			break;
413 		default:
414 			usage();
415 			/*NOTREACHED*/
416 		}
417 	}
418 	pflags |= PUFFS_KFLAG_NOCACHE | PUFFS_KFLAG_LOOKUP_FULLPNBUF;
419 	if (pflags & PUFFS_FLAG_OPDUMP)
420 		detach = 0;
421 	argc -= optind;
422 	argv += optind;
423 
424 	if (argc != 2)
425 		usage();
426 
427 	PUFFSOP_INIT(pops);
428 
429 	PUFFSOP_SETFSNOP(pops, unmount);
430 	PUFFSOP_SETFSNOP(pops, sync);
431 	PUFFSOP_SETFSNOP(pops, statvfs);
432 
433 	PUFFSOP_SET(pops, portal, node, lookup);
434 	PUFFSOP_SET(pops, portal, node, getattr);
435 	PUFFSOP_SET(pops, portal, node, setattr);
436 	PUFFSOP_SET(pops, portal, node, open);
437 	PUFFSOP_SET(pops, portal, node, read);
438 	PUFFSOP_SET(pops, portal, node, write);
439 	PUFFSOP_SET(pops, portal, node, seek);
440 	PUFFSOP_SET(pops, portal, node, poll);
441 	PUFFSOP_SET(pops, portal, node, inactive);
442 	PUFFSOP_SET(pops, portal, node, reclaim);
443 
444 	pu = puffs_init(pops, _PATH_PUFFS, "portal", NULL, pflags);
445 	if (pu == NULL)
446 		err(1, "init");
447 
448 	if (signal(SIGHUP, sighup) == SIG_ERR)
449 		warn("cannot set sighup handler");
450 	if (signal(SIGCHLD, sigcry) == SIG_ERR)
451 		err(1, "cannot set sigchild handler");
452 	if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
453 		err(1, "cannot ignore sigpipe");
454 
455 	readcfg = 0;
456 	cfg = argv[0];
457 	if (*cfg != '/')
458 		errx(1, "need absolute path for config");
459 	q.q_forw = q.q_back = &q;
460 	if (conf_read(&q, cfg) == -1)
461 		err(1, "cannot read cfg \"%s\"", cfg);
462 
463 	puffs_ml_setloopfn(pu, portal_loopfn);
464 	puffs_framev_init(pu, portal_frame_rf, portal_frame_wf, NULL,NULL,NULL);
465 
466 	if (detach)
467 		if (puffs_daemon(pu, 1, 1) == -1)
468 			err(1, "puffs_daemon");
469 
470 	if (puffs_mount(pu,  argv[1], mntflags, PORTAL_ROOT) == -1)
471 		err(1, "mount");
472 	if (puffs_mainloop(pu) == -1)
473 		err(1, "mainloop");
474 
475 	return 0;
476 }
477 
478 static struct portal_node *
479 makenode(const char *path)
480 {
481 	struct portal_node *portn;
482 
483 	portn = emalloc(sizeof(struct portal_node));
484 	portn->path = estrdup(path);
485 	portn->fd = -1;
486 
487 	return portn;
488 }
489 
490 static void
491 credtr(struct portal_cred *portc, const struct puffs_cred *puffc, int mode)
492 {
493 	memset(portc, 0, sizeof(struct portal_cred));
494 
495 	portc->pcr_flag = mode;
496 	puffs_cred_getuid(puffc, &portc->pcr_uid);
497 	puffs_cred_getgid(puffc, &portc->pcr_gid);
498 	puffs_cred_getgroups(puffc, portc->pcr_groups,
499 	    (short *)&portc->pcr_ngroups);
500 }
501 
502 /*
503  * XXX: we could also simply already resolve the name at this stage
504  * instead of deferring it to open.  But doing it in open is how the
505  * original portald does it, and I don't want to introduce any funny
506  * incompatibilities.
507  */
508 int
509 portal_node_lookup(struct puffs_usermount *pu, puffs_cookie_t opc,
510 	struct puffs_newinfo *pni, const struct puffs_cn *pcn)
511 {
512 	struct portal_node *portn;
513 
514 	assert(opc == PORTAL_ROOT);
515 
516 	if (pcn->pcn_nameiop != NAMEI_LOOKUP
517 	    && pcn->pcn_nameiop != NAMEI_CREATE)
518 		return EOPNOTSUPP;
519 
520 	portn = makenode(pcn->pcn_name);
521 	puffs_newinfo_setcookie(pni, portn);
522 	puffs_newinfo_setvtype(pni, VREG);
523 
524 	pcn->pcn_flags &= ~NAMEI_REQUIREDIR;
525 	pcn->pcn_consume = strlen(pcn->pcn_name) - pcn->pcn_namelen;
526 
527 	return 0;
528 }
529 
530 int fakeid = 3;
531 
532 /* XXX: libpuffs'ize */
533 int
534 portal_node_getattr(struct puffs_usermount *pu, puffs_cookie_t opc,
535 	struct vattr *va, const struct puffs_cred *pcr)
536 {
537 	struct timeval tv;
538 	struct timespec ts;
539 
540 	puffs_vattr_null(va);
541 	if (opc == PORTAL_ROOT) {
542 		va->va_type = VDIR;
543 		va->va_mode = 0777;
544 		va->va_nlink = 2;
545 	} else {
546 		va->va_type = VREG;
547 		va->va_mode = 0666;
548 		va->va_nlink = 1;
549 	}
550 	va->va_uid = va->va_gid = 0;
551 	va->va_fileid = fakeid++;
552 	va->va_size = va->va_bytes = 0;
553 	va->va_gen = 0;
554 	va->va_rdev = PUFFS_VNOVAL;
555 	va->va_blocksize = DEV_BSIZE;
556 
557 	gettimeofday(&tv, NULL);
558 	TIMEVAL_TO_TIMESPEC(&tv, &ts);
559 	va->va_atime = va->va_ctime = va->va_mtime = va->va_birthtime = ts;
560 
561 	return 0;
562 }
563 
564 /* for writing, just pretend we care */
565 int
566 portal_node_setattr(struct puffs_usermount *pu, puffs_cookie_t opc,
567 	const struct vattr *va, const struct puffs_cred *pcr)
568 {
569 
570 	return 0;
571 }
572 
573 int
574 portal_node_open(struct puffs_usermount *pu, puffs_cookie_t opc, int mode,
575 	const struct puffs_cred *pcr)
576 {
577 	struct portal_node *portn = opc;
578 	struct portal_cred portc;
579 	char **v;
580 
581 	if (opc == PORTAL_ROOT)
582 		return 0;
583 
584 	if (mode & O_NONBLOCK)
585 		return EOPNOTSUPP;
586 
587 	v = conf_match(&q, portn->path);
588 	if (v == NULL)
589 		return ENOENT;
590 
591 	credtr(&portc, pcr, mode);
592 	return provide(pu, portn, &portc, v);
593 }
594 
595 int
596 portal_node_read(struct puffs_usermount *pu, puffs_cookie_t opc,
597 	uint8_t *buf, off_t offset, size_t *resid,
598 	const struct puffs_cred *pcr, int ioflag)
599 {
600 	struct puffs_cc *pcc = puffs_cc_getcc(pu);
601 	struct portal_node *portn = opc;
602 	struct puffs_framebuf *pufbuf;
603 	size_t xfersize, winsize, boff;
604 	void *win;
605 	int rv, error;
606 	int data, dummy;
607 
608 	assert(opc != PORTAL_ROOT);
609 	error = 0;
610 
611 	/* if we can't (re-)enable it, treat it as EOF */
612 	rv = puffs_framev_enablefd(pu, portn->fd, PUFFS_FBIO_READ);
613 	if (rv == -1)
614 		return 0;
615 
616 	pufbuf = puffs_framebuf_make();
617 	data = PUFBUF_DATA;
618 	puffs_framebuf_putdata(pufbuf, &data, sizeof(int));
619 	puffs_framebuf_putdata(pufbuf, resid, sizeof(size_t));
620 
621 	/* if we are doing nodelay, do read directly */
622 	if (ioflag & PUFFS_IO_NDELAY) {
623 		rv = readdata(pufbuf, portn->fd, &dummy);
624 		if (rv != 0) {
625 			error = rv;
626 			goto out;
627 		}
628 	} else {
629 		rv = puffs_framev_enqueue_directreceive(pcc,
630 		    portn->fd, pufbuf, 0);
631 
632 		if (rv == -1) {
633 			error = errno;
634 			goto out;
635 		}
636 	}
637 
638 	xfersize = puffs_framebuf_tellsize(pufbuf) - METADATASIZE;
639 	if (xfersize == 0) {
640 		assert(ioflag & PUFFS_IO_NDELAY);
641 		error = EAGAIN;
642 		goto out;
643 	}
644 
645 	*resid -= xfersize;
646 	boff = 0;
647 	while (xfersize > 0) {
648 		winsize = xfersize;
649 		rv = puffs_framebuf_getwindow(pufbuf, METADATASIZE,
650 		    &win, &winsize);
651 		assert(rv == 0);
652 		assert(winsize > 0);
653 
654 		memcpy(buf + boff, win, winsize);
655 		xfersize -= winsize;
656 		boff += winsize;
657 	}
658 
659  out:
660 	puffs_framev_disablefd(pu, portn->fd, PUFFS_FBIO_READ);
661 	puffs_framebuf_destroy(pufbuf);
662 
663 	/* a trickery, from readdata() */
664 	if (error == -1)
665 		return 0;
666 	return error;
667 }
668 
669 int
670 portal_node_write(struct puffs_usermount *pu, puffs_cookie_t opc,
671 	uint8_t *buf, off_t offset, size_t *resid,
672 	const struct puffs_cred *pcr, int ioflag)
673 {
674 	struct puffs_cc *pcc = puffs_cc_getcc(pu);
675 	struct portal_node *portn = opc;
676 	struct puffs_framebuf *pufbuf;
677 	size_t written;
678 	int error, rv, dummy;
679 
680 	assert(opc != PORTAL_ROOT);
681 
682 	pufbuf = puffs_framebuf_make();
683 	puffs_framebuf_putdata(pufbuf, buf, *resid);
684 
685 	error = 0;
686 	if (ioflag & PUFFS_IO_NDELAY) {
687 		rv = portal_frame_wf(pu, pufbuf, portn->fd, &dummy);
688 		if (rv) {
689 			error = rv;
690 			goto out;
691 		}
692 	} else  {
693 		rv = puffs_framev_enqueue_directsend(pcc, portn->fd, pufbuf, 0);
694 		if (rv == -1) {
695 			error = errno;
696 			goto out;
697 		}
698 	}
699 
700 	rv = puffs_framebuf_getdata_atoff(pufbuf, 0, &written, sizeof(size_t));
701 	assert(rv == 0);
702 	assert(written <= *resid);
703 	*resid -= written;
704 
705  out:
706 	puffs_framebuf_destroy(pufbuf);
707 	if (error == -1)
708 		error = 0;
709 	return 0;
710 }
711 
712 int
713 portal_node_seek(struct puffs_usermount *pu, puffs_cookie_t opc,
714 	off_t oldoff, off_t newoff, const struct puffs_cred *pcr)
715 {
716 	struct portal_node *portn = opc;
717 
718 	if (opc == PORTAL_ROOT || portn->fd == -1)
719 		return EOPNOTSUPP;
720 
721 	if (lseek(portn->fd, newoff, SEEK_SET) == -1)
722 		return errno;
723 	return 0;
724 }
725 
726 int
727 portal_node_poll(struct puffs_usermount *pu, puffs_cookie_t opc, int *events)
728 {
729 	struct puffs_cc *pcc = puffs_cc_getcc(pu);
730 	struct portal_node *portn = opc;
731 	int what;
732 	int rv;
733 
734 	what = 0;
735 	if (*events & POLLIN)
736 		what |= PUFFS_FBIO_READ;
737 	if (*events & POLLOUT)
738 		what |= PUFFS_FBIO_WRITE;
739 	if (*events & POLLERR)
740 		what |= PUFFS_FBIO_ERROR;
741 
742 	rv = puffs_framev_enqueue_waitevent(pcc, portn->fd, &what);
743 	if (rv) {
744 		*events = POLLERR;
745 		return rv;
746 	}
747 
748 	*events = 0;
749 	if (what & PUFFS_FBIO_READ)
750 		*events |= POLLIN;
751 	if (what & PUFFS_FBIO_WRITE)
752 		*events |= POLLOUT;
753 	if (what & PUFFS_FBIO_ERROR)
754 		*events |= POLLERR;
755 
756 	return 0;
757 }
758 
759 int
760 portal_node_inactive(struct puffs_usermount *pu, puffs_cookie_t opc)
761 {
762 
763 	if (opc == PORTAL_ROOT)
764 		return 0;
765 
766 	puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N1);
767 	return 0;
768 }
769 
770 int
771 portal_node_reclaim(struct puffs_usermount *pu, puffs_cookie_t opc)
772 {
773 	struct portal_node *portn = opc;
774 
775 	if (portn->fd != -1) {
776 		puffs_framev_removefd(pu, portn->fd, 0);
777 		close(portn->fd);
778 	}
779 	free(portn->path);
780 	free(portn);
781 
782 	return 0;
783 }
784