Index: sys/fs/fuse/fuse_node.c =================================================================== --- sys/fs/fuse/fuse_node.c (revisione 238487) +++ sys/fs/fuse/fuse_node.c (copia locale) @@ -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 (revisione 238487) +++ sys/fs/fuse/fuse_vnops.c (copia locale) @@ -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.h =================================================================== --- sys/fs/fuse/fuse_ipc.h (revisione 238487) +++ sys/fs/fuse/fuse_ipc.h (copia locale) @@ -320,6 +320,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; Index: sys/fs/fuse/fuse_device.c =================================================================== --- sys/fs/fuse/fuse_device.c (revisione 238487) +++ sys/fs/fuse/fuse_device.c (copia locale) @@ -428,7 +428,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! */ @@ -474,31 +479,11 @@ fuse_device_clone(void *arg, struct ucred *cred, c if (*dev != NULL) return; - if (strcmp(name, "fuse") == 0) { - struct cdev *xdev; - + if (strcmp(name, "fuse") == 0) unit = -1; + else if (dev_stdclone(name, NULL, "fuse", &unit) != 1) + return; - /* - * 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) {