Skip site navigation (1)Skip section navigation (2)
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>