19821f1d3SAlan Somers /*- 29821f1d3SAlan Somers * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 39821f1d3SAlan Somers * 49821f1d3SAlan Somers * Copyright (c) 2019 The FreeBSD Foundation 59821f1d3SAlan Somers * 69821f1d3SAlan Somers * This software was developed by BFF Storage Systems, LLC under sponsorship 79821f1d3SAlan Somers * from the FreeBSD Foundation. 89821f1d3SAlan Somers * 99821f1d3SAlan Somers * Redistribution and use in source and binary forms, with or without 109821f1d3SAlan Somers * modification, are permitted provided that the following conditions 119821f1d3SAlan Somers * are met: 129821f1d3SAlan Somers * 1. Redistributions of source code must retain the above copyright 139821f1d3SAlan Somers * notice, this list of conditions and the following disclaimer. 149821f1d3SAlan Somers * 2. Redistributions in binary form must reproduce the above copyright 159821f1d3SAlan Somers * notice, this list of conditions and the following disclaimer in the 169821f1d3SAlan Somers * documentation and/or other materials provided with the distribution. 179821f1d3SAlan Somers * 189821f1d3SAlan Somers * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 199821f1d3SAlan Somers * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 209821f1d3SAlan Somers * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 219821f1d3SAlan Somers * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 229821f1d3SAlan Somers * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 239821f1d3SAlan Somers * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 249821f1d3SAlan Somers * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 259821f1d3SAlan Somers * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 269821f1d3SAlan Somers * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 279821f1d3SAlan Somers * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 289821f1d3SAlan Somers * SUCH DAMAGE. 291fa8ebfbSAlan Somers * 301fa8ebfbSAlan Somers * $FreeBSD$ 319821f1d3SAlan Somers */ 329821f1d3SAlan Somers 339821f1d3SAlan Somers extern "C" { 34a1542146SAlan Somers #include <sys/wait.h> 3527537990SAlan Somers 369821f1d3SAlan Somers #include <fcntl.h> 3727537990SAlan Somers #include <semaphore.h> 389821f1d3SAlan Somers } 399821f1d3SAlan Somers 409821f1d3SAlan Somers #include "mockfs.hh" 419821f1d3SAlan Somers #include "utils.hh" 429821f1d3SAlan Somers 439821f1d3SAlan Somers using namespace testing; 449821f1d3SAlan Somers 459821f1d3SAlan Somers class Open: public FuseTest { 469821f1d3SAlan Somers 479821f1d3SAlan Somers public: 489821f1d3SAlan Somers 499821f1d3SAlan Somers /* Test an OK open of a file with the given flags */ 509821f1d3SAlan Somers void test_ok(int os_flags, int fuse_flags) { 519821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 529821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 539821f1d3SAlan Somers uint64_t ino = 42; 549821f1d3SAlan Somers int fd; 559821f1d3SAlan Somers 569821f1d3SAlan Somers FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 579821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 589821f1d3SAlan Somers ResultOf([=](auto in) { 5929edc611SAlan Somers return (in.header.opcode == FUSE_OPEN && 6029edc611SAlan Somers in.body.open.flags == (uint32_t)fuse_flags && 6129edc611SAlan Somers in.header.nodeid == ino); 629821f1d3SAlan Somers }, Eq(true)), 639821f1d3SAlan Somers _) 6429edc611SAlan Somers ).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto& out) { 6529edc611SAlan Somers out.header.len = sizeof(out.header); 669821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, open); 679821f1d3SAlan Somers }))); 689821f1d3SAlan Somers 699821f1d3SAlan Somers fd = open(FULLPATH, os_flags); 70d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 717fc0921dSAlan Somers leak(fd); 729821f1d3SAlan Somers } 739821f1d3SAlan Somers }; 749821f1d3SAlan Somers 759821f1d3SAlan Somers 76*7124d2bcSAlan Somers class OpenNoOpenSupport: public FuseTest { 77*7124d2bcSAlan Somers virtual void SetUp() { 78*7124d2bcSAlan Somers m_init_flags = FUSE_NO_OPEN_SUPPORT; 79*7124d2bcSAlan Somers FuseTest::SetUp(); 80*7124d2bcSAlan Somers } 81*7124d2bcSAlan Somers }; 82*7124d2bcSAlan Somers 839821f1d3SAlan Somers /* 84bf4d7084SAlan Somers * fusefs(5) does not support I/O on device nodes (neither does UFS). But it 85bf4d7084SAlan Somers * shouldn't crash 86bf4d7084SAlan Somers */ 87bf4d7084SAlan Somers TEST_F(Open, chr) 88bf4d7084SAlan Somers { 89bf4d7084SAlan Somers const char FULLPATH[] = "mountpoint/zero"; 90bf4d7084SAlan Somers const char RELPATH[] = "zero"; 91bf4d7084SAlan Somers uint64_t ino = 42; 92bf4d7084SAlan Somers 93a34cdd26SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) 9429edc611SAlan Somers .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 95bf4d7084SAlan Somers SET_OUT_HEADER_LEN(out, entry); 9629edc611SAlan Somers out.body.entry.attr.mode = S_IFCHR | 0644; 9729edc611SAlan Somers out.body.entry.nodeid = ino; 9829edc611SAlan Somers out.body.entry.attr.nlink = 1; 9929edc611SAlan Somers out.body.entry.attr_valid = UINT64_MAX; 10029edc611SAlan Somers out.body.entry.attr.rdev = 44; /* /dev/zero's rdev */ 101bf4d7084SAlan Somers }))); 102bf4d7084SAlan Somers 103bf4d7084SAlan Somers ASSERT_EQ(-1, open(FULLPATH, O_RDONLY)); 104bf4d7084SAlan Somers EXPECT_EQ(EOPNOTSUPP, errno); 105bf4d7084SAlan Somers } 106bf4d7084SAlan Somers 107bf4d7084SAlan Somers /* 1089821f1d3SAlan Somers * The fuse daemon fails the request with enoent. This usually indicates a 1099821f1d3SAlan Somers * race condition: some other FUSE client removed the file in between when the 1109821f1d3SAlan Somers * kernel checked for it with lookup and tried to open it 1119821f1d3SAlan Somers */ 1129821f1d3SAlan Somers TEST_F(Open, enoent) 1139821f1d3SAlan Somers { 1149821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1159821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 1169821f1d3SAlan Somers uint64_t ino = 42; 11727537990SAlan Somers sem_t sem; 11827537990SAlan Somers 11927537990SAlan Somers ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno); 1209821f1d3SAlan Somers 1219821f1d3SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 1229821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 1239821f1d3SAlan Somers ResultOf([=](auto in) { 12429edc611SAlan Somers return (in.header.opcode == FUSE_OPEN && 12529edc611SAlan Somers in.header.nodeid == ino); 1269821f1d3SAlan Somers }, Eq(true)), 1279821f1d3SAlan Somers _) 1289821f1d3SAlan Somers ).WillOnce(Invoke(ReturnErrno(ENOENT))); 12927537990SAlan Somers // Since FUSE_OPEN returns ENOENT, the kernel will reclaim the vnode 13027537990SAlan Somers // and send a FUSE_FORGET 13127537990SAlan Somers expect_forget(ino, 1, &sem); 13227537990SAlan Somers 1338e765737SAlan Somers ASSERT_EQ(-1, open(FULLPATH, O_RDONLY)); 1349821f1d3SAlan Somers EXPECT_EQ(ENOENT, errno); 13527537990SAlan Somers 13627537990SAlan Somers sem_wait(&sem); 13727537990SAlan Somers sem_destroy(&sem); 1389821f1d3SAlan Somers } 1399821f1d3SAlan Somers 1409821f1d3SAlan Somers /* 1419821f1d3SAlan Somers * The daemon is responsible for checking file permissions (unless the 1429821f1d3SAlan Somers * default_permissions mount option was used) 1439821f1d3SAlan Somers */ 1449821f1d3SAlan Somers TEST_F(Open, eperm) 1459821f1d3SAlan Somers { 1469821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1479821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 1489821f1d3SAlan Somers uint64_t ino = 42; 1499821f1d3SAlan Somers 1509821f1d3SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 1519821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 1529821f1d3SAlan Somers ResultOf([=](auto in) { 15329edc611SAlan Somers return (in.header.opcode == FUSE_OPEN && 15429edc611SAlan Somers in.header.nodeid == ino); 1559821f1d3SAlan Somers }, Eq(true)), 1569821f1d3SAlan Somers _) 1579821f1d3SAlan Somers ).WillOnce(Invoke(ReturnErrno(EPERM))); 1588e765737SAlan Somers ASSERT_EQ(-1, open(FULLPATH, O_RDONLY)); 1599821f1d3SAlan Somers EXPECT_EQ(EPERM, errno); 1609821f1d3SAlan Somers } 1619821f1d3SAlan Somers 16242d50d16SAlan Somers /* 16342d50d16SAlan Somers * fusefs must issue multiple FUSE_OPEN operations if clients with different 16442d50d16SAlan Somers * credentials open the same file, even if they use the same mode. This is 16542d50d16SAlan Somers * necessary so that the daemon can validate each set of credentials. 16642d50d16SAlan Somers */ 167f8d4af10SAlan Somers TEST_F(Open, multiple_creds) 16842d50d16SAlan Somers { 16942d50d16SAlan Somers const static char FULLPATH[] = "mountpoint/some_file.txt"; 17042d50d16SAlan Somers const static char RELPATH[] = "some_file.txt"; 171a1542146SAlan Somers int fd1, status; 17242d50d16SAlan Somers const static uint64_t ino = 42; 17342d50d16SAlan Somers const static uint64_t fh0 = 100, fh1 = 200; 17442d50d16SAlan Somers 17542d50d16SAlan Somers /* Fork a child to open the file with different credentials */ 176a1542146SAlan Somers fork(false, &status, [&] { 17742d50d16SAlan Somers 17842d50d16SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2); 17942d50d16SAlan Somers EXPECT_CALL(*m_mock, process( 18042d50d16SAlan Somers ResultOf([=](auto in) { 18129edc611SAlan Somers return (in.header.opcode == FUSE_OPEN && 18229edc611SAlan Somers in.header.pid == (uint32_t)getpid() && 18329edc611SAlan Somers in.header.nodeid == ino); 18442d50d16SAlan Somers }, Eq(true)), 18542d50d16SAlan Somers _) 18642d50d16SAlan Somers ).WillOnce(Invoke( 18729edc611SAlan Somers ReturnImmediate([](auto in __unused, auto& out) { 18829edc611SAlan Somers out.body.open.fh = fh0; 18929edc611SAlan Somers out.header.len = sizeof(out.header); 19042d50d16SAlan Somers SET_OUT_HEADER_LEN(out, open); 19142d50d16SAlan Somers }))); 19242d50d16SAlan Somers 19342d50d16SAlan Somers EXPECT_CALL(*m_mock, process( 19442d50d16SAlan Somers ResultOf([=](auto in) { 19529edc611SAlan Somers return (in.header.opcode == FUSE_OPEN && 19629edc611SAlan Somers in.header.pid != (uint32_t)getpid() && 19729edc611SAlan Somers in.header.nodeid == ino); 19842d50d16SAlan Somers }, Eq(true)), 19942d50d16SAlan Somers _) 20042d50d16SAlan Somers ).WillOnce(Invoke( 20129edc611SAlan Somers ReturnImmediate([](auto in __unused, auto& out) { 20229edc611SAlan Somers out.body.open.fh = fh1; 20329edc611SAlan Somers out.header.len = sizeof(out.header); 20442d50d16SAlan Somers SET_OUT_HEADER_LEN(out, open); 20542d50d16SAlan Somers }))); 2069f10f423SAlan Somers expect_flush(ino, 2, ReturnErrno(0)); 20742d50d16SAlan Somers expect_release(ino, fh0); 20842d50d16SAlan Somers expect_release(ino, fh1); 20942d50d16SAlan Somers 21042d50d16SAlan Somers fd1 = open(FULLPATH, O_RDONLY); 211d2621689SAlan Somers ASSERT_LE(0, fd1) << strerror(errno); 21242d50d16SAlan Somers }, [] { 21342d50d16SAlan Somers int fd0; 21442d50d16SAlan Somers 21542d50d16SAlan Somers fd0 = open(FULLPATH, O_RDONLY); 21642d50d16SAlan Somers if (fd0 < 0) { 21742d50d16SAlan Somers perror("open"); 21842d50d16SAlan Somers return(1); 21942d50d16SAlan Somers } 22042d50d16SAlan Somers return 0; 22142d50d16SAlan Somers } 22242d50d16SAlan Somers ); 223a1542146SAlan Somers ASSERT_EQ(0, WEXITSTATUS(status)); 22442d50d16SAlan Somers 22542d50d16SAlan Somers close(fd1); 22642d50d16SAlan Somers } 22742d50d16SAlan Somers 2289821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236340 */ 2299821f1d3SAlan Somers TEST_F(Open, DISABLED_o_append) 2309821f1d3SAlan Somers { 2319821f1d3SAlan Somers test_ok(O_WRONLY | O_APPEND, O_WRONLY | O_APPEND); 2329821f1d3SAlan Somers } 2339821f1d3SAlan Somers 2349821f1d3SAlan Somers /* The kernel is supposed to filter out this flag */ 2359821f1d3SAlan Somers TEST_F(Open, o_creat) 2369821f1d3SAlan Somers { 2379821f1d3SAlan Somers test_ok(O_WRONLY | O_CREAT, O_WRONLY); 2389821f1d3SAlan Somers } 2399821f1d3SAlan Somers 2409821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236340 */ 2419821f1d3SAlan Somers TEST_F(Open, DISABLED_o_direct) 2429821f1d3SAlan Somers { 2439821f1d3SAlan Somers test_ok(O_WRONLY | O_DIRECT, O_WRONLY | O_DIRECT); 2449821f1d3SAlan Somers } 2459821f1d3SAlan Somers 2469821f1d3SAlan Somers /* The kernel is supposed to filter out this flag */ 2479821f1d3SAlan Somers TEST_F(Open, o_excl) 2489821f1d3SAlan Somers { 2499821f1d3SAlan Somers test_ok(O_WRONLY | O_EXCL, O_WRONLY); 2509821f1d3SAlan Somers } 2519821f1d3SAlan Somers 252363a7416SAlan Somers TEST_F(Open, o_exec) 2539821f1d3SAlan Somers { 2549821f1d3SAlan Somers test_ok(O_EXEC, O_EXEC); 2559821f1d3SAlan Somers } 2569821f1d3SAlan Somers 2579821f1d3SAlan Somers /* The kernel is supposed to filter out this flag */ 2589821f1d3SAlan Somers TEST_F(Open, o_noctty) 2599821f1d3SAlan Somers { 2609821f1d3SAlan Somers test_ok(O_WRONLY | O_NOCTTY, O_WRONLY); 2619821f1d3SAlan Somers } 2629821f1d3SAlan Somers 2639821f1d3SAlan Somers TEST_F(Open, o_rdonly) 2649821f1d3SAlan Somers { 2659821f1d3SAlan Somers test_ok(O_RDONLY, O_RDONLY); 2669821f1d3SAlan Somers } 2679821f1d3SAlan Somers 2689821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236340 */ 2699821f1d3SAlan Somers TEST_F(Open, DISABLED_o_trunc) 2709821f1d3SAlan Somers { 2719821f1d3SAlan Somers test_ok(O_WRONLY | O_TRUNC, O_WRONLY | O_TRUNC); 2729821f1d3SAlan Somers } 2739821f1d3SAlan Somers 2749821f1d3SAlan Somers TEST_F(Open, o_wronly) 2759821f1d3SAlan Somers { 2769821f1d3SAlan Somers test_ok(O_WRONLY, O_WRONLY); 2779821f1d3SAlan Somers } 2789821f1d3SAlan Somers 2799821f1d3SAlan Somers TEST_F(Open, o_rdwr) 2809821f1d3SAlan Somers { 2819821f1d3SAlan Somers test_ok(O_RDWR, O_RDWR); 2829821f1d3SAlan Somers } 2839821f1d3SAlan Somers 284*7124d2bcSAlan Somers /* 285*7124d2bcSAlan Somers * Without FUSE_NO_OPEN_SUPPORT, returning ENOSYS is an error 286*7124d2bcSAlan Somers */ 287*7124d2bcSAlan Somers TEST_F(Open, enosys) 288*7124d2bcSAlan Somers { 289*7124d2bcSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 290*7124d2bcSAlan Somers const char RELPATH[] = "some_file.txt"; 291*7124d2bcSAlan Somers uint64_t ino = 42; 292*7124d2bcSAlan Somers int fd; 293*7124d2bcSAlan Somers 294*7124d2bcSAlan Somers FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 295*7124d2bcSAlan Somers EXPECT_CALL(*m_mock, process( 296*7124d2bcSAlan Somers ResultOf([=](auto in) { 297*7124d2bcSAlan Somers return (in.header.opcode == FUSE_OPEN && 298*7124d2bcSAlan Somers in.body.open.flags == (uint32_t)O_RDONLY && 299*7124d2bcSAlan Somers in.header.nodeid == ino); 300*7124d2bcSAlan Somers }, Eq(true)), 301*7124d2bcSAlan Somers _) 302*7124d2bcSAlan Somers ).Times(1) 303*7124d2bcSAlan Somers .WillOnce(Invoke(ReturnErrno(ENOSYS))); 304*7124d2bcSAlan Somers 305*7124d2bcSAlan Somers fd = open(FULLPATH, O_RDONLY); 306*7124d2bcSAlan Somers ASSERT_EQ(-1, fd) << strerror(errno); 307*7124d2bcSAlan Somers EXPECT_EQ(ENOSYS, errno); 308*7124d2bcSAlan Somers } 309*7124d2bcSAlan Somers 310*7124d2bcSAlan Somers /* 311*7124d2bcSAlan Somers * If a fuse server sets FUSE_NO_OPEN_SUPPORT and returns ENOSYS to a 312*7124d2bcSAlan Somers * FUSE_OPEN, then it and subsequent FUSE_OPEN and FUSE_RELEASE operations will 313*7124d2bcSAlan Somers * also succeed automatically without being sent to the server. 314*7124d2bcSAlan Somers */ 315*7124d2bcSAlan Somers TEST_F(OpenNoOpenSupport, enosys) 316*7124d2bcSAlan Somers { 317*7124d2bcSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 318*7124d2bcSAlan Somers const char RELPATH[] = "some_file.txt"; 319*7124d2bcSAlan Somers uint64_t ino = 42; 320*7124d2bcSAlan Somers int fd; 321*7124d2bcSAlan Somers 322*7124d2bcSAlan Somers FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2); 323*7124d2bcSAlan Somers EXPECT_CALL(*m_mock, process( 324*7124d2bcSAlan Somers ResultOf([=](auto in) { 325*7124d2bcSAlan Somers return (in.header.opcode == FUSE_OPEN && 326*7124d2bcSAlan Somers in.body.open.flags == (uint32_t)O_RDONLY && 327*7124d2bcSAlan Somers in.header.nodeid == ino); 328*7124d2bcSAlan Somers }, Eq(true)), 329*7124d2bcSAlan Somers _) 330*7124d2bcSAlan Somers ).Times(1) 331*7124d2bcSAlan Somers .WillOnce(Invoke(ReturnErrno(ENOSYS))); 332*7124d2bcSAlan Somers expect_flush(ino, 1, ReturnErrno(ENOSYS)); 333*7124d2bcSAlan Somers 334*7124d2bcSAlan Somers fd = open(FULLPATH, O_RDONLY); 335*7124d2bcSAlan Somers ASSERT_LE(0, fd) << strerror(errno); 336*7124d2bcSAlan Somers close(fd); 337*7124d2bcSAlan Somers 338*7124d2bcSAlan Somers fd = open(FULLPATH, O_RDONLY); 339*7124d2bcSAlan Somers ASSERT_LE(0, fd) << strerror(errno); 340*7124d2bcSAlan Somers 341*7124d2bcSAlan Somers leak(fd); 342*7124d2bcSAlan Somers } 343