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