github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fsimpl/kernfs/kernfs_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 kernfs_test 16 17 import ( 18 "bytes" 19 "fmt" 20 "testing" 21 22 "github.com/google/go-cmp/cmp" 23 "github.com/SagerNet/gvisor/pkg/abi/linux" 24 "github.com/SagerNet/gvisor/pkg/context" 25 "github.com/SagerNet/gvisor/pkg/errors/linuxerr" 26 "github.com/SagerNet/gvisor/pkg/sentry/contexttest" 27 "github.com/SagerNet/gvisor/pkg/sentry/fsimpl/kernfs" 28 "github.com/SagerNet/gvisor/pkg/sentry/fsimpl/testutil" 29 "github.com/SagerNet/gvisor/pkg/sentry/kernel/auth" 30 "github.com/SagerNet/gvisor/pkg/sentry/vfs" 31 "github.com/SagerNet/gvisor/pkg/usermem" 32 ) 33 34 const defaultMode linux.FileMode = 01777 35 const staticFileContent = "This is sample content for a static test file." 36 37 // RootDentryFn is a generator function for creating the root dentry of a test 38 // filesystem. See newTestSystem. 39 type RootDentryFn func(context.Context, *auth.Credentials, *filesystem) kernfs.Inode 40 41 // newTestSystem sets up a minimal environment for running a test, including an 42 // instance of a test filesystem. Tests can control the contents of the 43 // filesystem by providing an appropriate rootFn, which should return a 44 // pre-populated root dentry. 45 func newTestSystem(t *testing.T, rootFn RootDentryFn) *testutil.System { 46 ctx := contexttest.Context(t) 47 creds := auth.CredentialsFromContext(ctx) 48 v := &vfs.VirtualFilesystem{} 49 if err := v.Init(ctx); err != nil { 50 t.Fatalf("VFS init: %v", err) 51 } 52 v.MustRegisterFilesystemType("testfs", &fsType{rootFn: rootFn}, &vfs.RegisterFilesystemTypeOptions{ 53 AllowUserMount: true, 54 }) 55 mns, err := v.NewMountNamespace(ctx, creds, "", "testfs", &vfs.MountOptions{}) 56 if err != nil { 57 t.Fatalf("Failed to create testfs root mount: %v", err) 58 } 59 return testutil.NewSystem(ctx, t, v, mns) 60 } 61 62 type fsType struct { 63 rootFn RootDentryFn 64 } 65 66 type filesystem struct { 67 kernfs.Filesystem 68 } 69 70 // MountOptions implements vfs.FilesystemImpl.MountOptions. 71 func (fs *filesystem) MountOptions() string { 72 return "" 73 } 74 75 type file struct { 76 kernfs.DynamicBytesFile 77 content string 78 } 79 80 func (fs *filesystem) newFile(ctx context.Context, creds *auth.Credentials, content string) kernfs.Inode { 81 f := &file{} 82 f.content = content 83 f.DynamicBytesFile.Init(ctx, creds, 0 /* devMajor */, 0 /* devMinor */, fs.NextIno(), f, 0777) 84 return f 85 } 86 87 func (f *file) Generate(ctx context.Context, buf *bytes.Buffer) error { 88 fmt.Fprintf(buf, "%s", f.content) 89 return nil 90 } 91 92 type attrs struct { 93 kernfs.InodeAttrs 94 } 95 96 func (*attrs) SetStat(context.Context, *vfs.Filesystem, *auth.Credentials, vfs.SetStatOptions) error { 97 return linuxerr.EPERM 98 } 99 100 type readonlyDir struct { 101 readonlyDirRefs 102 attrs 103 kernfs.InodeAlwaysValid 104 kernfs.InodeDirectoryNoNewChildren 105 kernfs.InodeNoStatFS 106 kernfs.InodeNotSymlink 107 kernfs.InodeTemporary 108 kernfs.OrderedChildren 109 110 locks vfs.FileLocks 111 } 112 113 func (fs *filesystem) newReadonlyDir(ctx context.Context, creds *auth.Credentials, mode linux.FileMode, contents map[string]kernfs.Inode) kernfs.Inode { 114 dir := &readonlyDir{} 115 dir.attrs.Init(ctx, creds, 0 /* devMajor */, 0 /* devMinor */, fs.NextIno(), linux.ModeDirectory|mode) 116 dir.OrderedChildren.Init(kernfs.OrderedChildrenOptions{}) 117 dir.InitRefs() 118 dir.IncLinks(dir.OrderedChildren.Populate(contents)) 119 return dir 120 } 121 122 func (d *readonlyDir) Open(ctx context.Context, rp *vfs.ResolvingPath, kd *kernfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) { 123 fd, err := kernfs.NewGenericDirectoryFD(rp.Mount(), kd, &d.OrderedChildren, &d.locks, &opts, kernfs.GenericDirectoryFDOptions{ 124 SeekEnd: kernfs.SeekEndStaticEntries, 125 }) 126 if err != nil { 127 return nil, err 128 } 129 return fd.VFSFileDescription(), nil 130 } 131 132 func (d *readonlyDir) DecRef(ctx context.Context) { 133 d.readonlyDirRefs.DecRef(func() { d.Destroy(ctx) }) 134 } 135 136 type dir struct { 137 dirRefs 138 attrs 139 kernfs.InodeAlwaysValid 140 kernfs.InodeNotSymlink 141 kernfs.InodeNoStatFS 142 kernfs.InodeTemporary 143 kernfs.OrderedChildren 144 145 locks vfs.FileLocks 146 147 fs *filesystem 148 } 149 150 func (fs *filesystem) newDir(ctx context.Context, creds *auth.Credentials, mode linux.FileMode, contents map[string]kernfs.Inode) kernfs.Inode { 151 dir := &dir{} 152 dir.fs = fs 153 dir.attrs.Init(ctx, creds, 0 /* devMajor */, 0 /* devMinor */, fs.NextIno(), linux.ModeDirectory|mode) 154 dir.OrderedChildren.Init(kernfs.OrderedChildrenOptions{Writable: true}) 155 dir.InitRefs() 156 157 dir.IncLinks(dir.OrderedChildren.Populate(contents)) 158 return dir 159 } 160 161 func (d *dir) Open(ctx context.Context, rp *vfs.ResolvingPath, kd *kernfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) { 162 fd, err := kernfs.NewGenericDirectoryFD(rp.Mount(), kd, &d.OrderedChildren, &d.locks, &opts, kernfs.GenericDirectoryFDOptions{ 163 SeekEnd: kernfs.SeekEndStaticEntries, 164 }) 165 if err != nil { 166 return nil, err 167 } 168 return fd.VFSFileDescription(), nil 169 } 170 171 func (d *dir) DecRef(ctx context.Context) { 172 d.dirRefs.DecRef(func() { d.Destroy(ctx) }) 173 } 174 175 func (d *dir) NewDir(ctx context.Context, name string, opts vfs.MkdirOptions) (kernfs.Inode, error) { 176 creds := auth.CredentialsFromContext(ctx) 177 dir := d.fs.newDir(ctx, creds, opts.Mode, nil) 178 if err := d.OrderedChildren.Insert(name, dir); err != nil { 179 dir.DecRef(ctx) 180 return nil, err 181 } 182 d.TouchCMtime(ctx) 183 d.IncLinks(1) 184 return dir, nil 185 } 186 187 func (d *dir) NewFile(ctx context.Context, name string, opts vfs.OpenOptions) (kernfs.Inode, error) { 188 creds := auth.CredentialsFromContext(ctx) 189 f := d.fs.newFile(ctx, creds, "") 190 if err := d.OrderedChildren.Insert(name, f); err != nil { 191 f.DecRef(ctx) 192 return nil, err 193 } 194 d.TouchCMtime(ctx) 195 return f, nil 196 } 197 198 func (*dir) NewLink(context.Context, string, kernfs.Inode) (kernfs.Inode, error) { 199 return nil, linuxerr.EPERM 200 } 201 202 func (*dir) NewSymlink(context.Context, string, string) (kernfs.Inode, error) { 203 return nil, linuxerr.EPERM 204 } 205 206 func (*dir) NewNode(context.Context, string, vfs.MknodOptions) (kernfs.Inode, error) { 207 return nil, linuxerr.EPERM 208 } 209 210 func (fsType) Name() string { 211 return "kernfs" 212 } 213 214 func (fsType) Release(ctx context.Context) {} 215 216 func (fst fsType) GetFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials, source string, opt vfs.GetFilesystemOptions) (*vfs.Filesystem, *vfs.Dentry, error) { 217 fs := &filesystem{} 218 fs.VFSFilesystem().Init(vfsObj, &fst, fs) 219 root := fst.rootFn(ctx, creds, fs) 220 var d kernfs.Dentry 221 d.Init(&fs.Filesystem, root) 222 return fs.VFSFilesystem(), d.VFSDentry(), nil 223 } 224 225 // -------------------- Remainder of the file are test cases -------------------- 226 227 func TestBasic(t *testing.T) { 228 sys := newTestSystem(t, func(ctx context.Context, creds *auth.Credentials, fs *filesystem) kernfs.Inode { 229 return fs.newReadonlyDir(ctx, creds, 0755, map[string]kernfs.Inode{ 230 "file1": fs.newFile(ctx, creds, staticFileContent), 231 }) 232 }) 233 defer sys.Destroy() 234 sys.GetDentryOrDie(sys.PathOpAtRoot("file1")).DecRef(sys.Ctx) 235 } 236 237 func TestMkdirGetDentry(t *testing.T) { 238 sys := newTestSystem(t, func(ctx context.Context, creds *auth.Credentials, fs *filesystem) kernfs.Inode { 239 return fs.newReadonlyDir(ctx, creds, 0755, map[string]kernfs.Inode{ 240 "dir1": fs.newDir(ctx, creds, 0755, nil), 241 }) 242 }) 243 defer sys.Destroy() 244 245 pop := sys.PathOpAtRoot("dir1/a new directory") 246 if err := sys.VFS.MkdirAt(sys.Ctx, sys.Creds, pop, &vfs.MkdirOptions{Mode: 0755}); err != nil { 247 t.Fatalf("MkdirAt for PathOperation %+v failed: %v", pop, err) 248 } 249 sys.GetDentryOrDie(pop).DecRef(sys.Ctx) 250 } 251 252 func TestReadStaticFile(t *testing.T) { 253 sys := newTestSystem(t, func(ctx context.Context, creds *auth.Credentials, fs *filesystem) kernfs.Inode { 254 return fs.newReadonlyDir(ctx, creds, 0755, map[string]kernfs.Inode{ 255 "file1": fs.newFile(ctx, creds, staticFileContent), 256 }) 257 }) 258 defer sys.Destroy() 259 260 pop := sys.PathOpAtRoot("file1") 261 fd, err := sys.VFS.OpenAt(sys.Ctx, sys.Creds, pop, &vfs.OpenOptions{ 262 Flags: linux.O_RDONLY, 263 }) 264 if err != nil { 265 t.Fatalf("OpenAt for PathOperation %+v failed: %v", pop, err) 266 } 267 defer fd.DecRef(sys.Ctx) 268 269 content, err := sys.ReadToEnd(fd) 270 if err != nil { 271 t.Fatalf("Read failed: %v", err) 272 } 273 if diff := cmp.Diff(staticFileContent, content); diff != "" { 274 t.Fatalf("Read returned unexpected data:\n--- want\n+++ got\n%v", diff) 275 } 276 } 277 278 func TestCreateNewFileInStaticDir(t *testing.T) { 279 sys := newTestSystem(t, func(ctx context.Context, creds *auth.Credentials, fs *filesystem) kernfs.Inode { 280 return fs.newReadonlyDir(ctx, creds, 0755, map[string]kernfs.Inode{ 281 "dir1": fs.newDir(ctx, creds, 0755, nil), 282 }) 283 }) 284 defer sys.Destroy() 285 286 pop := sys.PathOpAtRoot("dir1/newfile") 287 opts := &vfs.OpenOptions{Flags: linux.O_CREAT | linux.O_EXCL, Mode: defaultMode} 288 fd, err := sys.VFS.OpenAt(sys.Ctx, sys.Creds, pop, opts) 289 if err != nil { 290 t.Fatalf("OpenAt(pop:%+v, opts:%+v) failed: %v", pop, opts, err) 291 } 292 293 // Close the file. The file should persist. 294 fd.DecRef(sys.Ctx) 295 296 fd, err = sys.VFS.OpenAt(sys.Ctx, sys.Creds, pop, &vfs.OpenOptions{ 297 Flags: linux.O_RDONLY, 298 }) 299 if err != nil { 300 t.Fatalf("OpenAt(pop:%+v) = %+v failed: %v", pop, fd, err) 301 } 302 fd.DecRef(sys.Ctx) 303 } 304 305 func TestDirFDReadWrite(t *testing.T) { 306 sys := newTestSystem(t, func(ctx context.Context, creds *auth.Credentials, fs *filesystem) kernfs.Inode { 307 return fs.newReadonlyDir(ctx, creds, 0755, nil) 308 }) 309 defer sys.Destroy() 310 311 pop := sys.PathOpAtRoot("/") 312 fd, err := sys.VFS.OpenAt(sys.Ctx, sys.Creds, pop, &vfs.OpenOptions{ 313 Flags: linux.O_RDONLY, 314 }) 315 if err != nil { 316 t.Fatalf("OpenAt for PathOperation %+v failed: %v", pop, err) 317 } 318 defer fd.DecRef(sys.Ctx) 319 320 // Read/Write should fail for directory FDs. 321 if _, err := fd.Read(sys.Ctx, usermem.BytesIOSequence([]byte{}), vfs.ReadOptions{}); !linuxerr.Equals(linuxerr.EISDIR, err) { 322 t.Fatalf("Read for directory FD failed with unexpected error: %v", err) 323 } 324 if _, err := fd.Write(sys.Ctx, usermem.BytesIOSequence([]byte{}), vfs.WriteOptions{}); !linuxerr.Equals(linuxerr.EBADF, err) { 325 t.Fatalf("Write for directory FD failed with unexpected error: %v", err) 326 } 327 } 328 329 func TestDirFDIterDirents(t *testing.T) { 330 sys := newTestSystem(t, func(ctx context.Context, creds *auth.Credentials, fs *filesystem) kernfs.Inode { 331 return fs.newReadonlyDir(ctx, creds, 0755, map[string]kernfs.Inode{ 332 // Fill root with nodes backed by various inode implementations. 333 "dir1": fs.newReadonlyDir(ctx, creds, 0755, nil), 334 "dir2": fs.newDir(ctx, creds, 0755, map[string]kernfs.Inode{ 335 "dir3": fs.newDir(ctx, creds, 0755, nil), 336 }), 337 "file1": fs.newFile(ctx, creds, staticFileContent), 338 }) 339 }) 340 defer sys.Destroy() 341 342 pop := sys.PathOpAtRoot("/") 343 sys.AssertAllDirentTypes(sys.ListDirents(pop), map[string]testutil.DirentType{ 344 "dir1": linux.DT_DIR, 345 "dir2": linux.DT_DIR, 346 "file1": linux.DT_REG, 347 }) 348 }