kernel: separate state for trace-deferred syscalls
[minix.git] / lib / libpuffs / puffs.c
blobe6eaea70c63c191cdc6f244e0b4386576ca3bb1a
1 /* $NetBSD: puffs.c,v 1.92.4.4 2009/10/27 20:37:38 bouyer Exp $ */
3 /*
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
12 * are met:
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
29 * SUCH DAMAGE.
32 /* TODO: We don't support PUFFS_COMFD used in original puffs_mount,
33 * add it to the docs if any.
37 #include "fs.h"
38 #include <sys/cdefs.h>
39 #if !defined(lint)
40 __RCSID("$NetBSD: puffs.c,v 1.92.4.4 2009/10/27 20:37:38 bouyer Exp $");
41 #endif /* !lint */
43 #include <sys/param.h>
44 #include <sys/mount.h>
46 #include <minix/endpoint.h>
47 #include <minix/vfsif.h>
49 #include <assert.h>
50 #include <err.h>
51 #include <errno.h>
52 #include <fcntl.h>
53 #include <paths.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <syslog.h>
58 #include <unistd.h>
60 #include "puffs.h"
61 #include "puffs_priv.h"
63 #ifdef PUFFS_WITH_THREADS
64 #include <pthread.h>
65 pthread_mutex_t pu_lock = PTHREAD_MUTEX_INITIALIZER;
66 #endif
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);
78 EXTERN int env_argc;
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[])
88 int i;
89 int new_argc = 0;
90 static char* new_argv[PUFFS_MAX_ARGS];
91 char *name;
93 /* SEF local startup. */
94 env_setargs(argc, argv);
95 sef_local_startup();
97 global_kcred.pkcr_type = PUFFCRED_TYPE_INTERNAL;
99 if (argc < 3) {
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])
106 name--;
107 if (name != argv[0])
108 name++;
109 strcpy(fs_name, name);
111 new_argv[new_argc] = argv[0];
112 new_argc++;
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];
119 new_argc++;
122 assert(new_argc > 0);
124 get_work(&fs_m_in);
126 return __real_main(new_argc, new_argv);
130 #define FILLOP(lower, upper) \
131 do { \
132 if (pops->puffs_node_##lower) \
133 opmask[PUFFS_VN_##upper] = 1; \
134 } while (/*CONSTCOND*/0)
135 static void
136 fillvnopmask(struct puffs_ops *pops, uint8_t *opmask)
139 memset(opmask, 0, PUFFS_VN_MAX);
141 FILLOP(create, CREATE);
142 FILLOP(mknod, MKNOD);
143 FILLOP(open, OPEN);
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 */
149 FILLOP(mmap, MMAP);
150 FILLOP(fsync, FSYNC);
151 FILLOP(seek, SEEK);
152 FILLOP(remove, REMOVE);
153 FILLOP(link, LINK);
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);
163 FILLOP(read, READ);
164 FILLOP(write, WRITE);
165 FILLOP(abortop, ABORTOP);
167 #undef FILLOP
170 /*ARGSUSED*/
171 static void
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);
178 abort();
183 puffs_getstate(struct puffs_usermount *pu)
186 return pu->pu_state & PU_STATEMASK;
189 void
190 puffs_setstacksize(struct puffs_usermount *pu, size_t ss)
192 long psize, minsize;
193 int stackshift;
194 int bonus;
196 assert(puffs_getstate(pu) == PUFFS_STATE_BEFOREMOUNT);
198 psize = sysconf(_SC_PAGESIZE);
199 minsize = 4*psize;
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);
204 ss = 4*psize;
207 stackshift = -1;
208 bonus = 0;
209 while (ss) {
210 if (ss & 0x1)
211 bonus++;
212 ss >>= 1;
213 stackshift++;
215 if (bonus > 1) {
216 stackshift++;
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;
230 if (pnr == NULL) {
231 errno = ENOENT;
232 return NULL;
235 return &pnr->pn_po;
238 void
239 puffs_setroot(struct puffs_usermount *pu, struct puffs_node *pn)
242 pu->pu_pn_root = pn;
245 struct puffs_node *
246 puffs_getroot(struct puffs_usermount *pu)
249 return pu->pu_pn_root;
252 void
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 "
260 "before mount\n");
261 return;
264 pargs->pa_root_vtype = vt;
265 pargs->pa_root_vsize = vsize;
266 pargs->pa_root_rdev = rdev;
269 void *
270 puffs_getspecific(struct puffs_usermount *pu)
273 return pu->pu_privdata;
276 void
277 puffs_setspecific(struct puffs_usermount *pu, void *privdata)
280 pu->pu_privdata = privdata;
283 void
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));
295 size_t
296 puffs_getmaxreqlen(struct puffs_usermount *pu)
299 return pu->pu_maxreqlen;
302 void
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 "
308 "before mount\n");
310 pu->pu_kargp->pa_maxmsglen = reqlen;
313 void
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;
324 void
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;
334 void
335 puffs_set_pathbuild(struct puffs_usermount *pu, pu_pathbuild_fn fn)
338 pu->pu_pathbuild = fn;
341 void
342 puffs_set_pathtransform(struct puffs_usermount *pu, pu_pathtransform_fn fn)
345 pu->pu_pathtransform = fn;
348 void
349 puffs_set_pathcmp(struct puffs_usermount *pu, pu_pathcmp_fn fn)
352 pu->pu_pathcmp = fn;
355 void
356 puffs_set_pathfree(struct puffs_usermount *pu, pu_pathfree_fn fn)
359 pu->pu_pathfree = fn;
362 void
363 puffs_set_namemod(struct puffs_usermount *pu, pu_namemod_fn fn)
366 pu->pu_namemod = fn;
369 void
370 puffs_set_errnotify(struct puffs_usermount *pu, pu_errnotify_fn fn)
373 pu->pu_errnotify = fn;
376 void
377 puffs_set_cmap(struct puffs_usermount *pu, pu_cmap_fn fn)
380 pu->pu_cmap = fn;
383 void
384 puffs_ml_setloopfn(struct puffs_usermount *pu, puffs_ml_loop_fn lfn)
387 pu->pu_ml_lfn = lfn;
390 void
391 puffs_ml_settimeout(struct puffs_usermount *pu, struct timespec *ts)
394 if (ts == NULL) {
395 pu->pu_ml_timep = NULL;
396 } else {
397 pu->pu_ml_timeout = *ts;
398 pu->pu_ml_timep = &pu->pu_ml_timeout;
402 void
403 puffs_set_prepost(struct puffs_usermount *pu,
404 pu_prepost_fn pre, pu_prepost_fn pst)
407 pu->pu_oppre = pre;
408 pu->pu_oppost = pst;
412 puffs_mount(struct puffs_usermount *pu, const char *dir, int mntflags,
413 puffs_cookie_t cookie)
415 endpoint_t src;
416 int error, ind;
418 pu->pu_kargp->pa_root_cookie = cookie;
420 src = fs_m_in.m_source;
421 error = OK;
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) {
435 error = EINVAL;
436 } else {
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);
448 if (error) {
449 free(pu->pu_kargp);
450 pu->pu_kargp = NULL;
451 errno = error;
452 return -1;
455 PU_SETSTATE(pu, PUFFS_STATE_RUNNING);
456 return 0;
459 /*ARGSUSED*/
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;
466 int sverrno;
468 if (puffsname == PUFFS_DEFER)
469 puffsname = "n/a";
470 if (mntfromname == PUFFS_DEFER)
471 mntfromname = "n/a";
472 if (priv == PUFFS_DEFER)
473 priv = NULL;
475 pu = malloc(sizeof(struct puffs_usermount));
476 if (pu == NULL)
477 goto failfree;
478 memset(pu, 0, sizeof(struct puffs_usermount));
480 pargs = pu->pu_kargp = malloc(sizeof(struct puffs_kargs));
481 if (pargs == NULL)
482 goto failfree;
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 */
499 pu->pu_ops = *pops;
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);
524 global_pu = pu;
526 return pu;
528 failfree:
529 /* can't unmount() from here for obvious reasons */
530 sverrno = errno;
531 free(pu);
532 errno = sverrno;
533 return NULL;
536 void
537 puffs_cancel(struct puffs_usermount *pu, int error)
539 assert(puffs_getstate(pu) < PUFFS_STATE_RUNNING);
540 free(pu);
543 /*ARGSUSED1*/
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)
552 puffs_pn_put(pn);
554 while ((pn = LIST_FIRST(&pu->pu_pnode_removed_lst)) != NULL)
555 puffs_pn_put(pn);
557 puffs__cc_exit(pu);
558 if (pu->pu_state & PU_HASKQ)
559 close(pu->pu_kq);
560 free(pu);
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()).
570 void
571 puffs__theloop(struct puffs_cc *pcc)
573 struct puffs_usermount *pu = pcc->pcc_pu;
574 int error, ind;
576 while (!unmountdone || !exitsignaled) {
577 endpoint_t src;
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);
585 puffs__goto(pcc);
588 if (pu->pu_ml_lfn) {
589 lpuffs_debug("Calling user mainloop handler\n");
590 pu->pu_ml_lfn(pu);
593 /* Wait for request message. */
594 get_work(&fs_m_in);
596 src = fs_m_in.m_source;
597 error = OK;
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) {
609 error = EINVAL;
610 } else {
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;
634 int sverrno;
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) {
649 goto out;
651 if (puffs__cc_savemain(pu) == -1) {
652 goto out;
654 if ((pu->pu_state & PU_MAINRESTORE) == 0)
655 puffs_cc_continue(pcc);
657 errno = 0;
659 out:
660 /* store the real error for a while */
661 sverrno = errno;
663 errno = sverrno;
664 if (errno)
665 return -1;
666 else
667 return 0;
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. */
686 sef_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();
696 return(OK);
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;
707 exitsignaled = 1;
708 fs_sync();
710 /* If unmounting has already been performed, exit immediately.
711 * We might not get another message.
713 if (unmountdone) {
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.
720 exit(0);
724 /*===========================================================================*
725 * get_work *
726 *===========================================================================*/
727 static void get_work(m_in)
728 message *m_in; /* pointer to message */
730 int r, srcok = 0;
731 endpoint_t src;
733 do {
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) {
739 if(unmountdone)
740 lpuffs_debug("libpuffs: unmounted: unexpected message from FS\n");
741 else
742 srcok = 1; /* Normal FS request. */
744 } else
745 lpuffs_debug("libpuffs: unexpected source %d\n", src);
746 } while(!srcok);
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;
756 } else
757 assert(IS_VFS_FS_TRANSID(last_request_transid));
761 /*===========================================================================*
762 * reply *
763 *===========================================================================*/
764 static void reply(
765 endpoint_t who,
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;