github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/runsc/fsgofer/fsgofer_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 fsgofer
    16  
    17  import (
    18  	"fmt"
    19  	"io/ioutil"
    20  	"net"
    21  	"os"
    22  	"path"
    23  	"path/filepath"
    24  	"testing"
    25  
    26  	"github.com/syndtr/gocapability/capability"
    27  	"golang.org/x/sys/unix"
    28  	"github.com/SagerNet/gvisor/pkg/fd"
    29  	"github.com/SagerNet/gvisor/pkg/log"
    30  	"github.com/SagerNet/gvisor/pkg/p9"
    31  	"github.com/SagerNet/gvisor/pkg/test/testutil"
    32  	"github.com/SagerNet/gvisor/runsc/specutils"
    33  )
    34  
    35  // Nodoby is the standard UID/GID for the nobody user/group.
    36  const nobody = 65534
    37  
    38  var allOpenFlags = []p9.OpenFlags{p9.ReadOnly, p9.WriteOnly, p9.ReadWrite}
    39  
    40  var (
    41  	allTypes = []uint32{unix.S_IFREG, unix.S_IFDIR, unix.S_IFLNK}
    42  
    43  	// allConfs is set in init().
    44  	allConfs []Config
    45  
    46  	rwConfs = []Config{{ROMount: false}}
    47  	roConfs = []Config{{ROMount: true}}
    48  )
    49  
    50  func init() {
    51  	log.SetLevel(log.Debug)
    52  
    53  	allConfs = append(allConfs, rwConfs...)
    54  	allConfs = append(allConfs, roConfs...)
    55  
    56  	if err := OpenProcSelfFD(); err != nil {
    57  		panic(err)
    58  	}
    59  }
    60  
    61  func configTestName(conf *Config) string {
    62  	if conf.ROMount {
    63  		return "ROMount"
    64  	}
    65  	return "RWMount"
    66  }
    67  
    68  func assertPanic(t *testing.T, f func()) {
    69  	defer func() {
    70  		if r := recover(); r == nil {
    71  			t.Errorf("function did not panic")
    72  		}
    73  	}()
    74  	f()
    75  }
    76  
    77  func testReadWrite(f p9.File, flags p9.OpenFlags, content []byte) error {
    78  	want := make([]byte, len(content))
    79  	copy(want, content)
    80  
    81  	b := []byte("test-1-2-3")
    82  	w, err := f.WriteAt(b, uint64(len(content)))
    83  	if flags == p9.WriteOnly || flags == p9.ReadWrite {
    84  		if err != nil {
    85  			return fmt.Errorf("WriteAt(): %v", err)
    86  		}
    87  		if w != len(b) {
    88  			return fmt.Errorf("WriteAt() was partial, got: %d, want: %d", w, len(b))
    89  		}
    90  		want = append(want, b...)
    91  	} else {
    92  		if e, ok := err.(unix.Errno); !ok || e != unix.EBADF {
    93  			return fmt.Errorf("WriteAt() should have failed, got: %d, want: EBADFD", err)
    94  		}
    95  	}
    96  
    97  	rBuf := make([]byte, len(want))
    98  	r, err := f.ReadAt(rBuf, 0)
    99  	if flags == p9.ReadOnly || flags == p9.ReadWrite {
   100  		if err != nil {
   101  			return fmt.Errorf("ReadAt(): %v", err)
   102  		}
   103  		if r != len(rBuf) {
   104  			return fmt.Errorf("ReadAt() was partial, got: %d, want: %d", r, len(rBuf))
   105  		}
   106  		if string(rBuf) != string(want) {
   107  			return fmt.Errorf("ReadAt() wrong data, got: %s, want: %s", string(rBuf), want)
   108  		}
   109  	} else {
   110  		if e, ok := err.(unix.Errno); !ok || e != unix.EBADF {
   111  			return fmt.Errorf("ReadAt() should have failed, got: %d, want: EBADFD", err)
   112  		}
   113  	}
   114  	return nil
   115  }
   116  
   117  type state struct {
   118  	root     *localFile
   119  	file     *localFile
   120  	conf     Config
   121  	fileType uint32
   122  }
   123  
   124  func (s state) String() string {
   125  	return fmt.Sprintf("type(%v)", s.fileType)
   126  }
   127  
   128  func typeName(fileType uint32) string {
   129  	switch fileType {
   130  	case unix.S_IFREG:
   131  		return "file"
   132  	case unix.S_IFDIR:
   133  		return "directory"
   134  	case unix.S_IFLNK:
   135  		return "symlink"
   136  	default:
   137  		panic(fmt.Sprintf("invalid file type for test: %d", fileType))
   138  	}
   139  }
   140  
   141  func runAll(t *testing.T, test func(*testing.T, state)) {
   142  	runCustom(t, allTypes, allConfs, test)
   143  }
   144  
   145  func runCustom(t *testing.T, types []uint32, confs []Config, test func(*testing.T, state)) {
   146  	for _, c := range confs {
   147  		for _, ft := range types {
   148  			name := fmt.Sprintf("%s/%s", configTestName(&c), typeName(ft))
   149  			t.Run(name, func(t *testing.T) {
   150  				path, name, err := setup(ft)
   151  				if err != nil {
   152  					t.Fatalf("%v", err)
   153  				}
   154  				defer os.RemoveAll(path)
   155  
   156  				a, err := NewAttachPoint(path, c)
   157  				if err != nil {
   158  					t.Fatalf("NewAttachPoint failed: %v", err)
   159  				}
   160  				root, err := a.Attach()
   161  				if err != nil {
   162  					t.Fatalf("Attach failed, err: %v", err)
   163  				}
   164  
   165  				_, file, err := root.Walk([]string{name})
   166  				if err != nil {
   167  					root.Close()
   168  					t.Fatalf("root.Walk({%q}) failed, err: %v", "symlink", err)
   169  				}
   170  
   171  				st := state{
   172  					root:     root.(*localFile),
   173  					file:     file.(*localFile),
   174  					conf:     c,
   175  					fileType: ft,
   176  				}
   177  				test(t, st)
   178  				file.Close()
   179  				root.Close()
   180  			})
   181  		}
   182  	}
   183  }
   184  
   185  func setup(fileType uint32) (string, string, error) {
   186  	path, err := ioutil.TempDir(testutil.TmpDir(), "root-")
   187  	if err != nil {
   188  		return "", "", fmt.Errorf("ioutil.TempDir() failed, err: %v", err)
   189  	}
   190  
   191  	// First attach with writable configuration to setup tree.
   192  	a, err := NewAttachPoint(path, Config{})
   193  	if err != nil {
   194  		return "", "", err
   195  	}
   196  	root, err := a.Attach()
   197  	if err != nil {
   198  		return "", "", fmt.Errorf("Attach failed, err: %v", err)
   199  	}
   200  	defer root.Close()
   201  
   202  	var name string
   203  	switch fileType {
   204  	case unix.S_IFREG:
   205  		name = "file"
   206  		fd, f, _, _, err := root.Create(name, p9.ReadWrite, 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid()))
   207  		if err != nil {
   208  			return "", "", fmt.Errorf("createFile(root, %q) failed, err: %v", "test", err)
   209  		}
   210  		if fd != nil {
   211  			fd.Close()
   212  		}
   213  		defer f.Close()
   214  	case unix.S_IFDIR:
   215  		name = "dir"
   216  		if _, err := root.Mkdir(name, 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != nil {
   217  			return "", "", fmt.Errorf("root.MkDir(%q) failed, err: %v", name, err)
   218  		}
   219  	case unix.S_IFLNK:
   220  		name = "symlink"
   221  		if _, err := root.Symlink("/some/target", name, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != nil {
   222  			return "", "", fmt.Errorf("root.Symlink(%q) failed, err: %v", name, err)
   223  		}
   224  	default:
   225  		panic(fmt.Sprintf("unknown file type %v", fileType))
   226  	}
   227  	return path, name, nil
   228  }
   229  
   230  func createFile(dir *localFile, name string) (*localFile, error) {
   231  	_, f, _, _, err := dir.Create(name, p9.ReadWrite, 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid()))
   232  	if err != nil {
   233  		return nil, err
   234  	}
   235  	return f.(*localFile), nil
   236  }
   237  
   238  func TestReadWrite(t *testing.T) {
   239  	runCustom(t, []uint32{unix.S_IFDIR}, rwConfs, func(t *testing.T, s state) {
   240  		child, err := createFile(s.file, "test")
   241  		if err != nil {
   242  			t.Fatalf("%v: createFile() failed, err: %v", s, err)
   243  		}
   244  		defer child.Close()
   245  		want := []byte("foobar")
   246  		w, err := child.WriteAt(want, 0)
   247  		if err != nil {
   248  			t.Fatalf("%v: Write() failed, err: %v", s, err)
   249  		}
   250  		if w != len(want) {
   251  			t.Fatalf("%v: Write() was partial, got: %d, expected: %d", s, w, len(want))
   252  		}
   253  		for _, flags := range allOpenFlags {
   254  			_, l, err := s.file.Walk([]string{"test"})
   255  			if err != nil {
   256  				t.Fatalf("%v: Walk(%s) failed, err: %v", s, "test", err)
   257  			}
   258  			fd, _, _, err := l.Open(flags)
   259  			if err != nil {
   260  				t.Fatalf("%v: Open(%v) failed, err: %v", s, flags, err)
   261  			}
   262  			if fd != nil {
   263  				defer fd.Close()
   264  			}
   265  			if err := testReadWrite(l, flags, want); err != nil {
   266  				t.Fatalf("%v: testReadWrite(%v) failed: %v", s, flags, err)
   267  			}
   268  		}
   269  	})
   270  }
   271  
   272  func TestCreate(t *testing.T) {
   273  	runCustom(t, []uint32{unix.S_IFDIR}, rwConfs, func(t *testing.T, s state) {
   274  		for i, flags := range allOpenFlags {
   275  			_, l, _, _, err := s.file.Create(fmt.Sprintf("test-%d", i), flags, 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid()))
   276  			if err != nil {
   277  				t.Fatalf("%v, %v: WriteAt() failed, err: %v", s, flags, err)
   278  			}
   279  
   280  			if err := testReadWrite(l, flags, nil); err != nil {
   281  				t.Fatalf("%v: testReadWrite(%v) failed: %v", s, flags, err)
   282  			}
   283  		}
   284  	})
   285  }
   286  
   287  func checkIDs(f p9.File, uid, gid int) error {
   288  	_, _, stat, err := f.GetAttr(p9.AttrMask{UID: true, GID: true})
   289  	if err != nil {
   290  		return fmt.Errorf("GetAttr() failed, err: %v", err)
   291  	}
   292  	if want := p9.UID(uid); stat.UID != want {
   293  		return fmt.Errorf("Wrong UID, want: %v, got: %v", want, stat.UID)
   294  	}
   295  	if want := p9.GID(gid); stat.GID != want {
   296  		return fmt.Errorf("Wrong GID, want: %v, got: %v", want, stat.GID)
   297  	}
   298  	return nil
   299  }
   300  
   301  // TestCreateSetGID checks files/dirs/symlinks are created with the proper
   302  // owner when the parent directory has setgid set,
   303  func TestCreateSetGID(t *testing.T) {
   304  	if !specutils.HasCapabilities(capability.CAP_CHOWN) {
   305  		t.Skipf("Test requires CAP_CHOWN")
   306  	}
   307  
   308  	runCustom(t, []uint32{unix.S_IFDIR}, rwConfs, func(t *testing.T, s state) {
   309  		// Change group and set setgid to the parent dir.
   310  		if err := unix.Chown(s.file.hostPath, os.Getuid(), nobody); err != nil {
   311  			t.Fatalf("Chown() failed: %v", err)
   312  		}
   313  		if err := unix.Chmod(s.file.hostPath, 02777); err != nil {
   314  			t.Fatalf("Chmod() failed: %v", err)
   315  		}
   316  
   317  		t.Run("create", func(t *testing.T) {
   318  			_, l, _, _, err := s.file.Create("test", p9.ReadOnly, 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid()))
   319  			if err != nil {
   320  				t.Fatalf("WriteAt() failed: %v", err)
   321  			}
   322  			defer l.Close()
   323  			if err := checkIDs(l, os.Getuid(), os.Getgid()); err != nil {
   324  				t.Error(err)
   325  			}
   326  		})
   327  
   328  		t.Run("mkdir", func(t *testing.T) {
   329  			_, err := s.file.Mkdir("test-dir", 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid()))
   330  			if err != nil {
   331  				t.Fatalf("WriteAt() failed: %v", err)
   332  			}
   333  			_, l, err := s.file.Walk([]string{"test-dir"})
   334  			if err != nil {
   335  				t.Fatalf("Walk() failed: %v", err)
   336  			}
   337  			defer l.Close()
   338  			if err := checkIDs(l, os.Getuid(), os.Getgid()); err != nil {
   339  				t.Error(err)
   340  			}
   341  		})
   342  
   343  		t.Run("symlink", func(t *testing.T) {
   344  			if _, err := s.file.Symlink("/some/target", "symlink", p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != nil {
   345  				t.Fatalf("Symlink() failed: %v", err)
   346  			}
   347  			_, l, err := s.file.Walk([]string{"symlink"})
   348  			if err != nil {
   349  				t.Fatalf("Walk() failed, err: %v", err)
   350  			}
   351  			defer l.Close()
   352  			if err := checkIDs(l, os.Getuid(), os.Getgid()); err != nil {
   353  				t.Error(err)
   354  			}
   355  		})
   356  
   357  		t.Run("mknod", func(t *testing.T) {
   358  			if _, err := s.file.Mknod("nod", p9.ModeRegular|0777, 0, 0, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != nil {
   359  				t.Fatalf("Mknod() failed: %v", err)
   360  			}
   361  			_, l, err := s.file.Walk([]string{"nod"})
   362  			if err != nil {
   363  				t.Fatalf("Walk() failed, err: %v", err)
   364  			}
   365  			defer l.Close()
   366  			if err := checkIDs(l, os.Getuid(), os.Getgid()); err != nil {
   367  				t.Error(err)
   368  			}
   369  		})
   370  	})
   371  }
   372  
   373  // TestReadWriteDup tests that a file opened in any mode can be dup'ed and
   374  // reopened in any other mode.
   375  func TestReadWriteDup(t *testing.T) {
   376  	runCustom(t, []uint32{unix.S_IFDIR}, rwConfs, func(t *testing.T, s state) {
   377  		child, err := createFile(s.file, "test")
   378  		if err != nil {
   379  			t.Fatalf("%v: createFile() failed, err: %v", s, err)
   380  		}
   381  		defer child.Close()
   382  		want := []byte("foobar")
   383  		w, err := child.WriteAt(want, 0)
   384  		if err != nil {
   385  			t.Fatalf("%v: Write() failed, err: %v", s, err)
   386  		}
   387  		if w != len(want) {
   388  			t.Fatalf("%v: Write() was partial, got: %d, expected: %d", s, w, len(want))
   389  		}
   390  		for _, flags := range allOpenFlags {
   391  			_, l, err := s.file.Walk([]string{"test"})
   392  			if err != nil {
   393  				t.Fatalf("%v: Walk(%s) failed, err: %v", s, "test", err)
   394  			}
   395  			defer l.Close()
   396  			if _, _, _, err := l.Open(flags); err != nil {
   397  				t.Fatalf("%v: Open(%v) failed, err: %v", s, flags, err)
   398  			}
   399  			for _, dupFlags := range allOpenFlags {
   400  				t.Logf("Original flags: %v, dup flags: %v", flags, dupFlags)
   401  				_, dup, err := l.Walk([]string{})
   402  				if err != nil {
   403  					t.Fatalf("%v: Walk(<empty>) failed: %v", s, err)
   404  				}
   405  				defer dup.Close()
   406  				fd, _, _, err := dup.Open(dupFlags)
   407  				if err != nil {
   408  					t.Fatalf("%v: Open(%v) failed: %v", s, flags, err)
   409  				}
   410  				if fd != nil {
   411  					defer fd.Close()
   412  				}
   413  				if err := testReadWrite(dup, dupFlags, want); err != nil {
   414  					t.Fatalf("%v: testReadWrite(%v) failed: %v", s, dupFlags, err)
   415  				}
   416  			}
   417  		}
   418  	})
   419  }
   420  
   421  func TestUnopened(t *testing.T) {
   422  	runCustom(t, []uint32{unix.S_IFREG}, allConfs, func(t *testing.T, s state) {
   423  		b := []byte("foobar")
   424  		if _, err := s.file.WriteAt(b, 0); err != unix.EBADF {
   425  			t.Errorf("%v: WriteAt() should have failed, got: %v, expected: unix.EBADF", s, err)
   426  		}
   427  		if _, err := s.file.ReadAt(b, 0); err != unix.EBADF {
   428  			t.Errorf("%v: ReadAt() should have failed, got: %v, expected: unix.EBADF", s, err)
   429  		}
   430  		if _, err := s.file.Readdir(0, 100); err != unix.EBADF {
   431  			t.Errorf("%v: Readdir() should have failed, got: %v, expected: unix.EBADF", s, err)
   432  		}
   433  		if err := s.file.FSync(); err != unix.EBADF {
   434  			t.Errorf("%v: FSync() should have failed, got: %v, expected: unix.EBADF", s, err)
   435  		}
   436  	})
   437  }
   438  
   439  // TestOpenOPath is a regression test to ensure that a file that cannot be open
   440  // for read is allowed to be open. This was happening because the control file
   441  // was open with O_PATH, but Open() was not checking for it and allowing the
   442  // control file to be reused.
   443  func TestOpenOPath(t *testing.T) {
   444  	runCustom(t, []uint32{unix.S_IFREG}, rwConfs, func(t *testing.T, s state) {
   445  		// Fist remove all permissions on the file.
   446  		if err := s.file.SetAttr(p9.SetAttrMask{Permissions: true}, p9.SetAttr{Permissions: p9.FileMode(0)}); err != nil {
   447  			t.Fatalf("SetAttr(): %v", err)
   448  		}
   449  		// Then walk to the file again to open a new control file.
   450  		filename := filepath.Base(s.file.hostPath)
   451  		_, newFile, err := s.root.Walk([]string{filename})
   452  		if err != nil {
   453  			t.Fatalf("root.Walk(%q): %v", filename, err)
   454  		}
   455  
   456  		if newFile.(*localFile).controlReadable {
   457  			t.Fatalf("control file didn't open with O_PATH: %+v", newFile)
   458  		}
   459  		if _, _, _, err := newFile.Open(p9.ReadOnly); err != unix.EACCES {
   460  			t.Fatalf("Open() should have failed, got: %v, wanted: EACCES", err)
   461  		}
   462  	})
   463  }
   464  
   465  func SetGetAttr(l *localFile, valid p9.SetAttrMask, attr p9.SetAttr) (p9.Attr, error) {
   466  	if err := l.SetAttr(valid, attr); err != nil {
   467  		return p9.Attr{}, err
   468  	}
   469  	_, _, a, err := l.GetAttr(p9.AttrMask{})
   470  	if err != nil {
   471  		return p9.Attr{}, err
   472  	}
   473  	return a, nil
   474  }
   475  
   476  func TestSetAttrPerm(t *testing.T) {
   477  	runCustom(t, allTypes, rwConfs, func(t *testing.T, s state) {
   478  		valid := p9.SetAttrMask{Permissions: true}
   479  		attr := p9.SetAttr{Permissions: 0777}
   480  		got, err := SetGetAttr(s.file, valid, attr)
   481  		if s.fileType == unix.S_IFLNK {
   482  			if err == nil {
   483  				t.Fatalf("%v: SetGetAttr(valid, %v) should have failed", s, attr.Permissions)
   484  			}
   485  		} else {
   486  			if err != nil {
   487  				t.Fatalf("%v: SetGetAttr(valid, %v) failed, err: %v", s, attr.Permissions, err)
   488  			}
   489  			if got.Mode.Permissions() != attr.Permissions {
   490  				t.Errorf("%v: wrong permission, got: %v, expected: %v", s, got.Mode.Permissions(), attr.Permissions)
   491  			}
   492  		}
   493  	})
   494  }
   495  
   496  func TestSetAttrSize(t *testing.T) {
   497  	runCustom(t, allTypes, rwConfs, func(t *testing.T, s state) {
   498  		for _, size := range []uint64{1024, 0, 1024 * 1024} {
   499  			valid := p9.SetAttrMask{Size: true}
   500  			attr := p9.SetAttr{Size: size}
   501  			got, err := SetGetAttr(s.file, valid, attr)
   502  			if s.fileType == unix.S_IFLNK || s.fileType == unix.S_IFDIR {
   503  				if err == nil {
   504  					t.Fatalf("%v: SetGetAttr(valid, %v) should have failed", s, attr.Permissions)
   505  				}
   506  				// Run for one size only, they will all fail the same way.
   507  				return
   508  			}
   509  			if err != nil {
   510  				t.Fatalf("%v: SetGetAttr(valid, %v) failed, err: %v", s, attr.Size, err)
   511  			}
   512  			if got.Size != size {
   513  				t.Errorf("%v: wrong size, got: %v, expected: %v", s, got.Size, size)
   514  			}
   515  		}
   516  	})
   517  }
   518  
   519  func TestSetAttrTime(t *testing.T) {
   520  	runCustom(t, allTypes, rwConfs, func(t *testing.T, s state) {
   521  		valid := p9.SetAttrMask{ATime: true, ATimeNotSystemTime: true}
   522  		attr := p9.SetAttr{ATimeSeconds: 123, ATimeNanoSeconds: 456}
   523  		got, err := SetGetAttr(s.file, valid, attr)
   524  		if err != nil {
   525  			t.Fatalf("%v: SetGetAttr(valid, %v:%v) failed, err: %v", s, attr.ATimeSeconds, attr.ATimeNanoSeconds, err)
   526  		}
   527  		if got.ATimeSeconds != 123 {
   528  			t.Errorf("%v: wrong ATimeSeconds, got: %v, expected: %v", s, got.ATimeSeconds, 123)
   529  		}
   530  		if got.ATimeNanoSeconds != 456 {
   531  			t.Errorf("%v: wrong ATimeNanoSeconds, got: %v, expected: %v", s, got.ATimeNanoSeconds, 456)
   532  		}
   533  
   534  		valid = p9.SetAttrMask{MTime: true, MTimeNotSystemTime: true}
   535  		attr = p9.SetAttr{MTimeSeconds: 789, MTimeNanoSeconds: 012}
   536  		got, err = SetGetAttr(s.file, valid, attr)
   537  		if err != nil {
   538  			t.Fatalf("%v: SetGetAttr(valid, %v:%v) failed, err: %v", s, attr.MTimeSeconds, attr.MTimeNanoSeconds, err)
   539  		}
   540  		if got.MTimeSeconds != 789 {
   541  			t.Errorf("%v: wrong MTimeSeconds, got: %v, expected: %v", s, got.MTimeSeconds, 789)
   542  		}
   543  		if got.MTimeNanoSeconds != 012 {
   544  			t.Errorf("%v: wrong MTimeNanoSeconds, got: %v, expected: %v", s, got.MTimeNanoSeconds, 012)
   545  		}
   546  	})
   547  }
   548  
   549  func TestSetAttrOwner(t *testing.T) {
   550  	if !specutils.HasCapabilities(capability.CAP_CHOWN) {
   551  		t.Skipf("SetAttr(owner) test requires CAP_CHOWN, running as %d", os.Getuid())
   552  	}
   553  
   554  	runCustom(t, allTypes, rwConfs, func(t *testing.T, s state) {
   555  		newUID := os.Getuid() + 1
   556  		valid := p9.SetAttrMask{UID: true}
   557  		attr := p9.SetAttr{UID: p9.UID(newUID)}
   558  		got, err := SetGetAttr(s.file, valid, attr)
   559  		if err != nil {
   560  			t.Fatalf("%v: SetGetAttr(valid, %v) failed, err: %v", s, attr.UID, err)
   561  		}
   562  		if got.UID != p9.UID(newUID) {
   563  			t.Errorf("%v: wrong uid, got: %v, expected: %v", s, got.UID, newUID)
   564  		}
   565  	})
   566  }
   567  
   568  func SetGetXattr(l *localFile, name string, value string) error {
   569  	if err := l.SetXattr(name, value, 0 /* flags */); err != nil {
   570  		return err
   571  	}
   572  	ret, err := l.GetXattr(name, uint64(len(value)))
   573  	if err != nil {
   574  		return err
   575  	}
   576  	if ret != value {
   577  		return fmt.Errorf("Got value %s, want %s", ret, value)
   578  	}
   579  	return nil
   580  }
   581  
   582  func TestSetGetDisabledXattr(t *testing.T) {
   583  	runCustom(t, []uint32{unix.S_IFREG}, rwConfs, func(t *testing.T, s state) {
   584  		name := "user.merkle.offset"
   585  		value := "tmp"
   586  		err := SetGetXattr(s.file, name, value)
   587  		if err == nil {
   588  			t.Fatalf("%v: SetGetXattr should have failed", s)
   589  		}
   590  	})
   591  }
   592  
   593  func TestSetGetXattr(t *testing.T) {
   594  	runCustom(t, []uint32{unix.S_IFREG}, []Config{{ROMount: false, EnableVerityXattr: true}}, func(t *testing.T, s state) {
   595  		name := "user.merkle.offset"
   596  		value := "tmp"
   597  		err := SetGetXattr(s.file, name, value)
   598  		if err != nil {
   599  			t.Fatalf("%v: SetGetXattr failed, err: %v", s, err)
   600  		}
   601  	})
   602  }
   603  
   604  func TestLink(t *testing.T) {
   605  	if !specutils.HasCapabilities(capability.CAP_DAC_READ_SEARCH) {
   606  		t.Skipf("Link test requires CAP_DAC_READ_SEARCH, running as %d", os.Getuid())
   607  	}
   608  	runCustom(t, allTypes, rwConfs, func(t *testing.T, s state) {
   609  		const dirName = "linkdir"
   610  		const linkFile = "link"
   611  		if _, err := s.root.Mkdir(dirName, 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != nil {
   612  			t.Fatalf("%v: MkDir(%s) failed, err: %v", s, dirName, err)
   613  		}
   614  		_, dir, err := s.root.Walk([]string{dirName})
   615  		if err != nil {
   616  			t.Fatalf("%v: Walk({%s}) failed, err: %v", s, dirName, err)
   617  		}
   618  
   619  		err = dir.Link(s.file, linkFile)
   620  		if s.fileType == unix.S_IFDIR {
   621  			if err != unix.EPERM {
   622  				t.Errorf("%v: Link(target, %s) should have failed, got: %v, expected: unix.EPERM", s, linkFile, err)
   623  			}
   624  			return
   625  		}
   626  		if err != nil {
   627  			t.Errorf("%v: Link(target, %s) failed, err: %v", s, linkFile, err)
   628  		}
   629  	})
   630  }
   631  
   632  func TestROMountChecks(t *testing.T) {
   633  	const want = unix.EROFS
   634  	uid := p9.UID(os.Getuid())
   635  	gid := p9.GID(os.Getgid())
   636  
   637  	runCustom(t, allTypes, roConfs, func(t *testing.T, s state) {
   638  		if s.fileType != unix.S_IFLNK {
   639  			if _, _, _, err := s.file.Open(p9.WriteOnly); err != want {
   640  				t.Errorf("Open() should have failed, got: %v, expected: %v", err, want)
   641  			}
   642  			if _, _, _, err := s.file.Open(p9.ReadWrite); err != want {
   643  				t.Errorf("Open() should have failed, got: %v, expected: %v", err, want)
   644  			}
   645  			if _, _, _, err := s.file.Open(p9.ReadOnly | p9.OpenTruncate); err != want {
   646  				t.Errorf("Open() should have failed, got: %v, expected: %v", err, want)
   647  			}
   648  			f, _, _, err := s.file.Open(p9.ReadOnly)
   649  			if err != nil {
   650  				t.Errorf("Open() failed: %v", err)
   651  			}
   652  			if f != nil {
   653  				_ = f.Close()
   654  			}
   655  		}
   656  
   657  		if _, _, _, _, err := s.file.Create("some_file", p9.ReadWrite, 0777, uid, gid); err != want {
   658  			t.Errorf("Create() should have failed, got: %v, expected: %v", err, want)
   659  		}
   660  		if _, err := s.file.Mkdir("some_dir", 0777, uid, gid); err != want {
   661  			t.Errorf("MkDir() should have failed, got: %v, expected: %v", err, want)
   662  		}
   663  		if err := s.file.RenameAt("some_file", s.file, "other_file"); err != want {
   664  			t.Errorf("Rename() should have failed, got: %v, expected: %v", err, want)
   665  		}
   666  		if _, err := s.file.Symlink("some_place", "some_symlink", uid, gid); err != want {
   667  			t.Errorf("Symlink() should have failed, got: %v, expected: %v", err, want)
   668  		}
   669  		if err := s.file.UnlinkAt("some_file", 0); err != want {
   670  			t.Errorf("UnlinkAt() should have failed, got: %v, expected: %v", err, want)
   671  		}
   672  		if err := s.file.Link(s.file, "some_link"); err != want {
   673  			t.Errorf("Link() should have failed, got: %v, expected: %v", err, want)
   674  		}
   675  		if _, err := s.file.Mknod("some-nod", 0777, 1, 2, uid, gid); err != want {
   676  			t.Errorf("Mknod() should have failed, got: %v, expected: %v", err, want)
   677  		}
   678  
   679  		valid := p9.SetAttrMask{Size: true}
   680  		attr := p9.SetAttr{Size: 0}
   681  		if err := s.file.SetAttr(valid, attr); err != want {
   682  			t.Errorf("SetAttr() should have failed, got: %v, expected: %v", err, want)
   683  		}
   684  	})
   685  }
   686  
   687  func TestWalkNotFound(t *testing.T) {
   688  	runCustom(t, []uint32{unix.S_IFDIR}, allConfs, func(t *testing.T, s state) {
   689  		if _, _, err := s.file.Walk([]string{"nobody-here"}); err != unix.ENOENT {
   690  			t.Errorf("Walk(%q) should have failed, got: %v, expected: unix.ENOENT", "nobody-here", err)
   691  		}
   692  		if _, _, err := s.file.Walk([]string{"nobody", "here"}); err != unix.ENOENT {
   693  			t.Errorf("Walk(%q) should have failed, got: %v, expected: unix.ENOENT", "nobody/here", err)
   694  		}
   695  		if !s.conf.ROMount {
   696  			if _, err := s.file.Mkdir("dir", 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != nil {
   697  				t.Fatalf("MkDir(dir) failed, err: %v", err)
   698  			}
   699  			if _, _, err := s.file.Walk([]string{"dir", "nobody-here"}); err != unix.ENOENT {
   700  				t.Errorf("Walk(%q) should have failed, got: %v, expected: unix.ENOENT", "dir/nobody-here", err)
   701  			}
   702  		}
   703  	})
   704  }
   705  
   706  func TestWalkDup(t *testing.T) {
   707  	runAll(t, func(t *testing.T, s state) {
   708  		_, dup, err := s.file.Walk([]string{})
   709  		if err != nil {
   710  			t.Fatalf("%v: Walk(nil) failed, err: %v", s, err)
   711  		}
   712  		// Check that 'dup' is usable.
   713  		if _, _, _, err := dup.GetAttr(p9.AttrMask{}); err != nil {
   714  			t.Errorf("%v: GetAttr() failed, err: %v", s, err)
   715  		}
   716  	})
   717  }
   718  
   719  func TestWalkMultiple(t *testing.T) {
   720  	runCustom(t, []uint32{unix.S_IFDIR}, rwConfs, func(t *testing.T, s state) {
   721  		var names []string
   722  		var parent p9.File = s.file
   723  		for i := 0; i < 5; i++ {
   724  			name := fmt.Sprintf("dir%d", i)
   725  			names = append(names, name)
   726  
   727  			if _, err := parent.Mkdir(name, 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != nil {
   728  				t.Fatalf("MkDir(%q) failed, err: %v", name, err)
   729  			}
   730  
   731  			var err error
   732  			_, parent, err = s.file.Walk(names)
   733  			if err != nil {
   734  				t.Errorf("Walk(%q): %v", name, err)
   735  			}
   736  		}
   737  	})
   738  }
   739  
   740  func TestReaddir(t *testing.T) {
   741  	runCustom(t, []uint32{unix.S_IFDIR}, rwConfs, func(t *testing.T, s state) {
   742  		name := "dir"
   743  		if _, err := s.file.Mkdir(name, 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != nil {
   744  			t.Fatalf("%v: MkDir(%s) failed, err: %v", s, name, err)
   745  		}
   746  		name = "symlink"
   747  		if _, err := s.file.Symlink("/some/target", name, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != nil {
   748  			t.Fatalf("%v: Symlink(%q) failed, err: %v", s, name, err)
   749  		}
   750  		name = "file"
   751  		_, f, _, _, err := s.file.Create(name, p9.ReadWrite, 0555, p9.UID(os.Getuid()), p9.GID(os.Getgid()))
   752  		if err != nil {
   753  			t.Fatalf("%v: createFile(root, %q) failed, err: %v", s, name, err)
   754  		}
   755  		f.Close()
   756  
   757  		if _, _, _, err := s.file.Open(p9.ReadOnly); err != nil {
   758  			t.Fatalf("%v: Open(ReadOnly) failed, err: %v", s, err)
   759  		}
   760  
   761  		dirents, err := s.file.Readdir(0, 10)
   762  		if err != nil {
   763  			t.Fatalf("%v: Readdir(0, 10) failed, err: %v", s, err)
   764  		}
   765  		if len(dirents) != 3 {
   766  			t.Fatalf("%v: Readdir(0, 10) wrong number of items, got: %v, expected: 3", s, len(dirents))
   767  		}
   768  		var dir, symlink, file bool
   769  		for _, d := range dirents {
   770  			switch d.Name {
   771  			case "dir":
   772  				if d.Type != p9.TypeDir {
   773  					t.Errorf("%v: dirent.Type got: %v, expected: %v", s, d.Type, p9.TypeDir)
   774  				}
   775  				dir = true
   776  			case "symlink":
   777  				if d.Type != p9.TypeSymlink {
   778  					t.Errorf("%v: dirent.Type got: %v, expected: %v", s, d.Type, p9.TypeSymlink)
   779  				}
   780  				symlink = true
   781  			case "file":
   782  				if d.Type != p9.TypeRegular {
   783  					t.Errorf("%v: dirent.Type got: %v, expected: %v", s, d.Type, p9.TypeRegular)
   784  				}
   785  				file = true
   786  			default:
   787  				t.Errorf("%v: dirent.Name got: %v", s, d.Name)
   788  			}
   789  
   790  			_, f, err := s.file.Walk([]string{d.Name})
   791  			if err != nil {
   792  				t.Fatalf("%v: Walk({%s}) failed, err: %v", s, d.Name, err)
   793  			}
   794  			_, _, a, err := f.GetAttr(p9.AttrMask{})
   795  			if err != nil {
   796  				t.Fatalf("%v: GetAttr() failed, err: %v", s, err)
   797  			}
   798  			if d.Type != a.Mode.QIDType() {
   799  				t.Errorf("%v: dirent.Type different than GetAttr().Mode.QIDType(), got: %v, expected: %v", s, d.Type, a.Mode.QIDType())
   800  			}
   801  		}
   802  		if !dir || !symlink || !file {
   803  			t.Errorf("%v: Readdir(0, 10) wrong files returned, dir: %v, symlink: %v, file: %v", s, dir, symlink, file)
   804  		}
   805  	})
   806  }
   807  
   808  // Test that attach point can be written to when it points to a file, e.g.
   809  // /etc/hosts.
   810  func TestAttachFile(t *testing.T) {
   811  	conf := Config{ROMount: false}
   812  	dir, err := ioutil.TempDir("", "root-")
   813  	if err != nil {
   814  		t.Fatalf("ioutil.TempDir() failed, err: %v", err)
   815  	}
   816  	defer os.RemoveAll(dir)
   817  
   818  	path := path.Join(dir, "test")
   819  	if _, err := os.Create(path); err != nil {
   820  		t.Fatalf("os.Create(%q) failed, err: %v", path, err)
   821  	}
   822  
   823  	a, err := NewAttachPoint(path, conf)
   824  	if err != nil {
   825  		t.Fatalf("NewAttachPoint failed: %v", err)
   826  	}
   827  	root, err := a.Attach()
   828  	if err != nil {
   829  		t.Fatalf("Attach failed, err: %v", err)
   830  	}
   831  
   832  	if _, _, _, err := root.Open(p9.ReadWrite); err != nil {
   833  		t.Fatalf("Open(ReadWrite) failed, err: %v", err)
   834  	}
   835  	defer root.Close()
   836  
   837  	b := []byte("foobar")
   838  	w, err := root.WriteAt(b, 0)
   839  	if err != nil {
   840  		t.Fatalf("Write() failed, err: %v", err)
   841  	}
   842  	if w != len(b) {
   843  		t.Fatalf("Write() was partial, got: %d, expected: %d", w, len(b))
   844  	}
   845  	rBuf := make([]byte, len(b))
   846  	r, err := root.ReadAt(rBuf, 0)
   847  	if err != nil {
   848  		t.Fatalf("ReadAt() failed, err: %v", err)
   849  	}
   850  	if r != len(rBuf) {
   851  		t.Fatalf("ReadAt() was partial, got: %d, expected: %d", r, len(rBuf))
   852  	}
   853  	if string(rBuf) != "foobar" {
   854  		t.Fatalf("ReadAt() wrong data, got: %s, expected: %s", string(rBuf), "foobar")
   855  	}
   856  }
   857  
   858  func TestAttachInvalidType(t *testing.T) {
   859  	dir, err := ioutil.TempDir("", "attach-")
   860  	if err != nil {
   861  		t.Fatalf("ioutil.TempDir() failed, err: %v", err)
   862  	}
   863  	defer os.RemoveAll(dir)
   864  
   865  	fifo := filepath.Join(dir, "fifo")
   866  	if err := unix.Mkfifo(fifo, 0755); err != nil {
   867  		t.Fatalf("Mkfifo(%q): %v", fifo, err)
   868  	}
   869  
   870  	dirFile, err := os.Open(dir)
   871  	if err != nil {
   872  		t.Fatalf("Open(%s): %v", dir, err)
   873  	}
   874  	defer dirFile.Close()
   875  
   876  	// Bind a socket via /proc to be sure that a length of a socket path
   877  	// is less than UNIX_PATH_MAX.
   878  	socket := filepath.Join(fmt.Sprintf("/proc/self/fd/%d", dirFile.Fd()), "socket")
   879  	l, err := net.Listen("unix", socket)
   880  	if err != nil {
   881  		t.Fatalf("net.Listen(unix, %q): %v", socket, err)
   882  	}
   883  	defer l.Close()
   884  
   885  	for _, tc := range []struct {
   886  		name string
   887  		path string
   888  	}{
   889  		{name: "fifo", path: fifo},
   890  		{name: "socket", path: socket},
   891  	} {
   892  		t.Run(tc.name, func(t *testing.T) {
   893  			conf := Config{ROMount: false}
   894  			a, err := NewAttachPoint(tc.path, conf)
   895  			if err != nil {
   896  				t.Fatalf("NewAttachPoint failed: %v", err)
   897  			}
   898  			f, err := a.Attach()
   899  			if f != nil || err == nil {
   900  				t.Fatalf("Attach should have failed, got (%v, %v)", f, err)
   901  			}
   902  		})
   903  	}
   904  }
   905  
   906  func TestDoubleAttachError(t *testing.T) {
   907  	conf := Config{ROMount: false}
   908  	root, err := ioutil.TempDir("", "root-")
   909  	if err != nil {
   910  		t.Fatalf("ioutil.TempDir() failed, err: %v", err)
   911  	}
   912  	defer os.RemoveAll(root)
   913  	a, err := NewAttachPoint(root, conf)
   914  	if err != nil {
   915  		t.Fatalf("NewAttachPoint failed: %v", err)
   916  	}
   917  
   918  	if _, err := a.Attach(); err != nil {
   919  		t.Fatalf("Attach failed: %v", err)
   920  	}
   921  	if _, err := a.Attach(); err == nil {
   922  		t.Fatalf("Attach should have failed, got %v want non-nil", err)
   923  	}
   924  }
   925  
   926  func TestTruncate(t *testing.T) {
   927  	runCustom(t, []uint32{unix.S_IFDIR}, rwConfs, func(t *testing.T, s state) {
   928  		child, err := createFile(s.file, "test")
   929  		if err != nil {
   930  			t.Fatalf("createFile() failed: %v", err)
   931  		}
   932  		defer child.Close()
   933  		want := []byte("foobar")
   934  		w, err := child.WriteAt(want, 0)
   935  		if err != nil {
   936  			t.Fatalf("Write() failed: %v", err)
   937  		}
   938  		if w != len(want) {
   939  			t.Fatalf("Write() was partial, got: %d, expected: %d", w, len(want))
   940  		}
   941  
   942  		_, l, err := s.file.Walk([]string{"test"})
   943  		if err != nil {
   944  			t.Fatalf("Walk(%s) failed: %v", "test", err)
   945  		}
   946  		if _, _, _, err := l.Open(p9.ReadOnly | p9.OpenTruncate); err != nil {
   947  			t.Fatalf("Open() failed: %v", err)
   948  		}
   949  		_, mask, attr, err := l.GetAttr(p9.AttrMask{Size: true})
   950  		if err != nil {
   951  			t.Fatalf("GetAttr() failed: %v", err)
   952  		}
   953  		if !mask.Size {
   954  			t.Fatalf("GetAttr() didn't return size: %+v", mask)
   955  		}
   956  		if attr.Size != 0 {
   957  			t.Fatalf("truncate didn't work, want: 0, got: %d", attr.Size)
   958  		}
   959  	})
   960  }
   961  
   962  func TestMknod(t *testing.T) {
   963  	runCustom(t, []uint32{unix.S_IFDIR}, rwConfs, func(t *testing.T, s state) {
   964  		_, err := s.file.Mknod("test", p9.ModeRegular|0777, 1, 2, p9.UID(os.Getuid()), p9.GID(os.Getgid()))
   965  		if err != nil {
   966  			t.Fatalf("Mknod() failed: %v", err)
   967  		}
   968  
   969  		_, f, err := s.file.Walk([]string{"test"})
   970  		if err != nil {
   971  			t.Fatalf("Walk() failed: %v", err)
   972  		}
   973  		fd, _, _, err := f.Open(p9.ReadWrite)
   974  		if err != nil {
   975  			t.Fatalf("Open() failed: %v", err)
   976  		}
   977  		if fd != nil {
   978  			defer fd.Close()
   979  		}
   980  		if err := testReadWrite(f, p9.ReadWrite, nil); err != nil {
   981  			t.Fatalf("testReadWrite() failed: %v", err)
   982  		}
   983  	})
   984  }
   985  
   986  func BenchmarkWalkOne(b *testing.B) {
   987  	path, name, err := setup(unix.S_IFDIR)
   988  	if err != nil {
   989  		b.Fatalf("%v", err)
   990  	}
   991  	defer os.RemoveAll(path)
   992  
   993  	a, err := NewAttachPoint(path, Config{})
   994  	if err != nil {
   995  		b.Fatalf("NewAttachPoint failed: %v", err)
   996  	}
   997  	root, err := a.Attach()
   998  	if err != nil {
   999  		b.Fatalf("Attach failed, err: %v", err)
  1000  	}
  1001  	defer root.Close()
  1002  
  1003  	names := []string{name}
  1004  	files := make([]p9.File, 0, 1000)
  1005  
  1006  	b.ResetTimer()
  1007  	for i := 0; i < b.N; i++ {
  1008  		_, file, err := root.Walk(names)
  1009  		if err != nil {
  1010  			b.Fatalf("Walk(%q): %v", name, err)
  1011  		}
  1012  		files = append(files, file)
  1013  
  1014  		// Avoid running out of FDs.
  1015  		if len(files) == cap(files) {
  1016  			b.StopTimer()
  1017  			for _, file := range files {
  1018  				file.Close()
  1019  			}
  1020  			files = files[:0]
  1021  			b.StartTimer()
  1022  		}
  1023  	}
  1024  
  1025  	b.StopTimer()
  1026  	for _, file := range files {
  1027  		file.Close()
  1028  	}
  1029  }
  1030  
  1031  func BenchmarkCreate(b *testing.B) {
  1032  	path, _, err := setup(unix.S_IFDIR)
  1033  	if err != nil {
  1034  		b.Fatalf("%v", err)
  1035  	}
  1036  	defer os.RemoveAll(path)
  1037  
  1038  	a, err := NewAttachPoint(path, Config{})
  1039  	if err != nil {
  1040  		b.Fatalf("NewAttachPoint failed: %v", err)
  1041  	}
  1042  	root, err := a.Attach()
  1043  	if err != nil {
  1044  		b.Fatalf("Attach failed, err: %v", err)
  1045  	}
  1046  	defer root.Close()
  1047  
  1048  	files := make([]p9.File, 0, 500)
  1049  	fds := make([]*fd.FD, 0, 500)
  1050  	uid := p9.UID(os.Getuid())
  1051  	gid := p9.GID(os.Getgid())
  1052  
  1053  	b.ResetTimer()
  1054  	for i := 0; i < b.N; i++ {
  1055  		name := fmt.Sprintf("same-%d", i)
  1056  		fd, file, _, _, err := root.Create(name, p9.ReadOnly, 0777, uid, gid)
  1057  		if err != nil {
  1058  			b.Fatalf("Create(%q): %v", name, err)
  1059  		}
  1060  		files = append(files, file)
  1061  		if fd != nil {
  1062  			fds = append(fds, fd)
  1063  		}
  1064  
  1065  		// Avoid running out of FDs.
  1066  		if len(files) == cap(files) {
  1067  			b.StopTimer()
  1068  			for _, file := range files {
  1069  				file.Close()
  1070  			}
  1071  			files = files[:0]
  1072  			for _, fd := range fds {
  1073  				fd.Close()
  1074  			}
  1075  			fds = fds[:0]
  1076  			b.StartTimer()
  1077  		}
  1078  	}
  1079  
  1080  	b.StopTimer()
  1081  	for _, file := range files {
  1082  		file.Close()
  1083  	}
  1084  	for _, fd := range fds {
  1085  		fd.Close()
  1086  	}
  1087  }
  1088  
  1089  func BenchmarkCreateDiffOwner(b *testing.B) {
  1090  	if !specutils.HasCapabilities(capability.CAP_CHOWN) {
  1091  		b.Skipf("Test requires CAP_CHOWN")
  1092  	}
  1093  
  1094  	path, _, err := setup(unix.S_IFDIR)
  1095  	if err != nil {
  1096  		b.Fatalf("%v", err)
  1097  	}
  1098  	defer os.RemoveAll(path)
  1099  
  1100  	a, err := NewAttachPoint(path, Config{})
  1101  	if err != nil {
  1102  		b.Fatalf("NewAttachPoint failed: %v", err)
  1103  	}
  1104  	root, err := a.Attach()
  1105  	if err != nil {
  1106  		b.Fatalf("Attach failed, err: %v", err)
  1107  	}
  1108  	defer root.Close()
  1109  
  1110  	files := make([]p9.File, 0, 500)
  1111  	fds := make([]*fd.FD, 0, 500)
  1112  	gid := p9.GID(os.Getgid())
  1113  
  1114  	b.ResetTimer()
  1115  	for i := 0; i < b.N; i++ {
  1116  		name := fmt.Sprintf("diff-%d", i)
  1117  		fd, file, _, _, err := root.Create(name, p9.ReadOnly, 0777, nobody, gid)
  1118  		if err != nil {
  1119  			b.Fatalf("Create(%q): %v", name, err)
  1120  		}
  1121  		files = append(files, file)
  1122  		if fd != nil {
  1123  			fds = append(fds, fd)
  1124  		}
  1125  
  1126  		// Avoid running out of FDs.
  1127  		if len(files) == cap(files) {
  1128  			b.StopTimer()
  1129  			for _, file := range files {
  1130  				file.Close()
  1131  			}
  1132  			files = files[:0]
  1133  			for _, fd := range fds {
  1134  				fd.Close()
  1135  			}
  1136  			fds = fds[:0]
  1137  			b.StartTimer()
  1138  		}
  1139  	}
  1140  
  1141  	b.StopTimer()
  1142  	for _, file := range files {
  1143  		file.Close()
  1144  	}
  1145  	for _, fd := range fds {
  1146  		fd.Close()
  1147  	}
  1148  }