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