diff options
author | Martin Natano <natano@cvs.openbsd.org> | 2016-09-07 17:53:36 +0000 |
---|---|---|
committer | Martin Natano <natano@cvs.openbsd.org> | 2016-09-07 17:53:36 +0000 |
commit | 587b8d5f6cb8ddb5c2ef3598d49e8064e025bc93 (patch) | |
tree | f9a6811178669d088b0bffc1e2c950abc67961e1 /lib/libfuse/fuse_ops.c | |
parent | 6732fa617ab1e37ccf7d3ed7505a90bd35dfe941 (diff) |
Fix fuse node lookups. 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. Fix
this by using reference counting in libfuse, so that parent mapping are
retained as long as a child uses them. Also, don't free the root node.
This commit resolves following issue:
$ 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
$
ok tedu
Diffstat (limited to 'lib/libfuse/fuse_ops.c')
-rw-r--r-- | lib/libfuse/fuse_ops.c | 37 |
1 files changed, 24 insertions, 13 deletions
diff --git a/lib/libfuse/fuse_ops.c b/lib/libfuse/fuse_ops.c index ebbadfd1417..74853d17444 100644 --- a/lib/libfuse/fuse_ops.c +++ b/lib/libfuse/fuse_ops.c @@ -1,4 +1,4 @@ -/* $OpenBSD: fuse_ops.c,v 1.25 2016/08/30 16:45:54 natano Exp $ */ +/* $OpenBSD: fuse_ops.c,v 1.26 2016/09/07 17:53:35 natano Exp $ */ /* * Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com> * @@ -432,15 +432,28 @@ ifuse_ops_lookup(struct fuse *f, struct fusebuf *fbuf) 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 fusebuf *fbuf) { 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); } |