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];
 

Reply via email to