xref: /minix3/lib/libpuffs/puffs.c (revision 7c48de6cc4c6d56f2277d378dba01dbac8a8c3b9)
1 /*	$NetBSD: puffs.c,v 1.117 2011/11/14 01:27:42 chs Exp $	*/
2 
3 /*
4  * Copyright (c) 2005, 2006, 2007  Antti Kantee.  All Rights Reserved.
5  *
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.
9  *
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.
18  *
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.
30  */
31 
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 */
36 
37 #include <sys/param.h>
38 #include <sys/mount.h>
39 
40 #if defined(__minix)
41 #include "fs.h"
42 #endif /* defined(__minix) */
43 
44 #include <assert.h>
45 #include <err.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <mntopts.h>
49 #include <paths.h>
50 #include <puffs.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <syslog.h>
55 #include <unistd.h>
56 
57 #include "puffs_priv.h"
58 
59 /* Most file systems want this for opts, so just give it to them */
60 const struct mntopt puffsmopts[] = {
61 	MOPT_STDOPTS,
62 	PUFFSMOPT_STD,
63 	MOPT_NULL,
64 };
65 #ifdef PUFFS_WITH_THREADS
66 #include <pthread.h>
67 pthread_mutex_t pu_lock = PTHREAD_MUTEX_INITIALIZER;
68 #endif
69 
70 #if defined(__minix)
71 static message fs_msg;
72 static int fs_ipc_status;
73 #endif
74 
75 /* Declare some local functions. */
76 static int get_work(message *msg, int *ipc_status);
77 
78 /* SEF functions and variables. */
79 static void sef_local_startup(void);
80 static int sef_cb_init_fresh(int type, sef_init_info_t *info);
81 static void sef_cb_signal_handler(int signo);
82 
83 EXTERN int env_argc;
84 EXTERN char **env_argv;
85 
86 
87 #define PUFFS_MAX_ARGS 20
88 
89 int __real_main(int argc, char* argv[]);
90 int __wrap_main(int argc, char* argv[]);
91 
92 int __wrap_main(int argc, char *argv[])
93 {
94   int i;
95   int new_argc = 0;
96   static char* new_argv[PUFFS_MAX_ARGS];
97   char *name;
98 
99   /* SEF local startup. */
100   env_setargs(argc, argv);
101   sef_local_startup();
102 
103   global_kcred.pkcr_type = PUFFCRED_TYPE_INTERNAL;
104 
105   if (argc < 3) {
106 	panic("Unexpected arguments, use:\
107 		mount -t fs /dev/ /dir [-o option1,option2]\n");
108   }
109 
110   name = argv[0] + strlen(argv[0]);
111   while (*name != '/' && name != argv[0])
112 	  name--;
113   if (name != argv[0])
114 	  name++;
115   strcpy(fs_name, name);
116 
117   new_argv[new_argc] = argv[0];
118   new_argc++;
119 
120   for (i = 1; i < argc; i++) {
121 	if (new_argc >= PUFFS_MAX_ARGS) {
122 		panic("Too many arguments, change PUFFS_MAX_ARGS");
123 	}
124 	new_argv[new_argc] = argv[i];
125 	new_argc++;
126   }
127 
128   assert(new_argc > 0);
129 
130   /* Get the mount request from VFS, so we can deal with it later. */
131   (void)get_work(&fs_msg, &fs_ipc_status);
132 
133   return __real_main(new_argc, new_argv);
134 }
135 
136 
137 #define FILLOP(lower, upper)						\
138 do {									\
139 	if (pops->puffs_node_##lower)					\
140 		opmask[PUFFS_VN_##upper] = 1;				\
141 } while (/*CONSTCOND*/0)
142 static void
143 fillvnopmask(struct puffs_ops *pops, struct puffs_kargs *pa)
144 {
145 	uint8_t *opmask = pa->pa_vnopmask;
146 
147 	memset(opmask, 0, sizeof(pa->pa_vnopmask));
148 
149 	FILLOP(create,   CREATE);
150 	FILLOP(mknod,    MKNOD);
151 	FILLOP(open,     OPEN);
152 	FILLOP(close,    CLOSE);
153 	FILLOP(access,   ACCESS);
154 	FILLOP(getattr,  GETATTR);
155 	FILLOP(setattr,  SETATTR);
156 	FILLOP(poll,     POLL);
157 	FILLOP(mmap,     MMAP);
158 	FILLOP(fsync,    FSYNC);
159 	FILLOP(seek,     SEEK);
160 	FILLOP(remove,   REMOVE);
161 	FILLOP(link,     LINK);
162 	FILLOP(rename,   RENAME);
163 	FILLOP(mkdir,    MKDIR);
164 	FILLOP(rmdir,    RMDIR);
165 	FILLOP(symlink,  SYMLINK);
166 	FILLOP(readdir,  READDIR);
167 	FILLOP(readlink, READLINK);
168 	FILLOP(reclaim,  RECLAIM);
169 	FILLOP(inactive, INACTIVE);
170 	FILLOP(print,    PRINT);
171 	FILLOP(read,     READ);
172 	FILLOP(write,    WRITE);
173 	FILLOP(abortop,  ABORTOP);
174 }
175 #undef FILLOP
176 
177 
178 /*ARGSUSED*/
179 __dead static void
180 puffs_defaulterror(struct puffs_usermount *pu, uint8_t type,
181 	int error, const char *str, puffs_cookie_t cookie)
182 {
183 
184 	lpuffs_debug("abort: type %d, error %d, cookie %p (%s)\n",
185 	    type, error, cookie, str);
186 	abort();
187 }
188 
189 
190 int
191 puffs_getstate(struct puffs_usermount *pu)
192 {
193 
194 	return pu->pu_state & PU_STATEMASK;
195 }
196 
197 void
198 puffs_setstacksize(struct puffs_usermount *pu, size_t ss)
199 {
200 	long psize, minsize;
201 	int stackshift;
202 	int bonus;
203 
204 	assert(puffs_getstate(pu) == PUFFS_STATE_BEFOREMOUNT);
205 
206 	psize = sysconf(_SC_PAGESIZE);
207 	minsize = 4*psize;
208 	if (ss < (size_t)minsize || ss == PUFFS_STACKSIZE_MIN) {
209 		if (ss != PUFFS_STACKSIZE_MIN)
210 			lpuffs_debug("puffs_setstacksize: adjusting "
211 			    "stacksize to minimum %ld\n", minsize);
212 		ss = 4*psize;
213 	}
214 
215 	stackshift = -1;
216 	bonus = 0;
217 	while (ss) {
218 		if (ss & 0x1)
219 			bonus++;
220 		ss >>= 1;
221 		stackshift++;
222 	}
223 	if (bonus > 1) {
224 		stackshift++;
225 		lpuffs_debug("puffs_setstacksize: using next power of two: "
226 		    "%d\n", 1<<stackshift);
227 	}
228 
229 	pu->pu_cc_stackshift = stackshift;
230 }
231 
232 struct puffs_pathobj *
233 puffs_getrootpathobj(struct puffs_usermount *pu)
234 {
235 	struct puffs_node *pnr;
236 
237 	pnr = pu->pu_pn_root;
238 	if (pnr == NULL) {
239 		errno = ENOENT;
240 		return NULL;
241 	}
242 
243 	return &pnr->pn_po;
244 }
245 
246 void
247 puffs_setroot(struct puffs_usermount *pu, struct puffs_node *pn)
248 {
249 
250 	pu->pu_pn_root = pn;
251 }
252 
253 struct puffs_node *
254 puffs_getroot(struct puffs_usermount *pu)
255 {
256 
257 	return pu->pu_pn_root;
258 }
259 
260 void
261 puffs_setrootinfo(struct puffs_usermount *pu, enum vtype vt,
262 	vsize_t vsize, dev_t rdev)
263 {
264 	struct puffs_kargs *pargs = pu->pu_kargp;
265 
266 	if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT) {
267 		warnx("puffs_setrootinfo: call has effect only "
268 		    "before mount\n");
269 		return;
270 	}
271 
272 	pargs->pa_root_vtype = vt;
273 	pargs->pa_root_vsize = vsize;
274 	pargs->pa_root_rdev = rdev;
275 }
276 
277 void *
278 puffs_getspecific(struct puffs_usermount *pu)
279 {
280 
281 	return pu->pu_privdata;
282 }
283 
284 void
285 puffs_setspecific(struct puffs_usermount *pu, void *privdata)
286 {
287 
288 	pu->pu_privdata = privdata;
289 }
290 
291 void
292 puffs_setmntinfo(struct puffs_usermount *pu,
293 	const char *mntfromname, const char *puffsname)
294 {
295 	struct puffs_kargs *pargs = pu->pu_kargp;
296 
297 	(void)strlcpy(pargs->pa_mntfromname, mntfromname,
298 	    sizeof(pargs->pa_mntfromname));
299 	(void)strlcpy(pargs->pa_typename, puffsname,
300 	    sizeof(pargs->pa_typename));
301 }
302 
303 size_t
304 puffs_getmaxreqlen(struct puffs_usermount *pu)
305 {
306 
307 	return pu->pu_maxreqlen;
308 }
309 
310 void
311 puffs_setmaxreqlen(struct puffs_usermount *pu, size_t reqlen)
312 {
313 
314 	if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT)
315 		warnx("puffs_setmaxreqlen: call has effect only "
316 		    "before mount\n");
317 
318 	pu->pu_kargp->pa_maxmsglen = reqlen;
319 }
320 
321 void
322 puffs_setfhsize(struct puffs_usermount *pu, size_t fhsize, int flags)
323 {
324 
325 	if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT)
326 		warnx("puffs_setfhsize: call has effect only before mount\n");
327 
328 	pu->pu_kargp->pa_fhsize = fhsize;
329 	pu->pu_kargp->pa_fhflags = flags;
330 }
331 
332 void
333 puffs_setncookiehash(struct puffs_usermount *pu, int nhash)
334 {
335 
336 	if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT)
337 		warnx("puffs_setfhsize: call has effect only before mount\n");
338 
339 	pu->pu_kargp->pa_nhashbuckets = nhash;
340 }
341 
342 void
343 puffs_set_pathbuild(struct puffs_usermount *pu, pu_pathbuild_fn fn)
344 {
345 
346 	pu->pu_pathbuild = fn;
347 }
348 
349 void
350 puffs_set_pathtransform(struct puffs_usermount *pu, pu_pathtransform_fn fn)
351 {
352 
353 	pu->pu_pathtransform = fn;
354 }
355 
356 void
357 puffs_set_pathcmp(struct puffs_usermount *pu, pu_pathcmp_fn fn)
358 {
359 
360 	pu->pu_pathcmp = fn;
361 }
362 
363 void
364 puffs_set_pathfree(struct puffs_usermount *pu, pu_pathfree_fn fn)
365 {
366 
367 	pu->pu_pathfree = fn;
368 }
369 
370 void
371 puffs_set_namemod(struct puffs_usermount *pu, pu_namemod_fn fn)
372 {
373 
374 	pu->pu_namemod = fn;
375 }
376 
377 void
378 puffs_set_errnotify(struct puffs_usermount *pu, pu_errnotify_fn fn)
379 {
380 
381 	pu->pu_errnotify = fn;
382 }
383 
384 void
385 puffs_set_cmap(struct puffs_usermount *pu, pu_cmap_fn fn)
386 {
387 
388 	pu->pu_cmap = fn;
389 }
390 
391 void
392 puffs_ml_setloopfn(struct puffs_usermount *pu, puffs_ml_loop_fn lfn)
393 {
394 
395 	pu->pu_ml_lfn = lfn;
396 }
397 
398 void
399 puffs_ml_settimeout(struct puffs_usermount *pu, struct timespec *ts)
400 {
401 
402 	if (ts == NULL) {
403 		pu->pu_ml_timep = NULL;
404 	} else {
405 		pu->pu_ml_timeout = *ts;
406 		pu->pu_ml_timep = &pu->pu_ml_timeout;
407 	}
408 }
409 
410 void
411 puffs_set_prepost(struct puffs_usermount *pu,
412 	pu_prepost_fn pre, pu_prepost_fn pst)
413 {
414 
415 	pu->pu_oppre = pre;
416 	pu->pu_oppost = pst;
417 }
418 
419 int
420 puffs_mount(struct puffs_usermount *pu, const char *dir, int mntflags,
421 	puffs_cookie_t cookie)
422 {
423 #if defined(__minix)
424 	pu->pu_kargp->pa_root_cookie = cookie;
425 
426 	/* Process the already-received mount request. */
427 	fsdriver_process(&puffs_table, &fs_msg, fs_ipc_status, FALSE);
428 
429 	if (!mounted) {
430 		/* This should never happen, unless VFS misbehaves.. */
431 		free(pu->pu_kargp);
432 		pu->pu_kargp = NULL;
433 		errno = -EINVAL;
434 		return -1;
435 	}
436 
437 	PU_SETSTATE(pu, PUFFS_STATE_RUNNING);
438 	return 0;
439 #endif /* defined(__minix) */
440 }
441 
442 /*ARGSUSED*/
443 struct puffs_usermount *
444 puffs_init(struct puffs_ops *pops, const char *mntfromname,
445 	const char *puffsname, void *priv, uint32_t pflags)
446 {
447 	struct puffs_usermount *pu;
448 	struct puffs_kargs *pargs;
449 	int sverrno;
450 
451 	if (puffsname == PUFFS_DEFER)
452 		puffsname = "n/a";
453 	if (mntfromname == PUFFS_DEFER)
454 		mntfromname = "n/a";
455 	if (priv == PUFFS_DEFER)
456 		priv = NULL;
457 
458 	pu = malloc(sizeof(struct puffs_usermount));
459 	if (pu == NULL)
460 		goto failfree;
461 	memset(pu, 0, sizeof(struct puffs_usermount));
462 
463 	pargs = pu->pu_kargp = malloc(sizeof(struct puffs_kargs));
464 	if (pargs == NULL)
465 		goto failfree;
466 	memset(pargs, 0, sizeof(struct puffs_kargs));
467 
468 	pargs->pa_vers = PUFFSVERSION;
469 	pargs->pa_flags = PUFFS_FLAG_KERN(pflags);
470 	fillvnopmask(pops, pargs);
471 	puffs_setmntinfo(pu, mntfromname, puffsname);
472 
473 	puffs_zerostatvfs(&pargs->pa_svfsb);
474 	pargs->pa_root_cookie = NULL;
475 	pargs->pa_root_vtype = VDIR;
476 	pargs->pa_root_vsize = 0;
477 	pargs->pa_root_rdev = 0;
478 	pargs->pa_maxmsglen = 0;
479 	if (/*CONSTCOND*/ sizeof(time_t) == 4)
480 		pargs->pa_time32 = 1;
481 	else
482 		pargs->pa_time32 = 0;
483 
484 	pu->pu_flags = pflags;
485 	buildpath = pu->pu_flags & PUFFS_FLAG_BUILDPATH; /* XXX */
486 	pu->pu_ops = *pops;
487 	free(pops); /* XXX */
488 
489 	pu->pu_privdata = priv;
490 	pu->pu_cc_stackshift = PUFFS_CC_STACKSHIFT_DEFAULT;
491 	LIST_INIT(&pu->pu_pnodelst);
492 	LIST_INIT(&pu->pu_pnode_removed_lst);
493 	LIST_INIT(&pu->pu_ios);
494 	LIST_INIT(&pu->pu_ios_rmlist);
495 	LIST_INIT(&pu->pu_ccmagazin);
496 	TAILQ_INIT(&pu->pu_sched);
497 
498 	/* defaults for some user-settable translation functions */
499 	pu->pu_cmap = NULL; /* identity translation */
500 
501 	pu->pu_pathbuild = puffs_stdpath_buildpath;
502 	pu->pu_pathfree = puffs_stdpath_freepath;
503 	pu->pu_pathcmp = puffs_stdpath_cmppath;
504 	pu->pu_pathtransform = NULL;
505 	pu->pu_namemod = NULL;
506 
507         pu->pu_errnotify = puffs_defaulterror;
508 
509 	PU_SETSTATE(pu, PUFFS_STATE_BEFOREMOUNT);
510 
511 	global_pu = pu;
512 
513 	return pu;
514 
515  failfree:
516 	/* can't unmount() from here for obvious reasons */
517 	sverrno = errno;
518 	free(pu);
519 	errno = sverrno;
520 	return NULL;
521 }
522 
523 void
524 puffs_cancel(struct puffs_usermount *pu, int error)
525 {
526 
527 	assert(puffs_getstate(pu) < PUFFS_STATE_RUNNING);
528 	free(pu);
529 }
530 
531 /*ARGSUSED1*/
532 int
533 puffs_exit(struct puffs_usermount *pu, int force)
534 {
535 	struct puffs_node *pn;
536 
537         lpuffs_debug("puffs_exit\n");
538 
539 	while ((pn = LIST_FIRST(&pu->pu_pnodelst)) != NULL)
540 		puffs_pn_put(pn);
541 
542 	while ((pn = LIST_FIRST(&pu->pu_pnode_removed_lst)) != NULL)
543 		puffs_pn_put(pn);
544 
545 	puffs__cc_exit(pu);
546 	if (pu->pu_state & PU_HASKQ)
547 		close(pu->pu_kq);
548 	free(pu);
549 
550 	return 0; /* always succesful for now, WILL CHANGE */
551 }
552 
553 /*
554  * Actual mainloop.  This is called from a context which can block.
555  * It is called either from puffs_mainloop (indirectly, via
556  * puffs_cc_continue() or from puffs_cc_yield()).
557  */
558 void
559 puffs__theloop(struct puffs_cc *pcc)
560 {
561 	struct puffs_usermount *pu = pcc->pcc_pu;
562 
563 	while (mounted || !exitsignaled) {
564 		/*
565 		 * Schedule existing requests.
566 		 */
567 		while ((pcc = TAILQ_FIRST(&pu->pu_sched)) != NULL) {
568 			lpuffs_debug("scheduling existing tasks\n");
569 			TAILQ_REMOVE(&pu->pu_sched, pcc, pcc_schedent);
570 			puffs__goto(pcc);
571 		}
572 
573 		if (pu->pu_ml_lfn) {
574                         lpuffs_debug("Calling user mainloop handler\n");
575 			pu->pu_ml_lfn(pu);
576 		}
577 
578 		/* Wait for request message. */
579 		if (get_work(&fs_msg, &fs_ipc_status) != OK)
580 			continue; /* recheck loop conditions */
581 
582 		/* Process it, and send a reply. */
583 		fsdriver_process(&puffs_table, &fs_msg, fs_ipc_status, FALSE);
584 	}
585 
586 	if (puffs__cc_restoremain(pu) == -1)
587 		warn("cannot restore main context.  impending doom");
588 
589 	/* May get here, if puffs_fakecc is set to 1. Currently librefuse sets it.
590 	 * Now we just return to the caller.
591 	 */
592 }
593 int
594 puffs_mainloop(struct puffs_usermount *pu)
595 {
596 	struct puffs_cc *pcc;
597 	int sverrno;
598 
599 	assert(puffs_getstate(pu) >= PUFFS_STATE_RUNNING);
600 
601 	pu->pu_state |= PU_HASKQ | PU_INLOOP;
602 
603 	/*
604 	 * Create alternate execution context and jump to it.  Note
605 	 * that we come "out" of savemain twice.  Where we come out
606 	 * of it depends on the architecture.  If the return address is
607 	 * stored on the stack, we jump out from puffs_cc_continue(),
608 	 * for a register return address from puffs__cc_savemain().
609 	 * PU_MAINRESTORE makes sure we DTRT in both cases.
610 	 */
611 	if (puffs__cc_create(pu, puffs__theloop, &pcc) == -1) {
612 		goto out;
613 	}
614 	if (puffs__cc_savemain(pu) == -1) {
615 		goto out;
616 	}
617 	if ((pu->pu_state & PU_MAINRESTORE) == 0)
618 		puffs_cc_continue(pcc);
619 
620 	errno = 0;
621 
622  out:
623 	/* store the real error for a while */
624 	sverrno = errno;
625 
626 	errno = sverrno;
627 	if (errno)
628 		return -1;
629 	else
630 		return 0;
631 }
632 
633 #if defined(__minix)
634 /*===========================================================================*
635  *			       sef_local_startup			     *
636  *===========================================================================*/
637 static void sef_local_startup(void)
638 {
639   /* Register init callbacks. */
640   sef_setcb_init_fresh(sef_cb_init_fresh);
641 
642   /* Register signal callbacks. */
643   sef_setcb_signal_handler(sef_cb_signal_handler);
644 
645   /* Let SEF perform startup. */
646   sef_startup();
647 }
648 
649 /*===========================================================================*
650  *		            sef_cb_init_fresh                                *
651  *===========================================================================*/
652 static int sef_cb_init_fresh(int type, sef_init_info_t *info)
653 {
654 /* Initialize the Minix file server. */
655   return(OK);
656 }
657 
658 /*===========================================================================*
659  *		           sef_cb_signal_handler                             *
660  *===========================================================================*/
661 static void sef_cb_signal_handler(int signo)
662 {
663   /* Only check for termination signal, ignore anything else. */
664   if (signo != SIGTERM) return;
665 
666   exitsignaled = 1;
667   fs_sync();
668 
669   sef_cancel();
670 }
671 
672 /*===========================================================================*
673  *				get_work				     *
674  *===========================================================================*/
675 static int get_work(message *msg, int *ipc_status)
676 {
677   int r;
678 
679   for (;;) {
680 	if ((r = sef_receive_status(ANY, msg, ipc_status)) != OK) {
681 		if (r == EINTR) /* sef_cancel from signal handler? */
682 			break; /* see if we can exit the main loop */
683 		panic("sef_receive failed: %d", r);
684 	}
685 	if (msg->m_source == VFS_PROC_NR)
686 		break;
687 	lpuffs_debug("libpuffs: unexpected source %d\n", msg->m_source);
688   }
689 
690   return r;
691 }
692 #endif /* defined(__minix) */
693