fsutils_linux.go 1.6 KB

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