Super User's BSD Cross Reference: /FreeBSD/tests/sys/fs/fusefs/open.cc

1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 * $FreeBSD$
31 */
32
33 extern "C" {
34#include <sys/wait.h>
35
36#include <fcntl.h>
37#include <semaphore.h>
38}
39
40#include "mockfs.hh"
41#include "utils.hh"
42
43 using namespace testing;
44
45 class Open: public FuseTest {
46
47 public:
48
49 /* Test an OK open of a file with the given flags */
50 void test_ok(int os_flags, int fuse_flags) {
51 const char FULLPATH[] = "mountpoint/some_file.txt";
52 const char RELPATH[] = "some_file.txt";
53 uint64_t ino = 42;
54 int fd;
55
56 FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
57 EXPECT_CALL(*m_mock, process(
58 ResultOf([=](auto in) {
59 return (in.header.opcode == FUSE_OPEN &&
60 in.body.open.flags == (uint32_t)fuse_flags &&
61 in.header.nodeid == ino);
62 }, Eq(true)),
63 _)
64 ).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto& out) {
65 out.header.len = sizeof(out.header);
66 SET_OUT_HEADER_LEN(out, open);
67 })));
68
69 fd = open(FULLPATH, os_flags);
70 ASSERT_LE(0, fd) << strerror(errno);
71 leak(fd);
72}
73};
74
75
76 /*
77 * fusefs(5) does not support I/O on device nodes (neither does UFS). But it
78 * shouldn't crash
79 */
80 TEST_F(Open, chr)
81{
82 const char FULLPATH[] = "mountpoint/zero";
83 const char RELPATH[] = "zero";
84 uint64_t ino = 42;
85
86 EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
87 .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
88 SET_OUT_HEADER_LEN(out, entry);
89 out.body.entry.attr.mode = S_IFCHR | 0644;
90 out.body.entry.nodeid = ino;
91 out.body.entry.attr.nlink = 1;
92 out.body.entry.attr_valid = UINT64_MAX;
93 out.body.entry.attr.rdev = 44; /* /dev/zero's rdev */
94 })));
95
96 ASSERT_EQ(-1, open(FULLPATH, O_RDONLY));
97 EXPECT_EQ(EOPNOTSUPP, errno);
98}
99
100 /*
101 * The fuse daemon fails the request with enoent. This usually indicates a
102 * race condition: some other FUSE client removed the file in between when the
103 * kernel checked for it with lookup and tried to open it
104 */
105 TEST_F(Open, enoent)
106{
107 const char FULLPATH[] = "mountpoint/some_file.txt";
108 const char RELPATH[] = "some_file.txt";
109 uint64_t ino = 42;
110 sem_t sem;
111
112 ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
113
114 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
115 EXPECT_CALL(*m_mock, process(
116 ResultOf([=](auto in) {
117 return (in.header.opcode == FUSE_OPEN &&
118 in.header.nodeid == ino);
119 }, Eq(true)),
120 _)
121 ).WillOnce(Invoke(ReturnErrno(ENOENT)));
122 // Since FUSE_OPEN returns ENOENT, the kernel will reclaim the vnode
123 // and send a FUSE_FORGET
124 expect_forget(ino, 1, &sem);
125
126 ASSERT_EQ(-1, open(FULLPATH, O_RDONLY));
127 EXPECT_EQ(ENOENT, errno);
128
129 sem_wait(&sem);
130 sem_destroy(&sem);
131}
132
133 /*
134 * The daemon is responsible for checking file permissions (unless the
135 * default_permissions mount option was used)
136 */
137 TEST_F(Open, eperm)
138{
139 const char FULLPATH[] = "mountpoint/some_file.txt";
140 const char RELPATH[] = "some_file.txt";
141 uint64_t ino = 42;
142
143 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
144 EXPECT_CALL(*m_mock, process(
145 ResultOf([=](auto in) {
146 return (in.header.opcode == FUSE_OPEN &&
147 in.header.nodeid == ino);
148 }, Eq(true)),
149 _)
150 ).WillOnce(Invoke(ReturnErrno(EPERM)));
151 ASSERT_EQ(-1, open(FULLPATH, O_RDONLY));
152 EXPECT_EQ(EPERM, errno);
153}
154
155 /*
156 * fusefs must issue multiple FUSE_OPEN operations if clients with different
157 * credentials open the same file, even if they use the same mode. This is
158 * necessary so that the daemon can validate each set of credentials.
159 */
160 TEST_F(Open, multiple_creds)
161{
162 const static char FULLPATH[] = "mountpoint/some_file.txt";
163 const static char RELPATH[] = "some_file.txt";
164 int fd1, status;
165 const static uint64_t ino = 42;
166 const static uint64_t fh0 = 100, fh1 = 200;
167
168 /* Fork a child to open the file with different credentials */
169 fork(false, &status, [&] {
170
171 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2);
172 EXPECT_CALL(*m_mock, process(
173 ResultOf([=](auto in) {
174 return (in.header.opcode == FUSE_OPEN &&
175 in.header.pid == (uint32_t)getpid() &&
176 in.header.nodeid == ino);
177 }, Eq(true)),
178 _)
179 ).WillOnce(Invoke(
180 ReturnImmediate([](auto in __unused, auto& out) {
181 out.body.open.fh = fh0;
182 out.header.len = sizeof(out.header);
183 SET_OUT_HEADER_LEN(out, open);
184 })));
185
186 EXPECT_CALL(*m_mock, process(
187 ResultOf([=](auto in) {
188 return (in.header.opcode == FUSE_OPEN &&
189 in.header.pid != (uint32_t)getpid() &&
190 in.header.nodeid == ino);
191 }, Eq(true)),
192 _)
193 ).WillOnce(Invoke(
194 ReturnImmediate([](auto in __unused, auto& out) {
195 out.body.open.fh = fh1;
196 out.header.len = sizeof(out.header);
197 SET_OUT_HEADER_LEN(out, open);
198 })));
199 expect_flush(ino, 2, ReturnErrno(0));
200 expect_release(ino, fh0);
201 expect_release(ino, fh1);
202
203 fd1 = open(FULLPATH, O_RDONLY);
204 ASSERT_LE(0, fd1) << strerror(errno);
205 }, [] {
206 int fd0;
207
208 fd0 = open(FULLPATH, O_RDONLY);
209 if (fd0 < 0) {
210 perror("open");
211 return(1);
212 }
213 return 0;
214 }
215 );
216 ASSERT_EQ(0, WEXITSTATUS(status));
217
218 close(fd1);
219}
220
221 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236340 */
222 TEST_F(Open, DISABLED_o_append)
223{
224 test_ok(O_WRONLY | O_APPEND, O_WRONLY | O_APPEND);
225}
226
227 /* The kernel is supposed to filter out this flag */
228 TEST_F(Open, o_creat)
229{
230 test_ok(O_WRONLY | O_CREAT, O_WRONLY);
231}
232
233 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236340 */
234 TEST_F(Open, DISABLED_o_direct)
235{
236 test_ok(O_WRONLY | O_DIRECT, O_WRONLY | O_DIRECT);
237}
238
239 /* The kernel is supposed to filter out this flag */
240 TEST_F(Open, o_excl)
241{
242 test_ok(O_WRONLY | O_EXCL, O_WRONLY);
243}
244
245 TEST_F(Open, o_exec)
246{
247 test_ok(O_EXEC, O_EXEC);
248}
249
250 /* The kernel is supposed to filter out this flag */
251 TEST_F(Open, o_noctty)
252{
253 test_ok(O_WRONLY | O_NOCTTY, O_WRONLY);
254}
255
256 TEST_F(Open, o_rdonly)
257{
258 test_ok(O_RDONLY, O_RDONLY);
259}
260
261 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236340 */
262 TEST_F(Open, DISABLED_o_trunc)
263{
264 test_ok(O_WRONLY | O_TRUNC, O_WRONLY | O_TRUNC);
265}
266
267 TEST_F(Open, o_wronly)
268{
269 test_ok(O_WRONLY, O_WRONLY);
270}
271
272 TEST_F(Open, o_rdwr)
273{
274 test_ok(O_RDWR, O_RDWR);
275}
276
277 

AltStyle によって変換されたページ (->オリジナル) /