github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/syscall/dirent_test.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build unix 6 7 package syscall_test 8 9 import ( 10 "bytes" 11 "fmt" 12 "os" 13 "path/filepath" 14 "runtime" 15 "sort" 16 "strconv" 17 "strings" 18 "syscall" 19 "testing" 20 "unsafe" 21 ) 22 23 func TestDirent(t *testing.T) { 24 const ( 25 direntBufSize = 2048 // arbitrary? See https://go.dev/issue/37323. 26 filenameMinSize = 11 27 ) 28 29 d := t.TempDir() 30 t.Logf("tmpdir: %s", d) 31 32 for i, c := range []byte("0123456789") { 33 name := string(bytes.Repeat([]byte{c}, filenameMinSize+i)) 34 err := os.WriteFile(filepath.Join(d, name), nil, 0644) 35 if err != nil { 36 t.Fatalf("writefile: %v", err) 37 } 38 } 39 40 names := make([]string, 0, 10) 41 42 fd, err := syscall.Open(d, syscall.O_RDONLY, 0) 43 if err != nil { 44 t.Fatalf("syscall.open: %v", err) 45 } 46 defer syscall.Close(fd) 47 48 buf := bytes.Repeat([]byte{0xCD}, direntBufSize) 49 for { 50 n, err := syscall.ReadDirent(fd, buf) 51 if err == syscall.EINVAL { 52 // On linux, 'man getdents64' says that EINVAL indicates “result buffer is too small”. 53 // Try a bigger buffer. 54 t.Logf("ReadDirent: %v; retrying with larger buffer", err) 55 buf = bytes.Repeat([]byte{0xCD}, len(buf)*2) 56 continue 57 } 58 if err != nil { 59 t.Fatalf("syscall.readdir: %v", err) 60 } 61 t.Logf("ReadDirent: read %d bytes", n) 62 if n == 0 { 63 break 64 } 65 66 var consumed, count int 67 consumed, count, names = syscall.ParseDirent(buf[:n], -1, names) 68 t.Logf("ParseDirent: %d new name(s)", count) 69 if consumed != n { 70 t.Fatalf("ParseDirent: consumed %d bytes; expected %d", consumed, n) 71 } 72 } 73 74 sort.Strings(names) 75 t.Logf("names: %q", names) 76 77 if len(names) != 10 { 78 t.Errorf("got %d names; expected 10", len(names)) 79 } 80 for i, name := range names { 81 ord, err := strconv.Atoi(name[:1]) 82 if err != nil { 83 t.Fatalf("names[%d] is non-integer %q: %v", i, names[i], err) 84 } 85 if expected := strings.Repeat(name[:1], filenameMinSize+ord); name != expected { 86 t.Errorf("names[%d] is %q (len %d); expected %q (len %d)", i, name, len(name), expected, len(expected)) 87 } 88 } 89 } 90 91 func TestDirentRepeat(t *testing.T) { 92 const N = 100 93 // Note: the size of the buffer is small enough that the loop 94 // below will need to execute multiple times. See issue #31368. 95 size := N * unsafe.Offsetof(syscall.Dirent{}.Name) / 4 96 if runtime.GOOS == "freebsd" || runtime.GOOS == "netbsd" { 97 if size < 1024 { 98 size = 1024 // DIRBLKSIZ, see issue 31403. 99 } 100 } 101 102 // Make a directory containing N files 103 d := t.TempDir() 104 105 var files []string 106 for i := 0; i < N; i++ { 107 files = append(files, fmt.Sprintf("file%d", i)) 108 } 109 for _, file := range files { 110 err := os.WriteFile(filepath.Join(d, file), []byte("contents"), 0644) 111 if err != nil { 112 t.Fatalf("writefile: %v", err) 113 } 114 } 115 116 // Read the directory entries using ReadDirent. 117 fd, err := syscall.Open(d, syscall.O_RDONLY, 0) 118 if err != nil { 119 t.Fatalf("syscall.open: %v", err) 120 } 121 defer syscall.Close(fd) 122 var files2 []string 123 for { 124 buf := make([]byte, size) 125 n, err := syscall.ReadDirent(fd, buf) 126 if err != nil { 127 t.Fatalf("syscall.readdir: %v", err) 128 } 129 if n == 0 { 130 break 131 } 132 buf = buf[:n] 133 for len(buf) > 0 { 134 var consumed int 135 consumed, _, files2 = syscall.ParseDirent(buf, -1, files2) 136 buf = buf[consumed:] 137 } 138 } 139 140 // Check results 141 sort.Strings(files) 142 sort.Strings(files2) 143 if strings.Join(files, "|") != strings.Join(files2, "|") { 144 t.Errorf("bad file list: want\n%q\ngot\n%q", files, files2) 145 } 146 }