gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/sentry/fsimpl/fuse/dev_test.go (about) 1 // Copyright 2020 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package fuse 16 17 import ( 18 "fmt" 19 "math/rand" 20 "testing" 21 22 "gvisor.dev/gvisor/pkg/abi/linux" 23 "gvisor.dev/gvisor/pkg/errors/linuxerr" 24 "gvisor.dev/gvisor/pkg/marshal/primitive" 25 "gvisor.dev/gvisor/pkg/sentry/fsimpl/testutil" 26 "gvisor.dev/gvisor/pkg/sentry/kernel" 27 "gvisor.dev/gvisor/pkg/sentry/kernel/auth" 28 "gvisor.dev/gvisor/pkg/sentry/vfs" 29 "gvisor.dev/gvisor/pkg/usermem" 30 "gvisor.dev/gvisor/pkg/waiter" 31 ) 32 33 // echoTestOpcode is the Opcode used during testing. The server used in tests 34 // will simply echo the payload back with the appropriate headers. 35 const echoTestOpcode linux.FUSEOpcode = 1000 36 37 // TestFUSECommunication tests that the communication layer between the Sentry and the 38 // FUSE server daemon works as expected. 39 func TestFUSECommunication(t *testing.T) { 40 s := setup(t) 41 defer s.Destroy() 42 43 k := kernel.KernelFromContext(s.Ctx) 44 creds := auth.CredentialsFromContext(s.Ctx) 45 46 // Create test cases with different number of concurrent clients and servers. 47 testCases := []struct { 48 Name string 49 NumClients int 50 NumServers int 51 MaxActiveRequests uint64 52 }{ 53 { 54 Name: "SingleClientSingleServer", 55 NumClients: 1, 56 NumServers: 1, 57 MaxActiveRequests: maxActiveRequestsDefault, 58 }, 59 { 60 Name: "SingleClientMultipleServers", 61 NumClients: 1, 62 NumServers: 10, 63 MaxActiveRequests: maxActiveRequestsDefault, 64 }, 65 { 66 Name: "MultipleClientsSingleServer", 67 NumClients: 10, 68 NumServers: 1, 69 MaxActiveRequests: maxActiveRequestsDefault, 70 }, 71 { 72 Name: "MultipleClientsMultipleServers", 73 NumClients: 10, 74 NumServers: 10, 75 MaxActiveRequests: maxActiveRequestsDefault, 76 }, 77 { 78 Name: "RequestCapacityFull", 79 NumClients: 10, 80 NumServers: 1, 81 MaxActiveRequests: 1, 82 }, 83 { 84 Name: "RequestCapacityContinuouslyFull", 85 NumClients: 100, 86 NumServers: 2, 87 MaxActiveRequests: 2, 88 }, 89 } 90 91 for _, testCase := range testCases { 92 t.Run(testCase.Name, func(t *testing.T) { 93 conn, fd, err := newTestConnection(s, testCase.MaxActiveRequests) 94 if err != nil { 95 t.Fatalf("newTestConnection: %v", err) 96 } 97 98 clientsDone := make([]chan struct{}, testCase.NumClients) 99 serversDone := make([]chan struct{}, testCase.NumServers) 100 serversKill := make([]chan struct{}, testCase.NumServers) 101 102 // FUSE clients. 103 for i := 0; i < testCase.NumClients; i++ { 104 clientsDone[i] = make(chan struct{}) 105 go func(i int) { 106 fuseClientRun(t, s, k, conn, creds, uint32(i), uint64(i), clientsDone[i]) 107 }(i) 108 } 109 110 // FUSE servers. 111 for j := 0; j < testCase.NumServers; j++ { 112 serversDone[j] = make(chan struct{}) 113 serversKill[j] = make(chan struct{}, 1) // The kill command shouldn't block. 114 go func(j int) { 115 fuseServerRun(t, s, k, fd, serversDone[j], serversKill[j]) 116 }(j) 117 } 118 119 // Tear down. 120 // 121 // Make sure all the clients are done. 122 for i := 0; i < testCase.NumClients; i++ { 123 <-clientsDone[i] 124 } 125 126 // Kill any server that is potentially waiting. 127 for j := 0; j < testCase.NumServers; j++ { 128 serversKill[j] <- struct{}{} 129 } 130 131 // Make sure all the servers are done. 132 for j := 0; j < testCase.NumServers; j++ { 133 <-serversDone[j] 134 } 135 }) 136 } 137 } 138 139 func TestReuseFd(t *testing.T) { 140 s := setup(t) 141 defer s.Destroy() 142 _, fd, err := newTestConnection(s, maxActiveRequestsDefault) 143 if err != nil { 144 t.Fatalf("newTestConnection: %v", err) 145 } 146 fs1, err := newTestFilesystem(s, fd, maxActiveRequestsDefault) 147 if err != nil { 148 t.Fatalf("newTestFilesystem: %v", err) 149 } 150 defer fs1.Release(s.Ctx) 151 fs2, err := newTestFilesystem(s, fd, maxActiveRequestsDefault) 152 if err != nil { 153 t.Fatalf("newTestFilesystem: %v", err) 154 } 155 defer fs2.Release(s.Ctx) 156 if fs1.conn != fs2.conn { 157 t.Errorf("second fs connection = %v, want = %v", fs2.conn, fs1.conn) 158 } 159 } 160 161 // CallTest makes a request to the server and blocks the invoking 162 // goroutine until a server responds with a response. Doesn't block 163 // a kernel.Task. Analogous to Connection.Call but used for testing. 164 func CallTest(conn *connection, t *kernel.Task, r *Request, i uint32) (*Response, error) { 165 conn.fd.mu.Lock() 166 167 // Wait until we're certain that a new request can be processed. 168 for conn.fd.numActiveRequests == conn.maxActiveRequests { 169 conn.fd.mu.Unlock() 170 select { 171 case <-conn.fd.fullQueueCh: 172 } 173 conn.fd.mu.Lock() 174 } 175 176 fut, err := conn.callFutureLocked(r) // No task given. 177 conn.fd.mu.Unlock() 178 179 if err != nil { 180 return nil, err 181 } 182 183 // Resolve the response. 184 // 185 // Block without a task. 186 select { 187 case <-fut.ch: 188 } 189 190 // A response is ready. Resolve and return it. 191 return fut.getResponse(), nil 192 } 193 194 // ReadTest is analogous to vfs.FileDescription.Read and reads from the FUSE 195 // device. However, it does so by - not blocking the task that is calling - and 196 // instead just waits on a channel. The behaviour is essentially the same as 197 // DeviceFD.Read except it guarantees that the task is not blocked. 198 func ReadTest(serverTask *kernel.Task, fd *vfs.FileDescription, inIOseq usermem.IOSequence, killServer chan struct{}) (int64, bool, error) { 199 var err error 200 var n, total int64 201 202 dev := fd.Impl().(*DeviceFD) 203 204 // Register for notifications. 205 w, ch := waiter.NewChannelEntry(waiter.ReadableEvents) 206 dev.EventRegister(&w) 207 for { 208 // Issue the request and break out if it completes with anything other than 209 // "would block". 210 n, err = dev.Read(serverTask, inIOseq, vfs.ReadOptions{}) 211 total += n 212 if err != linuxerr.ErrWouldBlock { 213 break 214 } 215 216 // Wait for a notification that we should retry. 217 // Emulate the blocking for when no requests are available 218 select { 219 case <-ch: 220 case <-killServer: 221 // Server killed by the main program. 222 return 0, true, nil 223 } 224 } 225 226 dev.EventUnregister(&w) 227 return total, false, err 228 } 229 230 // fuseClientRun emulates all the actions of a normal FUSE request. It creates 231 // a header, a payload, calls the server, waits for the response, and processes 232 // the response. 233 func fuseClientRun(t *testing.T, s *testutil.System, k *kernel.Kernel, conn *connection, creds *auth.Credentials, pid uint32, inode uint64, clientDone chan struct{}) { 234 defer func() { 235 if !t.Failed() { 236 clientDone <- struct{}{} 237 } 238 }() 239 240 tc := k.NewThreadGroup(k.RootPIDNamespace(), kernel.NewSignalHandlers(), linux.SIGCHLD, k.GlobalInit().Limits()) 241 clientTask, err := testutil.CreateTask(s.Ctx, fmt.Sprintf("fuse-client-%v", pid), tc, s.MntNs, s.Root, s.Root) 242 if err != nil { 243 t.Fatal(err) 244 } 245 246 testObj := primitive.Uint32(rand.Uint32()) 247 req := conn.NewRequest(creds, pid, inode, echoTestOpcode, &testObj) 248 249 // Queue up a request. 250 // Analogous to Call except it doesn't block on the task. 251 resp, err := CallTest(conn, clientTask, req, pid) 252 if err != nil { 253 t.Fatalf("CallTaskNonBlock failed: %v", err) 254 } 255 256 if err = resp.Error(); err != nil { 257 t.Fatalf("Server responded with an error: %v", err) 258 } 259 260 var respTestPayload primitive.Uint32 261 if err := resp.UnmarshalPayload(&respTestPayload); err != nil { 262 t.Fatalf("Unmarshalling payload error: %v", err) 263 } 264 265 if resp.hdr.Unique != req.hdr.Unique { 266 t.Fatalf("got response for another request. Expected response for req %v but got response for req %v", 267 req.hdr.Unique, resp.hdr.Unique) 268 } 269 270 if respTestPayload != testObj { 271 t.Fatalf("read incorrect data. Data expected: %d, but got %d", testObj, respTestPayload) 272 } 273 274 } 275 276 // fuseServerRun creates a task and emulates all the actions of a simple FUSE server 277 // that simply reads a request and echos the same struct back as a response using the 278 // appropriate headers. 279 func fuseServerRun(t *testing.T, s *testutil.System, k *kernel.Kernel, fd *vfs.FileDescription, serverDone, killServer chan struct{}) { 280 defer func() { 281 if !t.Failed() { 282 serverDone <- struct{}{} 283 } 284 }() 285 286 // Create the tasks that the server will be using. 287 tc := k.NewThreadGroup(k.RootPIDNamespace(), kernel.NewSignalHandlers(), linux.SIGCHLD, k.GlobalInit().Limits()) 288 289 var readPayload primitive.Uint32 290 serverTask, err := testutil.CreateTask(s.Ctx, "fuse-server", tc, s.MntNs, s.Root, s.Root) 291 if err != nil { 292 t.Fatal(err) 293 } 294 295 // Read the request. 296 for { 297 payloadLen := uint32(readPayload.SizeBytes()) 298 299 // The read buffer must meet some certain size criteria. 300 buffSize := linux.SizeOfFUSEHeaderIn + payloadLen 301 if buffSize < linux.FUSE_MIN_READ_BUFFER { 302 buffSize = linux.FUSE_MIN_READ_BUFFER 303 } 304 inBuf := make([]byte, buffSize) 305 inIOseq := usermem.BytesIOSequence(inBuf) 306 307 n, serverKilled, err := ReadTest(serverTask, fd, inIOseq, killServer) 308 if err != nil { 309 t.Fatalf("Read failed :%v", err) 310 } 311 312 // The server should shut down. No new requests are going to be made. 313 if serverKilled { 314 break 315 } 316 317 if n <= 0 { 318 t.Fatalf("Read read no bytes") 319 } 320 321 var readFUSEHeaderIn linux.FUSEHeaderIn 322 inBuf = readFUSEHeaderIn.UnmarshalUnsafe(inBuf) 323 readPayload.UnmarshalUnsafe(inBuf) 324 325 if readFUSEHeaderIn.Opcode != echoTestOpcode { 326 t.Fatalf("read incorrect data. Header: %v, Payload: %v", readFUSEHeaderIn, readPayload) 327 } 328 329 // Write the response. 330 outBuf := make([]byte, linux.SizeOfFUSEHeaderOut+payloadLen) 331 outHeader := linux.FUSEHeaderOut{ 332 Len: linux.SizeOfFUSEHeaderOut + payloadLen, 333 Error: 0, 334 Unique: readFUSEHeaderIn.Unique, 335 } 336 337 // Echo the payload back. 338 outHeader.MarshalUnsafe(outBuf[:linux.SizeOfFUSEHeaderOut]) 339 readPayload.MarshalUnsafe(outBuf[linux.SizeOfFUSEHeaderOut:]) 340 outIOseq := usermem.BytesIOSequence(outBuf) 341 342 _, err = fd.Write(s.Ctx, outIOseq, vfs.WriteOptions{}) 343 if err != nil { 344 t.Fatalf("Write failed :%v", err) 345 } 346 } 347 }