gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/sentry/fsimpl/tmpfs/pipe_test.go (about) 1 // Copyright 2019 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 tmpfs 16 17 import ( 18 "bytes" 19 "testing" 20 21 "gvisor.dev/gvisor/pkg/abi/linux" 22 "gvisor.dev/gvisor/pkg/context" 23 "gvisor.dev/gvisor/pkg/errors/linuxerr" 24 "gvisor.dev/gvisor/pkg/fspath" 25 "gvisor.dev/gvisor/pkg/sentry/contexttest" 26 "gvisor.dev/gvisor/pkg/sentry/kernel/auth" 27 "gvisor.dev/gvisor/pkg/sentry/vfs" 28 "gvisor.dev/gvisor/pkg/usermem" 29 ) 30 31 const fileName = "mypipe" 32 33 func TestSeparateFDs(t *testing.T) { 34 ctx, creds, vfsObj, root := setup(t) 35 defer root.DecRef(ctx) 36 37 // Open the read side. This is done in a concurrently because opening 38 // One end the pipe blocks until the other end is opened. 39 pop := vfs.PathOperation{ 40 Root: root, 41 Start: root, 42 Path: fspath.Parse(fileName), 43 FollowFinalSymlink: true, 44 } 45 rfdchan := make(chan *vfs.FileDescription) 46 go func() { 47 openOpts := vfs.OpenOptions{Flags: linux.O_RDONLY} 48 rfd, _ := vfsObj.OpenAt(ctx, creds, &pop, &openOpts) 49 rfdchan <- rfd 50 }() 51 52 // Open the write side. 53 openOpts := vfs.OpenOptions{Flags: linux.O_WRONLY} 54 wfd, err := vfsObj.OpenAt(ctx, creds, &pop, &openOpts) 55 if err != nil { 56 t.Fatalf("failed to open pipe for writing %q: %v", fileName, err) 57 } 58 defer wfd.DecRef(ctx) 59 60 rfd, ok := <-rfdchan 61 if !ok { 62 t.Fatalf("failed to open pipe for reading %q", fileName) 63 } 64 defer rfd.DecRef(ctx) 65 66 const msg = "vamos azul" 67 checkEmpty(ctx, t, rfd) 68 checkWrite(ctx, t, wfd, msg) 69 checkRead(ctx, t, rfd, msg) 70 } 71 72 func TestNonblockingRead(t *testing.T) { 73 ctx, creds, vfsObj, root := setup(t) 74 defer root.DecRef(ctx) 75 76 // Open the read side as nonblocking. 77 pop := vfs.PathOperation{ 78 Root: root, 79 Start: root, 80 Path: fspath.Parse(fileName), 81 FollowFinalSymlink: true, 82 } 83 openOpts := vfs.OpenOptions{Flags: linux.O_RDONLY | linux.O_NONBLOCK} 84 rfd, err := vfsObj.OpenAt(ctx, creds, &pop, &openOpts) 85 if err != nil { 86 t.Fatalf("failed to open pipe for reading %q: %v", fileName, err) 87 } 88 defer rfd.DecRef(ctx) 89 90 // Open the write side. 91 openOpts = vfs.OpenOptions{Flags: linux.O_WRONLY} 92 wfd, err := vfsObj.OpenAt(ctx, creds, &pop, &openOpts) 93 if err != nil { 94 t.Fatalf("failed to open pipe for writing %q: %v", fileName, err) 95 } 96 defer wfd.DecRef(ctx) 97 98 const msg = "geh blau" 99 checkEmpty(ctx, t, rfd) 100 checkWrite(ctx, t, wfd, msg) 101 checkRead(ctx, t, rfd, msg) 102 } 103 104 func TestNonblockingWriteError(t *testing.T) { 105 ctx, creds, vfsObj, root := setup(t) 106 defer root.DecRef(ctx) 107 108 // Open the write side as nonblocking, which should return ENXIO. 109 pop := vfs.PathOperation{ 110 Root: root, 111 Start: root, 112 Path: fspath.Parse(fileName), 113 FollowFinalSymlink: true, 114 } 115 openOpts := vfs.OpenOptions{Flags: linux.O_WRONLY | linux.O_NONBLOCK} 116 _, err := vfsObj.OpenAt(ctx, creds, &pop, &openOpts) 117 if !linuxerr.Equals(linuxerr.ENXIO, err) { 118 t.Fatalf("expected ENXIO, but got error: %v", err) 119 } 120 } 121 122 func TestSingleFD(t *testing.T) { 123 ctx, creds, vfsObj, root := setup(t) 124 defer root.DecRef(ctx) 125 126 // Open the pipe as readable and writable. 127 pop := vfs.PathOperation{ 128 Root: root, 129 Start: root, 130 Path: fspath.Parse(fileName), 131 FollowFinalSymlink: true, 132 } 133 openOpts := vfs.OpenOptions{Flags: linux.O_RDWR} 134 fd, err := vfsObj.OpenAt(ctx, creds, &pop, &openOpts) 135 if err != nil { 136 t.Fatalf("failed to open pipe for writing %q: %v", fileName, err) 137 } 138 defer fd.DecRef(ctx) 139 140 const msg = "forza blu" 141 checkEmpty(ctx, t, fd) 142 checkWrite(ctx, t, fd, msg) 143 checkRead(ctx, t, fd, msg) 144 } 145 146 // setup creates a VFS with a pipe in the root directory at path fileName. The 147 // returned VirtualDentry must be DecRef()'d be the caller. It calls t.Fatal 148 // upon failure. 149 func setup(t *testing.T) (context.Context, *auth.Credentials, *vfs.VirtualFilesystem, vfs.VirtualDentry) { 150 ctx := contexttest.Context(t) 151 creds := auth.CredentialsFromContext(ctx) 152 153 // Create VFS. 154 vfsObj := &vfs.VirtualFilesystem{} 155 if err := vfsObj.Init(ctx); err != nil { 156 t.Fatalf("VFS init: %v", err) 157 } 158 vfsObj.MustRegisterFilesystemType("tmpfs", FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{ 159 AllowUserMount: true, 160 }) 161 mntns, err := vfsObj.NewMountNamespace(ctx, creds, "", "tmpfs", &vfs.MountOptions{}, nil) 162 if err != nil { 163 t.Fatalf("failed to create tmpfs root mount: %v", err) 164 } 165 166 // Create the pipe. 167 root := mntns.Root(ctx) 168 pop := vfs.PathOperation{ 169 Root: root, 170 Start: root, 171 Path: fspath.Parse(fileName), 172 } 173 mknodOpts := vfs.MknodOptions{Mode: linux.ModeNamedPipe | 0644} 174 if err := vfsObj.MknodAt(ctx, creds, &pop, &mknodOpts); err != nil { 175 t.Fatalf("failed to create file %q: %v", fileName, err) 176 } 177 178 // Sanity check: the file pipe exists and has the correct mode. 179 stat, err := vfsObj.StatAt(ctx, creds, &vfs.PathOperation{ 180 Root: root, 181 Start: root, 182 Path: fspath.Parse(fileName), 183 FollowFinalSymlink: true, 184 }, &vfs.StatOptions{}) 185 if err != nil { 186 t.Fatalf("stat(%q) failed: %v", fileName, err) 187 } 188 if stat.Mode&^linux.S_IFMT != 0644 { 189 t.Errorf("got wrong permissions (%0o)", stat.Mode) 190 } 191 if stat.Mode&linux.S_IFMT != linux.ModeNamedPipe { 192 t.Errorf("got wrong file type (%0o)", stat.Mode) 193 } 194 195 return ctx, creds, vfsObj, root 196 } 197 198 // checkEmpty calls t.Fatal if the pipe in fd is not empty. 199 func checkEmpty(ctx context.Context, t *testing.T, fd *vfs.FileDescription) { 200 readData := make([]byte, 1) 201 dst := usermem.BytesIOSequence(readData) 202 bytesRead, err := fd.Read(ctx, dst, vfs.ReadOptions{}) 203 if err != linuxerr.ErrWouldBlock { 204 t.Fatalf("expected ErrWouldBlock reading from empty pipe %q, but got: %v", fileName, err) 205 } 206 if bytesRead != 0 { 207 t.Fatalf("expected to read 0 bytes, but got %d", bytesRead) 208 } 209 } 210 211 // checkWrite calls t.Fatal if it fails to write all of msg to fd. 212 func checkWrite(ctx context.Context, t *testing.T, fd *vfs.FileDescription, msg string) { 213 writeData := []byte(msg) 214 src := usermem.BytesIOSequence(writeData) 215 bytesWritten, err := fd.Write(ctx, src, vfs.WriteOptions{}) 216 if err != nil { 217 t.Fatalf("error writing to pipe %q: %v", fileName, err) 218 } 219 if bytesWritten != int64(len(writeData)) { 220 t.Fatalf("expected to write %d bytes, but wrote %d", len(writeData), bytesWritten) 221 } 222 } 223 224 // checkRead calls t.Fatal if it fails to read msg from fd. 225 func checkRead(ctx context.Context, t *testing.T, fd *vfs.FileDescription, msg string) { 226 readData := make([]byte, len(msg)) 227 dst := usermem.BytesIOSequence(readData) 228 bytesRead, err := fd.Read(ctx, dst, vfs.ReadOptions{}) 229 if err != nil { 230 t.Fatalf("error reading from pipe %q: %v", fileName, err) 231 } 232 if bytesRead != int64(len(msg)) { 233 t.Fatalf("expected to read %d bytes, but got %d", len(msg), bytesRead) 234 } 235 if !bytes.Equal(readData, []byte(msg)) { 236 t.Fatalf("expected to read %q from pipe, but got %q", msg, string(readData)) 237 } 238 }