fusefs: fix two bugs regarding VOP_RECLAIM of the root inode

* We never send FUSE_LOOKUP for the root inode, since its inode number
  is hard-coded to 1.  Therefore, we should not send FUSE_FORGET for it,
  lest the server see its lookup count fall below 0.

* During VOP_RECLAIM, if we are reclaiming the root inode, we must clear
  the file system's vroot pointer.  Otherwise it will be left pointing
  at a reclaimed vnode, which will cause future VOP_LOOKUP operations to
  fail.  Previously we only cleared that pointer during VFS_UMOUNT.  I
  don't know of any real-world way to trigger this bug.

MFC after:	2 weeks
Reviewed by:	pfg
Differential Revision: https://reviews.freebsd.org/D34753
This commit is contained in:
Alan Somers
2022-04-02 13:31:24 -06:00
parent 4710aa248b
commit 3227325366
5 changed files with 51 additions and 5 deletions
+18 -1
View File
@@ -1980,7 +1980,24 @@ fuse_vnop_reclaim(struct vop_reclaim_args *ap)
fuse_filehandle_close(vp, fufh, td, NULL);
}
if (!fuse_isdeadfs(vp) && fvdat->nlookup > 0) {
if (VTOI(vp) == 1) {
/*
* Don't send FUSE_FORGET for the root inode, because
* we never send FUSE_LOOKUP for it (see
* fuse_vfsop_root) and we don't want the server to see
* mismatched lookup counts.
*/
struct fuse_data *data;
struct vnode *vroot;
data = fuse_get_mpdata(vnode_mount(vp));
FUSE_LOCK();
vroot = data->vroot;
data->vroot = NULL;
FUSE_UNLOCK();
if (vroot)
vrele(vroot);
} else if (!fuse_isdeadfs(vp) && fvdat->nlookup > 0) {
fuse_internal_forget_send(vnode_mount(vp), td, NULL, VTOI(vp),
fvdat->nlookup);
}
-1
View File
@@ -141,7 +141,6 @@ TEST_F(Destroy, ok)
uint64_t ino = 42;
expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2);
expect_forget(FUSE_ROOT_ID, 1);
expect_forget(ino, 2);
expect_destroy(0);
-1
View File
@@ -93,7 +93,6 @@ TEST_P(DevFusePoll, access)
/* Ensure that we wake up pollers during unmount */
TEST_P(DevFusePoll, destroy)
{
expect_forget(FUSE_ROOT_ID, 1);
expect_destroy(0);
m_mock->unmount();
+33
View File
@@ -32,6 +32,7 @@
extern "C" {
#include <sys/types.h>
#include <sys/mount.h>
#include <sys/sysctl.h>
#include <fcntl.h>
@@ -145,3 +146,35 @@ TEST_F(Forget, invalidate_names)
/* Access the file again, causing another lookup */
ASSERT_EQ(0, access(FULLFPATH, F_OK)) << strerror(errno);
}
/*
* Reclaiming the root inode should not send a FUSE_FORGET request, nor should
* it interfere with further lookup operations.
*/
TEST_F(Forget, root)
{
const char FULLPATH[] = "mountpoint/some_file.txt";
const char RELPATH[] = "some_file.txt";
uint64_t ino = 42;
mode_t mode = S_IFREG | 0755;
EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
.WillRepeatedly(Invoke(
ReturnImmediate([=](auto in __unused, auto& out) {
SET_OUT_HEADER_LEN(out, entry);
out.body.entry.attr.mode = mode;
out.body.entry.nodeid = ino;
out.body.entry.attr.nlink = 1;
out.body.entry.attr_valid = UINT64_MAX;
out.body.entry.entry_valid = UINT64_MAX;
})));
/* access(2) the file to force a lookup. */
ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
reclaim_vnode("mountpoint");
nap();
/* Access it again, to make sure it's still possible. */
ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
}
-2
View File
@@ -263,7 +263,6 @@ TEST_F(Lookup, dotdot_no_parent_nid)
).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
SET_OUT_HEADER_LEN(out, open);
})));
expect_forget(FUSE_ROOT_ID, 1, NULL);
expect_forget(foo_ino, 1, NULL);
fd = open("mountpoint/foo/bar", O_EXEC| O_DIRECTORY);
@@ -574,7 +573,6 @@ TEST_F(LookupExportable, dotdot_no_parent_nid)
).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
SET_OUT_HEADER_LEN(out, open);
})));
expect_forget(FUSE_ROOT_ID, 1, NULL);
expect_forget(foo_ino, 1, NULL);
EXPECT_LOOKUP(bar_ino, "..")
.WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {