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:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user