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