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 | |
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')
-rw-r--r-- | lib/libfuse/fuse_ops.c | 37 | ||||
-rw-r--r-- | lib/libfuse/fuse_private.h | 7 | ||||
-rw-r--r-- | lib/libfuse/fuse_subr.c | 51 |
3 files changed, 69 insertions, 26 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); } diff --git a/lib/libfuse/fuse_private.h b/lib/libfuse/fuse_private.h index 2fe2d23bc86..51d8a4a2a86 100644 --- a/lib/libfuse/fuse_private.h +++ b/lib/libfuse/fuse_private.h @@ -1,4 +1,4 @@ -/* $OpenBSD: fuse_private.h,v 1.13 2016/08/30 16:45:54 natano Exp $ */ +/* $OpenBSD: fuse_private.h,v 1.14 2016/09/07 17:53:35 natano Exp $ */ /* * Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com> * @@ -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 *, struct fusebuf *); /* 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 *, diff --git a/lib/libfuse/fuse_subr.c b/lib/libfuse/fuse_subr.c index c991c909cb7..9ea0015c02a 100644 --- a/lib/libfuse/fuse_subr.c +++ b/lib/libfuse/fuse_subr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: fuse_subr.c,v 1.10 2016/05/24 19:24:46 okan Exp $ */ +/* $OpenBSD: fuse_subr.c,v 1.11 2016/09/07 17:53:35 natano Exp $ */ /* * Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com> * @@ -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 *path, ino_t ino, ino_t parent) } 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 *path, ino_t ino, ino_t parent) 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 *v) 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 *f, struct fuse_vnode *vn) } 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, uint8_t *xpath, ino_t parent) 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) |