filepath.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. package util
  2. import (
  3. "errors"
  4. "fmt"
  5. "io/ioutil"
  6. "os"
  7. "path/filepath"
  8. )
  9. //WalkSkipDir is the Error returned when we want to skip descending into a directory
  10. var WalkSkipDir = errors.New("skip this directory")
  11. //WalkFunc is a callback function called for each path as a directory is walked
  12. //If resolvedPath != "", then we are following symbolic links.
  13. type WalkFunc func(resolvedPath string, info os.FileInfo, err error) error
  14. //Walk walks a path, optionally following symbolic links, and for each path,
  15. //it calls the walkFn passed.
  16. //
  17. //It is similar to filepath.Walk, except that it supports symbolic links and
  18. //can detect infinite loops while following sym links.
  19. //It solves the issue where your WalkFunc needs a path relative to the symbolic link
  20. //(resolving links within walkfunc loses the path to the symbolic link for each traversal).
  21. func Walk(path string, followSymlinks bool, detectSymlinkInfiniteLoop bool, walkFn WalkFunc) error {
  22. info, err := os.Lstat(path)
  23. if err != nil {
  24. return err
  25. }
  26. var symlinkPathsFollowed map[string]bool
  27. var resolvedPath string
  28. if followSymlinks {
  29. resolvedPath = path
  30. if detectSymlinkInfiniteLoop {
  31. symlinkPathsFollowed = make(map[string]bool, 8)
  32. }
  33. }
  34. return walk(path, info, resolvedPath, symlinkPathsFollowed, walkFn)
  35. }
  36. //walk walks the path. It is a helper/sibling function to Walk.
  37. //It takes a resolvedPath into consideration. This way, paths being walked are
  38. //always relative to the path argument, even if symbolic links were resolved).
  39. //
  40. //If resolvedPath is "", then we are not following symbolic links.
  41. //If symlinkPathsFollowed is not nil, then we need to detect infinite loop.
  42. func walk(path string, info os.FileInfo, resolvedPath string, symlinkPathsFollowed map[string]bool, walkFn WalkFunc) error {
  43. if info == nil {
  44. return errors.New("Walk: Nil FileInfo passed")
  45. }
  46. err := walkFn(resolvedPath, info, nil)
  47. if err != nil {
  48. if info.IsDir() && err == WalkSkipDir {
  49. err = nil
  50. }
  51. return err
  52. }
  53. if resolvedPath != "" && info.Mode()&os.ModeSymlink == os.ModeSymlink {
  54. path2, err := os.Readlink(resolvedPath)
  55. if err != nil {
  56. return err
  57. }
  58. //vout("SymLink Path: %v, links to: %v", resolvedPath, path2)
  59. if symlinkPathsFollowed != nil {
  60. if _, ok := symlinkPathsFollowed[path2]; ok {
  61. errMsg := "Potential SymLink Infinite Loop. Path: %v, Link To: %v"
  62. return fmt.Errorf(errMsg, resolvedPath, path2)
  63. } else {
  64. symlinkPathsFollowed[path2] = true
  65. }
  66. }
  67. info2, err := os.Lstat(path2)
  68. if err != nil {
  69. return err
  70. }
  71. return walk(path, info2, path2, symlinkPathsFollowed, walkFn)
  72. }
  73. if info.IsDir() {
  74. list, err := ioutil.ReadDir(path)
  75. if err != nil {
  76. return walkFn(resolvedPath, info, err)
  77. }
  78. var subFiles = make([]subFile, 0)
  79. for _, fileInfo := range list {
  80. path2 := filepath.Join(path, fileInfo.Name())
  81. var resolvedPath2 string
  82. if resolvedPath != "" {
  83. resolvedPath2 = filepath.Join(resolvedPath, fileInfo.Name())
  84. }
  85. subFiles = append(subFiles, subFile{path: path2, resolvedPath: resolvedPath2, fileInfo: fileInfo})
  86. }
  87. if containsDistFolder(subFiles) {
  88. err := walk(
  89. filepath.Join(path, "dist"),
  90. info,
  91. filepath.Join(resolvedPath, "dist"),
  92. symlinkPathsFollowed,
  93. walkFn)
  94. if err != nil {
  95. return err
  96. }
  97. } else {
  98. for _, p := range subFiles {
  99. err = walk(p.path, p.fileInfo, p.resolvedPath, symlinkPathsFollowed, walkFn)
  100. if err != nil {
  101. return err
  102. }
  103. }
  104. }
  105. return nil
  106. }
  107. return nil
  108. }
  109. type subFile struct {
  110. path, resolvedPath string
  111. fileInfo os.FileInfo
  112. }
  113. func containsDistFolder(subFiles []subFile) bool {
  114. for _, p := range subFiles {
  115. if p.fileInfo.IsDir() && p.fileInfo.Name() == "dist" {
  116. return true
  117. }
  118. }
  119. return false
  120. }