/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2019 The FreeBSD Foundation4*5* This software was developed by BFF Storage Systems, LLC under sponsorship6* from the FreeBSD Foundation.7*8* Redistribution and use in source and binary forms, with or without9* modification, are permitted provided that the following conditions10* are met:11* 1. Redistributions of source code must retain the above copyright12* notice, this list of conditions and the following disclaimer.13* 2. Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in the15* documentation and/or other materials provided with the distribution.16*17* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND18* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE19* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE20* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE21* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL22* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS23* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)24* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT25* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY26* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF27* SUCH DAMAGE.28*/2930extern "C" {31#include <sys/types.h>32#include <sys/mount.h>3334#include <fcntl.h>35#include <semaphore.h>36#include <unistd.h>37}3839#include "mockfs.hh"40#include "utils.hh"4142using namespace testing;4344class Forget: public FuseTest {45public:46void SetUp() {47if (geteuid() != 0)48GTEST_SKIP() << "Only root may use " << reclaim_mib;4950FuseTest::SetUp();51}5253};5455/*56* When a fusefs vnode is reclaimed, it should send a FUSE_FORGET operation.57*/58TEST_F(Forget, ok)59{60const char FULLPATH[] = "mountpoint/some_file.txt";61const char RELPATH[] = "some_file.txt";62uint64_t ino = 42;63mode_t mode = S_IFREG | 0755;64sem_t sem;6566ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);6768EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)69.Times(3)70.WillRepeatedly(Invoke(71ReturnImmediate([=](auto in __unused, auto& out) {72SET_OUT_HEADER_LEN(out, entry);73out.body.entry.attr.mode = mode;74out.body.entry.nodeid = ino;75out.body.entry.attr.nlink = 1;76out.body.entry.attr_valid = UINT64_MAX;77})));78expect_forget(ino, 3, &sem);7980/*81* access(2) the file to force a lookup. Access it twice to double its82* lookup count.83*/84ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);85ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);8687reclaim_vnode(FULLPATH);8889sem_wait(&sem);90sem_destroy(&sem);91}9293/*94* When a directory is reclaimed, the names of its entries vanish from the95* namecache96*/97TEST_F(Forget, invalidate_names)98{99const char FULLFPATH[] = "mountpoint/some_dir/some_file.txt";100const char FULLDPATH[] = "mountpoint/some_dir";101const char DNAME[] = "some_dir";102const char FNAME[] = "some_file.txt";103uint64_t dir_ino = 42;104uint64_t file_ino = 43;105106EXPECT_LOOKUP(FUSE_ROOT_ID, DNAME)107.Times(2)108.WillRepeatedly(Invoke(109ReturnImmediate([=](auto in __unused, auto& out) {110SET_OUT_HEADER_LEN(out, entry);111out.body.entry.attr.mode = S_IFDIR | 0755;112out.body.entry.nodeid = dir_ino;113out.body.entry.attr.nlink = 2;114out.body.entry.attr_valid = UINT64_MAX;115out.body.entry.entry_valid = UINT64_MAX;116})));117118/*119* Even though we don't reclaim FNAME and its entry is cacheable, we120* should get two lookups because the reclaim of DNAME will invalidate121* the cached FNAME entry.122*/123EXPECT_LOOKUP(dir_ino, FNAME)124.Times(2)125.WillRepeatedly(Invoke(126ReturnImmediate([=](auto in __unused, auto& out) {127SET_OUT_HEADER_LEN(out, entry);128out.body.entry.attr.mode = S_IFREG | 0644;129out.body.entry.nodeid = file_ino;130out.body.entry.attr.nlink = 1;131out.body.entry.attr_valid = UINT64_MAX;132out.body.entry.entry_valid = UINT64_MAX;133})));134expect_forget(dir_ino, 1);135136/* Access the file to cache its name */137ASSERT_EQ(0, access(FULLFPATH, F_OK)) << strerror(errno);138139/* Reclaim the directory, invalidating its children from namecache */140reclaim_vnode(FULLDPATH);141142/* Access the file again, causing another lookup */143ASSERT_EQ(0, access(FULLFPATH, F_OK)) << strerror(errno);144}145146/*147* Reclaiming the root inode should not send a FUSE_FORGET request, nor should148* it interfere with further lookup operations.149*/150TEST_F(Forget, root)151{152const char FULLPATH[] = "mountpoint/some_file.txt";153const char RELPATH[] = "some_file.txt";154uint64_t ino = 42;155mode_t mode = S_IFREG | 0755;156157EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)158.WillRepeatedly(Invoke(159ReturnImmediate([=](auto in __unused, auto& out) {160SET_OUT_HEADER_LEN(out, entry);161out.body.entry.attr.mode = mode;162out.body.entry.nodeid = ino;163out.body.entry.attr.nlink = 1;164out.body.entry.attr_valid = UINT64_MAX;165out.body.entry.entry_valid = UINT64_MAX;166})));167168/* access(2) the file to force a lookup. */169ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);170171reclaim_vnode("mountpoint");172nap();173174/* Access it again, to make sure it's still possible. */175ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);176}177178179