Date: Tue, 20 Sep 2005 15:56:24 GMT From: soc-chenk <soc-chenk@FreeBSD.org> To: Perforce Change Reviews <perforce@freebsd.org> Subject: PERFORCE change 83984 for review Message-ID: <200509201556.j8KFuOow070811@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=83984 Change 83984 by soc-chenk@soc-chenk_leavemealone on 2005/09/20 15:56:02 Improved device handling Submitted by: soc-chenk Affected files ... .. //depot/projects/soc2005/fuse4bsd2/Changelog#8 edit .. //depot/projects/soc2005/fuse4bsd2/IMPLEMENTATION_NOTES#6 edit .. //depot/projects/soc2005/fuse4bsd2/fuse_module/fuse.c#7 edit .. //depot/projects/soc2005/fuse4bsd2/fuse_module/fuse.h#5 edit .. //depot/projects/soc2005/fuse4bsd2/mount_fusefs/mount_fusefs.8#2 edit Differences ... ==== //depot/projects/soc2005/fuse4bsd2/Changelog#8 (text+ko) ==== @@ -1,3 +1,14 @@ +Tue Sep 20 17:41:17 CEST 2005 at node: creo.hu, nick: csaba + * Improved device handling + + Refactored device/mount related data structures + + Rewrote module unload related code + + Fixed double unlock bug in fuse_setattr() + + Minor doc enhancements + Sat Sep 17 23:24:31 CEST 2005 at node: creo.hu, nick: csaba * Fixed mount/unmount related problems ==== //depot/projects/soc2005/fuse4bsd2/IMPLEMENTATION_NOTES#6 (text+ko) ==== @@ -139,6 +139,17 @@ can be delegated. This makes easy to provide a dedicated fuse device to each Fuse daemon. +(The imaginative reader might wonder how a Fuse module could be implemented for +other BSDs. Devfs is a FreeBSD specific thingy, so the above musings can't be +applied verbatim to the other flavours. Just as in FreeBSD, Dragonfly's open +handlers have access to the file structure involved [note: this seems to be a +post-fork invention, so the api is slightly different], but no devfs in Dfly; +so a Linux style (struct) file based mount implementation seems to be feasible. +Net's and Open's more traditional open handlers are not file aware. What I can +imagine there is a one-daemon-for-one-device scheme, as in FreeBSD, but with a +static set of fuse devices. This latter design could as well be chosen for +Dfly, of course.) + At this point, one could spot one definite advantage of the Linux way of doing the mount: it's comfortable. That the state of Fuse daemons is explicitly reflected in the devfs namespace might seem to be an elegant ==== //depot/projects/soc2005/fuse4bsd2/fuse_module/fuse.c#7 (text+ko) ==== @@ -39,24 +39,37 @@ #include <sys/kdb.h> #endif -#define NOT_YET_USED 0 - #if FMASTER #define __static #else #define __static static #endif +#define NOT_YET_USED 0 + +#ifndef DO_GIANT_MANUALLY +#define DO_GIANT_MANUALLY 1 +#endif +#ifndef USE_FUSE_LOCK +#define USE_FUSE_LOCK 1 +#endif + +#if USE_FUSE_LOCK +#define FUSE_LOCK mtx_lock(&fuse_mtx) +#define FUSE_UNLOCK mtx_unlock(&fuse_mtx) +#else +#define FUSE_LOCK +#define FUSE_UNLOCK +#endif + MALLOC_DEFINE(M_FUSEMSG, "fuse messaging", "buffer for fuse messaging related things"); static uint32_t fuse_useco = 0; -static struct mtx fuse_useco_mtx; +#if USE_FUSE_LOCK +static struct mtx fuse_mtx; +#endif -/* Lifted from kern/kern_conf.c -- headers don't advertise this */ -struct clonedevs { - LIST_HEAD(,cdev) head; -}; __static struct clonedevs *fuseclones; static d_open_t fusedev_open; @@ -71,10 +84,14 @@ .d_read = fusedev_read, .d_write = fusedev_write, .d_version = D_VERSION, +#if ! DO_GIANT_MANUALLY + .d_flags = D_NEEDGIANT, +#endif }; static void fusedev_clone(void *arg, char *name, int namelen, struct cdev **dev); +static void fuse_bringdown(eventhandler_tag eh_tag); static int fuse_loader(struct module *m, int what, void *arg); static __inline void fuse_iov_init(struct fuse_iov *fiov, size_t size); @@ -141,7 +158,6 @@ struct fuse_ticket *tick, uint64_t nid, enum fuse_opcode op, size_t blen, struct thread* td, struct ucred *cred); -__static __inline struct fuse_gate *fusedev_get_gate(struct cdev *fdev); __static __inline struct sx *fusedev_get_lock(struct cdev *fdev); __static __inline struct fuse_data *fusedev_get_data(struct cdev *fdev); @@ -476,6 +492,8 @@ data->freeticket_counter = 0; data->daemoncred = crhold(cred); + sx_init(&data->slock, "lock protecting fuse_data"); + return (data); } @@ -498,7 +516,7 @@ crfree(data->daemoncred); - /* sx_destroy(&data->shareslock); */ + sx_destroy(&data->slock); FREE(data,M_FUSEMSG); } @@ -506,6 +524,7 @@ static __inline void fdata_kick_set(struct fuse_data *data) { + DEBUG2G("banning daemon\n"); mtx_lock(&data->msg_mtx); data->dataflag |= FDAT_KICK; cv_signal(&data->msg_cv); @@ -515,6 +534,10 @@ static __inline int fdata_kick_get(struct fuse_data *data) { +#if _DEBUG + DEBUG2G("0x%x\n", data->dataflag & FDAT_KICK); + kdb_backtrace(); +#endif return (data->dataflag & FDAT_KICK); } @@ -866,22 +889,16 @@ ihead->nodeid); } -__static __inline struct fuse_gate * -fusedev_get_gate(struct cdev *fdev) -{ - return (fdev->si_drv1); -} - __static __inline struct sx * fusedev_get_lock(struct cdev *fdev) { - return (&fusedev_get_gate(fdev)->slock); + return (&fusedev_get_data(fdev)->slock); } __static __inline struct fuse_data * fusedev_get_data(struct cdev *fdev) { - return (fusedev_get_gate(fdev)->fdata); + return (fdev->si_drv1); } /******************** @@ -935,66 +952,115 @@ * ****************************/ -#define FUSEREF \ -do { \ - mtx_lock(&fuse_useco_mtx); \ - if (fuse_useco < 0) { \ - /* Module unload is going on */ \ - mtx_unlock(&fuse_useco_mtx); \ - return (ENOENT); \ - } else \ - fuse_useco++; \ - mtx_unlock(&fuse_useco_mtx); \ -} while (0) - /* * Resources are set up on a per-open basis */ static int fusedev_open(struct cdev *dev, int oflags, int devtype, struct thread *td) { - struct sx *slock; - struct fuse_gate *fgate; + struct fuse_data *fdata; + + /* + * We need to ensure data consistency on the main entry points + * of the module (so that we won't race with module unloading). + * I don't see a better way of doing this than using Giant. + * The lightest reliance on Giant is realized by the + * DO_GIANT_MANUALLY == USE_FUSE_LOCK == 1 combination. + * + * Then the device will operate D_NEEDGIANT-less, and the only + * point where Giant is used (apart from the obligatory places + * like module (un)load, (un)mount, etc.) is at the beginning + * of the device open routine, where destructive racing is + * prevented. + */ + +#if DO_GIANT_MANUALLY + mtx_lock(&Giant); +#endif + if (fuse_useco < 0) { + /* Module unload is going on */ +#if DO_GIANT_MANUALLY + mtx_unlock(&Giant); +#endif + DEBUG2G("caught in the middle of unload\n"); + return (ENOENT); + } +#if DO_GIANT_MANUALLY && USE_FUSE_LOCK + fuse_useco++; + mtx_unlock(&Giant); +#endif if (dev->si_usecount > 1) - return (EBUSY); + goto busy; + + DEBUG2G("device %p\n", dev); - FUSEREF; + fdata = fdata_alloc(td->td_ucred); - slock = fusedev_get_lock(dev); - fgate = fusedev_get_gate(dev); - sx_xlock(slock); - if (fgate->mp) { - sx_xunlock(slock); - return (EBUSY); - } - fgate->fdata = fdata_alloc(td->td_ucred); - sx_xunlock(slock); + FUSE_LOCK; + if (fusedev_get_data(dev)) { + FUSE_UNLOCK; + fdata_destroy(fdata); + goto busy; + } else { +#if ! (DO_GIANT_MANUALLY && USE_FUSE_LOCK) + fuse_useco++; +#endif + fdata->dataflag |= FDAT_OPENED; + dev->si_drv1 = fdata; + } + FUSE_UNLOCK; +#if DO_GIANT_MANUALLY && ! USE_FUSE_LOCK + mtx_unlock(&Giant); +#endif DEBUG("Opened device \"fuse\" (that of minor %d) successfully on thread %d.\n", minor(dev), td->td_tid); return(0); + +busy: +#if DO_GIANT_MANUALLY +#if USE_FUSE_LOCK + fuse_useco--; +#else + mtx_unlock(&Giant); +#endif +#endif + return (EBUSY); } static int -fusedev_close(struct cdev *dev, int fflag, int devtype, struct thread *p) +fusedev_close(struct cdev *dev, int fflag, int devtype, struct thread *td) { struct fuse_data *data; - struct fuse_gate *fgate; - struct sx *slock; +#if DO_GIANT_MANUALLY && ! USE_FUSE_LOCK + mtx_lock(&Giant); +#endif + FUSE_LOCK; data = fusedev_get_data(dev); - slock = fusedev_get_lock(dev); - sx_xlock(slock); - fgate = fusedev_get_gate(dev); - fgate->fdata = NULL; - sx_xunlock(slock); + if (! data) + panic("no fuse data upon fuse device close"); + KASSERT(data->dataflag | FDAT_OPENED, + ("fuse device is already closed upon close")); + data->dataflag &= ~FDAT_OPENED; + DEBUG2G("mntco %d\n", data->mntco); + if (data->mntco > 0) { + FUSE_UNLOCK; + goto out; + } + dev->si_drv1 = NULL; + FUSE_UNLOCK; fdata_destroy(data); +out: +#if DO_GIANT_MANUALLY && ! USE_FUSE_LOCK + mtx_unlock(&Giant); +#endif fuse_useco--; - DEBUG("Closing device \"fuse\" (that of minor %d) on thread %d.\n", minor(dev), p->td_tid); + DEBUG("Closing device \"fuse\" (that of minor %d) on thread %d.\n", minor(dev), td->td_tid); return(0); } @@ -1025,7 +1091,7 @@ err = cv_wait_sig(&data->msg_cv, &data->msg_mtx); if (err != 0) { mtx_unlock(&data->msg_mtx); - return err; + return (fdata_kick_get(data) ? ENODEV : err); } fmsgn = fdata_pop_msg(data); } @@ -1214,7 +1280,8 @@ fdip->fdev = ((struct fuse_mnt_data *)mp->mnt_data)->fdev; fdip->slock = fusedev_get_lock(fdip->fdev); sx_slock(fdip->slock); - if (! (fdip->data = fusedev_get_data(fdip->fdev))) { + fdip->data = fusedev_get_data(fdip->fdev); + if (! (fdip->data->dataflag & FDAT_OPENED)) { sx_sunlock(fdip->slock); return EIO; } @@ -1603,19 +1670,22 @@ fuse_mount(struct mount *mp, struct thread *td) { int err = 0; - int len, sharecount = 0; + int len; char *fspec; struct vnode *devvp; struct vfsoptlist *opts; struct nameidata nd, *ndp = &nd; struct cdev *fdev; struct sx *slock; - struct fuse_gate *fgate; struct fuse_data *data; struct fuse_mnt_data *fmnt; struct vnode *rvp; struct fuse_vnode_data *fvdat; + GIANT_REQUIRED; + KASSERT(fuse_useco >= 0, + ("negative fuse usecount despite Giant")); + if (mp->mnt_flag & MNT_UPDATE) { uprintf("fuse: updating mounts is not supported\n"); return (EOPNOTSUPP); @@ -1646,7 +1716,7 @@ if (!fspec || fspec[len - 1] != '\0') return (EINVAL); - FUSEREF; + mp->mnt_data = NULL; /* * Not an update, or updating the name: look up the name @@ -1655,31 +1725,41 @@ NDINIT(ndp, LOOKUP, FOLLOW, UIO_SYSSPACE, fspec, td); if ((err = namei(ndp)) != 0) - goto out; + return (err); NDFREE(ndp, NDF_ONLY_PNBUF); devvp = ndp->ni_vp; if (devvp->v_type != VCHR) { vrele(devvp); - err = ENXIO; - goto out; + return (ENXIO); } fdev = devvp->v_rdev; - /* dev_ref(fdev); */ + dev_ref(fdev); /* * according to coda code, no extra lock is needed -- * although in sys/vnode.h this field is marked "v" */ vrele(devvp); + FUSE_LOCK; if (! fdev->si_devsw || strcmp("fuse", fdev->si_devsw->d_name) || ! (slock = fusedev_get_lock(fdev))) { - err = ENXIO; - goto out; + FUSE_UNLOCK; + return (ENXIO); } + if ((data = fusedev_get_data(fdev)) && + data->dataflag & FDAT_OPENED) + data->mntco++; + else { + FUSE_UNLOCK; + dev_rel(fdev); + return (ENXIO); + } + FUSE_UNLOCK; + MALLOC(fmnt, struct fuse_mnt_data *, sizeof(*fmnt), M_FUSEFS, M_WAITOK| M_ZERO); @@ -1690,77 +1770,63 @@ vfs_flagopt(opts, "neglect_shares", &fmnt->mntopts, FUSEFS_NEGLECT_SHARES); vfs_flagopt(opts, "allow_other", &fmnt->mntopts, FUSEFS_DAEMON_CAN_SPY); + + if (fdata_kick_get(data)) + err = ENOTCONN; if (fmnt->mntopts & FUSEFS_DAEMON_CAN_SPY && suser(td)) { uprintf("only root can use \"allow_other\"\n"); - FREE(fmnt, M_FUSEFS); err = EPERM; + } + + if (err) goto out; - } + DEBUG2G("mntopts 0x%x\n", fmnt->mntopts); - sx_xlock(slock); - /* Sanity + permission checks */ - if (! (data = fusedev_get_data(fdev))) { - err = ENOTCONN; - uprintf("no daemon listening on fuse device\n"); - } - - if ((! err) && ! data->daemoncred) + if (! data->daemoncred) panic("fuse daemon found, but identity unknown"); - if ((! err) && fdata_kick_get(data)) { - err = ENOTCONN; - uprintf("fuse daemon found, but has been backlisted\n"); - } - - fgate = fusedev_get_gate(fdev); - if (!err) { - if (fgate->mp) { - fmnt->master = fgate->mp->mnt_data; - fmnt->mntopts |= FUSEFS_SECONDARY; - if (fmnt->master->mntopts & FUSEFS_BUSY) - /* - * Umount attempt is going on - */ - err = EBUSY; - if (fmnt->master->mntopts & FUSEFS_PRIVATE) - /* - * device is owned and owner doesn't - * wanna share it with us - */ - err = EPERM; - if (fmnt->mntopts & ~FUSEFS_SECONDARY) - /* - * Secondary mounts not allowed to have - * options (basicly, that would be - * useless though harmless, just let's - * be explicit about it) - */ - err = EINVAL; - } else { - if (suser(td) && - td->td_ucred->cr_uid != data->daemoncred->cr_uid) - /* we are not allowed to do the first mount */ - err = EPERM; - } + sx_xlock(slock); + if (data->mp) { + fmnt->master = data->mp->mnt_data; + fmnt->mntopts |= FUSEFS_SECONDARY; + if (fmnt->master->mntopts & FUSEFS_BUSY) + /* + * Umount attempt is going on + */ + err = EBUSY; + if (fmnt->master->mntopts & FUSEFS_PRIVATE) + /* + * device is owned and owner doesn't + * wanna share it with us + */ + err = EPERM; + if (fmnt->mntopts & ~FUSEFS_SECONDARY) + /* + * Secondary mounts not allowed to have + * options (basicly, that would be + * useless though harmless, just let's + * be explicit about it) + */ + err = EINVAL; + } else { + if (suser(td) && + td->td_ucred->cr_uid != data->daemoncred->cr_uid) + /* we are not allowed to do the first mount */ + err = EPERM; } if (err) { sx_xunlock(slock); - FREE(fmnt, M_FUSEFS); goto out; } - if (fmnt->mntopts & FUSEFS_SECONDARY) { - struct fuse_mnt_data *x_fmnt; - + if (fmnt->mntopts & FUSEFS_SECONDARY) LIST_INSERT_HEAD(&fmnt->master->slaves_head, fmnt, slaves_link); - LIST_FOREACH(x_fmnt, &fmnt->master->slaves_head, slaves_link) - sharecount++; - } else { + else { LIST_INIT(&fmnt->slaves_head); /* Now handshaking with daemon */ @@ -1774,7 +1840,6 @@ fdata_kick_set(data); sx_xunlock(slock); - FREE(fmnt, M_FUSEFS); goto out; } } @@ -1800,10 +1865,8 @@ if (err) { fdata_kick_set(data); - FREE(fmnt, M_FUSEFS); + sx_xunlock(slock); FREE(fvdat, M_FUSEFS); - - sx_xunlock(slock); goto out; } @@ -1820,7 +1883,7 @@ rootdone: if (! (fmnt->mntopts & FUSEFS_SECONDARY)) { - fgate->mp = mp; + data->mp = mp; #if ! REALTIME_TRACK_UNPRIVPROCDBG fmnt->mntopts &= ~FUSEFS_UNPRIVPROCDBG; fmnt->mntopts |= get_unprivileged_proc_debug(td) ? FUSEFS_UNPRIVPROCDBG : 0; @@ -1845,7 +1908,7 @@ if (len < MNAMELEN - 1) { /* Duhh, ain't there a better way of doing this? */ int log = 0, lim = 1; - while (sharecount >= lim) { + while (data->mntco > lim) { lim *= 10; log++; } @@ -1854,7 +1917,7 @@ len++; } else { sprintf(mp->mnt_stat.f_mntfromname + len, "%d", - sharecount); + data->mntco - 1); len += log; } } @@ -1863,9 +1926,17 @@ DEBUG2G("mp %p: %s\n", mp, mp->mnt_stat.f_mntfromname); out: - if (err) - fuse_useco--; - + if (err ) { + data->mntco--; + FUSE_LOCK; + if (data->mntco == 0 && (! data->dataflag & FDAT_OPENED)) { + fdev->si_drv1 = NULL; + fdata_destroy(data); + } + FUSE_UNLOCK; + dev_rel(fdev); + FREE(fmnt, M_FUSEFS); + } return (err); } @@ -1880,6 +1951,8 @@ struct fuse_data *data; struct fuse_mnt_data *fmnt; + GIANT_REQUIRED; + DEBUG2G("mp %p: %s\n", mp, mp->mnt_stat.f_mntfromname); /* Flag handling */ if (mntflags & MNT_FORCE) @@ -1921,6 +1994,7 @@ } } + data = fusedev_get_data(fmnt->fdev); sx_xlock(slock); if (fmnt->mntopts & FUSEFS_SECONDARY) { if (fmnt->master) @@ -1935,18 +2009,26 @@ if ((data = fusedev_get_data(fmnt->fdev))) fdata_kick_set(data); - fusedev_get_gate(fmnt->fdev)->mp = NULL; + data->mp = NULL; } sx_xunlock(slock); + data->mntco--; + FUSE_LOCK; + DEBUG2G("mntco %d, opened 0x%x\n", + data->mntco, data->dataflag & FDAT_OPENED); + if (data->mntco == 0 && ! (data->dataflag & FDAT_OPENED)) { + fmnt->fdev->si_drv1 = NULL; + fdata_destroy(data); + } + FUSE_UNLOCK; + dev_rel(fmnt->fdev); mp->mnt_data = NULL; FREE(fmnt, M_FUSEFS); /* Other guys do this, I don't know what it is good for... */ mp->mnt_flag &= ~MNT_LOCAL; - /* dev_rel(fmnt->fdev); */ - fuse_useco--; return (0); } @@ -4379,7 +4461,7 @@ } if ((err = fdisp_wait_answ(&fdi))) - goto out; + return (err); if (vp->v_type != IFTOVT(((struct fuse_attr_out *)fdi.answ)->attr.mode)) { fuse_vnode_kick(vp); @@ -5090,7 +5172,6 @@ */ int i, unit; - struct fuse_gate *fgate; if (*dev != NULL) return; @@ -5110,18 +5191,8 @@ "fuse%d", unit); if (*dev != NULL) { dev_ref(*dev); - MALLOC(fgate, struct fuse_gate *, sizeof(*fgate), M_FUSEMSG, - M_WAITOK | M_ZERO); - sx_init(&fgate->slock, "lock protecting fuse_data"); - (*dev)->si_drv1 = fgate; - /* - * Garbage collection of fuse devices is a neat idea - * but their fuse_gate is permanent and it doesn't let - * us go that way. (There is no cdev_reclaim callback.) - + (*dev)->si_drv1 = NULL; (*dev)->si_flags |= SI_CHEAPCLONE; - */ - (*dev)->si_flags &= ~SI_CHEAPCLONE; } } @@ -5134,20 +5205,35 @@ extern struct vfsconf fuse_vfsconf; +static void +fuse_bringdown(eventhandler_tag eh_tag) +{ + EVENTHANDLER_DEREGISTER(dev_clone, eh_tag); + + clone_cleanup(&fuseclones); +#if USE_FUSE_LOCK + mtx_destroy(&fuse_mtx); +#endif +#if FMASTER + for (i = 0; i < 5; i++) { + DEBUG("destroying fmaster%d\n", i); + destroy_dev(fmaster_dev[i]); + } +#endif +} + static int fuse_loader(struct module *m, int what, void *arg) { static eventhandler_tag eh_tag = NULL; - struct cdev *fdev; int err = 0; #if FMASTER int i; static struct cdev *fmaster_dev[5]; char *fmaster_name = "fmasterx"; #endif - /* vfs_modevent ignores its first arg */ - if ((err = vfs_modevent(NULL, what, &fuse_vfsconf))) - return err; + + GIANT_REQUIRED; switch (what) { case MOD_LOAD: /* kldload */ @@ -5162,12 +5248,16 @@ fuse_fileops.fo_flags = DFLAG_PASSABLE | DFLAG_SEEKABLE; clone_setup(&fuseclones); - mtx_init(&fuse_useco_mtx, "fuse_useco_mtx", NULL, MTX_DEF); +#if USE_FUSE_LOCK + mtx_init(&fuse_mtx, "fuse_mtx", NULL, MTX_DEF); +#endif eh_tag = EVENTHANDLER_REGISTER(dev_clone, fusedev_clone, 0, 1000); if (eh_tag == NULL) { clone_cleanup(&fuseclones); - mtx_destroy(&fuse_useco_mtx); +#if USE_FUSE_LOCK + mtx_destroy(&fuse_mtx); +#endif return (ENOMEM); } #if FMASTER @@ -5181,52 +5271,41 @@ /* Duh, it's static... */ /* vfs_register(&fuse_vfsconf); */ - printf("fuse module loaded.\n"); + /* vfs_modevent ignores its first arg */ + if ((err = vfs_modevent(NULL, what, &fuse_vfsconf))) + fuse_bringdown(eh_tag); + break; case MOD_UNLOAD: - mtx_lock(&fuse_useco_mtx); KASSERT(fuse_useco >= 0, ("fuse_useco is negative: %d", fuse_useco)); if (fuse_useco > 0) { - mtx_unlock(&fuse_useco_mtx); + DEBUG2G("fuse_useco %d\n", fuse_useco); return (EBUSY); } - fuse_useco--; + + if ((err = vfs_modevent(NULL, what, &fuse_vfsconf))) + return (err); + /* * at this point the counter falls below zero thus new init * attempts will know that no brownie for them */ - /* - * It's *not* like in net/if_tap.c - * we do *not* release the lock before deregistering the - * eventhandler - * cry cry if it pains - */ - EVENTHANDLER_DEREGISTER(dev_clone, eh_tag); - mtx_unlock(&fuse_useco_mtx); + fuse_useco--; + + fuse_bringdown(eh_tag); - LIST_FOREACH(fdev, &fuseclones->head, si_clone) { - sx_destroy(fusedev_get_lock(fdev)); - FREE(fdev->si_drv1, M_FUSEMSG); - fdev->si_flags |= SI_CHEAPCLONE; - } - clone_cleanup(&fuseclones); - mtx_destroy(&fuse_useco_mtx); -#if FMASTER - for (i = 0; i < 5; i++) { - DEBUG("destroying fmaster%d\n", i); - destroy_dev(fmaster_dev[i]); - } -#endif /* vfs_unregister(&fuse_vfsconf); */ - printf("fuse module unloaded.\n"); break; default: return (EINVAL); } - return (0); + printf("fuse module %sloaded\n", + what == MOD_LOAD ? "" : "un"); + + return (err); } /* Registering the module */ ==== //depot/projects/soc2005/fuse4bsd2/fuse_module/fuse.h#5 (text+ko) ==== @@ -105,12 +105,10 @@ struct ucred *daemoncred; int dataflag; -}; -struct fuse_gate { + int mntco; struct mount *mp; struct sx slock; - struct fuse_data *fdata; }; struct fuse_dispatcher { @@ -131,9 +129,10 @@ #define FU_ANSW 0x01 #define FU_INVAL 0x02 -#define FU_DIRTY 0x08 +#define FU_DIRTY 0x04 #define FDAT_KICK 0x01 +#define FDAT_OPENED 0x02 #define FUSE_ROOT_INODE 1 /* Fuse convention: node id of root node is 1 */ @@ -198,7 +197,9 @@ #endif #define DEBUG(args, ...) \ printf(DEBLABEL "%s:%d: " args, __func__, __LINE__, ## __VA_ARGS__) -#define __inline +#ifndef IGNORE_INLINE +#define IGNORE_INLINE 1 +#endif #else #define DEBUG(args ...) #endif @@ -226,6 +227,10 @@ void fprettyprint(struct fuse_iov *fiov, size_t dlen); #endif +#if IGNORE_INLINE +#define __inline +#endif + #if _DEBUG || _DEBUG2G #define bp_print(bp) \ printf("b_bcount %d, b_data %p, b_error 0x%x, b_iocmd 0x%x, b_ioflags 0x%x, b_iooffset %d, b_resid %d, b_blkno %d, b_offset %d, b_flags 0x%x, b_bufsize %d, b_lblkno %d, b_vp %p, b_vp_ino %d, b_dirtyoff %d, b_dirtyend %d, b_npages %d\n", \ ==== //depot/projects/soc2005/fuse4bsd2/mount_fusefs/mount_fusefs.8#2 (text+ko) ==== @@ -92,11 +92,9 @@ The following options are available: .Bl -tag -width indent .It Cm allow_other -Let other users see mounter's fs. By default this is not allowed in order not -to let the fs daemon to -.Dq spy -on other users by tracing their I/O activity. Only root can use this option. -Users can relax this limitation via secondary mounts. +Do not apply +.Sx STRICT ACCESS POLICY . +Only root can use this option. .It Cm private Refuse shared mounting of the daemon. .It Cm neglect_shares @@ -138,6 +136,61 @@ failsafe simple automation shown in the .Sx EXAMPLES section should be good enough. +.Sh STRICT ACCESS POLICY +The strict access policy for Fuse filesystems lets one to use the filesystem +only if the filesystem daemon is +.Dq more privileged +than the user. +.Pp +This is applied for Fuse mounts by default, and only root can mount without +the strict access policy (cf. the +.Cm allow_other +mount option). +.Pp +The reason is to shield users from the daemon "spying" on their I/O activities. +So, the above +.Dq more privileged +means that the daemon's privileges permit for her to trace the user's activity +(via tools like +.Xr truss 1 +or +.Xr ktrace 1 ) +anyway. +.Pp +Users might opt for willingly relax strict access policy (as far they +are concerned) by doing their own secondary mount (cf. +.Sx SHARED MOUNTS ) . +.Pp +.ul +Technical note: +it is impossible to use exactly the same check +as the one used with the above mentioned utilities. These use the +.Xr p_candebug 9 +function which is process and not ucred based (as we would need). +So we use our own bad ripoff of p_candebug which is indeed ucred based. +The differences from p_candebug: +.Bl -bullet -offset indent +.It +We immediately let a cred pass if the real uid of it is the same as the +real uid of the other, +.Dq to be traced +cred. +.It +Those parts which rely directly on operating on a process are omitted. +This includes +.Xr mac 9 +support as that's realised via mac_check_proc_debug in p_candebug. +.It +p_candebug utilizes the value of the +.Va security.bsd.unprivileged_proc_debug +sysctl, which is directly available where p_candebug is implemented; we can +access this value only via the in-kernel sysctl api, which involves locking. +So, for efficiency reasons, we cache the value of this sysctl at mount time +and use that value later on (this value makes a small difference, but really +just a small one, given the above mentioned +.Dq same real uids can go +policy). +.El .Sh SHARED MOUNTS A Fuse daemon can be shared, ie. mounted multiple times. When doing the first (primary) mount, the spawner and the mounter of the daemon @@ -219,7 +272,15 @@ framework (see http://fuse.sourceforge.net). This user interface is FreeBSD specific. .Sh BUGS On rare occasions (not clear, when exactly) the daemon fails to terminate upon -unmounting. +unmounting. Example: mount fusexmp (in multi-threaded mode [default]). Do a +.Xr statfs 2 +(practically, via +.Xr df 1 ) . +Then unmount the filesystem by device name. The daemon will stay alive. +But if we edit the module code such that a call to kdb_backtrace is inserted +into the fdata_kick_get function, the daemon will terminate properly. This +shows that there are ununderstood race conditions between kernel code and +userspace threading. .Pp Logically independent occasions of using Fuse filesystems can have hidden dependencies. This won't cause your system panic, but getting stuck in a
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200509201556.j8KFuOow070811>