/*-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 <fcntl.h>32#include <pthread.h>33#include <semaphore.h>34}3536#include "mockfs.hh"37#include "utils.hh"3839using namespace testing;4041/* Tests for orderly unmounts */42class Destroy: public FuseTest {};4344/* Tests for unexpected deaths of the server */45class Death: public FuseTest{};4647static void* open_th(void* arg) {48int fd;49const char *path = (const char*)arg;5051fd = open(path, O_RDONLY);52EXPECT_EQ(-1, fd);53EXPECT_EQ(ENOTCONN, errno);54return 0;55}5657/*58* The server dies with unsent operations still on the message queue.59* Check for any memory leaks like this:60* 1) kldunload fusefs, if necessary61* 2) kldload fusefs62* 3) ./destroy --gtest_filter=Death.unsent_operations63* 4) kldunload fusefs64* 5) check /var/log/messages for anything like this:65Freed UMA keg (fuse_ticket) was not empty (31 items). Lost 2 pages of memory.66Warning: memory type fuse_msgbuf leaked memory on destroy (68 allocations, 428800 bytes leaked).67*/68TEST_F(Death, unsent_operations)69{70const char FULLPATH0[] = "mountpoint/some_file.txt";71const char FULLPATH1[] = "mountpoint/other_file.txt";72const char RELPATH0[] = "some_file.txt";73const char RELPATH1[] = "other_file.txt";74pthread_t th0, th1;75ino_t ino0 = 42, ino1 = 43;76sem_t sem;77mode_t mode = S_IFREG | 0644;7879sem_init(&sem, 0, 0);8081EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH0)82.WillRepeatedly(Invoke(83ReturnImmediate([=](auto in __unused, auto& out) {84SET_OUT_HEADER_LEN(out, entry);85out.body.entry.attr.mode = mode;86out.body.entry.nodeid = ino0;87out.body.entry.attr.nlink = 1;88})));89EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH1)90.WillRepeatedly(Invoke(91ReturnImmediate([=](auto in __unused, auto& out) {92SET_OUT_HEADER_LEN(out, entry);93out.body.entry.attr.mode = mode;94out.body.entry.nodeid = ino1;95out.body.entry.attr.nlink = 1;96})));9798EXPECT_CALL(*m_mock, process(99ResultOf([&](auto in) {100return (in.header.opcode == FUSE_OPEN);101}, Eq(true)),102_)103).WillOnce(Invoke([&](auto in __unused, auto &out __unused) {104sem_post(&sem);105pause();106}));107108/*109* One thread's operation will be sent to the daemon and block, and the110* other's will be stuck in the message queue.111*/112ASSERT_EQ(0, pthread_create(&th0, NULL, open_th,113__DECONST(void*, FULLPATH0))) << strerror(errno);114ASSERT_EQ(0, pthread_create(&th1, NULL, open_th,115__DECONST(void*, FULLPATH1))) << strerror(errno);116117/* Wait for the first thread to block */118sem_wait(&sem);119/* Give the second thread time to block */120nap();121122m_mock->kill_daemon();123124pthread_join(th0, NULL);125pthread_join(th1, NULL);126127sem_destroy(&sem);128}129130/*131* On unmount the kernel should send a FUSE_DESTROY operation. It should also132* send FUSE_FORGET operations for all inodes with lookup_count > 0.133*/134TEST_F(Destroy, ok)135{136const char FULLPATH[] = "mountpoint/some_file.txt";137const char RELPATH[] = "some_file.txt";138uint64_t ino = 42;139140expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2);141expect_forget(ino, 2);142expect_destroy(0);143144/*145* access(2) the file to force a lookup. Access it twice to double its146* lookup count.147*/148ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);149ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);150151/*152* Unmount, triggering a FUSE_DESTROY and also causing a VOP_RECLAIM153* for every vnode on this mp, triggering FUSE_FORGET for each of them.154*/155m_mock->unmount();156}157158159