fsutils_linux.go 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  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. err = dummyFile.Close()
  24. return name, err
  25. }
  26. // SupportsDType returns whether the filesystem mounted on path supports d_type
  27. func SupportsDType(path string) (bool, error) {
  28. // locate dummy so that we have at least one dirent
  29. dummy, err := locateDummyIfEmpty(path)
  30. if err != nil {
  31. return false, err
  32. }
  33. if dummy != "" {
  34. defer os.Remove(dummy)
  35. }
  36. visited := 0
  37. supportsDType := true
  38. fn := func(ent *syscall.Dirent) bool {
  39. visited++
  40. if ent.Type == syscall.DT_UNKNOWN {
  41. supportsDType = false
  42. // stop iteration
  43. return true
  44. }
  45. // continue iteration
  46. return false
  47. }
  48. if err = iterateReadDir(path, fn); err != nil {
  49. return false, err
  50. }
  51. if visited == 0 {
  52. return false, fmt.Errorf("did not hit any dirent during iteration %s", path)
  53. }
  54. return supportsDType, nil
  55. }
  56. func iterateReadDir(path string, fn func(*syscall.Dirent) bool) error {
  57. d, err := os.Open(path)
  58. if err != nil {
  59. return err
  60. }
  61. defer d.Close()
  62. fd := int(d.Fd())
  63. buf := make([]byte, 4096)
  64. for {
  65. nbytes, err := syscall.ReadDirent(fd, buf)
  66. if err != nil {
  67. return err
  68. }
  69. if nbytes == 0 {
  70. break
  71. }
  72. for off := 0; off < nbytes; {
  73. ent := (*syscall.Dirent)(unsafe.Pointer(&buf[off]))
  74. if stop := fn(ent); stop {
  75. return nil
  76. }
  77. off += int(ent.Reclen)
  78. }
  79. }
  80. return nil
  81. }