PERFORCE change 83984 for review
soc-chenk
soc-chenk at FreeBSD.org
Tue Sep 20 08:56:26 PDT 2005
http://perforce.freebsd.org/chv.cgi?CH=83984
Change 83984 by soc-chenk at 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
More information about the p4-projects
mailing list