Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tests/sys/fs/fusefs/destroy.cc
39537 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2019 The FreeBSD Foundation
5
*
6
* This software was developed by BFF Storage Systems, LLC under sponsorship
7
* from the FreeBSD Foundation.
8
*
9
* Redistribution and use in source and binary forms, with or without
10
* modification, are permitted provided that the following conditions
11
* are met:
12
* 1. Redistributions of source code must retain the above copyright
13
* notice, this list of conditions and the following disclaimer.
14
* 2. Redistributions in binary form must reproduce the above copyright
15
* notice, this list of conditions and the following disclaimer in the
16
* documentation and/or other materials provided with the distribution.
17
*
18
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
* SUCH DAMAGE.
29
*/
30
31
extern "C" {
32
#include <fcntl.h>
33
#include <pthread.h>
34
#include <semaphore.h>
35
}
36
37
#include "mockfs.hh"
38
#include "utils.hh"
39
40
using namespace testing;
41
42
/* Tests for orderly unmounts */
43
class Destroy: public FuseTest {};
44
45
/* Tests for unexpected deaths of the server */
46
class Death: public FuseTest{};
47
48
static void* open_th(void* arg) {
49
int fd;
50
const char *path = (const char*)arg;
51
52
fd = open(path, O_RDONLY);
53
EXPECT_EQ(-1, fd);
54
EXPECT_EQ(ENOTCONN, errno);
55
return 0;
56
}
57
58
/*
59
* The server dies with unsent operations still on the message queue.
60
* Check for any memory leaks like this:
61
* 1) kldunload fusefs, if necessary
62
* 2) kldload fusefs
63
* 3) ./destroy --gtest_filter=Death.unsent_operations
64
* 4) kldunload fusefs
65
* 5) check /var/log/messages for anything like this:
66
Freed UMA keg (fuse_ticket) was not empty (31 items). Lost 2 pages of memory.
67
Warning: memory type fuse_msgbuf leaked memory on destroy (68 allocations, 428800 bytes leaked).
68
*/
69
TEST_F(Death, unsent_operations)
70
{
71
const char FULLPATH0[] = "mountpoint/some_file.txt";
72
const char FULLPATH1[] = "mountpoint/other_file.txt";
73
const char RELPATH0[] = "some_file.txt";
74
const char RELPATH1[] = "other_file.txt";
75
pthread_t th0, th1;
76
ino_t ino0 = 42, ino1 = 43;
77
sem_t sem;
78
mode_t mode = S_IFREG | 0644;
79
80
sem_init(&sem, 0, 0);
81
82
EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH0)
83
.WillRepeatedly(Invoke(
84
ReturnImmediate([=](auto in __unused, auto& out) {
85
SET_OUT_HEADER_LEN(out, entry);
86
out.body.entry.attr.mode = mode;
87
out.body.entry.nodeid = ino0;
88
out.body.entry.attr.nlink = 1;
89
})));
90
EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH1)
91
.WillRepeatedly(Invoke(
92
ReturnImmediate([=](auto in __unused, auto& out) {
93
SET_OUT_HEADER_LEN(out, entry);
94
out.body.entry.attr.mode = mode;
95
out.body.entry.nodeid = ino1;
96
out.body.entry.attr.nlink = 1;
97
})));
98
99
EXPECT_CALL(*m_mock, process(
100
ResultOf([&](auto in) {
101
return (in.header.opcode == FUSE_OPEN);
102
}, Eq(true)),
103
_)
104
).WillOnce(Invoke([&](auto in __unused, auto &out __unused) {
105
sem_post(&sem);
106
pause();
107
}));
108
109
/*
110
* One thread's operation will be sent to the daemon and block, and the
111
* other's will be stuck in the message queue.
112
*/
113
ASSERT_EQ(0, pthread_create(&th0, NULL, open_th,
114
__DECONST(void*, FULLPATH0))) << strerror(errno);
115
ASSERT_EQ(0, pthread_create(&th1, NULL, open_th,
116
__DECONST(void*, FULLPATH1))) << strerror(errno);
117
118
/* Wait for the first thread to block */
119
sem_wait(&sem);
120
/* Give the second thread time to block */
121
nap();
122
123
m_mock->kill_daemon();
124
125
pthread_join(th0, NULL);
126
pthread_join(th1, NULL);
127
128
sem_destroy(&sem);
129
}
130
131
/*
132
* On unmount the kernel should send a FUSE_DESTROY operation. It should also
133
* send FUSE_FORGET operations for all inodes with lookup_count > 0.
134
*/
135
TEST_F(Destroy, ok)
136
{
137
const char FULLPATH[] = "mountpoint/some_file.txt";
138
const char RELPATH[] = "some_file.txt";
139
uint64_t ino = 42;
140
141
expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2);
142
expect_forget(ino, 2);
143
expect_destroy(0);
144
145
/*
146
* access(2) the file to force a lookup. Access it twice to double its
147
* lookup count.
148
*/
149
ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
150
ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
151
152
/*
153
* Unmount, triggering a FUSE_DESTROY and also causing a VOP_RECLAIM
154
* for every vnode on this mp, triggering FUSE_FORGET for each of them.
155
*/
156
m_mock->unmount();
157
}
158
159