fsutils_linux.go 1.7 KB

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