1 /* $NetBSD: puffs_portal.c,v 1.2 2009/12/05 20:29:19 pooka Exp $ */
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
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
29 #include <sys/cdefs.h>
31 __RCSID("$NetBSD: puffs_portal.c,v 1.2 2009/12/05 20:29:19 pooka Exp $");
34 #include <sys/types.h>
57 static void usage(void);
59 PUFFSOP_PROTOS(portal
);
61 #define PORTAL_ROOT NULL
62 #define METADATASIZE (sizeof(int) + sizeof(size_t))
65 int readcfg
, sigchild
;
72 errx(1, "usage: %s [-o options] /path/portal.conf mount_point",
91 portal_loopfn(struct puffs_usermount
*pu
)
100 while (waitpid(-1, NULL
, WNOHANG
) != -1)
106 #define PUFBUF_DATA 2
108 #define CMSIZE (sizeof(struct cmsghdr) + sizeof(int))
110 /* receive file descriptor produced by our child process */
112 readfd(struct puffs_framebuf
*pufbuf
, int fd
, int *done
)
121 cmp
= emalloc(CMSG_SPACE(sizeof(int)));
123 iov
.iov_base
= &error
;
124 iov
.iov_len
= sizeof(int);
129 msg
.msg_control
= cmp
;
130 msg
.msg_controllen
= CMSG_SPACE(sizeof(int));
132 n
= recvmsg(fd
, &msg
, 0);
142 /* the data for the server */
143 puffs_framebuf_putdata_atoff(pufbuf
, 0, &error
, sizeof(int));
148 puffs_framebuf_putdata_atoff(pufbuf
, sizeof(int),
149 CMSG_DATA(cmp
), sizeof(int));
158 * receive data from provider
160 * XXX: should read directly into the buffer and adjust offsets
161 * instead of doing memcpy
164 readdata(struct puffs_framebuf
*pufbuf
, int fd
, int *done
)
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
);
180 n
= read(fd
, buf
, MIN(sizeof(buf
), max
));
185 return -1; /* caught by read */
197 puffs_framebuf_putdata(pufbuf
, buf
, n
);
208 portal_frame_rf(struct puffs_usermount
*pu
, struct puffs_framebuf
*pufbuf
,
213 if (puffs_framebuf_getdata_atoff(pufbuf
, 0, &type
, sizeof(int)) == -1)
216 if (type
== PUFBUF_FD
)
217 return readfd(pufbuf
, fd
, done
);
218 else if (type
== PUFBUF_DATA
)
219 return readdata(pufbuf
, fd
, done
);
225 portal_frame_wf(struct puffs_usermount
*pu
, struct puffs_framebuf
*pufbuf
,
229 size_t pbsize
, pboff
, winlen
;
233 pboff
= puffs_framebuf_telloff(pufbuf
);
234 pbsize
= puffs_framebuf_tellsize(pufbuf
);
238 assert(pbsize
> pboff
);
239 winlen
= pbsize
- pboff
;
240 if (puffs_framebuf_getwindow(pufbuf
, pboff
, &win
, &winlen
)==-1)
242 n
= write(fd
, win
, winlen
);
247 return -1; /* caught by node_write */
259 puffs_framebuf_seekset(pufbuf
, pboff
);
260 } while (pboff
!= pbsize
);
263 puffs_framebuf_putdata_atoff(pufbuf
, 0, &pboff
, sizeof(size_t));
267 /* transfer file descriptor to master file server */
269 sendfd(int s
, int fd
, int error
)
278 cmp
= emalloc(CMSG_LEN(sizeof(int)));
280 iov
.iov_base
= &error
;
281 iov
.iov_len
= sizeof(int);
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
;
296 msg
.msg_control
= NULL
;
297 msg
.msg_controllen
= 0;
300 n
= sendmsg(s
, &msg
, 0);
303 else if (n
< (ssize_t
)sizeof(int))
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.
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
;
326 pufbuf
= puffs_framebuf_make();
331 if (puffs_framebuf_putdata(pufbuf
, &data
, sizeof(int)) == -1)
334 if (socketpair(AF_LOCAL
, SOCK_STREAM
, 0, s
) == -1)
341 error
= activate_argv(portc
, portn
->path
, v
, &fd
);
342 sendfd(s
[1], fd
, error
);
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);
351 if (puffs_framebuf_tellsize(pufbuf
) < sizeof(int)) {
355 puffs_framebuf_getdata_atoff(pufbuf
, 0, &error
, sizeof(int));
361 if (puffs_framebuf_tellsize(pufbuf
) != 2*sizeof(int)) {
366 puffs_framebuf_getdata_atoff(pufbuf
, sizeof(int),
368 puffs_framebuf_destroy(pufbuf
);
371 if (ioctl(fd
, FIONBIO
, &data
) == -1)
374 if (puffs_framev_addfd(pu
, fd
, PUFFS_FBIO_WRITE
) == -1)
382 puffs_framebuf_destroy(pufbuf
);
387 main(int argc
, char *argv
[])
391 struct puffs_usermount
*pu
;
392 struct puffs_ops
*pops
;
394 int pflags
, mntflags
;
398 setprogname(argv
[0]);
400 mntflags
= pflags
= 0;
402 while ((ch
= getopt(argc
, argv
, "o:s")) != -1) {
405 mp
= getmntopts(optarg
, puffsmopts
, &mntflags
, &pflags
);
407 err(1, "getmntopts");
410 case 's': /* stay on top */
418 pflags
|= PUFFS_KFLAG_NOCACHE
| PUFFS_KFLAG_LOOKUP_FULLPNBUF
;
419 if (pflags
& PUFFS_FLAG_OPDUMP
)
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
);
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");
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
);
467 if (puffs_daemon(pu
, 1, 1) == -1)
468 err(1, "puffs_daemon");
470 if (puffs_mount(pu
, argv
[1], mntflags
, PORTAL_ROOT
) == -1)
472 if (puffs_mainloop(pu
) == -1)
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
);
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
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
)
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
;
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
)
540 puffs_vattr_null(va
);
541 if (opc
== PORTAL_ROOT
) {
550 va
->va_uid
= va
->va_gid
= 0;
551 va
->va_fileid
= fakeid
++;
552 va
->va_size
= va
->va_bytes
= 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
;
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
)
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
;
581 if (opc
== PORTAL_ROOT
)
584 if (mode
& O_NONBLOCK
)
587 v
= conf_match(&q
, portn
->path
);
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
;
608 assert(opc
!= PORTAL_ROOT
);
611 /* if we can't (re-)enable it, treat it as EOF */
612 rv
= puffs_framev_enablefd(pu
, portn
->fd
, PUFFS_FBIO_READ
);
616 pufbuf
= puffs_framebuf_make();
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
);
629 rv
= puffs_framev_enqueue_directreceive(pcc
,
630 portn
->fd
, pufbuf
, 0);
638 xfersize
= puffs_framebuf_tellsize(pufbuf
) - METADATASIZE
;
640 assert(ioflag
& PUFFS_IO_NDELAY
);
647 while (xfersize
> 0) {
649 rv
= puffs_framebuf_getwindow(pufbuf
, METADATASIZE
,
654 memcpy(buf
+ boff
, win
, winsize
);
660 puffs_framev_disablefd(pu
, portn
->fd
, PUFFS_FBIO_READ
);
661 puffs_framebuf_destroy(pufbuf
);
663 /* a trickery, from readdata() */
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
;
678 int error
, rv
, dummy
;
680 assert(opc
!= PORTAL_ROOT
);
682 pufbuf
= puffs_framebuf_make();
683 puffs_framebuf_putdata(pufbuf
, buf
, *resid
);
686 if (ioflag
& PUFFS_IO_NDELAY
) {
687 rv
= portal_frame_wf(pu
, pufbuf
, portn
->fd
, &dummy
);
693 rv
= puffs_framev_enqueue_directsend(pcc
, portn
->fd
, pufbuf
, 0);
700 rv
= puffs_framebuf_getdata_atoff(pufbuf
, 0, &written
, sizeof(size_t));
702 assert(written
<= *resid
);
706 puffs_framebuf_destroy(pufbuf
);
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)
721 if (lseek(portn
->fd
, newoff
, SEEK_SET
) == -1)
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
;
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
);
749 if (what
& PUFFS_FBIO_READ
)
751 if (what
& PUFFS_FBIO_WRITE
)
753 if (what
& PUFFS_FBIO_ERROR
)
760 portal_node_inactive(struct puffs_usermount
*pu
, puffs_cookie_t opc
)
763 if (opc
== PORTAL_ROOT
)
766 puffs_setback(puffs_cc_getcc(pu
), PUFFS_SETBACK_NOREF_N1
);
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);