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 31 extern "C" { 32 #include <sys/types.h> 33 #include <sys/socket.h> 34 #include <sys/un.h> 35 #include <fcntl.h> 36 } 37 38 #include "mockfs.hh" 39 #include "utils.hh" 40 41 using namespace testing; 42 43 const char FULLPATH[] = "mountpoint/some_fifo"; 44 const char RELPATH[] = "some_fifo"; 45 const char MESSAGE[] = "Hello, World!\n"; 46 const int msgsize = sizeof(MESSAGE); 47 48 class Fifo: public FuseTest { 49 public: 50 pthread_t m_child; 51 52 Fifo(): m_child(NULL) {}; 53 54 void TearDown() { 55 if (m_child != NULL) { 56 pthread_join(m_child, NULL); 57 } 58 FuseTest::TearDown(); 59 } 60 }; 61 62 class Socket: public Fifo {}; 63 64 /* Writer thread */ 65 static void* writer(void* arg) { 66 ssize_t sent = 0; 67 int fd; 68 69 fd = *(int*)arg; 70 while (sent < msgsize) { 71 ssize_t r; 72 73 r = write(fd, MESSAGE + sent, msgsize - sent); 74 if (r < 0) 75 return (void*)(intptr_t)errno; 76 else 77 sent += r; 78 79 } 80 return 0; 81 } 82 83 /* 84 * Reading and writing FIFOs works. None of the I/O actually goes through FUSE 85 */ 86 TEST_F(Fifo, read_write) 87 { 88 mode_t mode = S_IFIFO | 0755; 89 const int bufsize = 80; 90 char message[bufsize]; 91 ssize_t recvd = 0, r; 92 uint64_t ino = 42; 93 int fd; 94 95 expect_lookup(RELPATH, ino, mode, 0, 1); 96 97 fd = open(FULLPATH, O_RDWR); 98 ASSERT_LE(0, fd) << strerror(errno); 99 ASSERT_EQ(0, pthread_create(&m_child, NULL, writer, &fd)) 100 << strerror(errno); 101 while (recvd < msgsize) { 102 r = read(fd, message + recvd, bufsize - recvd); 103 ASSERT_LE(0, r) << strerror(errno); 104 ASSERT_LT(0, r) << "unexpected EOF"; 105 recvd += r; 106 } 107 ASSERT_STREQ(message, MESSAGE); 108 109 leak(fd); 110 } 111 112 /* Writer thread */ 113 static void* socket_writer(void* arg __unused) { 114 ssize_t sent = 0; 115 int fd, err; 116 struct sockaddr_un sa; 117 118 fd = socket(AF_UNIX, SOCK_STREAM, 0); 119 if (fd < 0) { 120 perror("socket"); 121 return (void*)(intptr_t)errno; 122 } 123 sa.sun_family = AF_UNIX; 124 strlcpy(sa.sun_path, FULLPATH, sizeof(sa.sun_path)); 125 err = connect(fd, (struct sockaddr*)&sa, sizeof(sa)); 126 if (err < 0) { 127 perror("connect"); 128 return (void*)(intptr_t)errno; 129 } 130 131 while (sent < msgsize) { 132 ssize_t r; 133 134 r = write(fd, MESSAGE + sent, msgsize - sent); 135 if (r < 0) 136 return (void*)(intptr_t)errno; 137 else 138 sent += r; 139 140 } 141 return 0; 142 } 143 144 /* 145 * Reading and writing unix-domain sockets works. None of the I/O actually 146 * goes through FUSE. 147 */ 148 TEST_F(Socket, read_write) 149 { 150 mode_t mode = S_IFSOCK | 0755; 151 const int bufsize = 80; 152 char message[bufsize]; 153 struct sockaddr_un sa; 154 ssize_t recvd = 0, r; 155 uint64_t ino = 42; 156 int fd, connected; 157 Sequence seq; 158 159 EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) 160 .WillOnce(Invoke(ReturnErrno(ENOENT))); 161 EXPECT_CALL(*m_mock, process( 162 ResultOf([=](auto in) { 163 return (in.header.opcode == FUSE_MKNOD); 164 }, Eq(true)), 165 _) 166 ).InSequence(seq) 167 .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 168 SET_OUT_HEADER_LEN(out, entry); 169 out.body.entry.attr.mode = mode; 170 out.body.entry.nodeid = ino; 171 out.body.entry.entry_valid = UINT64_MAX; 172 out.body.entry.attr_valid = UINT64_MAX; 173 }))); 174 175 EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) 176 .InSequence(seq) 177 .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 178 SET_OUT_HEADER_LEN(out, entry); 179 out.body.entry.attr.mode = mode; 180 out.body.entry.nodeid = ino; 181 out.body.entry.attr.nlink = 1; 182 out.body.entry.attr_valid = UINT64_MAX; 183 out.body.entry.entry_valid = UINT64_MAX; 184 }))); 185 186 fd = socket(AF_UNIX, SOCK_STREAM, 0); 187 ASSERT_LE(0, fd) << strerror(errno); 188 sa.sun_family = AF_UNIX; 189 strlcpy(sa.sun_path, FULLPATH, sizeof(sa.sun_path)); 190 ASSERT_EQ(0, bind(fd, (struct sockaddr*)&sa, sizeof(sa))) 191 << strerror(errno); 192 listen(fd, 5); 193 ASSERT_EQ(0, pthread_create(&m_child, NULL, socket_writer, NULL)) 194 << strerror(errno); 195 connected = accept(fd, 0, 0); 196 ASSERT_LE(0, connected) << strerror(errno); 197 198 while (recvd < msgsize) { 199 r = read(connected, message + recvd, bufsize - recvd); 200 ASSERT_LE(0, r) << strerror(errno); 201 ASSERT_LT(0, r) << "unexpected EOF"; 202 recvd += r; 203 } 204 ASSERT_STREQ(message, MESSAGE); 205 206 leak(fd); 207 } 208