custom message type for VM_INFO
[minix3.git] / lib / libpuffs / puffs.c
blob1d5bfe32cfc73a8257b0854b1dff2b7d501bf74e
1 /* $NetBSD: puffs.c,v 1.117 2011/11/14 01:27:42 chs 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 #include <sys/cdefs.h>
33 #if !defined(lint)
34 __RCSID("$NetBSD: puffs.c,v 1.117 2011/11/14 01:27:42 chs Exp $");
35 #endif /* !lint */
37 #include <sys/param.h>
38 #include <sys/mount.h>
40 #if defined(__minix)
41 #include "fs.h"
42 #include <minix/endpoint.h>
43 #include <minix/vfsif.h>
44 #endif /* defined(__minix) */
46 #include <assert.h>
47 #include <err.h>
48 #include <errno.h>
49 #include <fcntl.h>
50 #include <mntopts.h>
51 #include <paths.h>
52 #include <puffs.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <syslog.h>
57 #include <unistd.h>
59 #include "puffs_priv.h"
61 /* Most file systems want this for opts, so just give it to them */
62 const struct mntopt puffsmopts[] = {
63 MOPT_STDOPTS,
64 PUFFSMOPT_STD,
65 MOPT_NULL,
67 #ifdef PUFFS_WITH_THREADS
68 #include <pthread.h>
69 pthread_mutex_t pu_lock = PTHREAD_MUTEX_INITIALIZER;
70 #endif
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);
82 EXTERN int env_argc;
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[])
93 int i;
94 int new_argc = 0;
95 static char* new_argv[PUFFS_MAX_ARGS];
96 char *name;
98 /* SEF local startup. */
99 env_setargs(argc, argv);
100 sef_local_startup();
102 global_kcred.pkcr_type = PUFFCRED_TYPE_INTERNAL;
104 if (argc < 3) {
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])
111 name--;
112 if (name != argv[0])
113 name++;
114 strcpy(fs_name, name);
116 new_argv[new_argc] = argv[0];
117 new_argc++;
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];
124 new_argc++;
127 assert(new_argc > 0);
129 get_work(&fs_m_in);
131 return __real_main(new_argc, new_argv);
135 #define FILLOP(lower, upper) \
136 do { \
137 if (pops->puffs_node_##lower) \
138 opmask[PUFFS_VN_##upper] = 1; \
139 } while (/*CONSTCOND*/0)
140 static void
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);
149 FILLOP(open, OPEN);
150 FILLOP(close, CLOSE);
151 FILLOP(access, ACCESS);
152 FILLOP(getattr, GETATTR);
153 FILLOP(setattr, SETATTR);
154 FILLOP(poll, POLL);
155 FILLOP(mmap, MMAP);
156 FILLOP(fsync, FSYNC);
157 FILLOP(seek, SEEK);
158 FILLOP(remove, REMOVE);
159 FILLOP(link, LINK);
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);
169 FILLOP(read, READ);
170 FILLOP(write, WRITE);
171 FILLOP(abortop, ABORTOP);
173 #undef FILLOP
176 /*ARGSUSED*/
177 __dead static void
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);
184 abort();
189 puffs_getstate(struct puffs_usermount *pu)
192 return pu->pu_state & PU_STATEMASK;
195 void
196 puffs_setstacksize(struct puffs_usermount *pu, size_t ss)
198 long psize, minsize;
199 int stackshift;
200 int bonus;
202 assert(puffs_getstate(pu) == PUFFS_STATE_BEFOREMOUNT);
204 psize = sysconf(_SC_PAGESIZE);
205 minsize = 4*psize;
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);
210 ss = 4*psize;
213 stackshift = -1;
214 bonus = 0;
215 while (ss) {
216 if (ss & 0x1)
217 bonus++;
218 ss >>= 1;
219 stackshift++;
221 if (bonus > 1) {
222 stackshift++;
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;
236 if (pnr == NULL) {
237 errno = ENOENT;
238 return NULL;
241 return &pnr->pn_po;
244 void
245 puffs_setroot(struct puffs_usermount *pu, struct puffs_node *pn)
248 pu->pu_pn_root = pn;
251 struct puffs_node *
252 puffs_getroot(struct puffs_usermount *pu)
255 return pu->pu_pn_root;
258 void
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 "
266 "before mount\n");
267 return;
270 pargs->pa_root_vtype = vt;
271 pargs->pa_root_vsize = vsize;
272 pargs->pa_root_rdev = rdev;
275 void *
276 puffs_getspecific(struct puffs_usermount *pu)
279 return pu->pu_privdata;
282 void
283 puffs_setspecific(struct puffs_usermount *pu, void *privdata)
286 pu->pu_privdata = privdata;
289 void
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));
301 size_t
302 puffs_getmaxreqlen(struct puffs_usermount *pu)
305 return pu->pu_maxreqlen;
308 void
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 "
314 "before mount\n");
316 pu->pu_kargp->pa_maxmsglen = reqlen;
319 void
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;
330 void
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;
340 void
341 puffs_set_pathbuild(struct puffs_usermount *pu, pu_pathbuild_fn fn)
344 pu->pu_pathbuild = fn;
347 void
348 puffs_set_pathtransform(struct puffs_usermount *pu, pu_pathtransform_fn fn)
351 pu->pu_pathtransform = fn;
354 void
355 puffs_set_pathcmp(struct puffs_usermount *pu, pu_pathcmp_fn fn)
358 pu->pu_pathcmp = fn;
361 void
362 puffs_set_pathfree(struct puffs_usermount *pu, pu_pathfree_fn fn)
365 pu->pu_pathfree = fn;
368 void
369 puffs_set_namemod(struct puffs_usermount *pu, pu_namemod_fn fn)
372 pu->pu_namemod = fn;
375 void
376 puffs_set_errnotify(struct puffs_usermount *pu, pu_errnotify_fn fn)
379 pu->pu_errnotify = fn;
382 void
383 puffs_set_cmap(struct puffs_usermount *pu, pu_cmap_fn fn)
386 pu->pu_cmap = fn;
389 void
390 puffs_ml_setloopfn(struct puffs_usermount *pu, puffs_ml_loop_fn lfn)
393 pu->pu_ml_lfn = lfn;
396 void
397 puffs_ml_settimeout(struct puffs_usermount *pu, struct timespec *ts)
400 if (ts == NULL) {
401 pu->pu_ml_timep = NULL;
402 } else {
403 pu->pu_ml_timeout = *ts;
404 pu->pu_ml_timep = &pu->pu_ml_timeout;
408 void
409 puffs_set_prepost(struct puffs_usermount *pu,
410 pu_prepost_fn pre, pu_prepost_fn pst)
413 pu->pu_oppre = pre;
414 pu->pu_oppost = pst;
418 puffs_mount(struct puffs_usermount *pu, const char *dir, int mntflags,
419 puffs_cookie_t cookie)
421 #if defined(__minix)
422 endpoint_t src;
423 int error, ind;
425 pu->pu_kargp->pa_root_cookie = cookie;
427 src = fs_m_in.m_source;
428 error = OK;
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) {
442 error = EINVAL;
443 } else {
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);
455 if (error) {
456 free(pu->pu_kargp);
457 pu->pu_kargp = NULL;
458 errno = error;
459 return -1;
462 PU_SETSTATE(pu, PUFFS_STATE_RUNNING);
463 return 0;
464 #endif /* defined(__minix) */
467 /*ARGSUSED*/
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;
474 int sverrno;
476 if (puffsname == PUFFS_DEFER)
477 puffsname = "n/a";
478 if (mntfromname == PUFFS_DEFER)
479 mntfromname = "n/a";
480 if (priv == PUFFS_DEFER)
481 priv = NULL;
483 pu = malloc(sizeof(struct puffs_usermount));
484 if (pu == NULL)
485 goto failfree;
486 memset(pu, 0, sizeof(struct puffs_usermount));
488 pargs = pu->pu_kargp = malloc(sizeof(struct puffs_kargs));
489 if (pargs == NULL)
490 goto failfree;
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;
506 else
507 pargs->pa_time32 = 0;
509 pu->pu_flags = pflags;
510 buildpath = pu->pu_flags & PUFFS_FLAG_BUILDPATH; /* XXX */
511 pu->pu_ops = *pops;
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);
536 global_pu = pu;
538 return pu;
540 failfree:
541 /* can't unmount() from here for obvious reasons */
542 sverrno = errno;
543 free(pu);
544 errno = sverrno;
545 return NULL;
548 void
549 puffs_cancel(struct puffs_usermount *pu, int error)
552 assert(puffs_getstate(pu) < PUFFS_STATE_RUNNING);
553 free(pu);
556 /*ARGSUSED1*/
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)
565 puffs_pn_put(pn);
567 while ((pn = LIST_FIRST(&pu->pu_pnode_removed_lst)) != NULL)
568 puffs_pn_put(pn);
570 puffs__cc_exit(pu);
571 if (pu->pu_state & PU_HASKQ)
572 close(pu->pu_kq);
573 free(pu);
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()).
583 void
584 puffs__theloop(struct puffs_cc *pcc)
586 struct puffs_usermount *pu = pcc->pcc_pu;
587 int error, ind;
589 while (!unmountdone || !exitsignaled) {
590 endpoint_t src;
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);
598 puffs__goto(pcc);
601 if (pu->pu_ml_lfn) {
602 lpuffs_debug("Calling user mainloop handler\n");
603 pu->pu_ml_lfn(pu);
606 /* Wait for request message. */
607 get_work(&fs_m_in);
609 src = fs_m_in.m_source;
610 error = OK;
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) {
622 error = EINVAL;
623 } else {
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;
646 int sverrno;
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) {
661 goto out;
663 if (puffs__cc_savemain(pu) == -1) {
664 goto out;
666 if ((pu->pu_state & PU_MAINRESTORE) == 0)
667 puffs_cc_continue(pcc);
669 errno = 0;
671 out:
672 /* store the real error for a while */
673 sverrno = errno;
675 errno = sverrno;
676 if (errno)
677 return -1;
678 else
679 return 0;
682 #if defined(__minix)
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. */
698 sef_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. */
707 return(OK);
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;
718 exitsignaled = 1;
719 fs_sync();
721 /* If unmounting has already been performed, exit immediately.
722 * We might not get another message.
724 if (unmountdone) {
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.
731 exit(0);
735 /*===========================================================================*
736 * get_work *
737 *===========================================================================*/
738 static void get_work(
739 message *m_in /* pointer to message */
742 int r, srcok = 0;
743 endpoint_t src;
745 do {
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) {
751 if(unmountdone)
752 lpuffs_debug("libpuffs: unmounted: unexpected message from FS\n");
753 else
754 srcok = 1; /* Normal FS request. */
756 } else
757 lpuffs_debug("libpuffs: unexpected source %d\n", src);
758 } while(!srcok);
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;
768 } else
769 assert(IS_VFS_FS_TRANSID(last_request_transid));
773 /*===========================================================================*
774 * reply *
775 *===========================================================================*/
776 static void reply(
777 endpoint_t who,
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) */