Watch this: $ doas fuse-zip ~/Downloads/foo.zip /mnt $ ls /mnt openbsd-www $ grep -IR foo /usr/src > /dev/null # force vfs to reclaim vnodes $ ls /mnt ls: /mnt: No such file or directory $
Currently fusefs nodes in the kernel remember the parent inode number for ".." lookups. This only works until the kernel starts to reuse vnodes and the parent's vnode is reclaimed and the ino to path mapping is removed from the userland process by libfuse. I suggest to fix this by using reference counting in libfuse, so that parent mapping are retained as long as a child uses them. Also, the root node should never be freed. Ok? natano Index: lib/libfuse/fuse_ops.c =================================================================== RCS file: /cvs/src/lib/libfuse/fuse_ops.c,v retrieving revision 1.25 diff -u -p -r1.25 fuse_ops.c --- lib/libfuse/fuse_ops.c 30 Aug 2016 16:45:54 -0000 1.25 +++ lib/libfuse/fuse_ops.c 31 Aug 2016 10:14:58 -0000 @@ -432,15 +432,28 @@ ifuse_ops_lookup(struct fuse *f, struct DPRINTF("Inode:\t%llu\n", (unsigned long long)fbuf->fb_ino); DPRINTF("For file %s\n", fbuf->fb_dat); - vn = get_vn_by_name_and_parent(f, fbuf->fb_dat, fbuf->fb_ino); - if (vn == NULL) { - vn = alloc_vn(f, (const char *)fbuf->fb_dat, -1, fbuf->fb_ino); - if (vn == NULL) { - fbuf->fb_err = -errno; - free(fbuf->fb_dat); + if (strcmp((const char *)fbuf->fb_dat, "..") == 0) { + vn = tree_get(&f->vnode_tree, fbuf->fb_ino); + if (vn == NULL || vn->parent == NULL) { + fbuf->fb_err = -ENOENT; return (0); } - set_vn(f, vn); /*XXX*/ + vn = vn->parent; + if (vn->ino != FUSE_ROOT_INO) + ref_vn(vn); + } else { + vn = get_vn_by_name_and_parent(f, fbuf->fb_dat, fbuf->fb_ino); + if (vn == NULL) { + vn = alloc_vn(f, (const char *)fbuf->fb_dat, -1, + fbuf->fb_ino); + if (vn == NULL) { + fbuf->fb_err = -errno; + free(fbuf->fb_dat); + return (0); + } + set_vn(f, vn); /*XXX*/ + } else if (vn->ino != FUSE_ROOT_INO) + ref_vn(vn); } DPRINTF("new ino %llu\n", (unsigned long long)vn->ino); @@ -991,11 +1004,9 @@ ifuse_ops_reclaim(struct fuse *f, struct { struct fuse_vnode *vn; - vn = tree_pop(&f->vnode_tree, fbuf->fb_ino); - if (vn) { - remove_vnode_from_name_tree(f, vn); - free(vn); - } + vn = tree_get(&f->vnode_tree, fbuf->fb_ino); + if (vn != NULL) + unref_vn(f, vn); return (0); } Index: lib/libfuse/fuse_private.h =================================================================== RCS file: /cvs/src/lib/libfuse/fuse_private.h,v retrieving revision 1.13 diff -u -p -r1.13 fuse_private.h --- lib/libfuse/fuse_private.h 30 Aug 2016 16:45:54 -0000 1.13 +++ lib/libfuse/fuse_private.h 30 Aug 2016 17:06:27 -0000 @@ -34,7 +34,8 @@ struct fuse_args; struct fuse_vnode { ino_t ino; - ino_t parent; + struct fuse_vnode *parent; + unsigned int ref; char path[NAME_MAX + 1]; struct fuse_dirhandle *fd; @@ -99,6 +100,8 @@ int ifuse_exec_opcode(struct fuse *, str /* fuse_subr.c */ struct fuse_vnode *alloc_vn(struct fuse *, const char *, ino_t, ino_t); +void ref_vn(struct fuse_vnode *); +void unref_vn(struct fuse *, struct fuse_vnode *); struct fuse_vnode *get_vn_by_name_and_parent(struct fuse *, uint8_t *, ino_t); void remove_vnode_from_name_tree(struct fuse *, Index: lib/libfuse/fuse_subr.c =================================================================== RCS file: /cvs/src/lib/libfuse/fuse_subr.c,v retrieving revision 1.10 diff -u -p -r1.10 fuse_subr.c --- lib/libfuse/fuse_subr.c 24 May 2016 19:24:46 -0000 1.10 +++ lib/libfuse/fuse_subr.c 31 Aug 2016 10:15:36 -0000 @@ -24,7 +24,7 @@ #include "debug.h" struct fuse_vnode * -alloc_vn(struct fuse *f, const char *path, ino_t ino, ino_t parent) +alloc_vn(struct fuse *f, const char *path, ino_t ino, ino_t pino) { struct fuse_vnode *vn; @@ -34,13 +34,26 @@ alloc_vn(struct fuse *f, const char *pat } vn->ino = ino; - vn->parent = parent; + vn->ref = 1; if (strlcpy(vn->path, path, sizeof(vn->path)) >= sizeof(vn->path)) { DPRINTF("%s: strlcpy name too long\n", __func__); free(vn); return (NULL); } + if (pino == (ino_t)0) + vn->parent = NULL; + else { + if ((vn->parent = tree_get(&f->vnode_tree, pino)) == NULL) { + DPRINTF("%s: parent vnode %llu not in the vnode tree\n", + __func__, pino); + free(vn); + errno = ENOENT; + return (NULL); + } + ref_vn(vn->parent); + } + if (ino == (ino_t)-1) { f->max_ino++; vn->ino = f->max_ino; @@ -49,6 +62,24 @@ alloc_vn(struct fuse *f, const char *pat return (vn); } +void +ref_vn(struct fuse_vnode *vn) +{ + vn->ref++; +} + +void +unref_vn(struct fuse *f, struct fuse_vnode *vn) +{ + if (--vn->ref == 0) { + tree_pop(&f->vnode_tree, vn->ino); + remove_vnode_from_name_tree(f, vn); + if (vn->parent != NULL) + unref_vn(f, vn->parent); + free(vn); + } +} + int set_vn(struct fuse *f, struct fuse_vnode *v) { @@ -56,7 +87,8 @@ set_vn(struct fuse *f, struct fuse_vnode struct fuse_vnode *vn; DPRINTF("%s: create or update vnode %llu@%llu = %s\n", __func__, - (unsigned long long)v->ino, (unsigned long long)v->parent, + (unsigned long long)v->ino, + v->parent ? (unsigned long long)v->parent->ino : 0, v->path); if (tree_set(&f->vnode_tree, v->ino, v) == NULL) @@ -119,7 +151,7 @@ remove_vnode_from_name_tree(struct fuse } struct fuse_vnode * -get_vn_by_name_and_parent(struct fuse *f, uint8_t *xpath, ino_t parent) +get_vn_by_name_and_parent(struct fuse *f, uint8_t *xpath, ino_t pino) { struct fuse_vn_head *vn_head; struct fuse_vnode *v = NULL; @@ -131,7 +163,7 @@ get_vn_by_name_and_parent(struct fuse *f goto fail; SIMPLEQ_FOREACH(v, vn_head, node) - if (v->parent == parent) + if (v->parent && v->parent->ino == pino) return (v); fail: @@ -157,7 +189,7 @@ build_realname(struct fuse *f, ino_t ino return (NULL); } - while (vn->parent != 0) { + while (vn->parent != NULL) { if (firstshot++) ret = asprintf(&tmp, "/%s%s", vn->path, name); else @@ -171,10 +203,7 @@ build_realname(struct fuse *f, ino_t ino free(name); name = tmp; tmp = NULL; - vn = tree_get(&f->vnode_tree, vn->parent); - - if (!vn) - return (NULL); + vn = vn->parent; } if (ino == (ino_t)0) Index: sys/miscfs/fuse/fuse_lookup.c =================================================================== RCS file: /cvs/src/sys/miscfs/fuse/fuse_lookup.c,v retrieving revision 1.15 diff -u -p -r1.15 fuse_lookup.c --- sys/miscfs/fuse/fuse_lookup.c 30 Aug 2016 16:45:54 -0000 1.15 +++ sys/miscfs/fuse/fuse_lookup.c 31 Aug 2016 10:37:45 -0000 @@ -39,16 +39,17 @@ fusefs_lookup(void *v) struct fusefs_mnt *fmp; /* file system that directory is in */ int lockparent; /* 1 => lockparent flag is set */ struct vnode *tdp; /* returned by VOP_VGET */ - struct fusebuf *fbuf = NULL; + struct fusebuf *fbuf; struct vnode **vpp = ap->a_vpp; struct componentname *cnp = ap->a_cnp; struct proc *p = cnp->cn_proc; struct ucred *cred = cnp->cn_cred; + uint64_t nid; + enum vtype nvtype; int flags; int nameiop = cnp->cn_nameiop; int wantparent; int error = 0; - uint64_t nid; flags = cnp->cn_flags; *vpp = NULL; @@ -65,12 +66,7 @@ fusefs_lookup(void *v) (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) return (EROFS); - if (flags & ISDOTDOT) { - /* got ".." */ - nid = dp->parent; - if (nid == 0) - return (ENOENT); - } else if (cnp->cn_namelen == 1 && *(cnp->cn_nameptr) == '.') { + if (cnp->cn_namelen == 1 && *(cnp->cn_nameptr) == '.') { nid = dp->ufs_ino.i_number; } else { if (!fmp->sess_init) @@ -85,11 +81,13 @@ fusefs_lookup(void *v) error = fb_queue(fmp->dev, fbuf); - /* tsleep return */ - if (error == EWOULDBLOCK) - goto out; - if (error) { + fb_delete(fbuf); + + /* tsleep return */ + if (error == EWOULDBLOCK) + return (error); + if ((nameiop == CREATE || nameiop == RENAME) && (flags & ISLASTCN)) { if (vdp->v_mount->mnt_flag & MNT_RDONLY) @@ -102,15 +100,15 @@ fusefs_lookup(void *v) cnp->cn_flags |= PDIRUNLOCK; } - error = EJUSTRETURN; - goto out; + return (EJUSTRETURN); } - error = ENOENT; - goto out; + return (ENOENT); } nid = fbuf->fb_attr.st_ino; + nvtype = IFTOVT(fbuf->fb_attr.st_mode); + fb_delete(fbuf); } if (nameiop == DELETE && (flags & ISLASTCN)) { @@ -119,7 +117,7 @@ fusefs_lookup(void *v) */ error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc); if (error) - goto out; + goto reclaim; cnp->cn_flags |= SAVENAME; } @@ -129,22 +127,20 @@ fusefs_lookup(void *v) * Write access to directory required to delete files. */ if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0) - goto out; + goto reclaim; - if (nid == VTOI(vdp)->ufs_ino.i_number) { - error = EISDIR; - goto out; - } + if (nid == dp->ufs_ino.i_number) + return (EISDIR); error = VFS_VGET(fmp->mp, nid, &tdp); if (error) - goto out; + goto reclaim; - tdp->v_type = IFTOVT(fbuf->fb_attr.st_mode); + tdp->v_type = nvtype; *vpp = tdp; cnp->cn_flags |= SAVENAME; - goto out; + return (0); } if (flags & ISDOTDOT) { @@ -157,9 +153,11 @@ fusefs_lookup(void *v) if (vn_lock(vdp, LK_EXCLUSIVE | LK_RETRY, p) == 0) cnp->cn_flags &= ~PDIRUNLOCK; - return (error); + goto reclaim; } + tdp->v_type = nvtype; + if (lockparent && (flags & ISLASTCN)) { if ((error = vn_lock(vdp, LK_EXCLUSIVE, p))) { vput(tdp); @@ -176,12 +174,9 @@ fusefs_lookup(void *v) } else { error = VFS_VGET(fmp->mp, nid, &tdp); if (error) - goto out; + goto reclaim; - tdp->v_type = IFTOVT(fbuf->fb_attr.st_mode); - - if (vdp != NULL && vdp->v_type == VDIR) - VTOI(tdp)->parent = dp->ufs_ino.i_number; + tdp->v_type = nvtype; if (!lockparent || !(flags & ISLASTCN)) { VOP_UNLOCK(vdp, p); @@ -191,7 +186,14 @@ fusefs_lookup(void *v) *vpp = tdp; } -out: - fb_delete(fbuf); + return (error); + +reclaim: + if (nid != dp->ufs_ino.i_number && nid != FUSE_ROOTINO) { + fbuf = fb_setup(0, nid, FBT_RECLAIM, p); + if (fb_queue(fmp->dev, fbuf)) + printf("fusefs: libfuse vnode reclaim failed\n"); + fb_delete(fbuf); + } return (error); } Index: sys/miscfs/fuse/fuse_vfsops.c =================================================================== RCS file: /cvs/src/sys/miscfs/fuse/fuse_vfsops.c,v retrieving revision 1.27 diff -u -p -r1.27 fuse_vfsops.c --- sys/miscfs/fuse/fuse_vfsops.c 30 Aug 2016 16:45:54 -0000 1.27 +++ sys/miscfs/fuse/fuse_vfsops.c 30 Aug 2016 17:02:51 -0000 @@ -273,7 +273,6 @@ retry: ip->ufs_ino.i_vnode = nvp; ip->ufs_ino.i_dev = fmp->dev; ip->ufs_ino.i_number = ino; - ip->parent = 0; for (i = 0; i < FUFH_MAXTYPE; i++) ip->fufh[i].fh_type = FUFH_INVALID; Index: sys/miscfs/fuse/fuse_vnops.c =================================================================== RCS file: /cvs/src/sys/miscfs/fuse/fuse_vnops.c,v retrieving revision 1.32 diff -u -p -r1.32 fuse_vnops.c --- sys/miscfs/fuse/fuse_vnops.c 30 Aug 2016 16:45:54 -0000 1.32 +++ sys/miscfs/fuse/fuse_vnops.c 31 Aug 2016 10:36:42 -0000 @@ -660,7 +660,6 @@ fusefs_symlink(void *v) } tdp->v_type = VLNK; - VTOI(tdp)->parent = dp->ufs_ino.i_number; VN_KNOTE(ap->a_dvp, NOTE_WRITE); *vpp = tdp; @@ -832,16 +831,12 @@ fusefs_reclaim(void *v) (vp->v_type == VDIR), ap->a_p); } } - /* - * Purge old data structures associated with the inode. - */ - ip->parent = 0; /* * if the fuse connection is opened * ask libfuse to free the vnodes */ - if (fmp->sess_init) { + if (fmp->sess_init && ip->ufs_ino.i_number != FUSE_ROOTINO) { fbuf = fb_setup(0, ip->ufs_ino.i_number, FBT_RECLAIM, ap->a_p); if (fb_queue(fmp->dev, fbuf)) printf("fusefs: libfuse vnode reclaim failed\n"); @@ -925,8 +920,6 @@ fusefs_create(void *v) } tdp->v_type = IFTOVT(fbuf->fb_io_mode); - if (dvp != NULL && dvp->v_type == VDIR) - VTOI(tdp)->parent = ip->ufs_ino.i_number; *vpp = tdp; VN_KNOTE(ap->a_dvp, NOTE_WRITE); @@ -989,8 +982,6 @@ fusefs_mknod(void *v) } tdp->v_type = IFTOVT(fbuf->fb_io_mode); - if (dvp != NULL && dvp->v_type == VDIR) - VTOI(tdp)->parent = ip->ufs_ino.i_number; *vpp = tdp; VN_KNOTE(ap->a_dvp, NOTE_WRITE); @@ -1314,8 +1305,6 @@ fusefs_mkdir(void *v) } tdp->v_type = IFTOVT(fbuf->fb_io_mode); - if (dvp != NULL && dvp->v_type == VDIR) - VTOI(tdp)->parent = ip->ufs_ino.i_number; *vpp = tdp; VN_KNOTE(ap->a_dvp, NOTE_WRITE | NOTE_LINK); Index: sys/miscfs/fuse/fusefs_node.h =================================================================== RCS file: /cvs/src/sys/miscfs/fuse/fusefs_node.h,v retrieving revision 1.3 diff -u -p -r1.3 fusefs_node.h --- sys/miscfs/fuse/fusefs_node.h 12 Aug 2016 20:18:44 -0000 1.3 +++ sys/miscfs/fuse/fusefs_node.h 30 Aug 2016 17:01:47 -0000 @@ -38,9 +38,6 @@ struct fusefs_filehandle { struct fusefs_node { struct inode ufs_ino; - /* fd of fuse session and parent ino_t*/ - uint64_t parent; - /** I/O **/ struct fusefs_filehandle fufh[FUFH_MAXTYPE];