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 <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 sa.sun_len = sizeof(FULLPATH); 126 err = connect(fd, (struct sockaddr*)&sa, sizeof(sa)); 127 if (err < 0) { 128 perror("connect"); 129 return (void*)(intptr_t)errno; 130 } 131 132 while (sent < msgsize) { 133 ssize_t r; 134 135 r = write(fd, MESSAGE + sent, msgsize - sent); 136 if (r < 0) 137 return (void*)(intptr_t)errno; 138 else 139 sent += r; 140 141 } 142 143 FuseTest::leak(fd); 144 return 0; 145 } 146 147 /* 148 * Reading and writing unix-domain sockets works. None of the I/O actually 149 * goes through FUSE. 150 */ 151 TEST_F(Socket, read_write) 152 { 153 mode_t mode = S_IFSOCK | 0755; 154 const int bufsize = 80; 155 char message[bufsize]; 156 struct sockaddr_un sa; 157 ssize_t recvd = 0, r; 158 uint64_t ino = 42; 159 int fd, connected; 160 Sequence seq; 161 162 EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) 163 .WillOnce(Invoke(ReturnErrno(ENOENT))); 164 EXPECT_CALL(*m_mock, process( 165 ResultOf([=](auto in) { 166 return (in.header.opcode == FUSE_MKNOD); 167 }, Eq(true)), 168 _) 169 ).InSequence(seq) 170 .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 171 SET_OUT_HEADER_LEN(out, entry); 172 out.body.entry.attr.mode = mode; 173 out.body.entry.nodeid = ino; 174 out.body.entry.entry_valid = UINT64_MAX; 175 out.body.entry.attr_valid = UINT64_MAX; 176 }))); 177 178 EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) 179 .InSequence(seq) 180 .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 181 SET_OUT_HEADER_LEN(out, entry); 182 out.body.entry.attr.mode = mode; 183 out.body.entry.nodeid = ino; 184 out.body.entry.attr.nlink = 1; 185 out.body.entry.attr_valid = UINT64_MAX; 186 out.body.entry.entry_valid = UINT64_MAX; 187 }))); 188 189 fd = socket(AF_UNIX, SOCK_STREAM, 0); 190 ASSERT_LE(0, fd) << strerror(errno); 191 sa.sun_family = AF_UNIX; 192 strlcpy(sa.sun_path, FULLPATH, sizeof(sa.sun_path)); 193 sa.sun_len = sizeof(FULLPATH); 194 ASSERT_EQ(0, bind(fd, (struct sockaddr*)&sa, sizeof(sa))) 195 << strerror(errno); 196 listen(fd, 5); 197 ASSERT_EQ(0, pthread_create(&m_child, NULL, socket_writer, NULL)) 198 << strerror(errno); 199 connected = accept(fd, 0, 0); 200 ASSERT_LE(0, connected) << strerror(errno); 201 202 while (recvd < msgsize) { 203 r = read(connected, message + recvd, bufsize - recvd); 204 ASSERT_LE(0, r) << strerror(errno); 205 ASSERT_LT(0, r) << "unexpected EOF"; 206 recvd += r; 207 } 208 ASSERT_STREQ(message, MESSAGE); 209 210 leak(fd); 211 } 212