github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/vfs/file_description_impl_util_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 vfs 16 17 import ( 18 "bytes" 19 "fmt" 20 "io" 21 "sync/atomic" 22 "testing" 23 24 "github.com/SagerNet/gvisor/pkg/abi/linux" 25 "github.com/SagerNet/gvisor/pkg/context" 26 "github.com/SagerNet/gvisor/pkg/errors/linuxerr" 27 "github.com/SagerNet/gvisor/pkg/sentry/contexttest" 28 "github.com/SagerNet/gvisor/pkg/syserror" 29 "github.com/SagerNet/gvisor/pkg/usermem" 30 ) 31 32 // fileDescription is the common fd struct which a filesystem implementation 33 // embeds in all of its file description implementations as required. 34 type fileDescription struct { 35 vfsfd FileDescription 36 FileDescriptionDefaultImpl 37 NoLockFD 38 } 39 40 // genCount contains the number of times its DynamicBytesSource.Generate() 41 // implementation has been called. 42 type genCount struct { 43 count uint64 // accessed using atomic memory ops 44 } 45 46 // Generate implements DynamicBytesSource.Generate. 47 func (g *genCount) Generate(ctx context.Context, buf *bytes.Buffer) error { 48 fmt.Fprintf(buf, "%d", atomic.AddUint64(&g.count, 1)) 49 return nil 50 } 51 52 type storeData struct { 53 data string 54 } 55 56 var _ WritableDynamicBytesSource = (*storeData)(nil) 57 58 // Generate implements DynamicBytesSource. 59 func (d *storeData) Generate(ctx context.Context, buf *bytes.Buffer) error { 60 buf.WriteString(d.data) 61 return nil 62 } 63 64 // Generate implements WritableDynamicBytesSource. 65 func (d *storeData) Write(ctx context.Context, src usermem.IOSequence, offset int64) (int64, error) { 66 buf := make([]byte, src.NumBytes()) 67 n, err := src.CopyIn(ctx, buf) 68 if err != nil { 69 return 0, err 70 } 71 72 d.data = string(buf[:n]) 73 return 0, nil 74 } 75 76 // testFD is a read-only FileDescriptionImpl representing a regular file. 77 type testFD struct { 78 fileDescription 79 DynamicBytesFileDescriptionImpl 80 81 data DynamicBytesSource 82 } 83 84 func newTestFD(ctx context.Context, vfsObj *VirtualFilesystem, statusFlags uint32, data DynamicBytesSource) *FileDescription { 85 vd := vfsObj.NewAnonVirtualDentry("genCountFD") 86 defer vd.DecRef(ctx) 87 var fd testFD 88 fd.vfsfd.Init(&fd, statusFlags, vd.Mount(), vd.Dentry(), &FileDescriptionOptions{}) 89 fd.DynamicBytesFileDescriptionImpl.SetDataSource(data) 90 return &fd.vfsfd 91 } 92 93 // Release implements FileDescriptionImpl.Release. 94 func (fd *testFD) Release(context.Context) { 95 } 96 97 // SetStatusFlags implements FileDescriptionImpl.SetStatusFlags. 98 // Stat implements FileDescriptionImpl.Stat. 99 func (fd *testFD) Stat(ctx context.Context, opts StatOptions) (linux.Statx, error) { 100 // Note that Statx.Mask == 0 in the return value. 101 return linux.Statx{}, nil 102 } 103 104 // SetStat implements FileDescriptionImpl.SetStat. 105 func (fd *testFD) SetStat(ctx context.Context, opts SetStatOptions) error { 106 return linuxerr.EPERM 107 } 108 109 func TestGenCountFD(t *testing.T) { 110 ctx := contexttest.Context(t) 111 112 vfsObj := &VirtualFilesystem{} 113 if err := vfsObj.Init(ctx); err != nil { 114 t.Fatalf("VFS init: %v", err) 115 } 116 fd := newTestFD(ctx, vfsObj, linux.O_RDWR, &genCount{}) 117 defer fd.DecRef(ctx) 118 119 // The first read causes Generate to be called to fill the FD's buffer. 120 buf := make([]byte, 2) 121 ioseq := usermem.BytesIOSequence(buf) 122 n, err := fd.Read(ctx, ioseq, ReadOptions{}) 123 if n != 1 || (err != nil && err != io.EOF) { 124 t.Fatalf("first Read: got (%d, %v), wanted (1, nil or EOF)", n, err) 125 } 126 if want := byte('1'); buf[0] != want { 127 t.Errorf("first Read: got byte %c, wanted %c", buf[0], want) 128 } 129 130 // A second read without seeking is still at EOF. 131 n, err = fd.Read(ctx, ioseq, ReadOptions{}) 132 if n != 0 || err != io.EOF { 133 t.Fatalf("second Read: got (%d, %v), wanted (0, EOF)", n, err) 134 } 135 136 // Seeking to the beginning of the file causes it to be regenerated. 137 n, err = fd.Seek(ctx, 0, linux.SEEK_SET) 138 if n != 0 || err != nil { 139 t.Fatalf("Seek: got (%d, %v), wanted (0, nil)", n, err) 140 } 141 n, err = fd.Read(ctx, ioseq, ReadOptions{}) 142 if n != 1 || (err != nil && err != io.EOF) { 143 t.Fatalf("Read after Seek: got (%d, %v), wanted (1, nil or EOF)", n, err) 144 } 145 if want := byte('2'); buf[0] != want { 146 t.Errorf("Read after Seek: got byte %c, wanted %c", buf[0], want) 147 } 148 149 // PRead at the beginning of the file also causes it to be regenerated. 150 n, err = fd.PRead(ctx, ioseq, 0, ReadOptions{}) 151 if n != 1 || (err != nil && err != io.EOF) { 152 t.Fatalf("PRead: got (%d, %v), wanted (1, nil or EOF)", n, err) 153 } 154 if want := byte('3'); buf[0] != want { 155 t.Errorf("PRead: got byte %c, wanted %c", buf[0], want) 156 } 157 158 // Write and PWrite fails. 159 if _, err := fd.Write(ctx, ioseq, WriteOptions{}); !linuxerr.Equals(linuxerr.EIO, err) { 160 t.Errorf("Write: got err %v, wanted %v", err, syserror.EIO) 161 } 162 if _, err := fd.PWrite(ctx, ioseq, 0, WriteOptions{}); !linuxerr.Equals(linuxerr.EIO, err) { 163 t.Errorf("Write: got err %v, wanted %v", err, syserror.EIO) 164 } 165 } 166 167 func TestWritable(t *testing.T) { 168 ctx := contexttest.Context(t) 169 170 vfsObj := &VirtualFilesystem{} 171 if err := vfsObj.Init(ctx); err != nil { 172 t.Fatalf("VFS init: %v", err) 173 } 174 fd := newTestFD(ctx, vfsObj, linux.O_RDWR, &storeData{data: "init"}) 175 defer fd.DecRef(ctx) 176 177 buf := make([]byte, 10) 178 ioseq := usermem.BytesIOSequence(buf) 179 if n, err := fd.Read(ctx, ioseq, ReadOptions{}); n != 4 && err != io.EOF { 180 t.Fatalf("Read: got (%v, %v), wanted (4, EOF)", n, err) 181 } 182 if want := "init"; want == string(buf) { 183 t.Fatalf("Read: got %v, wanted %v", string(buf), want) 184 } 185 186 // Test PWrite. 187 want := "write" 188 writeIOSeq := usermem.BytesIOSequence([]byte(want)) 189 if n, err := fd.PWrite(ctx, writeIOSeq, 0, WriteOptions{}); int(n) != len(want) && err != nil { 190 t.Errorf("PWrite: got err (%v, %v), wanted (%v, nil)", n, err, len(want)) 191 } 192 if n, err := fd.PRead(ctx, ioseq, 0, ReadOptions{}); int(n) != len(want) && err != io.EOF { 193 t.Fatalf("PRead: got (%v, %v), wanted (%v, EOF)", n, err, len(want)) 194 } 195 if want == string(buf) { 196 t.Fatalf("PRead: got %v, wanted %v", string(buf), want) 197 } 198 199 // Test Seek to 0 followed by Write. 200 want = "write2" 201 writeIOSeq = usermem.BytesIOSequence([]byte(want)) 202 if n, err := fd.Seek(ctx, 0, linux.SEEK_SET); n != 0 && err != nil { 203 t.Errorf("Seek: got err (%v, %v), wanted (0, nil)", n, err) 204 } 205 if n, err := fd.Write(ctx, writeIOSeq, WriteOptions{}); int(n) != len(want) && err != nil { 206 t.Errorf("Write: got err (%v, %v), wanted (%v, nil)", n, err, len(want)) 207 } 208 if n, err := fd.PRead(ctx, ioseq, 0, ReadOptions{}); int(n) != len(want) && err != io.EOF { 209 t.Fatalf("PRead: got (%v, %v), wanted (%v, EOF)", n, err, len(want)) 210 } 211 if want == string(buf) { 212 t.Fatalf("PRead: got %v, wanted %v", string(buf), want) 213 } 214 215 // Test failure if offset != 0. 216 if n, err := fd.Seek(ctx, 1, linux.SEEK_SET); n != 0 && err != nil { 217 t.Errorf("Seek: got err (%v, %v), wanted (0, nil)", n, err) 218 } 219 if n, err := fd.Write(ctx, writeIOSeq, WriteOptions{}); n != 0 && !linuxerr.Equals(linuxerr.EINVAL, err) { 220 t.Errorf("Write: got err (%v, %v), wanted (0, EINVAL)", n, err) 221 } 222 if n, err := fd.PWrite(ctx, writeIOSeq, 2, WriteOptions{}); n != 0 && !linuxerr.Equals(linuxerr.EINVAL, err) { 223 t.Errorf("PWrite: got err (%v, %v), wanted (0, EINVAL)", n, err) 224 } 225 }