github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/runsc/boot/loader_test.go (about)

     1  // Copyright 2018 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 boot
    16  
    17  import (
    18  	"fmt"
    19  	"math/rand"
    20  	"os"
    21  	"reflect"
    22  	"testing"
    23  	"time"
    24  
    25  	specs "github.com/opencontainers/runtime-spec/specs-go"
    26  	"golang.org/x/sys/unix"
    27  	"github.com/SagerNet/gvisor/pkg/control/server"
    28  	"github.com/SagerNet/gvisor/pkg/fd"
    29  	"github.com/SagerNet/gvisor/pkg/fspath"
    30  	"github.com/SagerNet/gvisor/pkg/log"
    31  	"github.com/SagerNet/gvisor/pkg/p9"
    32  	"github.com/SagerNet/gvisor/pkg/sentry/contexttest"
    33  	"github.com/SagerNet/gvisor/pkg/sentry/fs"
    34  	"github.com/SagerNet/gvisor/pkg/sentry/vfs"
    35  	"github.com/SagerNet/gvisor/pkg/sync"
    36  	"github.com/SagerNet/gvisor/pkg/unet"
    37  	"github.com/SagerNet/gvisor/runsc/config"
    38  	"github.com/SagerNet/gvisor/runsc/fsgofer"
    39  )
    40  
    41  func init() {
    42  	log.SetLevel(log.Debug)
    43  	rand.Seed(time.Now().UnixNano())
    44  	if err := fsgofer.OpenProcSelfFD(); err != nil {
    45  		panic(err)
    46  	}
    47  	config.RegisterFlags()
    48  }
    49  
    50  func testConfig() *config.Config {
    51  	conf, err := config.NewFromFlags()
    52  	if err != nil {
    53  		panic(err)
    54  	}
    55  	// Change test defaults.
    56  	conf.RootDir = "unused_root_dir"
    57  	conf.Network = config.NetworkNone
    58  	conf.DisableSeccomp = true
    59  	return conf
    60  }
    61  
    62  // testSpec returns a simple spec that can be used in tests.
    63  func testSpec() *specs.Spec {
    64  	return &specs.Spec{
    65  		// The host filesystem root is the sandbox root.
    66  		Root: &specs.Root{
    67  			Path:     "/",
    68  			Readonly: true,
    69  		},
    70  		Process: &specs.Process{
    71  			Args: []string{"/bin/true"},
    72  		},
    73  	}
    74  }
    75  
    76  // startGofer starts a new gofer routine serving 'root' path. It returns the
    77  // sandbox side of the connection, and a function that when called will stop the
    78  // gofer.
    79  func startGofer(root string) (int, func(), error) {
    80  	fds, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_STREAM|unix.SOCK_CLOEXEC, 0)
    81  	if err != nil {
    82  		return 0, nil, err
    83  	}
    84  	sandboxEnd, goferEnd := fds[0], fds[1]
    85  
    86  	socket, err := unet.NewSocket(goferEnd)
    87  	if err != nil {
    88  		unix.Close(sandboxEnd)
    89  		unix.Close(goferEnd)
    90  		return 0, nil, fmt.Errorf("error creating server on FD %d: %v", goferEnd, err)
    91  	}
    92  	at, err := fsgofer.NewAttachPoint(root, fsgofer.Config{ROMount: true})
    93  	if err != nil {
    94  		return 0, nil, err
    95  	}
    96  	go func() {
    97  		s := p9.NewServer(at)
    98  		if err := s.Handle(socket); err != nil {
    99  			log.Infof("Gofer is stopping. FD: %d, err: %v\n", goferEnd, err)
   100  		}
   101  	}()
   102  	// Closing the gofer socket will stop the gofer and exit goroutine above.
   103  	cleanup := func() {
   104  		if err := socket.Close(); err != nil {
   105  			log.Warningf("Error closing gofer socket: %v", err)
   106  		}
   107  	}
   108  	return sandboxEnd, cleanup, nil
   109  }
   110  
   111  func createLoader(vfsEnabled bool, spec *specs.Spec) (*Loader, func(), error) {
   112  	fd, err := server.CreateSocket(ControlSocketAddr(fmt.Sprintf("%010d", rand.Int())[:10]))
   113  	if err != nil {
   114  		return nil, nil, err
   115  	}
   116  	conf := testConfig()
   117  	conf.VFS2 = vfsEnabled
   118  
   119  	sandEnd, cleanup, err := startGofer(spec.Root.Path)
   120  	if err != nil {
   121  		return nil, nil, err
   122  	}
   123  
   124  	// Loader takes ownership of stdio.
   125  	var stdio []int
   126  	for _, f := range []*os.File{os.Stdin, os.Stdout, os.Stderr} {
   127  		newFd, err := unix.Dup(int(f.Fd()))
   128  		if err != nil {
   129  			return nil, nil, err
   130  		}
   131  		stdio = append(stdio, newFd)
   132  	}
   133  
   134  	args := Args{
   135  		ID:           "foo",
   136  		Spec:         spec,
   137  		Conf:         conf,
   138  		ControllerFD: fd,
   139  		GoferFDs:     []int{sandEnd},
   140  		StdioFDs:     stdio,
   141  	}
   142  	l, err := New(args)
   143  	if err != nil {
   144  		cleanup()
   145  		return nil, nil, err
   146  	}
   147  	return l, cleanup, nil
   148  }
   149  
   150  // TestRun runs a simple application in a sandbox and checks that it succeeds.
   151  func TestRun(t *testing.T) {
   152  	doRun(t, false)
   153  }
   154  
   155  // TestRunVFS2 runs TestRun in VFSv2.
   156  func TestRunVFS2(t *testing.T) {
   157  	doRun(t, true)
   158  }
   159  
   160  func doRun(t *testing.T, vfsEnabled bool) {
   161  	l, cleanup, err := createLoader(vfsEnabled, testSpec())
   162  	if err != nil {
   163  		t.Fatalf("error creating loader: %v", err)
   164  	}
   165  
   166  	defer l.Destroy()
   167  	defer cleanup()
   168  
   169  	// Start a goroutine to read the start chan result, otherwise Run will
   170  	// block forever.
   171  	var resultChanErr error
   172  	var wg sync.WaitGroup
   173  	wg.Add(1)
   174  	go func() {
   175  		resultChanErr = <-l.ctrl.manager.startResultChan
   176  		wg.Done()
   177  	}()
   178  
   179  	// Run the container.
   180  	if err := l.Run(); err != nil {
   181  		t.Errorf("error running container: %v", err)
   182  	}
   183  
   184  	// We should have not gotten an error on the startResultChan.
   185  	wg.Wait()
   186  	if resultChanErr != nil {
   187  		t.Errorf("error on startResultChan: %v", resultChanErr)
   188  	}
   189  
   190  	// Wait for the application to exit.  It should succeed.
   191  	if status := l.WaitExit(); status.Code != 0 || status.Signo != 0 {
   192  		t.Errorf("application exited with status %+v, want 0", status)
   193  	}
   194  }
   195  
   196  // TestStartSignal tests that the controller Start message will cause
   197  // WaitForStartSignal to return.
   198  func TestStartSignal(t *testing.T) {
   199  	doStartSignal(t, false)
   200  }
   201  
   202  // TestStartSignalVFS2 does TestStartSignal with VFS2.
   203  func TestStartSignalVFS2(t *testing.T) {
   204  	doStartSignal(t, true)
   205  }
   206  
   207  func doStartSignal(t *testing.T, vfsEnabled bool) {
   208  	l, cleanup, err := createLoader(vfsEnabled, testSpec())
   209  	if err != nil {
   210  		t.Fatalf("error creating loader: %v", err)
   211  	}
   212  	defer l.Destroy()
   213  	defer cleanup()
   214  
   215  	// We aren't going to wait on this application, so the control server
   216  	// needs to be shut down manually.
   217  	defer l.ctrl.srv.Stop(time.Hour)
   218  
   219  	// Start a goroutine that calls WaitForStartSignal and writes to a
   220  	// channel when it returns.
   221  	waitFinished := make(chan struct{})
   222  	go func() {
   223  		l.WaitForStartSignal()
   224  		// Pretend that Run() executed and returned no error.
   225  		l.ctrl.manager.startResultChan <- nil
   226  		waitFinished <- struct{}{}
   227  	}()
   228  
   229  	// Nothing has been written to the channel, so waitFinished should not
   230  	// return.  Give it a little bit of time to make sure the goroutine has
   231  	// started.
   232  	select {
   233  	case <-waitFinished:
   234  		t.Errorf("WaitForStartSignal completed but it should not have")
   235  	case <-time.After(50 * time.Millisecond):
   236  		// OK.
   237  	}
   238  
   239  	// Trigger the control server StartRoot method.
   240  	cid := "foo"
   241  	if err := l.ctrl.manager.StartRoot(&cid, nil); err != nil {
   242  		t.Errorf("error calling StartRoot: %v", err)
   243  	}
   244  
   245  	// Now WaitForStartSignal should return (within a short amount of
   246  	// time).
   247  	select {
   248  	case <-waitFinished:
   249  		// OK.
   250  	case <-time.After(50 * time.Millisecond):
   251  		t.Errorf("WaitForStartSignal did not complete but it should have")
   252  	}
   253  
   254  }
   255  
   256  type CreateMountTestcase struct {
   257  	name string
   258  	// Spec that will be used to create the mount manager.  Note
   259  	// that we can't mount procfs without a kernel, so each spec
   260  	// MUST contain something other than procfs mounted at /proc.
   261  	spec specs.Spec
   262  	// Paths that are expected to exist in the resulting fs.
   263  	expectedPaths []string
   264  }
   265  
   266  func createMountTestcases() []*CreateMountTestcase {
   267  	testCases := []*CreateMountTestcase{
   268  		{
   269  			// Only proc.
   270  			name: "only proc mount",
   271  			spec: specs.Spec{
   272  				Root: &specs.Root{
   273  					Path:     os.TempDir(),
   274  					Readonly: true,
   275  				},
   276  				Mounts: []specs.Mount{
   277  					{
   278  						Destination: "/proc",
   279  						Type:        "tmpfs",
   280  					},
   281  				},
   282  			},
   283  			// /proc, /dev, and /sys should always be mounted.
   284  			expectedPaths: []string{"/proc", "/dev", "/sys"},
   285  		},
   286  		{
   287  			// Mount at a deep path, with many components that do
   288  			// not exist in the root.
   289  			name: "deep mount path",
   290  			spec: specs.Spec{
   291  				Root: &specs.Root{
   292  					Path:     os.TempDir(),
   293  					Readonly: true,
   294  				},
   295  				Mounts: []specs.Mount{
   296  					{
   297  						Destination: "/some/very/very/deep/path",
   298  						Type:        "tmpfs",
   299  					},
   300  					{
   301  						Destination: "/proc",
   302  						Type:        "tmpfs",
   303  					},
   304  				},
   305  			},
   306  			// /some/deep/path should be mounted, along with /proc, /dev, and /sys.
   307  			expectedPaths: []string{"/some/very/very/deep/path", "/proc", "/dev", "/sys"},
   308  		},
   309  		{
   310  			// Mounts are nested inside each other.
   311  			name: "nested mounts",
   312  			spec: specs.Spec{
   313  				Root: &specs.Root{
   314  					Path:     os.TempDir(),
   315  					Readonly: true,
   316  				},
   317  				Mounts: []specs.Mount{
   318  					{
   319  						Destination: "/proc",
   320  						Type:        "tmpfs",
   321  					},
   322  					{
   323  						Destination: "/foo",
   324  						Type:        "tmpfs",
   325  					},
   326  					{
   327  						Destination: "/foo/qux",
   328  						Type:        "tmpfs",
   329  					},
   330  					{
   331  						// File mounts with the same prefix.
   332  						Destination: "/foo/qux-quz",
   333  						Type:        "tmpfs",
   334  					},
   335  					{
   336  						Destination: "/foo/bar",
   337  						Type:        "tmpfs",
   338  					},
   339  					{
   340  						Destination: "/foo/bar/baz",
   341  						Type:        "tmpfs",
   342  					},
   343  					{
   344  						// A deep path that is in foo but not the other mounts.
   345  						Destination: "/foo/some/very/very/deep/path",
   346  						Type:        "tmpfs",
   347  					},
   348  				},
   349  			},
   350  			expectedPaths: []string{"/foo", "/foo/bar", "/foo/bar/baz", "/foo/qux",
   351  				"/foo/qux-quz", "/foo/some/very/very/deep/path", "/proc", "/dev", "/sys"},
   352  		},
   353  		{
   354  			name: "mount inside /dev",
   355  			spec: specs.Spec{
   356  				Root: &specs.Root{
   357  					Path:     os.TempDir(),
   358  					Readonly: true,
   359  				},
   360  				Mounts: []specs.Mount{
   361  					{
   362  						Destination: "/proc",
   363  						Type:        "tmpfs",
   364  					},
   365  					{
   366  						Destination: "/dev",
   367  						Type:        "tmpfs",
   368  					},
   369  					{
   370  						// Mounted by runsc by default.
   371  						Destination: "/dev/fd",
   372  						Type:        "tmpfs",
   373  					},
   374  					{
   375  						// Mount with the same prefix.
   376  						Destination: "/dev/fd-foo",
   377  						Type:        "tmpfs",
   378  					},
   379  					{
   380  						// Unsupported fs type.
   381  						Destination: "/dev/mqueue",
   382  						Type:        "mqueue",
   383  					},
   384  					{
   385  						Destination: "/dev/foo",
   386  						Type:        "tmpfs",
   387  					},
   388  					{
   389  						Destination: "/dev/bar",
   390  						Type:        "tmpfs",
   391  					},
   392  				},
   393  			},
   394  			expectedPaths: []string{"/proc", "/dev", "/dev/fd-foo", "/dev/foo", "/dev/bar", "/sys"},
   395  		},
   396  		{
   397  			name: "mounts inside mandatory mounts",
   398  			spec: specs.Spec{
   399  				Root: &specs.Root{
   400  					Path:     os.TempDir(),
   401  					Readonly: true,
   402  				},
   403  				Mounts: []specs.Mount{
   404  					{
   405  						Destination: "/proc",
   406  						Type:        "tmpfs",
   407  					},
   408  					{
   409  						Destination: "/sys/bar",
   410  						Type:        "tmpfs",
   411  					},
   412  					{
   413  						Destination: "/tmp/baz",
   414  						Type:        "tmpfs",
   415  					},
   416  					{
   417  						Destination: "/dev/goo",
   418  						Type:        "tmpfs",
   419  					},
   420  				},
   421  			},
   422  			expectedPaths: []string{"/proc", "/sys", "/sys/bar", "/tmp", "/tmp/baz", "/dev/goo"},
   423  		},
   424  	}
   425  
   426  	return testCases
   427  }
   428  
   429  // Test that MountNamespace can be created with various specs.
   430  func TestCreateMountNamespace(t *testing.T) {
   431  	for _, tc := range createMountTestcases() {
   432  		t.Run(tc.name, func(t *testing.T) {
   433  			conf := testConfig()
   434  			ctx := contexttest.Context(t)
   435  
   436  			sandEnd, cleanup, err := startGofer(tc.spec.Root.Path)
   437  			if err != nil {
   438  				t.Fatalf("failed to create gofer: %v", err)
   439  			}
   440  			defer cleanup()
   441  
   442  			info := containerInfo{
   443  				conf:     conf,
   444  				spec:     &tc.spec,
   445  				goferFDs: []*fd.FD{fd.New(sandEnd)},
   446  			}
   447  
   448  			mntr := newContainerMounter(&info, nil, &podMountHints{}, false /* vfs2Enabled */)
   449  			mns, err := mntr.createMountNamespace(ctx, conf)
   450  			if err != nil {
   451  				t.Fatalf("failed to create mount namespace: %v", err)
   452  			}
   453  			ctx = fs.WithRoot(ctx, mns.Root())
   454  			if err := mntr.mountSubmounts(ctx, conf, mns); err != nil {
   455  				t.Fatalf("failed to create mount namespace: %v", err)
   456  			}
   457  
   458  			root := mns.Root()
   459  			defer root.DecRef(ctx)
   460  			for _, p := range tc.expectedPaths {
   461  				maxTraversals := uint(0)
   462  				if d, err := mns.FindInode(ctx, root, root, p, &maxTraversals); err != nil {
   463  					t.Errorf("expected path %v to exist with spec %v, but got error %v", p, tc.spec, err)
   464  				} else {
   465  					d.DecRef(ctx)
   466  				}
   467  			}
   468  		})
   469  	}
   470  }
   471  
   472  // Test that MountNamespace can be created with various specs.
   473  func TestCreateMountNamespaceVFS2(t *testing.T) {
   474  	for _, tc := range createMountTestcases() {
   475  		t.Run(tc.name, func(t *testing.T) {
   476  			spec := testSpec()
   477  			spec.Mounts = tc.spec.Mounts
   478  			spec.Root = tc.spec.Root
   479  
   480  			t.Logf("Using root: %q", spec.Root.Path)
   481  			l, loaderCleanup, err := createLoader(true /* VFS2 Enabled */, spec)
   482  			if err != nil {
   483  				t.Fatalf("failed to create loader: %v", err)
   484  			}
   485  			defer l.Destroy()
   486  			defer loaderCleanup()
   487  
   488  			mntr := newContainerMounter(&l.root, l.k, l.mountHints, true /* vfs2Enabled */)
   489  			if err := mntr.processHints(l.root.conf, l.root.procArgs.Credentials); err != nil {
   490  				t.Fatalf("failed process hints: %v", err)
   491  			}
   492  
   493  			ctx := l.k.SupervisorContext()
   494  			mns, err := mntr.mountAll(l.root.conf, &l.root.procArgs)
   495  			if err != nil {
   496  				t.Fatalf("mountAll: %v", err)
   497  			}
   498  
   499  			root := mns.Root()
   500  			root.IncRef()
   501  			defer root.DecRef(ctx)
   502  			for _, p := range tc.expectedPaths {
   503  				target := &vfs.PathOperation{
   504  					Root:  root,
   505  					Start: root,
   506  					Path:  fspath.Parse(p),
   507  				}
   508  
   509  				if d, err := l.k.VFS().GetDentryAt(ctx, l.root.procArgs.Credentials, target, &vfs.GetDentryOptions{}); err != nil {
   510  					t.Errorf("expected path %v to exist with spec %v, but got error %v", p, tc.spec, err)
   511  				} else {
   512  					d.DecRef(ctx)
   513  				}
   514  			}
   515  		})
   516  	}
   517  }
   518  
   519  // TestRestoreEnvironment tests that the correct mounts are collected from the spec and config
   520  // in order to build the environment for restoring.
   521  func TestRestoreEnvironment(t *testing.T) {
   522  	testCases := []struct {
   523  		name          string
   524  		spec          *specs.Spec
   525  		ioFDs         []int
   526  		errorExpected bool
   527  		expectedRenv  fs.RestoreEnvironment
   528  	}{
   529  		{
   530  			name: "basic spec test",
   531  			spec: &specs.Spec{
   532  				Root: &specs.Root{
   533  					Path:     os.TempDir(),
   534  					Readonly: true,
   535  				},
   536  				Mounts: []specs.Mount{
   537  					{
   538  						Destination: "/some/very/very/deep/path",
   539  						Type:        "tmpfs",
   540  					},
   541  					{
   542  						Destination: "/proc",
   543  						Type:        "tmpfs",
   544  					},
   545  				},
   546  			},
   547  			ioFDs:         []int{0},
   548  			errorExpected: false,
   549  			expectedRenv: fs.RestoreEnvironment{
   550  				MountSources: map[string][]fs.MountArgs{
   551  					"9p": {
   552  						{
   553  							Dev:        "9pfs-/",
   554  							Flags:      fs.MountSourceFlags{ReadOnly: true},
   555  							DataString: "trans=fd,rfdno=0,wfdno=0,privateunixsocket=true",
   556  						},
   557  					},
   558  					"tmpfs": {
   559  						{
   560  							Dev: "none",
   561  						},
   562  						{
   563  							Dev: "none",
   564  						},
   565  						{
   566  							Dev: "none",
   567  						},
   568  					},
   569  					"devtmpfs": {
   570  						{
   571  							Dev: "none",
   572  						},
   573  					},
   574  					"devpts": {
   575  						{
   576  							Dev: "none",
   577  						},
   578  					},
   579  					"sysfs": {
   580  						{
   581  							Dev: "none",
   582  						},
   583  					},
   584  				},
   585  			},
   586  		},
   587  		{
   588  			name: "bind type test",
   589  			spec: &specs.Spec{
   590  				Root: &specs.Root{
   591  					Path:     os.TempDir(),
   592  					Readonly: true,
   593  				},
   594  				Mounts: []specs.Mount{
   595  					{
   596  						Destination: "/dev/fd-foo",
   597  						Type:        "bind",
   598  					},
   599  				},
   600  			},
   601  			ioFDs:         []int{0, 1},
   602  			errorExpected: false,
   603  			expectedRenv: fs.RestoreEnvironment{
   604  				MountSources: map[string][]fs.MountArgs{
   605  					"9p": {
   606  						{
   607  							Dev:        "9pfs-/",
   608  							Flags:      fs.MountSourceFlags{ReadOnly: true},
   609  							DataString: "trans=fd,rfdno=0,wfdno=0,privateunixsocket=true",
   610  						},
   611  						{
   612  							Dev:        "9pfs-/dev/fd-foo",
   613  							DataString: "trans=fd,rfdno=1,wfdno=1,privateunixsocket=true,cache=remote_revalidating",
   614  						},
   615  					},
   616  					"tmpfs": {
   617  						{
   618  							Dev: "none",
   619  						},
   620  					},
   621  					"devtmpfs": {
   622  						{
   623  							Dev: "none",
   624  						},
   625  					},
   626  					"devpts": {
   627  						{
   628  							Dev: "none",
   629  						},
   630  					},
   631  					"proc": {
   632  						{
   633  							Dev: "none",
   634  						},
   635  					},
   636  					"sysfs": {
   637  						{
   638  							Dev: "none",
   639  						},
   640  					},
   641  				},
   642  			},
   643  		},
   644  		{
   645  			name: "options test",
   646  			spec: &specs.Spec{
   647  				Root: &specs.Root{
   648  					Path:     os.TempDir(),
   649  					Readonly: true,
   650  				},
   651  				Mounts: []specs.Mount{
   652  					{
   653  						Destination: "/dev/fd-foo",
   654  						Type:        "tmpfs",
   655  						Options:     []string{"uid=1022", "noatime"},
   656  					},
   657  				},
   658  			},
   659  			ioFDs:         []int{0},
   660  			errorExpected: false,
   661  			expectedRenv: fs.RestoreEnvironment{
   662  				MountSources: map[string][]fs.MountArgs{
   663  					"9p": {
   664  						{
   665  							Dev:        "9pfs-/",
   666  							Flags:      fs.MountSourceFlags{ReadOnly: true},
   667  							DataString: "trans=fd,rfdno=0,wfdno=0,privateunixsocket=true",
   668  						},
   669  					},
   670  					"tmpfs": {
   671  						{
   672  							Dev:        "none",
   673  							Flags:      fs.MountSourceFlags{NoAtime: true},
   674  							DataString: "uid=1022",
   675  						},
   676  						{
   677  							Dev: "none",
   678  						},
   679  					},
   680  					"devtmpfs": {
   681  						{
   682  							Dev: "none",
   683  						},
   684  					},
   685  					"devpts": {
   686  						{
   687  							Dev: "none",
   688  						},
   689  					},
   690  					"proc": {
   691  						{
   692  							Dev: "none",
   693  						},
   694  					},
   695  					"sysfs": {
   696  						{
   697  							Dev: "none",
   698  						},
   699  					},
   700  				},
   701  			},
   702  		},
   703  	}
   704  	for _, tc := range testCases {
   705  		t.Run(tc.name, func(t *testing.T) {
   706  			conf := testConfig()
   707  			var ioFDs []*fd.FD
   708  			for _, ioFD := range tc.ioFDs {
   709  				ioFDs = append(ioFDs, fd.New(ioFD))
   710  			}
   711  			info := containerInfo{
   712  				conf:     conf,
   713  				spec:     tc.spec,
   714  				goferFDs: ioFDs,
   715  			}
   716  			mntr := newContainerMounter(&info, nil, &podMountHints{}, false /* vfs2Enabled */)
   717  			actualRenv, err := mntr.createRestoreEnvironment(conf)
   718  			if !tc.errorExpected && err != nil {
   719  				t.Fatalf("could not create restore environment for test:%s", tc.name)
   720  			} else if tc.errorExpected {
   721  				if err == nil {
   722  					t.Errorf("expected an error, but no error occurred.")
   723  				}
   724  			} else {
   725  				if !reflect.DeepEqual(*actualRenv, tc.expectedRenv) {
   726  					t.Errorf("restore environments did not match for test:%s\ngot:%+v\nwant:%+v\n", tc.name, *actualRenv, tc.expectedRenv)
   727  				}
   728  			}
   729  		})
   730  	}
   731  }