1 /* $NetBSD: puffs.c,v 1.92.4.4 2009/10/27 20:37:38 bouyer 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 /* TODO: We don't support PUFFS_COMFD used in original puffs_mount,
33 * add it to the docs if any.
38 #include <sys/cdefs.h>
40 __RCSID("$NetBSD: puffs.c,v 1.92.4.4 2009/10/27 20:37:38 bouyer Exp $");
43 #include <sys/param.h>
44 #include <sys/mount.h>
46 #include <minix/endpoint.h>
47 #include <minix/vfsif.h>
61 #include "puffs_priv.h"
63 #ifdef PUFFS_WITH_THREADS
65 pthread_mutex_t pu_lock
= PTHREAD_MUTEX_INITIALIZER
;
69 /* Declare some local functions. */
70 static void get_work(message
*m_in
);
71 static void reply(endpoint_t who
, message
*m_out
);
73 /* SEF functions and variables. */
74 static void sef_local_startup(void);
75 static int sef_cb_init_fresh(int type
, sef_init_info_t
*info
);
76 static void sef_cb_signal_handler(int signo
);
79 EXTERN
char **env_argv
;
82 #define PUFFS_MAX_ARGS 20
84 int __real_main(int argc
, char* argv
[]);
86 int __wrap_main(int argc
, char *argv
[])
90 static char* new_argv
[PUFFS_MAX_ARGS
];
93 /* SEF local startup. */
94 env_setargs(argc
, argv
);
97 global_kcred
.pkcr_type
= PUFFCRED_TYPE_INTERNAL
;
100 panic("Unexpected arguments, use:\
101 mount -t fs /dev/ /dir [-o option1,option2]\n");
104 name
= argv
[0] + strlen(argv
[0]);
105 while (*name
!= '/' && name
!= argv
[0])
109 strcpy(fs_name
, name
);
111 new_argv
[new_argc
] = argv
[0];
114 for (i
= 1; i
< argc
; i
++) {
115 if (new_argc
>= PUFFS_MAX_ARGS
) {
116 panic("Too many arguments, change PUFFS_MAX_ARGS");
118 new_argv
[new_argc
] = argv
[i
];
122 assert(new_argc
> 0);
126 return __real_main(new_argc
, new_argv
);
130 #define FILLOP(lower, upper) \
132 if (pops->puffs_node_##lower) \
133 opmask[PUFFS_VN_##upper] = 1; \
134 } while (/*CONSTCOND*/0)
136 fillvnopmask(struct puffs_ops
*pops
, uint8_t *opmask
)
139 memset(opmask
, 0, PUFFS_VN_MAX
);
141 FILLOP(create
, CREATE
);
142 FILLOP(mknod
, MKNOD
);
144 FILLOP(close
, CLOSE
);
145 FILLOP(access
, ACCESS
);
146 FILLOP(getattr
, GETATTR
);
147 FILLOP(setattr
, SETATTR
);
148 FILLOP(poll
, POLL
); /* XXX: not ready in kernel */
150 FILLOP(fsync
, FSYNC
);
152 FILLOP(remove
, REMOVE
);
154 FILLOP(rename
, RENAME
);
155 FILLOP(mkdir
, MKDIR
);
156 FILLOP(rmdir
, RMDIR
);
157 FILLOP(symlink
, SYMLINK
);
158 FILLOP(readdir
, READDIR
);
159 FILLOP(readlink
, READLINK
);
160 FILLOP(reclaim
, RECLAIM
);
161 FILLOP(inactive
, INACTIVE
);
162 FILLOP(print
, PRINT
);
164 FILLOP(write
, WRITE
);
165 FILLOP(abortop
, ABORTOP
);
172 puffs_defaulterror(struct puffs_usermount
*pu
, uint8_t type
,
173 int error
, const char *str
, puffs_cookie_t cookie
)
176 lpuffs_debug("abort: type %d, error %d, cookie %p (%s)\n",
177 type
, error
, cookie
, str
);
183 puffs_getstate(struct puffs_usermount
*pu
)
186 return pu
->pu_state
& PU_STATEMASK
;
190 puffs_setstacksize(struct puffs_usermount
*pu
, size_t ss
)
196 assert(puffs_getstate(pu
) == PUFFS_STATE_BEFOREMOUNT
);
198 psize
= sysconf(_SC_PAGESIZE
);
200 if (ss
< minsize
|| ss
== PUFFS_STACKSIZE_MIN
) {
201 if (ss
!= PUFFS_STACKSIZE_MIN
)
202 lpuffs_debug("puffs_setstacksize: adjusting "
203 "stacksize to minimum %ld\n", minsize
);
217 lpuffs_debug("puffs_setstacksize: using next power of two: "
218 "%d\n", 1<<stackshift
);
221 pu
->pu_cc_stackshift
= stackshift
;
224 struct puffs_pathobj
*
225 puffs_getrootpathobj(struct puffs_usermount
*pu
)
227 struct puffs_node
*pnr
;
229 pnr
= pu
->pu_pn_root
;
239 puffs_setroot(struct puffs_usermount
*pu
, struct puffs_node
*pn
)
246 puffs_getroot(struct puffs_usermount
*pu
)
249 return pu
->pu_pn_root
;
253 puffs_setrootinfo(struct puffs_usermount
*pu
, enum vtype vt
,
254 vsize_t vsize
, dev_t rdev
)
256 struct puffs_kargs
*pargs
= pu
->pu_kargp
;
258 if (puffs_getstate(pu
) != PUFFS_STATE_BEFOREMOUNT
) {
259 warnx("puffs_setrootinfo: call has effect only "
264 pargs
->pa_root_vtype
= vt
;
265 pargs
->pa_root_vsize
= vsize
;
266 pargs
->pa_root_rdev
= rdev
;
270 puffs_getspecific(struct puffs_usermount
*pu
)
273 return pu
->pu_privdata
;
277 puffs_setspecific(struct puffs_usermount
*pu
, void *privdata
)
280 pu
->pu_privdata
= privdata
;
284 puffs_setmntinfo(struct puffs_usermount
*pu
,
285 const char *mntfromname
, const char *puffsname
)
287 struct puffs_kargs
*pargs
= pu
->pu_kargp
;
289 (void)strlcpy(pargs
->pa_mntfromname
, mntfromname
,
290 sizeof(pargs
->pa_mntfromname
));
291 (void)strlcpy(pargs
->pa_typename
, puffsname
,
292 sizeof(pargs
->pa_typename
));
296 puffs_getmaxreqlen(struct puffs_usermount
*pu
)
299 return pu
->pu_maxreqlen
;
303 puffs_setmaxreqlen(struct puffs_usermount
*pu
, size_t reqlen
)
306 if (puffs_getstate(pu
) != PUFFS_STATE_BEFOREMOUNT
)
307 warnx("puffs_setmaxreqlen: call has effect only "
310 pu
->pu_kargp
->pa_maxmsglen
= reqlen
;
314 puffs_setfhsize(struct puffs_usermount
*pu
, size_t fhsize
, int flags
)
317 if (puffs_getstate(pu
) != PUFFS_STATE_BEFOREMOUNT
)
318 warnx("puffs_setfhsize: call has effect only before mount\n");
320 pu
->pu_kargp
->pa_fhsize
= fhsize
;
321 pu
->pu_kargp
->pa_fhflags
= flags
;
325 puffs_setncookiehash(struct puffs_usermount
*pu
, int nhash
)
328 if (puffs_getstate(pu
) != PUFFS_STATE_BEFOREMOUNT
)
329 warnx("puffs_setfhsize: call has effect only before mount\n");
331 pu
->pu_kargp
->pa_nhashbuckets
= nhash
;
335 puffs_set_pathbuild(struct puffs_usermount
*pu
, pu_pathbuild_fn fn
)
338 pu
->pu_pathbuild
= fn
;
342 puffs_set_pathtransform(struct puffs_usermount
*pu
, pu_pathtransform_fn fn
)
345 pu
->pu_pathtransform
= fn
;
349 puffs_set_pathcmp(struct puffs_usermount
*pu
, pu_pathcmp_fn fn
)
356 puffs_set_pathfree(struct puffs_usermount
*pu
, pu_pathfree_fn fn
)
359 pu
->pu_pathfree
= fn
;
363 puffs_set_namemod(struct puffs_usermount
*pu
, pu_namemod_fn fn
)
370 puffs_set_errnotify(struct puffs_usermount
*pu
, pu_errnotify_fn fn
)
373 pu
->pu_errnotify
= fn
;
377 puffs_set_cmap(struct puffs_usermount
*pu
, pu_cmap_fn fn
)
384 puffs_ml_setloopfn(struct puffs_usermount
*pu
, puffs_ml_loop_fn lfn
)
391 puffs_ml_settimeout(struct puffs_usermount
*pu
, struct timespec
*ts
)
395 pu
->pu_ml_timep
= NULL
;
397 pu
->pu_ml_timeout
= *ts
;
398 pu
->pu_ml_timep
= &pu
->pu_ml_timeout
;
403 puffs_set_prepost(struct puffs_usermount
*pu
,
404 pu_prepost_fn pre
, pu_prepost_fn pst
)
412 puffs_mount(struct puffs_usermount
*pu
, const char *dir
, int mntflags
,
413 puffs_cookie_t cookie
)
418 pu
->pu_kargp
->pa_root_cookie
= cookie
;
420 src
= fs_m_in
.m_source
;
422 caller_uid
= INVAL_UID
; /* To trap errors */
423 caller_gid
= INVAL_GID
;
424 req_nr
= fs_m_in
.m_type
;
426 if (req_nr
< VFS_BASE
) {
427 fs_m_in
.m_type
+= VFS_BASE
;
428 req_nr
= fs_m_in
.m_type
;
430 ind
= req_nr
- VFS_BASE
;
432 assert(ind
== REQ_READ_SUPER
);
434 if (ind
< 0 || ind
>= NREQS
) {
437 error
= (*fs_call_vec
[ind
])();
440 fs_m_out
.m_type
= error
;
441 if (IS_VFS_FS_TRANSID(last_request_transid
)) {
442 /* If a transaction ID was set, reset it */
443 fs_m_out
.m_type
= TRNS_ADD_ID(fs_m_out
.m_type
,
444 last_request_transid
);
446 reply(src
, &fs_m_out
);
455 PU_SETSTATE(pu
, PUFFS_STATE_RUNNING
);
460 struct puffs_usermount
*
461 _puffs_init(int dummy
, struct puffs_ops
*pops
, const char *mntfromname
,
462 const char *puffsname
, void *priv
, uint32_t pflags
)
464 struct puffs_usermount
*pu
;
465 struct puffs_kargs
*pargs
;
468 if (puffsname
== PUFFS_DEFER
)
470 if (mntfromname
== PUFFS_DEFER
)
472 if (priv
== PUFFS_DEFER
)
475 pu
= malloc(sizeof(struct puffs_usermount
));
478 memset(pu
, 0, sizeof(struct puffs_usermount
));
480 pargs
= pu
->pu_kargp
= malloc(sizeof(struct puffs_kargs
));
483 memset(pargs
, 0, sizeof(struct puffs_kargs
));
485 pargs
->pa_vers
= PUFFSDEVELVERS
| PUFFSVERSION
;
486 pargs
->pa_flags
= PUFFS_FLAG_KERN(pflags
);
487 fillvnopmask(pops
, pargs
->pa_vnopmask
);
488 puffs_setmntinfo(pu
, mntfromname
, puffsname
);
490 puffs_zerostatvfs(&pargs
->pa_svfsb
);
491 pargs
->pa_root_cookie
= NULL
;
492 pargs
->pa_root_vtype
= VDIR
;
493 pargs
->pa_root_vsize
= 0;
494 pargs
->pa_root_rdev
= 0;
495 pargs
->pa_maxmsglen
= 0;
497 pu
->pu_flags
= pflags
;
498 buildpath
= pu
->pu_flags
& PUFFS_FLAG_BUILDPATH
; /* XXX */
500 free(pops
); /* XXX */
502 pu
->pu_privdata
= priv
;
503 pu
->pu_cc_stackshift
= PUFFS_CC_STACKSHIFT_DEFAULT
;
504 LIST_INIT(&pu
->pu_pnodelst
);
505 LIST_INIT(&pu
->pu_pnode_removed_lst
);
506 LIST_INIT(&pu
->pu_ios
);
507 LIST_INIT(&pu
->pu_ios_rmlist
);
508 LIST_INIT(&pu
->pu_ccmagazin
);
509 TAILQ_INIT(&pu
->pu_sched
);
511 /* defaults for some user-settable translation functions */
512 pu
->pu_cmap
= NULL
; /* identity translation */
514 pu
->pu_pathbuild
= puffs_stdpath_buildpath
;
515 pu
->pu_pathfree
= puffs_stdpath_freepath
;
516 pu
->pu_pathcmp
= puffs_stdpath_cmppath
;
517 pu
->pu_pathtransform
= NULL
;
518 pu
->pu_namemod
= NULL
;
520 pu
->pu_errnotify
= puffs_defaulterror
;
522 PU_SETSTATE(pu
, PUFFS_STATE_BEFOREMOUNT
);
529 /* can't unmount() from here for obvious reasons */
537 puffs_cancel(struct puffs_usermount
*pu
, int error
)
539 assert(puffs_getstate(pu
) < PUFFS_STATE_RUNNING
);
545 puffs_exit(struct puffs_usermount
*pu
, int force
)
547 struct puffs_node
*pn
;
549 lpuffs_debug("puffs_exit\n");
551 while ((pn
= LIST_FIRST(&pu
->pu_pnodelst
)) != NULL
)
554 while ((pn
= LIST_FIRST(&pu
->pu_pnode_removed_lst
)) != NULL
)
558 if (pu
->pu_state
& PU_HASKQ
)
562 return 0; /* always succesful for now, WILL CHANGE */
566 * Actual mainloop. This is called from a context which can block.
567 * It is called either from puffs_mainloop (indirectly, via
568 * puffs_cc_continue() or from puffs_cc_yield()).
571 puffs__theloop(struct puffs_cc
*pcc
)
573 struct puffs_usermount
*pu
= pcc
->pcc_pu
;
576 while (!unmountdone
|| !exitsignaled
) {
580 * Schedule existing requests.
582 while ((pcc
= TAILQ_FIRST(&pu
->pu_sched
)) != NULL
) {
583 lpuffs_debug("scheduling existing tasks\n");
584 TAILQ_REMOVE(&pu
->pu_sched
, pcc
, pcc_schedent
);
589 lpuffs_debug("Calling user mainloop handler\n");
593 /* Wait for request message. */
596 src
= fs_m_in
.m_source
;
598 caller_uid
= INVAL_UID
; /* To trap errors */
599 caller_gid
= INVAL_GID
;
600 req_nr
= fs_m_in
.m_type
;
602 if (req_nr
< VFS_BASE
) {
603 fs_m_in
.m_type
+= VFS_BASE
;
604 req_nr
= fs_m_in
.m_type
;
606 ind
= req_nr
- VFS_BASE
;
608 if (ind
< 0 || ind
>= NREQS
) {
611 error
= (*fs_call_vec
[ind
])();
614 fs_m_out
.m_type
= error
;
615 if (IS_VFS_FS_TRANSID(last_request_transid
)) {
616 /* If a transaction ID was set, reset it */
617 fs_m_out
.m_type
= TRNS_ADD_ID(fs_m_out
.m_type
, last_request_transid
);
619 reply(src
, &fs_m_out
);
622 if (puffs__cc_restoremain(pu
) == -1)
623 warn("cannot restore main context. impending doom");
625 /* May get here, if puffs_fakecc is set to 1. Currently librefuse sets it.
626 * Now we just return to the caller.
631 puffs_mainloop(struct puffs_usermount
*pu
)
633 struct puffs_cc
*pcc
;
636 assert(puffs_getstate(pu
) >= PUFFS_STATE_RUNNING
);
638 pu
->pu_state
|= PU_HASKQ
| PU_INLOOP
;
641 * Create alternate execution context and jump to it. Note
642 * that we come "out" of savemain twice. Where we come out
643 * of it depends on the architecture. If the return address is
644 * stored on the stack, we jump out from puffs_cc_continue(),
645 * for a register return address from puffs__cc_savemain().
646 * PU_MAINRESTORE makes sure we DTRT in both cases.
648 if (puffs__cc_create(pu
, puffs__theloop
, &pcc
) == -1) {
651 if (puffs__cc_savemain(pu
) == -1) {
654 if ((pu
->pu_state
& PU_MAINRESTORE
) == 0)
655 puffs_cc_continue(pcc
);
660 /* store the real error for a while */
671 /*===========================================================================*
672 * sef_local_startup *
673 *===========================================================================*/
674 static void sef_local_startup()
676 /* Register init callbacks. */
677 sef_setcb_init_fresh(sef_cb_init_fresh
);
678 sef_setcb_init_restart(sef_cb_init_fail
);
680 /* No live update support for now. */
682 /* Register signal callbacks. */
683 sef_setcb_signal_handler(sef_cb_signal_handler
);
685 /* Let SEF perform startup. */
689 /*===========================================================================*
690 * sef_cb_init_fresh *
691 *===========================================================================*/
692 static int sef_cb_init_fresh(int type
, sef_init_info_t
*info
)
694 /* Initialize the Minix file server. */
695 SELF_E
= getprocnr();
699 /*===========================================================================*
700 * sef_cb_signal_handler *
701 *===========================================================================*/
702 static void sef_cb_signal_handler(int signo
)
704 /* Only check for termination signal, ignore anything else. */
705 if (signo
!= SIGTERM
) return;
710 /* If unmounting has already been performed, exit immediately.
711 * We might not get another message.
714 if (puffs__cc_restoremain(global_pu
) == -1)
715 warn("cannot restore main context. impending doom");
716 /* May happen if puffs_fakecc is set to 1. Currently librefuse sets it.
717 * There is a chance, that main loop hangs in receive() and we will
718 * never get any new message, so we have to exit() here.
724 /*===========================================================================*
726 *===========================================================================*/
727 static void get_work(m_in
)
728 message
*m_in
; /* pointer to message */
734 if ((r
= sef_receive(ANY
, m_in
)) != OK
) /* wait for message */
735 panic("sef_receive failed: %d", r
);
736 src
= m_in
->m_source
;
738 if(src
== VFS_PROC_NR
) {
740 lpuffs_debug("libpuffs: unmounted: unexpected message from FS\n");
742 srcok
= 1; /* Normal FS request. */
745 lpuffs_debug("libpuffs: unexpected source %d\n", src
);
748 assert((src
== VFS_PROC_NR
&& !unmountdone
));
750 last_request_transid
= TRNS_GET_ID(fs_m_in
.m_type
);
751 fs_m_in
.m_type
= TRNS_DEL_ID(fs_m_in
.m_type
);
752 if (fs_m_in
.m_type
== 0) {
753 assert(!IS_VFS_FS_TRANSID(last_request_transid
));
754 fs_m_in
.m_type
= last_request_transid
; /* Backwards compat. */
755 last_request_transid
= 0;
757 assert(IS_VFS_FS_TRANSID(last_request_transid
));
761 /*===========================================================================*
763 *===========================================================================*/
766 message
*m_out
/* report result */
769 if (OK
!= send(who
, m_out
)) /* send the message */
770 lpuffs_debug("libpuffs(%d) was unable to send reply\n", SELF_E
);
772 last_request_transid
= 0;