Index: sys/fs/fuse/fuse_main.c =================================================================== --- sys/fs/fuse/fuse_main.c (revision 238550) +++ sys/fs/fuse/fuse_main.c (working copy) @@ -79,15 +79,9 @@ static int fuse_loader(struct module *m, int what, struct mtx fuse_mtx; - -extern void -fuse_device_clone(void *arg, struct ucred *cred, char *name, - int namelen, struct cdev **dev); - extern struct vfsops fuse_vfsops; extern struct cdevsw fuse_cdevsw; extern struct vop_vector fuse_vnops; -extern struct clonedevs *fuseclones; extern int fuse_pbuf_freecnt; static struct vfsconf fuse_vfsconf = { @@ -113,10 +107,8 @@ static void fuse_bringdown(eventhandler_tag eh_tag) { - EVENTHANDLER_DEREGISTER(dev_clone, eh_tag); - fuse_ipc_destroy(); - clone_cleanup(&fuseclones); + fuse_device_destroy(); mtx_destroy(&fuse_mtx); } @@ -129,14 +121,11 @@ fuse_loader(struct module *m, int what, void *arg) switch (what) { case MOD_LOAD: /* kldload */ fuse_pbuf_freecnt = nswbuf / 2 + 1; - clone_setup(&fuseclones); mtx_init(&fuse_mtx, "fuse_mtx", NULL, MTX_DEF); - eh_tag = EVENTHANDLER_REGISTER(dev_clone, fuse_device_clone, 0, - 1000); - if (eh_tag == NULL) { - clone_cleanup(&fuseclones); + err = fuse_device_init(); + if (err) { mtx_destroy(&fuse_mtx); - return (ENOMEM); + return (err); } fuse_ipc_init(); Index: sys/fs/fuse/fuse_node.c =================================================================== --- sys/fs/fuse/fuse_node.c (revision 238550) +++ sys/fs/fuse/fuse_node.c (working copy) @@ -213,18 +213,13 @@ fuse_vnode_alloc(struct mount *mp, } err = vfs_hash_insert(*vpp, fuse_vnode_hash(nodeid), LK_EXCLUSIVE, td, &vp2, fuse_vnode_cmp, &nodeid); - - if (err) { - fuse_vnode_destroy(*vpp); - *vpp = NULL; + if (err) return (err); + if (vp2 != NULL) { + *vpp = vp2; + return (0); } - /* - * XXXIP: Prevent silent vnode reuse. It may happen because several fuse - * filesystems ignore inode numbers - */ - KASSERT(vp2 == NULL, - ("vfs hash collision for node #%ju\n", (uintmax_t)nodeid)); + ASSERT_VOP_ELOCKED(*vpp, "fuse_vnode_alloc"); return (0); Index: sys/fs/fuse/fuse_vnops.c =================================================================== --- sys/fs/fuse/fuse_vnops.c (revision 238550) +++ sys/fs/fuse/fuse_vnops.c (working copy) @@ -933,9 +933,30 @@ calldaemon: goto out; } if (flags & ISDOTDOT) { + struct mount *mp; int ltype; + /* + * Expanded copy of vn_vget_ino() so that + * fuse_vnode_get() can be used. + */ + mp = dvp->v_mount; ltype = VOP_ISLOCKED(dvp); + err = vfs_busy(mp, MBF_NOWAIT); + if (err != 0) { + vfs_ref(mp); + VOP_UNLOCK(dvp, 0); + err = vfs_busy(mp, 0); + vn_lock(dvp, ltype | LK_RETRY); + vfs_rel(mp); + if (err) + goto out; + if ((dvp->v_iflag & VI_DOOMED) != 0) { + err = ENOENT; + vfs_unbusy(mp); + goto out; + } + } VOP_UNLOCK(dvp, 0); err = fuse_vnode_get(vnode_mount(dvp), nid, @@ -943,8 +964,15 @@ calldaemon: &vp, cnp, IFTOVT(fattr->mode)); + vfs_unbusy(mp); vn_lock(dvp, ltype | LK_RETRY); - vref(vp); + if ((dvp->v_iflag & VI_DOOMED) != 0) { + if (err == 0) + vput(vp); + err = ENOENT; + } + if (err) + goto out; *vpp = vp; } else if (nid == VTOI(dvp)) { vref(dvp); Index: sys/fs/fuse/fuse_ipc.c =================================================================== --- sys/fs/fuse/fuse_ipc.c (revision 238550) +++ sys/fs/fuse/fuse_ipc.c (working copy) @@ -404,21 +404,13 @@ fdata_alloc(struct cdev *fdev, struct ucred *cred) } void -fdata_trydestroy(struct fuse_data *data) +fdata_destroy(struct fuse_data *data) { DEBUG("data=%p data.mp=%p data.fdev=%p data.flags=%04x\n", data, data->mp, data->fdev, data->dataflags); - if (data->mp != NULL) { - MPASS(data->mp->mnt_data == data); - return; - } - if (data->fdev->si_drv1 != NULL) { - MPASS(data->fdev->si_drv1 == data); - return; - } + MPASS(data->mp != NULL); DEBUG("destroy: data=%p\n", data); - MPASS((data->dataflags & FSESS_OPENED) == 0); /* Driving off stage all that stuff thrown at device... */ mtx_destroy(&data->ms_mtx); Index: sys/fs/fuse/fuse_ipc.h =================================================================== --- sys/fs/fuse/fuse_ipc.h (revision 238550) +++ sys/fs/fuse/fuse_ipc.h (working copy) @@ -196,7 +196,7 @@ struct fuse_data { }; #define FSESS_DEAD 0x0001 /* session is to be closed */ -#define FSESS_OPENED 0x0002 /* session device has been opened */ +#define FSESS_UNUSED0 0x0002 /* unused */ #define FSESS_INITED 0x0004 /* session has been inited */ #define FSESS_DAEMON_CAN_SPY 0x0010 /* let non-owners access this fs */ /* (and being observed by the daemon) */ @@ -217,13 +217,6 @@ extern int fuse_fix_broken_io; static __inline__ struct fuse_data * -fuse_get_devdata(struct cdev *fdev) -{ - return fdev->si_drv1; -} - -static __inline__ -struct fuse_data * fuse_get_mpdata(struct mount *mp) { return mp->mnt_data; @@ -320,6 +313,7 @@ fuse_aw_remove(struct fuse_ticket *ftick) { DEBUGX(FUSE_DEBUG_IPC, "ftick=%p refcount=%d\n", ftick, ftick->tk_refcount); + mtx_assert(&ftick->tk_data->aw_mtx, MA_OWNED); TAILQ_REMOVE(&ftick->tk_data->aw_head, ftick, tk_aw_link); #ifdef INVARIANTS ftick->tk_aw_link.tqe_next = NULL; @@ -358,7 +352,7 @@ fuse_libabi_geq(struct fuse_data *data, uint32_t a } struct fuse_data *fdata_alloc(struct cdev *dev, struct ucred *cred); -void fdata_trydestroy(struct fuse_data *data); +void fdata_destroy(struct fuse_data *data); void fdata_set_dead(struct fuse_data *data); static __inline__ Index: sys/fs/fuse/fuse_vfsops.c =================================================================== --- sys/fs/fuse/fuse_vfsops.c (revision 238550) +++ sys/fs/fuse/fuse_vfsops.c (working copy) @@ -62,7 +62,9 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include +#include #include #include #include @@ -202,27 +204,31 @@ fuse_getdevice(const char *fspec, struct thread *t static int fuse_vfsop_mount(struct mount *mp) { - int err = 0; + int err; -#if __FreeBSD_version >= 900040 - uint64_t mntopts = 0, __mntopts = 0; - -#else - int mntopts = 0, __mntopts = 0; - -#endif - int max_read_set = 0; - uint32_t max_read = ~0; + uint64_t mntopts, __mntopts; + int max_read_set; + uint32_t max_read; int daemon_timeout; + int fd; size_t len; struct cdev *fdev; struct fuse_data *data; - struct thread *td = curthread; - char *fspec, *subtype = NULL; + struct thread *td; + struct file *fp, *fptmp; + char *fspec, *subtype; struct vfsoptlist *opts; + subtype = NULL; + max_read_set = 0; + max_read = ~0; + err = 0; + mntopts = 0; + __mntopts = 0; + td = curthread; + fuse_trace_printf_vfsop(); if (mp->mnt_flag & MNT_UPDATE) @@ -245,6 +251,12 @@ fuse_vfsop_mount(struct mount *mp) if (!fspec) return err; + /* `fd' contains the filedescriptor for this session; REQUIRED */ + if (!vfs_scanopt(opts, "fd", "%d", &fd)) { + printf("coglione\n"); + return err; + } + err = fuse_getdevice(fspec, td, &fdev); if (err != 0) return err; @@ -274,22 +286,26 @@ fuse_vfsop_mount(struct mount *mp) daemon_timeout = FUSE_DEFAULT_DAEMON_TIMEOUT; } subtype = vfs_getopts(opts, "subtype=", &err); - err = 0; DEBUG2G("mntopts 0x%jx\n", (uintmax_t)mntopts); + err = fget(td, fd, CAP_READ, &fp); + if (err != 0) { + DEBUG("invalid or not opened device: data=%p\n", data); + goto out; + } + fptmp = td->td_fpop; + td->td_fpop = fp; + err = devfs_get_cdevpriv((void **)&data); + td->td_fpop = fptmp; + fdrop(fp, td); FUSE_LOCK(); - data = fuse_get_devdata(fdev); - if (data == NULL || data->mp != NULL || - (data->dataflags & FSESS_OPENED) == 0) { + if (err != 0 || data == NULL || data->mp != NULL) { DEBUG("invalid or not opened device: data=%p data.mp=%p\n", data, data != NULL ? data->mp : NULL); err = ENXIO; FUSE_UNLOCK(); goto out; - } else { - DEBUG("set mp: data=%p mp=%p\n", data, mp); - data->mp = mp; } if (fdata_get_dead(data)) { DEBUG("device is dead during mount: data=%p\n", data); @@ -348,7 +364,7 @@ out: " err=%d\n", data, mp, err); data->mp = NULL; - fdata_trydestroy(data); + fdata_destroy(data); } FUSE_UNLOCK(); dev_rel(fdev); @@ -406,7 +422,6 @@ alreadydead: FUSE_LOCK(); data->mp = NULL; fdev = data->fdev; - fdata_trydestroy(data); FUSE_UNLOCK(); MNT_ILOCK(mp); Index: sys/fs/fuse/fuse.h =================================================================== --- sys/fs/fuse/fuse.h (revision 238550) +++ sys/fs/fuse/fuse.h (working copy) @@ -214,3 +214,6 @@ do { \ void fuse_ipc_init(void); void fuse_ipc_destroy(void); + +int fuse_device_init(void); +void fuse_device_destroy(void); Index: sys/fs/fuse/fuse_device.c =================================================================== --- sys/fs/fuse/fuse_device.c (revision 238550) +++ sys/fs/fuse/fuse_device.c (working copy) @@ -83,9 +83,7 @@ __FBSDID("$FreeBSD$"); #define FUSE_DEBUG_MODULE DEVICE #include "fuse_debug.h" -static __inline int -fuse_ohead_audit(struct fuse_out_header *ohead, - struct uio *uio); +static struct cdev *fuse_dev; static d_open_t fuse_device_open; static d_close_t fuse_device_close; @@ -93,10 +91,6 @@ static d_poll_t fuse_device_poll; static d_read_t fuse_device_read; static d_write_t fuse_device_write; -void -fuse_device_clone(void *arg, struct ucred *cred, char *name, - int namelen, struct cdev **dev); - static struct cdevsw fuse_device_cdevsw = { .d_open = fuse_device_open, .d_close = fuse_device_close, @@ -108,22 +102,21 @@ static struct cdevsw fuse_device_cdevsw = { .d_flags = D_NEEDMINOR, }; -/* - * This struct is not public, but we are eager to use it, - * so we have to put its def here. - */ -struct clonedevs { - LIST_HEAD(, cdev) head; -}; - -struct clonedevs *fuseclones; - /**************************** * * >>> Fuse device op defs * ****************************/ +static void +fdata_dtor(void *arg) +{ + struct fuse_data *fdata; + + fdata = arg; + fdata_destroy(fdata); +} + /* * Resources are set up on a per-open basis */ @@ -131,31 +124,18 @@ static int fuse_device_open(struct cdev *dev, int oflags, int devtype, struct thread *td) { struct fuse_data *fdata; + int error; - if (dev->si_usecount > 1) - goto busy; - DEBUG("device %p\n", dev); fdata = fdata_alloc(dev, td->td_ucred); - - FUSE_LOCK(); - if (fuse_get_devdata(dev)) { - fdata_trydestroy(fdata); - FUSE_UNLOCK(); - goto busy; - } else { - fdata->dataflags |= FSESS_OPENED; - dev->si_drv1 = fdata; - } - FUSE_UNLOCK(); - - DEBUG("%s: device opened by thread %d.\n", dev->si_name, td->td_tid); - - return (0); - -busy: - return (EBUSY); + error = devfs_set_cdevpriv(fdata, fdata_dtor); + if (error != 0) + fdata_destroy(fdata); + else + DEBUG("%s: device opened by thread %d.\n", dev->si_name, + td->td_tid); + return (error); } static int @@ -163,17 +143,16 @@ fuse_device_close(struct cdev *dev, int fflag, int { struct fuse_data *data; struct fuse_ticket *tick; + int error; - data = fuse_get_devdata(dev); + error = devfs_get_cdevpriv((void **)&data); + if (error != 0) + return (error); if (!data) panic("no fuse data upon fuse device close"); - KASSERT(data->dataflags | FSESS_OPENED, - ("fuse device is already closed upon close")); fdata_set_dead(data); FUSE_LOCK(); - data->dataflags &= ~FSESS_OPENED; - fuse_lck_mtx_lock(data->aw_mtx); /* wakup poll()ers */ selwakeuppri(&data->ks_rsel, PZERO + 1); @@ -188,9 +167,6 @@ fuse_device_close(struct cdev *dev, int fflag, int fuse_ticket_drop(tick); } fuse_lck_mtx_unlock(data->aw_mtx); - - dev->si_drv1 = NULL; - fdata_trydestroy(data); FUSE_UNLOCK(); DEBUG("%s: device closed by thread %d.\n", dev->si_name, td->td_tid); @@ -201,9 +177,12 @@ int fuse_device_poll(struct cdev *dev, int events, struct thread *td) { struct fuse_data *data; - int revents = 0; + int error, revents = 0; - data = fuse_get_devdata(dev); + error = devfs_get_cdevpriv((void **)&data); + if (error != 0) + return (events & + (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM)); if (events & (POLLIN | POLLRDNORM)) { fuse_lck_mtx_lock(data->ms_mtx); @@ -227,17 +206,19 @@ fuse_device_poll(struct cdev *dev, int events, str int fuse_device_read(struct cdev *dev, struct uio *uio, int ioflag) { - int err = 0; + int err; struct fuse_data *data; struct fuse_ticket *tick; void *buf[] = {NULL, NULL, NULL}; int buflen[3]; int i; - data = fuse_get_devdata(dev); - DEBUG("fuse device being read on thread %d\n", uio->uio_td->td_tid); + err = devfs_get_cdevpriv((void **)&data); + if (err != 0) + return (err); + fuse_lck_mtx_lock(data->ms_mtx); again: if (fdata_get_dead(data)) { @@ -374,7 +355,9 @@ fuse_device_write(struct cdev *dev, struct uio *ui DEBUG("resid: %zd, iovcnt: %d, thread: %d\n", uio->uio_resid, uio->uio_iovcnt, uio->uio_td->td_tid); - data = fuse_get_devdata(dev); + err = devfs_get_cdevpriv((void **)&data); + if (err != 0) + return (err); if (uio->uio_resid < sizeof(struct fuse_out_header)) { DEBUG("got less than a header!\n"); @@ -428,7 +411,12 @@ fuse_device_write(struct cdev *dev, struct uio *ui /* pretender doesn't wanna do anything with answer */ DEBUG("stuff devalidated, so we drop it\n"); } - FUSE_ASSERT_AW_DONE(tick); + + /* + * As aw_mtx was not held during the callback execution the + * ticket may have been inserted again. However, this is safe + * because fuse_ticket_drop() will deal with refcount anyway. + */ fuse_ticket_drop(tick); } else { /* no callback at all! */ @@ -439,81 +427,21 @@ fuse_device_write(struct cdev *dev, struct uio *ui return (err); } -/* - * Modeled after tunclone() of net/if_tun.c ... - * boosted with a hack so that devices can be reused. - */ -void -fuse_device_clone(void *arg, struct ucred *cred, char *name, int namelen, - struct cdev **dev) +int +fuse_device_init(void) { - /* - * Why cloning? We do need per-open info, but we could as well put our - * hands on the file struct assigned to an open by implementing - * d_fdopen instead of d_open. - * - * From that on, the usual way to per-open (that is, file aware) - * I/O would be pushing our preferred set of ops into the f_op - * field of that file at open time. But that wouldn't work in - * FreeBSD, as the devfs open routine (which is the one who calls - * the device's d_(fd)open) overwrites that f_op with its own - * file ops mercilessly. - * - * Yet... even if we could get devfs to keep our file ops intact, - * I'd still say cloning is better. It makes fuse daemons' identity - * explicit and globally visible to userspace, and we are not forced - * to get the mount done by the daemon itself like in linux (where - * I/O is file aware by default). (The possibilities of getting the - * daemon do the mount or getting the mount util spawn the daemon - * are still open, of course; I guess I will go for the latter - * appcocroach.) - */ - int i, unit; + fuse_dev = make_dev(&fuse_device_cdevsw, 0, UID_ROOT, GID_OPERATOR, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, "fuse"); + if (fuse_dev == NULL) + return (ENOMEM); + return (0); +} - if (*dev != NULL) - return; +void +fuse_device_destroy(void) +{ - if (strcmp(name, "fuse") == 0) { - struct cdev *xdev; - - unit = -1; - - /* - * Before falling back to the standard routine, we try - * to find an existing free device by ourselves, so that - * it will be reused instead of having the clone machinery - * dummily spawn a new one. - */ - dev_lock(); - LIST_FOREACH(xdev, &fuseclones->head, si_clone) { - KASSERT(xdev->si_flags & SI_CLONELIST, - ("Dev %p(%s) should be on clonelist", xdev, xdev->si_name)); - - if (!xdev->si_drv1) { - unit = dev2unit(xdev); - break; - } - } - dev_unlock(); - } else if (dev_stdclone(name, NULL, "fuse", &unit) != 1) { - return; - } - /* find any existing device, or allocate new unit number */ - i = clone_create(&fuseclones, &fuse_device_cdevsw, &unit, dev, 0); - if (i) { - *dev = make_dev(&fuse_device_cdevsw, - unit, - UID_ROOT, GID_OPERATOR, - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, - "fuse%d", unit); - if (*dev == NULL) - return; - } - KASSERT(*dev, ("no device after apparently successful cloning")); - dev_ref(*dev); - (*dev)->si_drv1 = NULL; - (*dev)->si_flags |= SI_CHEAPCLONE; - - DEBUG("clone done: %d\n", unit); + MPASS(fuse_dev != NULL); + destroy_dev(fuse_dev); }