1 /* $NetBSD: puffs.c,v 1.91 2008/08/11 16:23:37 pooka Exp $ */
4 * Copyright (c) 2005, 2006, 2007 Antti Kantee. All Rights Reserved.
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.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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
32 #include <sys/cdefs.h>
34 __RCSID("$NetBSD: puffs.c,v 1.91 2008/08/11 16:23:37 pooka Exp $");
37 #include <sys/param.h>
38 #include <sys/mount.h>
53 #include "puffs_priv.h"
55 /* Most file systems want this for opts, so just give it to them */
56 const struct mntopt puffsmopts
[] = {
62 #ifdef PUFFS_WITH_THREADS
64 pthread_mutex_t pu_lock
= PTHREAD_MUTEX_INITIALIZER
;
67 #define FILLOP(lower, upper) \
69 if (pops->puffs_node_##lower) \
70 opmask[PUFFS_VN_##upper] = 1; \
71 } while (/*CONSTCOND*/0)
73 fillvnopmask(struct puffs_ops
*pops
, uint8_t *opmask
)
76 memset(opmask
, 0, PUFFS_VN_MAX
);
78 FILLOP(create
, CREATE
);
82 FILLOP(access
, ACCESS
);
83 FILLOP(getattr
, GETATTR
);
84 FILLOP(setattr
, SETATTR
);
85 FILLOP(poll
, POLL
); /* XXX: not ready in kernel */
89 FILLOP(remove
, REMOVE
);
91 FILLOP(rename
, RENAME
);
94 FILLOP(symlink
, SYMLINK
);
95 FILLOP(readdir
, READDIR
);
96 FILLOP(readlink
, READLINK
);
97 FILLOP(reclaim
, RECLAIM
);
98 FILLOP(inactive
, INACTIVE
);
101 FILLOP(write
, WRITE
);
106 * Go over all framev entries and write everything we can. This is
107 * mostly for the benefit of delivering "unmount" to the kernel.
110 finalpush(struct puffs_usermount
*pu
)
112 struct puffs_fctrl_io
*fio
;
114 LIST_FOREACH(fio
, &pu
->pu_ios
, fio_entries
) {
115 if (fio
->stat
& FIO_WRGONE
)
118 puffs__framev_output(pu
, fio
->fctrl
, fio
);
124 puffs_defaulterror(struct puffs_usermount
*pu
, uint8_t type
,
125 int error
, const char *str
, puffs_cookie_t cookie
)
128 fprintf(stderr
, "abort: type %d, error %d, cookie %p (%s)\n",
129 type
, error
, cookie
, str
);
134 puffs_getselectable(struct puffs_usermount
*pu
)
141 puffs__nextreq(struct puffs_usermount
*pu
)
146 rv
= pu
->pu_nextreq
++;
153 puffs_setblockingmode(struct puffs_usermount
*pu
, int mode
)
157 assert(puffs_getstate(pu
) == PUFFS_STATE_RUNNING
);
159 if (mode
!= PUFFSDEV_BLOCK
&& mode
!= PUFFSDEV_NONBLOCK
) {
165 rv
= ioctl(pu
->pu_fd
, FIONBIO
, &x
);
168 if (mode
== PUFFSDEV_BLOCK
)
169 pu
->pu_state
&= ~PU_ASYNCFD
;
171 pu
->pu_state
|= PU_ASYNCFD
;
178 puffs_getstate(struct puffs_usermount
*pu
)
181 return pu
->pu_state
& PU_STATEMASK
;
185 puffs_setstacksize(struct puffs_usermount
*pu
, size_t ss
)
191 assert(puffs_getstate(pu
) == PUFFS_STATE_BEFOREMOUNT
);
193 psize
= sysconf(_SC_PAGESIZE
);
195 if (ss
< minsize
|| ss
== PUFFS_STACKSIZE_MIN
) {
196 if (ss
!= PUFFS_STACKSIZE_MIN
)
197 fprintf(stderr
, "puffs_setstacksize: adjusting "
198 "stacksize to minimum %ld\n", minsize
);
212 fprintf(stderr
, "puffs_setstacksize: using next power of two: "
213 "%d\n", 1<<stackshift
);
216 pu
->pu_cc_stackshift
= stackshift
;
219 struct puffs_pathobj
*
220 puffs_getrootpathobj(struct puffs_usermount
*pu
)
222 struct puffs_node
*pnr
;
224 pnr
= pu
->pu_pn_root
;
234 puffs_setroot(struct puffs_usermount
*pu
, struct puffs_node
*pn
)
241 puffs_getroot(struct puffs_usermount
*pu
)
244 return pu
->pu_pn_root
;
248 puffs_setrootinfo(struct puffs_usermount
*pu
, enum vtype vt
,
249 vsize_t vsize
, dev_t rdev
)
251 struct puffs_kargs
*pargs
= pu
->pu_kargp
;
253 if (puffs_getstate(pu
) != PUFFS_STATE_BEFOREMOUNT
) {
254 warnx("puffs_setrootinfo: call has effect only "
259 pargs
->pa_root_vtype
= vt
;
260 pargs
->pa_root_vsize
= vsize
;
261 pargs
->pa_root_rdev
= rdev
;
265 puffs_getspecific(struct puffs_usermount
*pu
)
268 return pu
->pu_privdata
;
272 puffs_getmaxreqlen(struct puffs_usermount
*pu
)
275 return pu
->pu_maxreqlen
;
279 puffs_setmaxreqlen(struct puffs_usermount
*pu
, size_t reqlen
)
282 if (puffs_getstate(pu
) != PUFFS_STATE_BEFOREMOUNT
)
283 warnx("puffs_setmaxreqlen: call has effect only "
286 pu
->pu_kargp
->pa_maxmsglen
= reqlen
;
290 puffs_setfhsize(struct puffs_usermount
*pu
, size_t fhsize
, int flags
)
293 if (puffs_getstate(pu
) != PUFFS_STATE_BEFOREMOUNT
)
294 warnx("puffs_setfhsize: call has effect only before mount\n");
296 pu
->pu_kargp
->pa_fhsize
= fhsize
;
297 pu
->pu_kargp
->pa_fhflags
= flags
;
301 puffs_setncookiehash(struct puffs_usermount
*pu
, int nhash
)
304 if (puffs_getstate(pu
) != PUFFS_STATE_BEFOREMOUNT
)
305 warnx("puffs_setfhsize: call has effect only before mount\n");
307 pu
->pu_kargp
->pa_nhashbuckets
= nhash
;
311 puffs_set_pathbuild(struct puffs_usermount
*pu
, pu_pathbuild_fn fn
)
314 pu
->pu_pathbuild
= fn
;
318 puffs_set_pathtransform(struct puffs_usermount
*pu
, pu_pathtransform_fn fn
)
321 pu
->pu_pathtransform
= fn
;
325 puffs_set_pathcmp(struct puffs_usermount
*pu
, pu_pathcmp_fn fn
)
332 puffs_set_pathfree(struct puffs_usermount
*pu
, pu_pathfree_fn fn
)
335 pu
->pu_pathfree
= fn
;
339 puffs_set_namemod(struct puffs_usermount
*pu
, pu_namemod_fn fn
)
346 puffs_set_errnotify(struct puffs_usermount
*pu
, pu_errnotify_fn fn
)
349 pu
->pu_errnotify
= fn
;
353 puffs_set_cmap(struct puffs_usermount
*pu
, pu_cmap_fn fn
)
360 puffs_ml_setloopfn(struct puffs_usermount
*pu
, puffs_ml_loop_fn lfn
)
367 puffs_ml_settimeout(struct puffs_usermount
*pu
, struct timespec
*ts
)
371 pu
->pu_ml_timep
= NULL
;
373 pu
->pu_ml_timeout
= *ts
;
374 pu
->pu_ml_timep
= &pu
->pu_ml_timeout
;
379 puffs_set_prepost(struct puffs_usermount
*pu
,
380 pu_prepost_fn pre
, pu_prepost_fn pst
)
388 puffs_setback(struct puffs_cc
*pcc
, int whatback
)
390 struct puffs_req
*preq
= puffs__framebuf_getdataptr(pcc
->pcc_pb
);
392 assert(PUFFSOP_OPCLASS(preq
->preq_opclass
) == PUFFSOP_VN
&& (
393 preq
->preq_optype
== PUFFS_VN_OPEN
||
394 preq
->preq_optype
== PUFFS_VN_MMAP
||
395 preq
->preq_optype
== PUFFS_VN_REMOVE
||
396 preq
->preq_optype
== PUFFS_VN_RMDIR
||
397 preq
->preq_optype
== PUFFS_VN_INACTIVE
));
399 preq
->preq_setbacks
|= whatback
& PUFFS_SETBACK_MASK
;
403 puffs_daemon(struct puffs_usermount
*pu
, int nochdir
, int noclose
)
406 int parent
, value
, fd
;
408 if (pipe(pu
->pu_dpipe
) == -1)
421 pu
->pu_state
|= PU_PUFFSDAEMON
;
424 n
= read(pu
->pu_dpipe
[0], &value
, sizeof(int));
426 err(1, "puffs_daemon");
427 assert(n
== sizeof(value
));
430 err(1, "puffs_daemon");
441 fd
= open(_PATH_DEVNULL
, O_RDWR
, 0);
444 dup2(fd
, STDIN_FILENO
);
445 dup2(fd
, STDOUT_FILENO
);
446 dup2(fd
, STDERR_FILENO
);
447 if (fd
> STDERR_FILENO
)
454 n
= write(pu
->pu_dpipe
[1], &errno
, sizeof(int));
460 puffs_mount(struct puffs_usermount
*pu
, const char *dir
, int mntflags
,
461 puffs_cookie_t cookie
)
468 pu
->pu_kargp
->pa_root_cookie
= cookie
;
471 /* kauth doesn't provide this service any longer */
473 mntflags
|= MNT_NOSUID
| MNT_NODEV
;
475 if (realpath(dir
, rp
) == NULL
) {
480 if (strcmp(dir
, rp
) != 0) {
481 warnx("puffs_mount: \"%s\" is a relative path.", dir
);
482 warnx("puffs_mount: using \"%s\" instead.", rp
);
486 * Undocumented... Well, documented only here.
488 * This is used for imaginative purposes. If the env variable is
489 * set, puffs_mount() doesn't do the regular mount procedure.
490 * Rather, it crams the mount data down the comfd and sets comfd as
491 * the puffs descriptor.
493 * This shouldn't be used unless you can read my mind ( ... or write
494 * it, not to mention execute it, but that's starting to get silly).
496 if ((comfd
= getenv("PUFFS_COMFD")) != NULL
) {
499 if (sscanf(comfd
, "%d", &pu
->pu_fd
) != 1) {
504 /* check that what we got at least resembles an fd */
505 if (fcntl(pu
->pu_fd
, F_GETFL
) == -1) {
512 #define allwrite(buf, len) \
515 al_rv = write(pu->pu_fd, buf, len); \
516 if (al_rv != len) { \
523 } while (/*CONSTCOND*/0)
524 allwrite(&len
, sizeof(len
));
526 len
= strlen(pu
->pu_kargp
->pa_mntfromname
)+1;
527 allwrite(&len
, sizeof(len
));
528 allwrite(pu
->pu_kargp
->pa_mntfromname
, len
);
529 allwrite(&mntflags
, sizeof(mntflags
));
530 allwrite(pu
->pu_kargp
, sizeof(*pu
->pu_kargp
));
531 allwrite(&pu
->pu_flags
, sizeof(pu
->pu_flags
));
536 fd
= open(_PATH_PUFFS
, O_RDWR
);
538 warnx("puffs_mount: cannot open %s", _PATH_PUFFS
);
543 warnx("puffs_mount: device fd %d (<= 2), sure this is "
544 "what you want?", fd
);
546 pu
->pu_kargp
->pa_fd
= pu
->pu_fd
= fd
;
547 if ((rv
= mount(MOUNT_PUFFS
, rp
, mntflags
,
548 pu
->pu_kargp
, sizeof(struct puffs_kargs
))) == -1)
552 PU_SETSTATE(pu
, PUFFS_STATE_RUNNING
);
562 if (pu
->pu_state
& PU_PUFFSDAEMON
) {
563 n
= write(pu
->pu_dpipe
[1], &sverrno
, sizeof(int));
565 close(pu
->pu_dpipe
[0]);
566 close(pu
->pu_dpipe
[1]);
573 struct puffs_usermount
*
574 _puffs_init(int develv
, struct puffs_ops
*pops
, const char *mntfromname
,
575 const char *puffsname
, void *priv
, uint32_t pflags
)
577 struct puffs_usermount
*pu
;
578 struct puffs_kargs
*pargs
;
581 if (develv
!= PUFFS_DEVEL_LIBVERSION
) {
582 warnx("puffs_init: mounting with lib version %d, need %d",
583 develv
, PUFFS_DEVEL_LIBVERSION
);
588 pu
= malloc(sizeof(struct puffs_usermount
));
591 memset(pu
, 0, sizeof(struct puffs_usermount
));
593 pargs
= pu
->pu_kargp
= malloc(sizeof(struct puffs_kargs
));
596 memset(pargs
, 0, sizeof(struct puffs_kargs
));
598 pargs
->pa_vers
= PUFFSDEVELVERS
| PUFFSVERSION
;
599 pargs
->pa_flags
= PUFFS_FLAG_KERN(pflags
);
600 fillvnopmask(pops
, pargs
->pa_vnopmask
);
601 (void)strlcpy(pargs
->pa_typename
, puffsname
,
602 sizeof(pargs
->pa_typename
));
603 (void)strlcpy(pargs
->pa_mntfromname
, mntfromname
,
604 sizeof(pargs
->pa_mntfromname
));
606 puffs_zerostatvfs(&pargs
->pa_svfsb
);
607 pargs
->pa_root_cookie
= NULL
;
608 pargs
->pa_root_vtype
= VDIR
;
609 pargs
->pa_root_vsize
= 0;
610 pargs
->pa_root_rdev
= 0;
611 pargs
->pa_maxmsglen
= 0;
613 pu
->pu_flags
= pflags
;
615 free(pops
); /* XXX */
617 pu
->pu_privdata
= priv
;
618 pu
->pu_cc_stackshift
= PUFFS_CC_STACKSHIFT_DEFAULT
;
619 LIST_INIT(&pu
->pu_pnodelst
);
620 LIST_INIT(&pu
->pu_ios
);
621 LIST_INIT(&pu
->pu_ios_rmlist
);
622 LIST_INIT(&pu
->pu_ccmagazin
);
623 TAILQ_INIT(&pu
->pu_sched
);
625 pu
->pu_framectrl
[PU_FRAMECTRL_FS
].rfb
= puffs__fsframe_read
;
626 pu
->pu_framectrl
[PU_FRAMECTRL_FS
].wfb
= puffs__fsframe_write
;
627 pu
->pu_framectrl
[PU_FRAMECTRL_FS
].cmpfb
= puffs__fsframe_cmp
;
628 pu
->pu_framectrl
[PU_FRAMECTRL_FS
].gotfb
= puffs__fsframe_gotframe
;
629 pu
->pu_framectrl
[PU_FRAMECTRL_FS
].fdnotfn
= puffs_framev_unmountonclose
;
631 /* defaults for some user-settable translation functions */
632 pu
->pu_cmap
= NULL
; /* identity translation */
634 pu
->pu_pathbuild
= puffs_stdpath_buildpath
;
635 pu
->pu_pathfree
= puffs_stdpath_freepath
;
636 pu
->pu_pathcmp
= puffs_stdpath_cmppath
;
637 pu
->pu_pathtransform
= NULL
;
638 pu
->pu_namemod
= NULL
;
640 pu
->pu_errnotify
= puffs_defaulterror
;
642 PU_SETSTATE(pu
, PUFFS_STATE_BEFOREMOUNT
);
647 /* can't unmount() from here for obvious reasons */
655 * XXX: there's currently no clean way to request unmount from
656 * within the user server, so be very brutal about it.
660 puffs_exit(struct puffs_usermount
*pu
, int force
)
662 struct puffs_node
*pn
;
664 force
= 1; /* currently */
669 while ((pn
= LIST_FIRST(&pu
->pu_pnodelst
)) != NULL
)
673 puffs__framev_exit(pu
);
675 if (pu
->pu_state
& PU_HASKQ
)
679 return 0; /* always succesful for now, WILL CHANGE */
683 * Actual mainloop. This is called from a context which can block.
684 * It is called either from puffs_mainloop (indirectly, via
685 * puffs_cc_continue() or from puffs_cc_yield()).
688 puffs__theloop(struct puffs_cc
*pcc
)
690 struct puffs_usermount
*pu
= pcc
->pcc_pu
;
691 struct puffs_framectrl
*pfctrl
;
692 struct puffs_fctrl_io
*fio
;
693 struct kevent
*curev
;
697 while (puffs_getstate(pu
) != PUFFS_STATE_UNMOUNTED
) {
699 * Schedule existing requests.
701 while ((pcc
= TAILQ_FIRST(&pu
->pu_sched
)) != NULL
) {
702 TAILQ_REMOVE(&pu
->pu_sched
, pcc
, pcc_schedent
);
709 /* XXX: can we still do these optimizations? */
712 * Do this here, because:
713 * a) loopfunc might generate some results
714 * b) it's still "after" event handling (except for round 1)
716 if (puffs_req_putput(ppr
) == -1)
718 puffs_req_resetput(ppr
);
720 /* micro optimization: skip kevent syscall if possible */
721 if (pu
->pu_nfds
== 1 && pu
->pu_ml_timep
== NULL
722 && (pu
->pu_state
& PU_ASYNCFD
) == 0) {
724 puffs_framev_input(pu
, pfctrl
, XXX
);
729 /* else: do full processing */
730 /* Don't bother worrying about O(n) for now */
731 LIST_FOREACH(fio
, &pu
->pu_ios
, fio_entries
) {
732 if (fio
->stat
& FIO_WRGONE
)
738 * Try to write out everything to avoid the
739 * need for enabling EVFILT_WRITE. The likely
740 * case is that we can fit everything into the
743 puffs__framev_output(pu
, pfctrl
, fio
);
747 * Build list of which to enable/disable in writecheck.
750 LIST_FOREACH(fio
, &pu
->pu_ios
, fio_entries
) {
751 if (fio
->stat
& FIO_WRGONE
)
754 /* en/disable write checks for kqueue as needed */
755 assert((FIO_EN_WRITE(fio
) && FIO_RM_WRITE(fio
)) == 0);
756 if (FIO_EN_WRITE(fio
)) {
757 EV_SET(&pu
->pu_evs
[nchanges
], fio
->io_fd
,
758 EVFILT_WRITE
, EV_ENABLE
, 0, 0,
763 if (FIO_RM_WRITE(fio
)) {
764 EV_SET(&pu
->pu_evs
[nchanges
], fio
->io_fd
,
765 EVFILT_WRITE
, EV_DISABLE
, 0, 0,
767 fio
->stat
&= ~FIO_WR
;
770 assert(nchanges
<= pu
->pu_nfds
);
773 ndone
= kevent(pu
->pu_kq
, pu
->pu_evs
, nchanges
,
774 pu
->pu_evs
, 2*pu
->pu_nfds
, pu
->pu_ml_timep
);
787 /* iterate over the results */
788 for (curev
= pu
->pu_evs
; ndone
--; curev
++) {
792 /* get & possibly dispatch events from kernel */
793 if (curev
->ident
== puffsfd
) {
794 if (puffs_req_handle(pgr
, ppr
, 0) == -1)
800 fio
= (void *)curev
->udata
;
802 if (curev
->flags
& EV_ERROR
) {
803 assert(curev
->filter
== EVFILT_WRITE
);
804 fio
->stat
&= ~FIO_WR
;
806 /* XXX: how to know if it's a transient error */
807 puffs__framev_writeclose(pu
, fio
,
809 puffs__framev_notify(fio
, PUFFS_FBIO_ERROR
);
814 if (curev
->filter
== EVFILT_READ
) {
815 puffs__framev_input(pu
, pfctrl
, fio
);
816 what
|= PUFFS_FBIO_READ
;
819 else if (curev
->filter
== EVFILT_WRITE
) {
820 puffs__framev_output(pu
, pfctrl
, fio
);
821 what
|= PUFFS_FBIO_WRITE
;
824 puffs__framev_notify(fio
, what
);
828 * Really free fd's now that we don't have references
831 while ((fio
= LIST_FIRST(&pu
->pu_ios_rmlist
)) != NULL
) {
832 LIST_REMOVE(fio
, fio_entries
);
837 if (puffs__cc_restoremain(pu
) == -1)
838 warn("cannot restore main context. impending doom");
842 puffs_mainloop(struct puffs_usermount
*pu
)
844 struct puffs_fctrl_io
*fio
;
845 struct puffs_cc
*pcc
;
846 struct kevent
*curev
;
849 assert(puffs_getstate(pu
) >= PUFFS_STATE_RUNNING
);
851 pu
->pu_kq
= kqueue();
854 pu
->pu_state
|= PU_HASKQ
;
856 puffs_setblockingmode(pu
, PUFFSDEV_NONBLOCK
);
857 if (puffs__framev_addfd_ctrl(pu
, puffs_getselectable(pu
),
858 PUFFS_FBIO_READ
| PUFFS_FBIO_WRITE
,
859 &pu
->pu_framectrl
[PU_FRAMECTRL_FS
]) == -1)
862 curev
= realloc(pu
->pu_evs
, (2*pu
->pu_nfds
)*sizeof(struct kevent
));
867 LIST_FOREACH(fio
, &pu
->pu_ios
, fio_entries
) {
868 EV_SET(curev
, fio
->io_fd
, EVFILT_READ
, EV_ADD
,
869 0, 0, (uintptr_t)fio
);
871 EV_SET(curev
, fio
->io_fd
, EVFILT_WRITE
, EV_ADD
| EV_DISABLE
,
872 0, 0, (uintptr_t)fio
);
875 if (kevent(pu
->pu_kq
, pu
->pu_evs
, 2*pu
->pu_nfds
, NULL
, 0, NULL
) == -1)
878 pu
->pu_state
|= PU_INLOOP
;
881 * Create alternate execution context and jump to it. Note
882 * that we come "out" of savemain twice. Where we come out
883 * of it depends on the architecture. If the return address is
884 * stored on the stack, we jump out from puffs_cc_continue(),
885 * for a register return address from puffs__cc_savemain().
886 * PU_MAINRESTORE makes sure we DTRT in both cases.
888 if (puffs__cc_create(pu
, puffs__theloop
, &pcc
) == -1) {
891 if (puffs__cc_savemain(pu
) == -1) {
894 if ((pu
->pu_state
& PU_MAINRESTORE
) == 0)
895 puffs_cc_continue(pcc
);
901 /* store the real error for a while */