...
1
2
3
4
19
20 package fs
21
22 import (
23 "fmt"
24 "os"
25 "syscall"
26 "unsafe"
27 )
28
29 func locateDummyIfEmpty(path string) (string, error) {
30 children, err := os.ReadDir(path)
31 if err != nil {
32 return "", err
33 }
34 if len(children) != 0 {
35 return "", nil
36 }
37 dummyFile, err := os.CreateTemp(path, "fsutils-dummy")
38 if err != nil {
39 return "", err
40 }
41 name := dummyFile.Name()
42 err = dummyFile.Close()
43 return name, err
44 }
45
46
47 func SupportsDType(path string) (bool, error) {
48
49 dummy, err := locateDummyIfEmpty(path)
50 if err != nil {
51 return false, err
52 }
53 if dummy != "" {
54 defer os.Remove(dummy)
55 }
56
57 visited := 0
58 supportsDType := true
59 fn := func(ent *syscall.Dirent) bool {
60 visited++
61 if ent.Type == syscall.DT_UNKNOWN {
62 supportsDType = false
63
64 return true
65 }
66
67 return false
68 }
69 if err = iterateReadDir(path, fn); err != nil {
70 return false, err
71 }
72 if visited == 0 {
73 return false, fmt.Errorf("did not hit any dirent during iteration %s", path)
74 }
75 return supportsDType, nil
76 }
77
78 func iterateReadDir(path string, fn func(*syscall.Dirent) bool) error {
79 d, err := os.Open(path)
80 if err != nil {
81 return err
82 }
83 defer d.Close()
84 fd := int(d.Fd())
85 buf := make([]byte, 4096)
86 for {
87 nbytes, err := syscall.ReadDirent(fd, buf)
88 if err != nil {
89 return err
90 }
91 if nbytes == 0 {
92 break
93 }
94 for off := 0; off < nbytes; {
95 ent := (*syscall.Dirent)(unsafe.Pointer(&buf[off]))
96 if stop := fn(ent); stop {
97 return nil
98 }
99 off += int(ent.Reclen)
100 }
101 }
102 return nil
103 }
104
View as plain text