| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131 |
- package util
- import (
- "errors"
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- )
- //ErrWalkSkipDir is the Error returned when we want to skip descending into a directory
- var ErrWalkSkipDir = errors.New("skip this directory")
- //WalkFunc is a callback function called for each path as a directory is walked
- //If resolvedPath != "", then we are following symbolic links.
- type WalkFunc func(resolvedPath string, info os.FileInfo, err error) error
- //Walk walks a path, optionally following symbolic links, and for each path,
- //it calls the walkFn passed.
- //
- //It is similar to filepath.Walk, except that it supports symbolic links and
- //can detect infinite loops while following sym links.
- //It solves the issue where your WalkFunc needs a path relative to the symbolic link
- //(resolving links within walkfunc loses the path to the symbolic link for each traversal).
- func Walk(path string, followSymlinks bool, detectSymlinkInfiniteLoop bool, walkFn WalkFunc) error {
- info, err := os.Lstat(path)
- if err != nil {
- return err
- }
- var symlinkPathsFollowed map[string]bool
- var resolvedPath string
- if followSymlinks {
- resolvedPath = path
- if detectSymlinkInfiniteLoop {
- symlinkPathsFollowed = make(map[string]bool, 8)
- }
- }
- return walk(path, info, resolvedPath, symlinkPathsFollowed, walkFn)
- }
- //walk walks the path. It is a helper/sibling function to Walk.
- //It takes a resolvedPath into consideration. This way, paths being walked are
- //always relative to the path argument, even if symbolic links were resolved).
- //
- //If resolvedPath is "", then we are not following symbolic links.
- //If symlinkPathsFollowed is not nil, then we need to detect infinite loop.
- func walk(path string, info os.FileInfo, resolvedPath string, symlinkPathsFollowed map[string]bool, walkFn WalkFunc) error {
- if info == nil {
- return errors.New("Walk: Nil FileInfo passed")
- }
- err := walkFn(resolvedPath, info, nil)
- if err != nil {
- if info.IsDir() && err == ErrWalkSkipDir {
- err = nil
- }
- return err
- }
- if resolvedPath != "" && info.Mode()&os.ModeSymlink == os.ModeSymlink {
- path2, err := os.Readlink(resolvedPath)
- if err != nil {
- return err
- }
- //vout("SymLink Path: %v, links to: %v", resolvedPath, path2)
- if symlinkPathsFollowed != nil {
- if _, ok := symlinkPathsFollowed[path2]; ok {
- errMsg := "Potential SymLink Infinite Loop. Path: %v, Link To: %v"
- return fmt.Errorf(errMsg, resolvedPath, path2)
- }
- symlinkPathsFollowed[path2] = true
- }
- info2, err := os.Lstat(path2)
- if err != nil {
- return err
- }
- return walk(path, info2, path2, symlinkPathsFollowed, walkFn)
- }
- if info.IsDir() {
- list, err := ioutil.ReadDir(path)
- if err != nil {
- return walkFn(resolvedPath, info, err)
- }
- var subFiles = make([]subFile, 0)
- for _, fileInfo := range list {
- path2 := filepath.Join(path, fileInfo.Name())
- var resolvedPath2 string
- if resolvedPath != "" {
- resolvedPath2 = filepath.Join(resolvedPath, fileInfo.Name())
- }
- subFiles = append(subFiles, subFile{path: path2, resolvedPath: resolvedPath2, fileInfo: fileInfo})
- }
- if containsDistFolder(subFiles) {
- err := walk(
- filepath.Join(path, "dist"),
- info,
- filepath.Join(resolvedPath, "dist"),
- symlinkPathsFollowed,
- walkFn)
- if err != nil {
- return err
- }
- } else {
- for _, p := range subFiles {
- err = walk(p.path, p.fileInfo, p.resolvedPath, symlinkPathsFollowed, walkFn)
- if err != nil {
- return err
- }
- }
- }
- return nil
- }
- return nil
- }
- type subFile struct {
- path, resolvedPath string
- fileInfo os.FileInfo
- }
- func containsDistFolder(subFiles []subFile) bool {
- for _, p := range subFiles {
- if p.fileInfo.IsDir() && p.fileInfo.Name() == "dist" {
- return true
- }
- }
- return false
- }
|