From 64f31d4f3b6bf4a7afabb3ea1f48475c5af04ba8 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Thu, 11 Apr 2019 22:32:34 +0000 Subject: [PATCH] fusefs: fix a panic in a stale vnode situation Don't panic if the server changes the file type of a file without us first deleting it. That could indicate a buggy server, but it could also be the result of one of several race conditions. Return EAGAIN as we do elsewhere. Sponsored by: The FreeBSD Foundation --- sys/fs/fuse/fuse_node.c | 12 +++++++++++- tests/sys/fs/fusefs/lookup.cc | 20 ++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/sys/fs/fuse/fuse_node.c b/sys/fs/fuse/fuse_node.c index 93b8b91ebd0f..e17199768ea9 100644 --- a/sys/fs/fuse/fuse_node.c +++ b/sys/fs/fuse/fuse_node.c @@ -233,7 +233,17 @@ fuse_vnode_alloc(struct mount *mp, return (err); if (*vpp) { - MPASS((*vpp)->v_type == vtyp && (*vpp)->v_data != NULL); + if ((*vpp)->v_type != vtyp) { + /* + * STALE vnode! This probably indicates a buggy + * server, but it could also be the result of a race + * between FUSE_LOOKUP and another client's + * FUSE_UNLINK/FUSE_CREATE + */ + fuse_internal_vnode_disappear(*vpp); + return (EAGAIN); + } + MPASS((*vpp)->v_data != NULL); SDT_PROBE2(fuse, , node, trace, 1, "vnode taken from hash"); return (0); } diff --git a/tests/sys/fs/fusefs/lookup.cc b/tests/sys/fs/fusefs/lookup.cc index 37f9a3be7fb6..07f0b82b16f6 100644 --- a/tests/sys/fs/fusefs/lookup.cc +++ b/tests/sys/fs/fusefs/lookup.cc @@ -349,3 +349,23 @@ TEST_F(Lookup, subdir) */ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); } + +/* + * The server returns two different vtypes for the same nodeid. This is a bad + * server! But we shouldn't crash. + */ +TEST_F(Lookup, vtype_conflict) +{ + const char FIRSTFULLPATH[] = "mountpoint/foo"; + const char SECONDFULLPATH[] = "mountpoint/bar"; + const char FIRSTRELPATH[] = "foo"; + const char SECONDRELPATH[] = "bar"; + uint64_t ino = 42; + + expect_lookup(FIRSTRELPATH, ino, S_IFREG | 0644, 0, 1, UINT64_MAX); + expect_lookup(SECONDRELPATH, ino, S_IFDIR | 0755, 0, 1, UINT64_MAX); + + ASSERT_EQ(0, access(FIRSTFULLPATH, F_OK)) << strerror(errno); + ASSERT_EQ(-1, access(SECONDFULLPATH, F_OK)); + ASSERT_EQ(EAGAIN, errno); +}