summaryrefslogtreecommitdiffhomepage
path: root/pkg/lisafs/testsuite/testsuite.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/lisafs/testsuite/testsuite.go')
-rw-r--r--pkg/lisafs/testsuite/testsuite.go637
1 files changed, 0 insertions, 637 deletions
diff --git a/pkg/lisafs/testsuite/testsuite.go b/pkg/lisafs/testsuite/testsuite.go
deleted file mode 100644
index 476ff76a5..000000000
--- a/pkg/lisafs/testsuite/testsuite.go
+++ /dev/null
@@ -1,637 +0,0 @@
-// Copyright 2021 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package testsuite provides a integration testing suite for lisafs.
-// These tests are intended for servers serving the local filesystem.
-package testsuite
-
-import (
- "bytes"
- "fmt"
- "io/ioutil"
- "math/rand"
- "os"
- "testing"
- "time"
-
- "github.com/syndtr/gocapability/capability"
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/abi/linux"
- "gvisor.dev/gvisor/pkg/context"
- "gvisor.dev/gvisor/pkg/lisafs"
- "gvisor.dev/gvisor/pkg/unet"
-)
-
-// Tester is the client code using this test suite. This interface abstracts
-// away all the caller specific details.
-type Tester interface {
- // NewServer returns a new instance of the tester server.
- NewServer(t *testing.T) *lisafs.Server
-
- // LinkSupported returns true if the backing server supports LinkAt.
- LinkSupported() bool
-
- // SetUserGroupIDSupported returns true if the backing server supports
- // changing UID/GID for files.
- SetUserGroupIDSupported() bool
-}
-
-// RunAllLocalFSTests runs all local FS tests as subtests.
-func RunAllLocalFSTests(t *testing.T, tester Tester) {
- for name, testFn := range localFSTests {
- t.Run(name, func(t *testing.T) {
- runServerClient(t, tester, testFn)
- })
- }
-}
-
-type testFunc func(context.Context, *testing.T, Tester, lisafs.ClientFD)
-
-var localFSTests map[string]testFunc = map[string]testFunc{
- "Stat": testStat,
- "RegularFileIO": testRegularFileIO,
- "RegularFileOpen": testRegularFileOpen,
- "SetStat": testSetStat,
- "Allocate": testAllocate,
- "StatFS": testStatFS,
- "Unlink": testUnlink,
- "Symlink": testSymlink,
- "HardLink": testHardLink,
- "Walk": testWalk,
- "Rename": testRename,
- "Mknod": testMknod,
- "Getdents": testGetdents,
-}
-
-func runServerClient(t *testing.T, tester Tester, testFn testFunc) {
- mountPath, err := ioutil.TempDir(os.Getenv("TEST_TMPDIR"), "")
- if err != nil {
- t.Fatalf("creation of temporary mountpoint failed: %v", err)
- }
- defer os.RemoveAll(mountPath)
-
- // fsgofer should run with a umask of 0, because we want to preserve file
- // modes exactly for testing purposes.
- unix.Umask(0)
-
- serverSocket, clientSocket, err := unet.SocketPair(false)
- if err != nil {
- t.Fatalf("socketpair got err %v expected nil", err)
- }
-
- server := tester.NewServer(t)
- conn, err := server.CreateConnection(serverSocket, false /* readonly */)
- if err != nil {
- t.Fatalf("starting connection failed: %v", err)
- return
- }
- server.StartConnection(conn)
-
- c, root, err := lisafs.NewClient(clientSocket, mountPath)
- if err != nil {
- t.Fatalf("client creation failed: %v", err)
- }
-
- if !root.ControlFD.Ok() {
- t.Fatalf("root control FD is not valid")
- }
- rootFile := c.NewFD(root.ControlFD)
-
- ctx := context.Background()
- testFn(ctx, t, tester, rootFile)
- closeFD(ctx, t, rootFile)
-
- c.Close() // This should trigger client and server shutdown.
- server.Wait()
-}
-
-func closeFD(ctx context.Context, t testing.TB, fdLisa lisafs.ClientFD) {
- if err := fdLisa.Close(ctx); err != nil {
- t.Errorf("failed to close FD: %v", err)
- }
-}
-
-func statTo(ctx context.Context, t *testing.T, fdLisa lisafs.ClientFD, stat *linux.Statx) {
- if err := fdLisa.StatTo(ctx, stat); err != nil {
- t.Fatalf("stat failed: %v", err)
- }
-}
-
-func openCreateFile(ctx context.Context, t *testing.T, fdLisa lisafs.ClientFD, name string) (lisafs.ClientFD, linux.Statx, lisafs.ClientFD, int) {
- child, childFD, childHostFD, err := fdLisa.OpenCreateAt(ctx, name, unix.O_RDWR, 0777, lisafs.UID(unix.Getuid()), lisafs.GID(unix.Getgid()))
- if err != nil {
- t.Fatalf("OpenCreateAt failed: %v", err)
- }
- if childHostFD == -1 {
- t.Error("no host FD donated")
- }
- client := fdLisa.Client()
- return client.NewFD(child.ControlFD), child.Stat, fdLisa.Client().NewFD(childFD), childHostFD
-}
-
-func openFile(ctx context.Context, t *testing.T, fdLisa lisafs.ClientFD, flags uint32, isReg bool) (lisafs.ClientFD, int) {
- newFD, hostFD, err := fdLisa.OpenAt(ctx, flags)
- if err != nil {
- t.Fatalf("OpenAt failed: %v", err)
- }
- if hostFD == -1 && isReg {
- t.Error("no host FD donated")
- }
- return fdLisa.Client().NewFD(newFD), hostFD
-}
-
-func unlinkFile(ctx context.Context, t *testing.T, dir lisafs.ClientFD, name string, isDir bool) {
- var flags uint32
- if isDir {
- flags = unix.AT_REMOVEDIR
- }
- if err := dir.UnlinkAt(ctx, name, flags); err != nil {
- t.Errorf("unlinking file %s failed: %v", name, err)
- }
-}
-
-func symlink(ctx context.Context, t *testing.T, dir lisafs.ClientFD, name, target string) (lisafs.ClientFD, linux.Statx) {
- linkIno, err := dir.SymlinkAt(ctx, name, target, lisafs.UID(unix.Getuid()), lisafs.GID(unix.Getgid()))
- if err != nil {
- t.Fatalf("symlink failed: %v", err)
- }
- return dir.Client().NewFD(linkIno.ControlFD), linkIno.Stat
-}
-
-func link(ctx context.Context, t *testing.T, dir lisafs.ClientFD, name string, target lisafs.ClientFD) (lisafs.ClientFD, linux.Statx) {
- linkIno, err := dir.LinkAt(ctx, target.ID(), name)
- if err != nil {
- t.Fatalf("link failed: %v", err)
- }
- return dir.Client().NewFD(linkIno.ControlFD), linkIno.Stat
-}
-
-func mkdir(ctx context.Context, t *testing.T, dir lisafs.ClientFD, name string) (lisafs.ClientFD, linux.Statx) {
- childIno, err := dir.MkdirAt(ctx, name, 0777, lisafs.UID(unix.Getuid()), lisafs.GID(unix.Getgid()))
- if err != nil {
- t.Fatalf("mkdir failed: %v", err)
- }
- return dir.Client().NewFD(childIno.ControlFD), childIno.Stat
-}
-
-func mknod(ctx context.Context, t *testing.T, dir lisafs.ClientFD, name string) (lisafs.ClientFD, linux.Statx) {
- nodeIno, err := dir.MknodAt(ctx, name, unix.S_IFREG|0777, lisafs.UID(unix.Getuid()), lisafs.GID(unix.Getgid()), 0, 0)
- if err != nil {
- t.Fatalf("mknod failed: %v", err)
- }
- return dir.Client().NewFD(nodeIno.ControlFD), nodeIno.Stat
-}
-
-func walk(ctx context.Context, t *testing.T, dir lisafs.ClientFD, names []string) []lisafs.Inode {
- _, inodes, err := dir.WalkMultiple(ctx, names)
- if err != nil {
- t.Fatalf("walk failed while trying to walk components %+v: %v", names, err)
- }
- return inodes
-}
-
-func walkStat(ctx context.Context, t *testing.T, dir lisafs.ClientFD, names []string) []linux.Statx {
- stats, err := dir.WalkStat(ctx, names)
- if err != nil {
- t.Fatalf("walk failed while trying to walk components %+v: %v", names, err)
- }
- return stats
-}
-
-func writeFD(ctx context.Context, t *testing.T, fdLisa lisafs.ClientFD, off uint64, buf []byte) error {
- count, err := fdLisa.Write(ctx, buf, off)
- if err != nil {
- return err
- }
- if int(count) != len(buf) {
- t.Errorf("partial write: buf size = %d, written = %d", len(buf), count)
- }
- return nil
-}
-
-func readFDAndCmp(ctx context.Context, t *testing.T, fdLisa lisafs.ClientFD, off uint64, want []byte) {
- buf := make([]byte, len(want))
- n, err := fdLisa.Read(ctx, buf, off)
- if err != nil {
- t.Errorf("read failed: %v", err)
- return
- }
- if int(n) != len(want) {
- t.Errorf("partial read: buf size = %d, read = %d", len(want), n)
- return
- }
- if bytes.Compare(buf, want) != 0 {
- t.Errorf("bytes read differ from what was expected: want = %v, got = %v", want, buf)
- }
-}
-
-func allocateAndVerify(ctx context.Context, t *testing.T, fdLisa lisafs.ClientFD, off uint64, length uint64) {
- if err := fdLisa.Allocate(ctx, 0, off, length); err != nil {
- t.Fatalf("fallocate failed: %v", err)
- }
-
- var stat linux.Statx
- statTo(ctx, t, fdLisa, &stat)
- if want := off + length; stat.Size != want {
- t.Errorf("incorrect file size after allocate: expected %d, got %d", off+length, stat.Size)
- }
-}
-
-func cmpStatx(t *testing.T, want, got linux.Statx) {
- if got.Mask&unix.STATX_MODE != 0 && want.Mask&unix.STATX_MODE != 0 {
- if got.Mode != want.Mode {
- t.Errorf("mode differs: want %d, got %d", want.Mode, got.Mode)
- }
- }
- if got.Mask&unix.STATX_INO != 0 && want.Mask&unix.STATX_INO != 0 {
- if got.Ino != want.Ino {
- t.Errorf("inode number differs: want %d, got %d", want.Ino, got.Ino)
- }
- }
- if got.Mask&unix.STATX_NLINK != 0 && want.Mask&unix.STATX_NLINK != 0 {
- if got.Nlink != want.Nlink {
- t.Errorf("nlink differs: want %d, got %d", want.Nlink, got.Nlink)
- }
- }
- if got.Mask&unix.STATX_UID != 0 && want.Mask&unix.STATX_UID != 0 {
- if got.UID != want.UID {
- t.Errorf("UID differs: want %d, got %d", want.UID, got.UID)
- }
- }
- if got.Mask&unix.STATX_GID != 0 && want.Mask&unix.STATX_GID != 0 {
- if got.GID != want.GID {
- t.Errorf("GID differs: want %d, got %d", want.GID, got.GID)
- }
- }
- if got.Mask&unix.STATX_SIZE != 0 && want.Mask&unix.STATX_SIZE != 0 {
- if got.Size != want.Size {
- t.Errorf("size differs: want %d, got %d", want.Size, got.Size)
- }
- }
- if got.Mask&unix.STATX_BLOCKS != 0 && want.Mask&unix.STATX_BLOCKS != 0 {
- if got.Blocks != want.Blocks {
- t.Errorf("blocks differs: want %d, got %d", want.Blocks, got.Blocks)
- }
- }
- if got.Mask&unix.STATX_ATIME != 0 && want.Mask&unix.STATX_ATIME != 0 {
- if got.Atime != want.Atime {
- t.Errorf("atime differs: want %d, got %d", want.Atime, got.Atime)
- }
- }
- if got.Mask&unix.STATX_MTIME != 0 && want.Mask&unix.STATX_MTIME != 0 {
- if got.Mtime != want.Mtime {
- t.Errorf("mtime differs: want %d, got %d", want.Mtime, got.Mtime)
- }
- }
- if got.Mask&unix.STATX_CTIME != 0 && want.Mask&unix.STATX_CTIME != 0 {
- if got.Ctime != want.Ctime {
- t.Errorf("ctime differs: want %d, got %d", want.Ctime, got.Ctime)
- }
- }
-}
-
-func hasCapability(c capability.Cap) bool {
- caps, err := capability.NewPid2(os.Getpid())
- if err != nil {
- return false
- }
- if err := caps.Load(); err != nil {
- return false
- }
- return caps.Get(capability.EFFECTIVE, c)
-}
-
-func testStat(ctx context.Context, t *testing.T, tester Tester, root lisafs.ClientFD) {
- var rootStat linux.Statx
- if err := root.StatTo(ctx, &rootStat); err != nil {
- t.Errorf("stat on the root dir failed: %v", err)
- }
-
- if ftype := rootStat.Mode & unix.S_IFMT; ftype != unix.S_IFDIR {
- t.Errorf("root inode is not a directory, file type = %d", ftype)
- }
-}
-
-func testRegularFileIO(ctx context.Context, t *testing.T, tester Tester, root lisafs.ClientFD) {
- name := "tempFile"
- controlFile, _, fd, hostFD := openCreateFile(ctx, t, root, name)
- defer closeFD(ctx, t, controlFile)
- defer closeFD(ctx, t, fd)
- defer unix.Close(hostFD)
-
- // Test Read/Write RPCs.
- data := make([]byte, 100)
- rand.Read(data)
- if err := writeFD(ctx, t, fd, 0, data); err != nil {
- t.Fatalf("write failed: %v", err)
- }
- readFDAndCmp(ctx, t, fd, 0, data)
- readFDAndCmp(ctx, t, fd, 50, data[50:])
-
- // Make sure the host FD is configured properly.
- hostReadData := make([]byte, len(data))
- if n, err := unix.Pread(hostFD, hostReadData, 0); err != nil {
- t.Errorf("host read failed: %v", err)
- } else if n != len(hostReadData) {
- t.Errorf("partial read: buf size = %d, read = %d", len(hostReadData), n)
- } else if bytes.Compare(hostReadData, data) != 0 {
- t.Errorf("bytes read differ from what was expected: want = %v, got = %v", data, hostReadData)
- }
-
- // Test syncing the writable FD.
- if err := fd.Sync(ctx); err != nil {
- t.Errorf("syncing the FD failed: %v", err)
- }
-}
-
-func testRegularFileOpen(ctx context.Context, t *testing.T, tester Tester, root lisafs.ClientFD) {
- name := "tempFile"
- controlFile, _, fd, hostFD := openCreateFile(ctx, t, root, name)
- defer closeFD(ctx, t, controlFile)
- defer closeFD(ctx, t, fd)
- defer unix.Close(hostFD)
-
- // Open a readonly FD and try writing to it to get an EBADF.
- roFile, roHostFD := openFile(ctx, t, controlFile, unix.O_RDONLY, true /* isReg */)
- defer closeFD(ctx, t, roFile)
- defer unix.Close(roHostFD)
- if err := writeFD(ctx, t, roFile, 0, []byte{1, 2, 3}); err != unix.EBADF {
- t.Errorf("writing to read only FD should generate EBADF, but got %v", err)
- }
-}
-
-func testSetStat(ctx context.Context, t *testing.T, tester Tester, root lisafs.ClientFD) {
- name := "tempFile"
- controlFile, _, fd, hostFD := openCreateFile(ctx, t, root, name)
- defer closeFD(ctx, t, controlFile)
- defer closeFD(ctx, t, fd)
- defer unix.Close(hostFD)
-
- now := time.Now()
- wantStat := linux.Statx{
- Mask: unix.STATX_MODE | unix.STATX_ATIME | unix.STATX_MTIME | unix.STATX_SIZE,
- Mode: 0760,
- UID: uint32(unix.Getuid()),
- GID: uint32(unix.Getgid()),
- Size: 50,
- Atime: linux.NsecToStatxTimestamp(now.UnixNano()),
- Mtime: linux.NsecToStatxTimestamp(now.UnixNano()),
- }
- if tester.SetUserGroupIDSupported() {
- wantStat.Mask |= unix.STATX_UID | unix.STATX_GID
- }
- failureMask, failureErr, err := controlFile.SetStat(ctx, &wantStat)
- if err != nil {
- t.Fatalf("setstat failed: %v", err)
- }
- if failureMask != 0 {
- t.Fatalf("some setstat operations failed: failureMask = %#b, failureErr = %v", failureMask, failureErr)
- }
-
- // Verify that attributes were updated.
- var gotStat linux.Statx
- statTo(ctx, t, controlFile, &gotStat)
- if gotStat.Mode&07777 != wantStat.Mode ||
- gotStat.Size != wantStat.Size ||
- gotStat.Atime.ToNsec() != wantStat.Atime.ToNsec() ||
- gotStat.Mtime.ToNsec() != wantStat.Mtime.ToNsec() ||
- (tester.SetUserGroupIDSupported() && (uint32(gotStat.UID) != wantStat.UID || uint32(gotStat.GID) != wantStat.GID)) {
- t.Errorf("setStat did not update file correctly: setStat = %+v, stat = %+v", wantStat, gotStat)
- }
-}
-
-func testAllocate(ctx context.Context, t *testing.T, tester Tester, root lisafs.ClientFD) {
- name := "tempFile"
- controlFile, _, fd, hostFD := openCreateFile(ctx, t, root, name)
- defer closeFD(ctx, t, controlFile)
- defer closeFD(ctx, t, fd)
- defer unix.Close(hostFD)
-
- allocateAndVerify(ctx, t, fd, 0, 40)
- allocateAndVerify(ctx, t, fd, 20, 100)
-}
-
-func testStatFS(ctx context.Context, t *testing.T, tester Tester, root lisafs.ClientFD) {
- var statFS lisafs.StatFS
- if err := root.StatFSTo(ctx, &statFS); err != nil {
- t.Errorf("statfs failed: %v", err)
- }
-}
-
-func testUnlink(ctx context.Context, t *testing.T, tester Tester, root lisafs.ClientFD) {
- name := "tempFile"
- controlFile, _, fd, hostFD := openCreateFile(ctx, t, root, name)
- defer closeFD(ctx, t, controlFile)
- defer closeFD(ctx, t, fd)
- defer unix.Close(hostFD)
-
- unlinkFile(ctx, t, root, name, false /* isDir */)
- if inodes := walk(ctx, t, root, []string{name}); len(inodes) > 0 {
- t.Errorf("deleted file should not be generating inodes on walk: %+v", inodes)
- }
-}
-
-func testSymlink(ctx context.Context, t *testing.T, tester Tester, root lisafs.ClientFD) {
- target := "/tmp/some/path"
- name := "symlinkFile"
- link, linkStat := symlink(ctx, t, root, name, target)
- defer closeFD(ctx, t, link)
-
- if linkStat.Mode&unix.S_IFMT != unix.S_IFLNK {
- t.Errorf("stat return from symlink RPC indicates that the inode is not a symlink: mode = %d", linkStat.Mode)
- }
-
- if gotTarget, err := link.ReadLinkAt(ctx); err != nil {
- t.Fatalf("readlink failed: %v", err)
- } else if gotTarget != target {
- t.Errorf("readlink return incorrect target: expected %q, got %q", target, gotTarget)
- }
-}
-
-func testHardLink(ctx context.Context, t *testing.T, tester Tester, root lisafs.ClientFD) {
- if !tester.LinkSupported() {
- t.Skipf("server does not support LinkAt RPC")
- }
- if !hasCapability(capability.CAP_DAC_READ_SEARCH) {
- t.Skipf("TestHardLink requires CAP_DAC_READ_SEARCH, running as %d", unix.Getuid())
- }
- name := "tempFile"
- controlFile, fileIno, fd, hostFD := openCreateFile(ctx, t, root, name)
- defer closeFD(ctx, t, controlFile)
- defer closeFD(ctx, t, fd)
- defer unix.Close(hostFD)
-
- link, linkStat := link(ctx, t, root, name, controlFile)
- defer closeFD(ctx, t, link)
-
- if linkStat.Ino != fileIno.Ino {
- t.Errorf("hard linked files have different inode numbers: %d %d", linkStat.Ino, fileIno.Ino)
- }
- if linkStat.DevMinor != fileIno.DevMinor {
- t.Errorf("hard linked files have different minor device numbers: %d %d", linkStat.DevMinor, fileIno.DevMinor)
- }
- if linkStat.DevMajor != fileIno.DevMajor {
- t.Errorf("hard linked files have different major device numbers: %d %d", linkStat.DevMajor, fileIno.DevMajor)
- }
-}
-
-func testWalk(ctx context.Context, t *testing.T, tester Tester, root lisafs.ClientFD) {
- // Create 10 nested directories.
- n := 10
- curDir := root
-
- dirNames := make([]string, 0, n)
- for i := 0; i < n; i++ {
- name := fmt.Sprintf("tmpdir-%d", i)
- childDir, _ := mkdir(ctx, t, curDir, name)
- defer closeFD(ctx, t, childDir)
- defer unlinkFile(ctx, t, curDir, name, true /* isDir */)
-
- curDir = childDir
- dirNames = append(dirNames, name)
- }
-
- // Walk all these directories. Add some junk at the end which should not be
- // walked on.
- dirNames = append(dirNames, []string{"a", "b", "c"}...)
- inodes := walk(ctx, t, root, dirNames)
- if len(inodes) != n {
- t.Errorf("walk returned the incorrect number of inodes: wanted %d, got %d", n, len(inodes))
- }
-
- // Close all control FDs and collect stat results for all dirs including
- // the root directory.
- dirStats := make([]linux.Statx, 0, n+1)
- var stat linux.Statx
- statTo(ctx, t, root, &stat)
- dirStats = append(dirStats, stat)
- for _, inode := range inodes {
- dirStats = append(dirStats, inode.Stat)
- closeFD(ctx, t, root.Client().NewFD(inode.ControlFD))
- }
-
- // Test WalkStat which additonally returns Statx for root because the first
- // path component is "".
- dirNames = append([]string{""}, dirNames...)
- gotStats := walkStat(ctx, t, root, dirNames)
- if len(gotStats) != len(dirStats) {
- t.Errorf("walkStat returned the incorrect number of statx: wanted %d, got %d", len(dirStats), len(gotStats))
- } else {
- for i := range gotStats {
- cmpStatx(t, dirStats[i], gotStats[i])
- }
- }
-}
-
-func testRename(ctx context.Context, t *testing.T, tester Tester, root lisafs.ClientFD) {
- name := "tempFile"
- tempFile, _, fd, hostFD := openCreateFile(ctx, t, root, name)
- defer closeFD(ctx, t, tempFile)
- defer closeFD(ctx, t, fd)
- defer unix.Close(hostFD)
-
- tempDir, _ := mkdir(ctx, t, root, "tempDir")
- defer closeFD(ctx, t, tempDir)
-
- // Move tempFile into tempDir.
- if err := tempFile.RenameTo(ctx, tempDir.ID(), "movedFile"); err != nil {
- t.Fatalf("rename failed: %v", err)
- }
-
- inodes := walkStat(ctx, t, root, []string{"tempDir", "movedFile"})
- if len(inodes) != 2 {
- t.Errorf("expected 2 files on walk but only found %d", len(inodes))
- }
-}
-
-func testMknod(ctx context.Context, t *testing.T, tester Tester, root lisafs.ClientFD) {
- name := "namedPipe"
- pipeFile, pipeStat := mknod(ctx, t, root, name)
- defer closeFD(ctx, t, pipeFile)
-
- var stat linux.Statx
- statTo(ctx, t, pipeFile, &stat)
-
- if stat.Mode != pipeStat.Mode {
- t.Errorf("mknod mode is incorrect: want %d, got %d", pipeStat.Mode, stat.Mode)
- }
- if stat.UID != pipeStat.UID {
- t.Errorf("mknod UID is incorrect: want %d, got %d", pipeStat.UID, stat.UID)
- }
- if stat.GID != pipeStat.GID {
- t.Errorf("mknod GID is incorrect: want %d, got %d", pipeStat.GID, stat.GID)
- }
-}
-
-func testGetdents(ctx context.Context, t *testing.T, tester Tester, root lisafs.ClientFD) {
- tempDir, _ := mkdir(ctx, t, root, "tempDir")
- defer closeFD(ctx, t, tempDir)
- defer unlinkFile(ctx, t, root, "tempDir", true /* isDir */)
-
- // Create 10 files in tempDir.
- n := 10
- fileStats := make(map[string]linux.Statx)
- for i := 0; i < n; i++ {
- name := fmt.Sprintf("file-%d", i)
- newFile, fileStat := mknod(ctx, t, tempDir, name)
- defer closeFD(ctx, t, newFile)
- defer unlinkFile(ctx, t, tempDir, name, false /* isDir */)
-
- fileStats[name] = fileStat
- }
-
- // Use opened directory FD for getdents.
- openDirFile, _ := openFile(ctx, t, tempDir, unix.O_RDONLY, false /* isReg */)
- defer closeFD(ctx, t, openDirFile)
-
- dirents := make([]lisafs.Dirent64, 0, n)
- for i := 0; i < n+2; i++ {
- gotDirents, err := openDirFile.Getdents64(ctx, 40)
- if err != nil {
- t.Fatalf("getdents failed: %v", err)
- }
- if len(gotDirents) == 0 {
- break
- }
- for _, dirent := range gotDirents {
- if dirent.Name != "." && dirent.Name != ".." {
- dirents = append(dirents, dirent)
- }
- }
- }
-
- if len(dirents) != n {
- t.Errorf("got incorrect number of dirents: wanted %d, got %d", n, len(dirents))
- }
- for _, dirent := range dirents {
- stat, ok := fileStats[string(dirent.Name)]
- if !ok {
- t.Errorf("received a dirent that was not created: %+v", dirent)
- continue
- }
-
- if dirent.Type != unix.DT_REG {
- t.Errorf("dirent type of %s is incorrect: %d", dirent.Name, dirent.Type)
- }
- if uint64(dirent.Ino) != stat.Ino {
- t.Errorf("dirent ino of %s is incorrect: want %d, got %d", dirent.Name, stat.Ino, dirent.Ino)
- }
- if uint32(dirent.DevMinor) != stat.DevMinor {
- t.Errorf("dirent dev minor of %s is incorrect: want %d, got %d", dirent.Name, stat.DevMinor, dirent.DevMinor)
- }
- if uint32(dirent.DevMajor) != stat.DevMajor {
- t.Errorf("dirent dev major of %s is incorrect: want %d, got %d", dirent.Name, stat.DevMajor, dirent.DevMajor)
- }
- }
-}