1 /* $NetBSD: puffs.c,v 1.117 2011/11/14 01:27:42 chs 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.117 2011/11/14 01:27:42 chs Exp $");
37 #include <sys/param.h>
38 #include <sys/mount.h>
42 #include <minix/endpoint.h>
43 #include <minix/vfsif.h>
44 #endif /* defined(__minix) */
59 #include "puffs_priv.h"
61 /* Most file systems want this for opts, so just give it to them */
62 const struct mntopt puffsmopts
[] = {
67 #ifdef PUFFS_WITH_THREADS
69 pthread_mutex_t pu_lock
= PTHREAD_MUTEX_INITIALIZER
;
73 /* Declare some local functions. */
74 static void get_work(message
*m_in
);
75 static void reply(endpoint_t who
, message
*m_out
);
77 /* SEF functions and variables. */
78 static void sef_local_startup(void);
79 static int sef_cb_init_fresh(int type
, sef_init_info_t
*info
);
80 static void sef_cb_signal_handler(int signo
);
83 EXTERN
char **env_argv
;
86 #define PUFFS_MAX_ARGS 20
88 int __real_main(int argc
, char* argv
[]);
89 int __wrap_main(int argc
, char* argv
[]);
91 int __wrap_main(int argc
, char *argv
[])
95 static char* new_argv
[PUFFS_MAX_ARGS
];
98 /* SEF local startup. */
99 env_setargs(argc
, argv
);
102 global_kcred
.pkcr_type
= PUFFCRED_TYPE_INTERNAL
;
105 panic("Unexpected arguments, use:\
106 mount -t fs /dev/ /dir [-o option1,option2]\n");
109 name
= argv
[0] + strlen(argv
[0]);
110 while (*name
!= '/' && name
!= argv
[0])
114 strcpy(fs_name
, name
);
116 new_argv
[new_argc
] = argv
[0];
119 for (i
= 1; i
< argc
; i
++) {
120 if (new_argc
>= PUFFS_MAX_ARGS
) {
121 panic("Too many arguments, change PUFFS_MAX_ARGS");
123 new_argv
[new_argc
] = argv
[i
];
127 assert(new_argc
> 0);
131 return __real_main(new_argc
, new_argv
);
135 #define FILLOP(lower, upper) \
137 if (pops->puffs_node_##lower) \
138 opmask[PUFFS_VN_##upper] = 1; \
139 } while (/*CONSTCOND*/0)
141 fillvnopmask(struct puffs_ops
*pops
, struct puffs_kargs
*pa
)
143 uint8_t *opmask
= pa
->pa_vnopmask
;
145 memset(opmask
, 0, sizeof(pa
->pa_vnopmask
));
147 FILLOP(create
, CREATE
);
148 FILLOP(mknod
, MKNOD
);
150 FILLOP(close
, CLOSE
);
151 FILLOP(access
, ACCESS
);
152 FILLOP(getattr
, GETATTR
);
153 FILLOP(setattr
, SETATTR
);
156 FILLOP(fsync
, FSYNC
);
158 FILLOP(remove
, REMOVE
);
160 FILLOP(rename
, RENAME
);
161 FILLOP(mkdir
, MKDIR
);
162 FILLOP(rmdir
, RMDIR
);
163 FILLOP(symlink
, SYMLINK
);
164 FILLOP(readdir
, READDIR
);
165 FILLOP(readlink
, READLINK
);
166 FILLOP(reclaim
, RECLAIM
);
167 FILLOP(inactive
, INACTIVE
);
168 FILLOP(print
, PRINT
);
170 FILLOP(write
, WRITE
);
171 FILLOP(abortop
, ABORTOP
);
178 puffs_defaulterror(struct puffs_usermount
*pu
, uint8_t type
,
179 int error
, const char *str
, puffs_cookie_t cookie
)
182 lpuffs_debug("abort: type %d, error %d, cookie %p (%s)\n",
183 type
, error
, cookie
, str
);
189 puffs_getstate(struct puffs_usermount
*pu
)
192 return pu
->pu_state
& PU_STATEMASK
;
196 puffs_setstacksize(struct puffs_usermount
*pu
, size_t ss
)
202 assert(puffs_getstate(pu
) == PUFFS_STATE_BEFOREMOUNT
);
204 psize
= sysconf(_SC_PAGESIZE
);
206 if (ss
< (size_t)minsize
|| ss
== PUFFS_STACKSIZE_MIN
) {
207 if (ss
!= PUFFS_STACKSIZE_MIN
)
208 lpuffs_debug("puffs_setstacksize: adjusting "
209 "stacksize to minimum %ld\n", minsize
);
223 lpuffs_debug("puffs_setstacksize: using next power of two: "
224 "%d\n", 1<<stackshift
);
227 pu
->pu_cc_stackshift
= stackshift
;
230 struct puffs_pathobj
*
231 puffs_getrootpathobj(struct puffs_usermount
*pu
)
233 struct puffs_node
*pnr
;
235 pnr
= pu
->pu_pn_root
;
245 puffs_setroot(struct puffs_usermount
*pu
, struct puffs_node
*pn
)
252 puffs_getroot(struct puffs_usermount
*pu
)
255 return pu
->pu_pn_root
;
259 puffs_setrootinfo(struct puffs_usermount
*pu
, enum vtype vt
,
260 vsize_t vsize
, dev_t rdev
)
262 struct puffs_kargs
*pargs
= pu
->pu_kargp
;
264 if (puffs_getstate(pu
) != PUFFS_STATE_BEFOREMOUNT
) {
265 warnx("puffs_setrootinfo: call has effect only "
270 pargs
->pa_root_vtype
= vt
;
271 pargs
->pa_root_vsize
= vsize
;
272 pargs
->pa_root_rdev
= rdev
;
276 puffs_getspecific(struct puffs_usermount
*pu
)
279 return pu
->pu_privdata
;
283 puffs_setspecific(struct puffs_usermount
*pu
, void *privdata
)
286 pu
->pu_privdata
= privdata
;
290 puffs_setmntinfo(struct puffs_usermount
*pu
,
291 const char *mntfromname
, const char *puffsname
)
293 struct puffs_kargs
*pargs
= pu
->pu_kargp
;
295 (void)strlcpy(pargs
->pa_mntfromname
, mntfromname
,
296 sizeof(pargs
->pa_mntfromname
));
297 (void)strlcpy(pargs
->pa_typename
, puffsname
,
298 sizeof(pargs
->pa_typename
));
302 puffs_getmaxreqlen(struct puffs_usermount
*pu
)
305 return pu
->pu_maxreqlen
;
309 puffs_setmaxreqlen(struct puffs_usermount
*pu
, size_t reqlen
)
312 if (puffs_getstate(pu
) != PUFFS_STATE_BEFOREMOUNT
)
313 warnx("puffs_setmaxreqlen: call has effect only "
316 pu
->pu_kargp
->pa_maxmsglen
= reqlen
;
320 puffs_setfhsize(struct puffs_usermount
*pu
, size_t fhsize
, int flags
)
323 if (puffs_getstate(pu
) != PUFFS_STATE_BEFOREMOUNT
)
324 warnx("puffs_setfhsize: call has effect only before mount\n");
326 pu
->pu_kargp
->pa_fhsize
= fhsize
;
327 pu
->pu_kargp
->pa_fhflags
= flags
;
331 puffs_setncookiehash(struct puffs_usermount
*pu
, int nhash
)
334 if (puffs_getstate(pu
) != PUFFS_STATE_BEFOREMOUNT
)
335 warnx("puffs_setfhsize: call has effect only before mount\n");
337 pu
->pu_kargp
->pa_nhashbuckets
= nhash
;
341 puffs_set_pathbuild(struct puffs_usermount
*pu
, pu_pathbuild_fn fn
)
344 pu
->pu_pathbuild
= fn
;
348 puffs_set_pathtransform(struct puffs_usermount
*pu
, pu_pathtransform_fn fn
)
351 pu
->pu_pathtransform
= fn
;
355 puffs_set_pathcmp(struct puffs_usermount
*pu
, pu_pathcmp_fn fn
)
362 puffs_set_pathfree(struct puffs_usermount
*pu
, pu_pathfree_fn fn
)
365 pu
->pu_pathfree
= fn
;
369 puffs_set_namemod(struct puffs_usermount
*pu
, pu_namemod_fn fn
)
376 puffs_set_errnotify(struct puffs_usermount
*pu
, pu_errnotify_fn fn
)
379 pu
->pu_errnotify
= fn
;
383 puffs_set_cmap(struct puffs_usermount
*pu
, pu_cmap_fn fn
)
390 puffs_ml_setloopfn(struct puffs_usermount
*pu
, puffs_ml_loop_fn lfn
)
397 puffs_ml_settimeout(struct puffs_usermount
*pu
, struct timespec
*ts
)
401 pu
->pu_ml_timep
= NULL
;
403 pu
->pu_ml_timeout
= *ts
;
404 pu
->pu_ml_timep
= &pu
->pu_ml_timeout
;
409 puffs_set_prepost(struct puffs_usermount
*pu
,
410 pu_prepost_fn pre
, pu_prepost_fn pst
)
418 puffs_mount(struct puffs_usermount
*pu
, const char *dir
, int mntflags
,
419 puffs_cookie_t cookie
)
425 pu
->pu_kargp
->pa_root_cookie
= cookie
;
427 src
= fs_m_in
.m_source
;
429 caller_uid
= INVAL_UID
; /* To trap errors */
430 caller_gid
= INVAL_GID
;
431 req_nr
= fs_m_in
.m_type
;
433 if (req_nr
< FS_BASE
) {
434 fs_m_in
.m_type
+= FS_BASE
;
435 req_nr
= fs_m_in
.m_type
;
437 ind
= req_nr
- FS_BASE
;
439 assert(ind
== REQ_READ_SUPER
);
441 if (ind
< 0 || ind
>= NREQS
) {
444 error
= (*fs_call_vec
[ind
])();
447 fs_m_out
.m_type
= error
;
448 if (IS_VFS_FS_TRANSID(last_request_transid
)) {
449 /* If a transaction ID was set, reset it */
450 fs_m_out
.m_type
= TRNS_ADD_ID(fs_m_out
.m_type
,
451 last_request_transid
);
453 reply(src
, &fs_m_out
);
462 PU_SETSTATE(pu
, PUFFS_STATE_RUNNING
);
464 #endif /* defined(__minix) */
468 struct puffs_usermount
*
469 _puffs_init(int dummy
, struct puffs_ops
*pops
, const char *mntfromname
,
470 const char *puffsname
, void *priv
, uint32_t pflags
)
472 struct puffs_usermount
*pu
;
473 struct puffs_kargs
*pargs
;
476 if (puffsname
== PUFFS_DEFER
)
478 if (mntfromname
== PUFFS_DEFER
)
480 if (priv
== PUFFS_DEFER
)
483 pu
= malloc(sizeof(struct puffs_usermount
));
486 memset(pu
, 0, sizeof(struct puffs_usermount
));
488 pargs
= pu
->pu_kargp
= malloc(sizeof(struct puffs_kargs
));
491 memset(pargs
, 0, sizeof(struct puffs_kargs
));
493 pargs
->pa_vers
= PUFFSVERSION
;
494 pargs
->pa_flags
= PUFFS_FLAG_KERN(pflags
);
495 fillvnopmask(pops
, pargs
->pa_vnopmask
);
496 puffs_setmntinfo(pu
, mntfromname
, puffsname
);
498 puffs_zerostatvfs(&pargs
->pa_svfsb
);
499 pargs
->pa_root_cookie
= NULL
;
500 pargs
->pa_root_vtype
= VDIR
;
501 pargs
->pa_root_vsize
= 0;
502 pargs
->pa_root_rdev
= 0;
503 pargs
->pa_maxmsglen
= 0;
504 if (/*CONSTCOND*/ sizeof(time_t) == 4)
505 pargs
->pa_time32
= 1;
507 pargs
->pa_time32
= 0;
509 pu
->pu_flags
= pflags
;
510 buildpath
= pu
->pu_flags
& PUFFS_FLAG_BUILDPATH
; /* XXX */
512 free(pops
); /* XXX */
514 pu
->pu_privdata
= priv
;
515 pu
->pu_cc_stackshift
= PUFFS_CC_STACKSHIFT_DEFAULT
;
516 LIST_INIT(&pu
->pu_pnodelst
);
517 LIST_INIT(&pu
->pu_pnode_removed_lst
);
518 LIST_INIT(&pu
->pu_ios
);
519 LIST_INIT(&pu
->pu_ios_rmlist
);
520 LIST_INIT(&pu
->pu_ccmagazin
);
521 TAILQ_INIT(&pu
->pu_sched
);
523 /* defaults for some user-settable translation functions */
524 pu
->pu_cmap
= NULL
; /* identity translation */
526 pu
->pu_pathbuild
= puffs_stdpath_buildpath
;
527 pu
->pu_pathfree
= puffs_stdpath_freepath
;
528 pu
->pu_pathcmp
= puffs_stdpath_cmppath
;
529 pu
->pu_pathtransform
= NULL
;
530 pu
->pu_namemod
= NULL
;
532 pu
->pu_errnotify
= puffs_defaulterror
;
534 PU_SETSTATE(pu
, PUFFS_STATE_BEFOREMOUNT
);
541 /* can't unmount() from here for obvious reasons */
549 puffs_cancel(struct puffs_usermount
*pu
, int error
)
552 assert(puffs_getstate(pu
) < PUFFS_STATE_RUNNING
);
558 puffs_exit(struct puffs_usermount
*pu
, int force
)
560 struct puffs_node
*pn
;
562 lpuffs_debug("puffs_exit\n");
564 while ((pn
= LIST_FIRST(&pu
->pu_pnodelst
)) != NULL
)
567 while ((pn
= LIST_FIRST(&pu
->pu_pnode_removed_lst
)) != NULL
)
571 if (pu
->pu_state
& PU_HASKQ
)
575 return 0; /* always succesful for now, WILL CHANGE */
579 * Actual mainloop. This is called from a context which can block.
580 * It is called either from puffs_mainloop (indirectly, via
581 * puffs_cc_continue() or from puffs_cc_yield()).
584 puffs__theloop(struct puffs_cc
*pcc
)
586 struct puffs_usermount
*pu
= pcc
->pcc_pu
;
589 while (!unmountdone
|| !exitsignaled
) {
593 * Schedule existing requests.
595 while ((pcc
= TAILQ_FIRST(&pu
->pu_sched
)) != NULL
) {
596 lpuffs_debug("scheduling existing tasks\n");
597 TAILQ_REMOVE(&pu
->pu_sched
, pcc
, pcc_schedent
);
602 lpuffs_debug("Calling user mainloop handler\n");
606 /* Wait for request message. */
609 src
= fs_m_in
.m_source
;
611 caller_uid
= INVAL_UID
; /* To trap errors */
612 caller_gid
= INVAL_GID
;
613 req_nr
= fs_m_in
.m_type
;
615 if (req_nr
< FS_BASE
) {
616 fs_m_in
.m_type
+= FS_BASE
;
617 req_nr
= fs_m_in
.m_type
;
619 ind
= req_nr
- FS_BASE
;
621 if (ind
< 0 || ind
>= NREQS
) {
624 error
= (*fs_call_vec
[ind
])();
627 fs_m_out
.m_type
= error
;
628 if (IS_VFS_FS_TRANSID(last_request_transid
)) {
629 /* If a transaction ID was set, reset it */
630 fs_m_out
.m_type
= TRNS_ADD_ID(fs_m_out
.m_type
, last_request_transid
);
632 reply(src
, &fs_m_out
);
635 if (puffs__cc_restoremain(pu
) == -1)
636 warn("cannot restore main context. impending doom");
638 /* May get here, if puffs_fakecc is set to 1. Currently librefuse sets it.
639 * Now we just return to the caller.
643 puffs_mainloop(struct puffs_usermount
*pu
)
645 struct puffs_cc
*pcc
;
648 assert(puffs_getstate(pu
) >= PUFFS_STATE_RUNNING
);
650 pu
->pu_state
|= PU_HASKQ
| PU_INLOOP
;
653 * Create alternate execution context and jump to it. Note
654 * that we come "out" of savemain twice. Where we come out
655 * of it depends on the architecture. If the return address is
656 * stored on the stack, we jump out from puffs_cc_continue(),
657 * for a register return address from puffs__cc_savemain().
658 * PU_MAINRESTORE makes sure we DTRT in both cases.
660 if (puffs__cc_create(pu
, puffs__theloop
, &pcc
) == -1) {
663 if (puffs__cc_savemain(pu
) == -1) {
666 if ((pu
->pu_state
& PU_MAINRESTORE
) == 0)
667 puffs_cc_continue(pcc
);
672 /* store the real error for a while */
683 /*===========================================================================*
684 * sef_local_startup *
685 *===========================================================================*/
686 static void sef_local_startup(void)
688 /* Register init callbacks. */
689 sef_setcb_init_fresh(sef_cb_init_fresh
);
690 sef_setcb_init_restart(sef_cb_init_fail
);
692 /* No live update support for now. */
694 /* Register signal callbacks. */
695 sef_setcb_signal_handler(sef_cb_signal_handler
);
697 /* Let SEF perform startup. */
701 /*===========================================================================*
702 * sef_cb_init_fresh *
703 *===========================================================================*/
704 static int sef_cb_init_fresh(int type
, sef_init_info_t
*info
)
706 /* Initialize the Minix file server. */
710 /*===========================================================================*
711 * sef_cb_signal_handler *
712 *===========================================================================*/
713 static void sef_cb_signal_handler(int signo
)
715 /* Only check for termination signal, ignore anything else. */
716 if (signo
!= SIGTERM
) return;
721 /* If unmounting has already been performed, exit immediately.
722 * We might not get another message.
725 if (puffs__cc_restoremain(global_pu
) == -1)
726 warn("cannot restore main context. impending doom");
727 /* May happen if puffs_fakecc is set to 1. Currently librefuse sets it.
728 * There is a chance, that main loop hangs in receive() and we will
729 * never get any new message, so we have to exit() here.
735 /*===========================================================================*
737 *===========================================================================*/
738 static void get_work(
739 message
*m_in
/* pointer to message */
746 if ((r
= sef_receive(ANY
, m_in
)) != OK
) /* wait for message */
747 panic("sef_receive failed: %d", r
);
748 src
= m_in
->m_source
;
750 if(src
== VFS_PROC_NR
) {
752 lpuffs_debug("libpuffs: unmounted: unexpected message from FS\n");
754 srcok
= 1; /* Normal FS request. */
757 lpuffs_debug("libpuffs: unexpected source %d\n", src
);
760 assert((src
== VFS_PROC_NR
&& !unmountdone
));
762 last_request_transid
= TRNS_GET_ID(fs_m_in
.m_type
);
763 fs_m_in
.m_type
= TRNS_DEL_ID(fs_m_in
.m_type
);
764 if (fs_m_in
.m_type
== 0) {
765 assert(!IS_VFS_FS_TRANSID(last_request_transid
));
766 fs_m_in
.m_type
= last_request_transid
; /* Backwards compat. */
767 last_request_transid
= 0;
769 assert(IS_VFS_FS_TRANSID(last_request_transid
));
773 /*===========================================================================*
775 *===========================================================================*/
778 message
*m_out
/* report result */
781 if (OK
!= ipc_send(who
, m_out
)) /* send the message */
782 lpuffs_debug("libpuffs(%d) was unable to send reply\n", sef_self());
784 last_request_transid
= 0;
786 #endif /* defined(__minix) */