github.com/cdoern/storage@v1.12.13/pkg/fsutils/fsutils_linux.go (about) 1 // +build linux 2 3 package fsutils 4 5 import ( 6 "fmt" 7 "io/ioutil" 8 "os" 9 "unsafe" 10 11 "golang.org/x/sys/unix" 12 ) 13 14 func locateDummyIfEmpty(path string) (string, error) { 15 children, err := ioutil.ReadDir(path) 16 if err != nil { 17 return "", err 18 } 19 if len(children) != 0 { 20 return "", nil 21 } 22 dummyFile, err := ioutil.TempFile(path, "fsutils-dummy") 23 if err != nil { 24 return "", err 25 } 26 name := dummyFile.Name() 27 err = dummyFile.Close() 28 return name, err 29 } 30 31 // SupportsDType returns whether the filesystem mounted on path supports d_type 32 func SupportsDType(path string) (bool, error) { 33 // locate dummy so that we have at least one dirent 34 dummy, err := locateDummyIfEmpty(path) 35 if err != nil { 36 return false, err 37 } 38 if dummy != "" { 39 defer os.Remove(dummy) 40 } 41 42 visited := 0 43 supportsDType := true 44 fn := func(ent *unix.Dirent) bool { 45 visited++ 46 if ent.Type == unix.DT_UNKNOWN { 47 supportsDType = false 48 // stop iteration 49 return true 50 } 51 // continue iteration 52 return false 53 } 54 if err = iterateReadDir(path, fn); err != nil { 55 return false, err 56 } 57 if visited == 0 { 58 return false, fmt.Errorf("did not hit any dirent during iteration %s", path) 59 } 60 return supportsDType, nil 61 } 62 63 func iterateReadDir(path string, fn func(*unix.Dirent) bool) error { 64 d, err := os.Open(path) 65 if err != nil { 66 return err 67 } 68 defer d.Close() 69 fd := int(d.Fd()) 70 buf := make([]byte, 4096) 71 for { 72 nbytes, err := unix.ReadDirent(fd, buf) 73 if err != nil { 74 return err 75 } 76 if nbytes == 0 { 77 break 78 } 79 for off := 0; off < nbytes; { 80 ent := (*unix.Dirent)(unsafe.Pointer(&buf[off])) 81 if stop := fn(ent); stop { 82 return nil 83 } 84 off += int(ent.Reclen) 85 } 86 } 87 return nil 88 }