Expand PMF_FN_* macros.
[netbsd-mini2440.git] / sbin / mount_portal / puffs_portal.c
blob9dd41baa9a0e07624b15c48cb37a787a5a71936d
1 /* $NetBSD: puffs_portal.c,v 1.2 2009/12/05 20:29:19 pooka Exp $ */
3 /*
4 * Copyright (c) 2007 Antti Kantee. All Rights Reserved.
5 * Development was supported by the Finnish Cultural Foundation.
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.
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.
29 #include <sys/cdefs.h>
30 #ifndef lint
31 __RCSID("$NetBSD: puffs_portal.c,v 1.2 2009/12/05 20:29:19 pooka Exp $");
32 #endif /* !lint */
34 #include <sys/types.h>
35 #include <sys/wait.h>
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>
50 #include "portald.h"
52 struct portal_node {
53 char *path;
54 int fd;
57 static void usage(void);
59 PUFFSOP_PROTOS(portal);
61 #define PORTAL_ROOT NULL
62 #define METADATASIZE (sizeof(int) + sizeof(size_t))
64 qelem q;
65 int readcfg, sigchild;
66 const char *cfg;
68 static void
69 usage()
72 errx(1, "usage: %s [-o options] /path/portal.conf mount_point",
73 getprogname());
76 static void
77 sighup(int sig)
80 readcfg = 1;
83 static void
84 sigcry(int sig)
87 sigchild = 1;
90 static void
91 portal_loopfn(struct puffs_usermount *pu)
94 if (readcfg)
95 conf_read(&q, cfg);
96 readcfg = 0;
98 if (sigchild) {
99 sigchild = 0;
100 while (waitpid(-1, NULL, WNOHANG) != -1)
101 continue;
105 #define PUFBUF_FD 1
106 #define PUFBUF_DATA 2
108 #define CMSIZE (sizeof(struct cmsghdr) + sizeof(int))
110 /* receive file descriptor produced by our child process */
111 static int
112 readfd(struct puffs_framebuf *pufbuf, int fd, int *done)
114 struct cmsghdr *cmp;
115 struct msghdr msg;
116 struct iovec iov;
117 ssize_t n;
118 int error, rv;
120 rv = 0;
121 cmp = emalloc(CMSG_SPACE(sizeof(int)));
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));
132 n = recvmsg(fd, &msg, 0);
133 if (n == -1) {
134 rv = errno;
135 goto out;
137 if (n == 0) {
138 rv = ECONNRESET;
139 goto out;
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;
148 puffs_framebuf_putdata_atoff(pufbuf, sizeof(int),
149 CMSG_DATA(cmp), sizeof(int));
150 *done = 1;
152 out:
153 free(cmp);
154 return rv;
158 * receive data from provider
160 * XXX: should read directly into the buffer and adjust offsets
161 * instead of doing memcpy
163 static int
164 readdata(struct puffs_framebuf *pufbuf, int fd, int *done)
166 char buf[1024];
167 size_t max;
168 ssize_t n;
169 size_t moved;
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;
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 */
187 if (n < 0) {
188 if (moved)
189 return 0;
191 if (errno != EAGAIN)
192 return errno;
193 else
194 return 0;
197 puffs_framebuf_putdata(pufbuf, buf, n);
198 moved += n;
199 max -= n;
200 } while (max > 0);
202 *done = 1;
204 return 0;
207 static int
208 portal_frame_rf(struct puffs_usermount *pu, struct puffs_framebuf *pufbuf,
209 int fd, int *done)
211 int type;
213 if (puffs_framebuf_getdata_atoff(pufbuf, 0, &type, sizeof(int)) == -1)
214 return EINVAL;
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();
224 static int
225 portal_frame_wf(struct puffs_usermount *pu, struct puffs_framebuf *pufbuf,
226 int fd, int *done)
228 void *win;
229 size_t pbsize, pboff, winlen;
230 ssize_t n;
231 int error;
233 pboff = puffs_framebuf_telloff(pufbuf);
234 pbsize = puffs_framebuf_tellsize(pufbuf);
235 error = 0;
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 */
249 if (n < 0) {
250 if (pboff != 0)
251 break;
253 if (errno != EAGAIN)
254 return errno;
255 return 0;
258 pboff += n;
259 puffs_framebuf_seekset(pufbuf, pboff);
260 } while (pboff != pbsize);
262 *done = 1;
263 puffs_framebuf_putdata_atoff(pufbuf, 0, &pboff, sizeof(size_t));
264 return error;
267 /* transfer file descriptor to master file server */
268 static int
269 sendfd(int s, int fd, int error)
271 struct cmsghdr *cmp;
272 struct msghdr msg;
273 struct iovec iov;
274 ssize_t n;
275 int rv;
277 rv = 0;
278 cmp = emalloc(CMSG_LEN(sizeof(int)));
280 iov.iov_base = &error;
281 iov.iov_len = sizeof(int);
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));
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;
300 n = sendmsg(s, &msg, 0);
301 if (n == -1)
302 rv = errno;
303 else if (n < (ssize_t)sizeof(int))
304 rv = EPROTO;
306 free(cmp);
307 return rv;
311 * Produce I/O file descriptor by forking (like original portald).
313 * child: run provider and transfer produced fd to parent
314 * parent: yield until child produces fd. receive it and store it.
316 static int
317 provide(struct puffs_usermount *pu, struct portal_node *portn,
318 struct portal_cred *portc, char **v)
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;
326 pufbuf = puffs_framebuf_make();
327 if (pufbuf == NULL)
328 return ENOMEM;
330 data = PUFBUF_FD;
331 if (puffs_framebuf_putdata(pufbuf, &data, sizeof(int)) == -1)
332 goto bad;
334 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, s) == -1)
335 goto bad;
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]);
351 if (puffs_framebuf_tellsize(pufbuf) < sizeof(int)) {
352 errno = EIO;
353 goto bad;
355 puffs_framebuf_getdata_atoff(pufbuf, 0, &error, sizeof(int));
356 if (error) {
357 errno = error;
358 goto bad;
361 if (puffs_framebuf_tellsize(pufbuf) != 2*sizeof(int)) {
362 errno = EIO;
363 goto bad;
366 puffs_framebuf_getdata_atoff(pufbuf, sizeof(int),
367 &fd, sizeof(int));
368 puffs_framebuf_destroy(pufbuf);
370 data = 1;
371 if (ioctl(fd, FIONBIO, &data) == -1)
372 return errno;
374 if (puffs_framev_addfd(pu, fd, PUFFS_FBIO_WRITE) == -1)
375 return errno;
377 portn->fd = fd;
378 return 0;
381 bad:
382 puffs_framebuf_destroy(pufbuf);
383 return errno;
387 main(int argc, char *argv[])
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;
398 setprogname(argv[0]);
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*/
418 pflags |= PUFFS_KFLAG_NOCACHE | PUFFS_KFLAG_LOOKUP_FULLPNBUF;
419 if (pflags & PUFFS_FLAG_OPDUMP)
420 detach = 0;
421 argc -= optind;
422 argv += optind;
424 if (argc != 2)
425 usage();
427 PUFFSOP_INIT(pops);
429 PUFFSOP_SETFSNOP(pops, unmount);
430 PUFFSOP_SETFSNOP(pops, sync);
431 PUFFSOP_SETFSNOP(pops, statvfs);
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);
444 pu = puffs_init(pops, _PATH_PUFFS, "portal", NULL, pflags);
445 if (pu == NULL)
446 err(1, "init");
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");
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);
463 puffs_ml_setloopfn(pu, portal_loopfn);
464 puffs_framev_init(pu, portal_frame_rf, portal_frame_wf, NULL,NULL,NULL);
466 if (detach)
467 if (puffs_daemon(pu, 1, 1) == -1)
468 err(1, "puffs_daemon");
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");
475 return 0;
478 static struct portal_node *
479 makenode(const char *path)
481 struct portal_node *portn;
483 portn = emalloc(sizeof(struct portal_node));
484 portn->path = estrdup(path);
485 portn->fd = -1;
487 return portn;
490 static void
491 credtr(struct portal_cred *portc, const struct puffs_cred *puffc, int mode)
493 memset(portc, 0, sizeof(struct portal_cred));
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);
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.
509 portal_node_lookup(struct puffs_usermount *pu, puffs_cookie_t opc,
510 struct puffs_newinfo *pni, const struct puffs_cn *pcn)
512 struct portal_node *portn;
514 assert(opc == PORTAL_ROOT);
516 if (pcn->pcn_nameiop != NAMEI_LOOKUP
517 && pcn->pcn_nameiop != NAMEI_CREATE)
518 return EOPNOTSUPP;
520 portn = makenode(pcn->pcn_name);
521 puffs_newinfo_setcookie(pni, portn);
522 puffs_newinfo_setvtype(pni, VREG);
524 pcn->pcn_flags &= ~NAMEI_REQUIREDIR;
525 pcn->pcn_consume = strlen(pcn->pcn_name) - pcn->pcn_namelen;
527 return 0;
530 int fakeid = 3;
532 /* XXX: libpuffs'ize */
534 portal_node_getattr(struct puffs_usermount *pu, puffs_cookie_t opc,
535 struct vattr *va, const struct puffs_cred *pcr)
537 struct timeval tv;
538 struct timespec ts;
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;
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;
557 gettimeofday(&tv, NULL);
558 TIMEVAL_TO_TIMESPEC(&tv, &ts);
559 va->va_atime = va->va_ctime = va->va_mtime = va->va_birthtime = ts;
561 return 0;
564 /* for writing, just pretend we care */
566 portal_node_setattr(struct puffs_usermount *pu, puffs_cookie_t opc,
567 const struct vattr *va, const struct puffs_cred *pcr)
570 return 0;
574 portal_node_open(struct puffs_usermount *pu, puffs_cookie_t opc, int mode,
575 const struct puffs_cred *pcr)
577 struct portal_node *portn = opc;
578 struct portal_cred portc;
579 char **v;
581 if (opc == PORTAL_ROOT)
582 return 0;
584 if (mode & O_NONBLOCK)
585 return EOPNOTSUPP;
587 v = conf_match(&q, portn->path);
588 if (v == NULL)
589 return ENOENT;
591 credtr(&portc, pcr, mode);
592 return provide(pu, portn, &portc, v);
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)
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;
608 assert(opc != PORTAL_ROOT);
609 error = 0;
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;
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));
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;
628 } else {
629 rv = puffs_framev_enqueue_directreceive(pcc,
630 portn->fd, pufbuf, 0);
632 if (rv == -1) {
633 error = errno;
634 goto out;
638 xfersize = puffs_framebuf_tellsize(pufbuf) - METADATASIZE;
639 if (xfersize == 0) {
640 assert(ioflag & PUFFS_IO_NDELAY);
641 error = EAGAIN;
642 goto out;
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);
654 memcpy(buf + boff, win, winsize);
655 xfersize -= winsize;
656 boff += winsize;
659 out:
660 puffs_framev_disablefd(pu, portn->fd, PUFFS_FBIO_READ);
661 puffs_framebuf_destroy(pufbuf);
663 /* a trickery, from readdata() */
664 if (error == -1)
665 return 0;
666 return error;
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)
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;
680 assert(opc != PORTAL_ROOT);
682 pufbuf = puffs_framebuf_make();
683 puffs_framebuf_putdata(pufbuf, buf, *resid);
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;
692 } else {
693 rv = puffs_framev_enqueue_directsend(pcc, portn->fd, pufbuf, 0);
694 if (rv == -1) {
695 error = errno;
696 goto out;
700 rv = puffs_framebuf_getdata_atoff(pufbuf, 0, &written, sizeof(size_t));
701 assert(rv == 0);
702 assert(written <= *resid);
703 *resid -= written;
705 out:
706 puffs_framebuf_destroy(pufbuf);
707 if (error == -1)
708 error = 0;
709 return 0;
713 portal_node_seek(struct puffs_usermount *pu, puffs_cookie_t opc,
714 off_t oldoff, off_t newoff, const struct puffs_cred *pcr)
716 struct portal_node *portn = opc;
718 if (opc == PORTAL_ROOT || portn->fd == -1)
719 return EOPNOTSUPP;
721 if (lseek(portn->fd, newoff, SEEK_SET) == -1)
722 return errno;
723 return 0;
727 portal_node_poll(struct puffs_usermount *pu, puffs_cookie_t opc, int *events)
729 struct puffs_cc *pcc = puffs_cc_getcc(pu);
730 struct portal_node *portn = opc;
731 int what;
732 int rv;
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;
742 rv = puffs_framev_enqueue_waitevent(pcc, portn->fd, &what);
743 if (rv) {
744 *events = POLLERR;
745 return rv;
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;
756 return 0;
760 portal_node_inactive(struct puffs_usermount *pu, puffs_cookie_t opc)
763 if (opc == PORTAL_ROOT)
764 return 0;
766 puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N1);
767 return 0;
771 portal_node_reclaim(struct puffs_usermount *pu, puffs_cookie_t opc)
773 struct portal_node *portn = opc;
775 if (portn->fd != -1) {
776 puffs_framev_removefd(pu, portn->fd, 0);
777 close(portn->fd);
779 free(portn->path);
780 free(portn);
782 return 0;