github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fsimpl/testutil/testutil.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 testutil provides common test utilities for kernfs-based
    16  // filesystems.
    17  package testutil
    18  
    19  import (
    20  	"fmt"
    21  	"io"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/google/go-cmp/cmp"
    26  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    27  	"github.com/SagerNet/gvisor/pkg/context"
    28  	"github.com/SagerNet/gvisor/pkg/fspath"
    29  	"github.com/SagerNet/gvisor/pkg/sentry/kernel/auth"
    30  	"github.com/SagerNet/gvisor/pkg/sentry/vfs"
    31  	"github.com/SagerNet/gvisor/pkg/sync"
    32  	"github.com/SagerNet/gvisor/pkg/usermem"
    33  
    34  	"github.com/SagerNet/gvisor/pkg/hostarch"
    35  )
    36  
    37  // System represents the context for a single test.
    38  //
    39  // Test systems must be explicitly destroyed with System.Destroy.
    40  type System struct {
    41  	t     *testing.T
    42  	Ctx   context.Context
    43  	Creds *auth.Credentials
    44  	VFS   *vfs.VirtualFilesystem
    45  	Root  vfs.VirtualDentry
    46  	MntNs *vfs.MountNamespace
    47  }
    48  
    49  // NewSystem constructs a System.
    50  //
    51  // Precondition: Caller must hold a reference on mns, whose ownership
    52  // is transferred to the new System.
    53  func NewSystem(ctx context.Context, t *testing.T, v *vfs.VirtualFilesystem, mns *vfs.MountNamespace) *System {
    54  	root := mns.Root()
    55  	root.IncRef()
    56  	s := &System{
    57  		t:     t,
    58  		Ctx:   ctx,
    59  		Creds: auth.CredentialsFromContext(ctx),
    60  		VFS:   v,
    61  		MntNs: mns,
    62  		Root:  root,
    63  	}
    64  	return s
    65  }
    66  
    67  // WithSubtest creates a temporary test system with a new test harness,
    68  // referencing all other resources from the original system. This is useful when
    69  // a system is reused for multiple subtests, and the T needs to change for each
    70  // case. Note that this is safe when test cases run in parallel, as all
    71  // resources referenced by the system are immutable, or handle interior
    72  // mutations in a thread-safe manner.
    73  //
    74  // The returned system must not outlive the original and should not be destroyed
    75  // via System.Destroy.
    76  func (s *System) WithSubtest(t *testing.T) *System {
    77  	return &System{
    78  		t:     t,
    79  		Ctx:   s.Ctx,
    80  		Creds: s.Creds,
    81  		VFS:   s.VFS,
    82  		MntNs: s.MntNs,
    83  		Root:  s.Root,
    84  	}
    85  }
    86  
    87  // WithTemporaryContext constructs a temporary test system with a new context
    88  // ctx. The temporary system borrows all resources and references from the
    89  // original system. The returned temporary system must not outlive the original
    90  // system, and should not be destroyed via System.Destroy.
    91  func (s *System) WithTemporaryContext(ctx context.Context) *System {
    92  	return &System{
    93  		t:     s.t,
    94  		Ctx:   ctx,
    95  		Creds: s.Creds,
    96  		VFS:   s.VFS,
    97  		MntNs: s.MntNs,
    98  		Root:  s.Root,
    99  	}
   100  }
   101  
   102  // Destroy release resources associated with a test system.
   103  func (s *System) Destroy() {
   104  	s.Root.DecRef(s.Ctx)
   105  	s.MntNs.DecRef(s.Ctx) // Reference on MntNs passed to NewSystem.
   106  }
   107  
   108  // ReadToEnd reads the contents of fd until EOF to a string.
   109  func (s *System) ReadToEnd(fd *vfs.FileDescription) (string, error) {
   110  	buf := make([]byte, hostarch.PageSize)
   111  	bufIOSeq := usermem.BytesIOSequence(buf)
   112  	opts := vfs.ReadOptions{}
   113  
   114  	var content strings.Builder
   115  	for {
   116  		n, err := fd.Read(s.Ctx, bufIOSeq, opts)
   117  		if n == 0 || err != nil {
   118  			if err == io.EOF {
   119  				err = nil
   120  			}
   121  			return content.String(), err
   122  		}
   123  		content.Write(buf[:n])
   124  	}
   125  }
   126  
   127  // PathOpAtRoot constructs a PathOperation with the given path from
   128  // the root of the filesystem.
   129  func (s *System) PathOpAtRoot(path string) *vfs.PathOperation {
   130  	return &vfs.PathOperation{
   131  		Root:  s.Root,
   132  		Start: s.Root,
   133  		Path:  fspath.Parse(path),
   134  	}
   135  }
   136  
   137  // GetDentryOrDie attempts to resolve a dentry referred to by the
   138  // provided path operation. If unsuccessful, the test fails.
   139  func (s *System) GetDentryOrDie(pop *vfs.PathOperation) vfs.VirtualDentry {
   140  	vd, err := s.VFS.GetDentryAt(s.Ctx, s.Creds, pop, &vfs.GetDentryOptions{})
   141  	if err != nil {
   142  		s.t.Fatalf("GetDentryAt(pop:%+v) failed: %v", pop, err)
   143  	}
   144  	return vd
   145  }
   146  
   147  // DirentType is an alias for values for linux_dirent64.d_type.
   148  type DirentType = uint8
   149  
   150  // ListDirents lists the Dirents for a directory at pop.
   151  func (s *System) ListDirents(pop *vfs.PathOperation) *DirentCollector {
   152  	fd, err := s.VFS.OpenAt(s.Ctx, s.Creds, pop, &vfs.OpenOptions{Flags: linux.O_RDONLY})
   153  	if err != nil {
   154  		s.t.Fatalf("OpenAt for PathOperation %+v failed: %v", pop, err)
   155  	}
   156  	defer fd.DecRef(s.Ctx)
   157  
   158  	collector := &DirentCollector{}
   159  	if err := fd.IterDirents(s.Ctx, collector); err != nil {
   160  		s.t.Fatalf("IterDirent failed: %v", err)
   161  	}
   162  	return collector
   163  }
   164  
   165  // AssertAllDirentTypes verifies that the set of dirents in collector contains
   166  // exactly the specified set of expected entries. AssertAllDirentTypes respects
   167  // collector.skipDots, and implicitly checks for "." and ".." accordingly.
   168  func (s *System) AssertAllDirentTypes(collector *DirentCollector, expected map[string]DirentType) {
   169  	if expected == nil {
   170  		expected = make(map[string]DirentType)
   171  	}
   172  	// Also implicitly check for "." and "..", if enabled.
   173  	if !collector.skipDots {
   174  		expected["."] = linux.DT_DIR
   175  		expected[".."] = linux.DT_DIR
   176  	}
   177  
   178  	dentryTypes := make(map[string]DirentType)
   179  	collector.mu.Lock()
   180  	for _, dirent := range collector.dirents {
   181  		dentryTypes[dirent.Name] = dirent.Type
   182  	}
   183  	collector.mu.Unlock()
   184  	if diff := cmp.Diff(expected, dentryTypes); diff != "" {
   185  		s.t.Fatalf("IterDirent had unexpected results:\n--- want\n+++ got\n%v", diff)
   186  	}
   187  }
   188  
   189  // AssertDirentOffsets verifies that collector contains at least the entries
   190  // specified in expected, with the given NextOff field. Entries specified in
   191  // expected but missing from collector result in failure. Extra entries in
   192  // collector are ignored. AssertDirentOffsets respects collector.skipDots, and
   193  // implicitly checks for "." and ".." accordingly.
   194  func (s *System) AssertDirentOffsets(collector *DirentCollector, expected map[string]int64) {
   195  	// Also implicitly check for "." and "..", if enabled.
   196  	if !collector.skipDots {
   197  		expected["."] = 1
   198  		expected[".."] = 2
   199  	}
   200  
   201  	dentryNextOffs := make(map[string]int64)
   202  	collector.mu.Lock()
   203  	for _, dirent := range collector.dirents {
   204  		// Ignore extra entries in dentries that are not in expected.
   205  		if _, ok := expected[dirent.Name]; ok {
   206  			dentryNextOffs[dirent.Name] = dirent.NextOff
   207  		}
   208  	}
   209  	collector.mu.Unlock()
   210  	if diff := cmp.Diff(expected, dentryNextOffs); diff != "" {
   211  		s.t.Fatalf("IterDirent had unexpected results:\n--- want\n+++ got\n%v", diff)
   212  	}
   213  }
   214  
   215  // DirentCollector provides an implementation for vfs.IterDirentsCallback for
   216  // testing. It simply iterates to the end of a given directory FD and collects
   217  // all dirents emitted by the callback.
   218  type DirentCollector struct {
   219  	mu      sync.Mutex
   220  	order   []*vfs.Dirent
   221  	dirents map[string]*vfs.Dirent
   222  	// When the collector is used in various Assert* functions, should "." and
   223  	// ".." be implicitly checked?
   224  	skipDots bool
   225  }
   226  
   227  // SkipDotsChecks enables or disables the implicit checks on "." and ".." when
   228  // the collector is used in various Assert* functions. Note that "." and ".."
   229  // are still collected if passed to d.Handle, so the caller should only disable
   230  // the checks when they aren't expected.
   231  func (d *DirentCollector) SkipDotsChecks(value bool) {
   232  	d.skipDots = value
   233  }
   234  
   235  // Handle implements vfs.IterDirentsCallback.Handle.
   236  func (d *DirentCollector) Handle(dirent vfs.Dirent) error {
   237  	d.mu.Lock()
   238  	if d.dirents == nil {
   239  		d.dirents = make(map[string]*vfs.Dirent)
   240  	}
   241  	d.order = append(d.order, &dirent)
   242  	d.dirents[dirent.Name] = &dirent
   243  	d.mu.Unlock()
   244  	return nil
   245  }
   246  
   247  // Count returns the number of dirents currently in the collector.
   248  func (d *DirentCollector) Count() int {
   249  	d.mu.Lock()
   250  	defer d.mu.Unlock()
   251  	return len(d.dirents)
   252  }
   253  
   254  // Contains checks whether the collector has a dirent with the given name and
   255  // type.
   256  func (d *DirentCollector) Contains(name string, typ uint8) error {
   257  	d.mu.Lock()
   258  	defer d.mu.Unlock()
   259  	dirent, ok := d.dirents[name]
   260  	if !ok {
   261  		return fmt.Errorf("no dirent named %q found", name)
   262  	}
   263  	if dirent.Type != typ {
   264  		return fmt.Errorf("dirent named %q found, but was expecting type %s, got: %+v", name, linux.DirentType.Parse(uint64(typ)), dirent)
   265  	}
   266  	return nil
   267  }
   268  
   269  // Dirents returns all dirents discovered by this collector.
   270  func (d *DirentCollector) Dirents() map[string]*vfs.Dirent {
   271  	d.mu.Lock()
   272  	dirents := make(map[string]*vfs.Dirent)
   273  	for n, d := range d.dirents {
   274  		dirents[n] = d
   275  	}
   276  	d.mu.Unlock()
   277  	return dirents
   278  }
   279  
   280  // OrderedDirents returns an ordered list of dirents as discovered by this
   281  // collector.
   282  func (d *DirentCollector) OrderedDirents() []*vfs.Dirent {
   283  	d.mu.Lock()
   284  	dirents := make([]*vfs.Dirent, len(d.order))
   285  	copy(dirents, d.order)
   286  	d.mu.Unlock()
   287  	return dirents
   288  }