diff --git a/cmd/gitbom/main.go b/cmd/gitbom/main.go index 6612189..53bd96b 100644 --- a/cmd/gitbom/main.go +++ b/cmd/gitbom/main.go @@ -2,8 +2,8 @@ package main import ( "fmt" - "github.com/facebookgo/symwalk" "github.com/fkautz/gitbom-go" + "github.com/fkautz/gitbom-go/pkg/util" "log" "os" ) @@ -20,7 +20,7 @@ func main() { } func addToGitBom(gb gitbom.ArtifactTree, fileName string) error { - err := symwalk.Walk(fileName, func(path string, info os.FileInfo, err error) error { + return util.Walk(fileName, true, true, func(path string, info os.FileInfo, err error) error { if err != nil { return err } @@ -43,5 +43,4 @@ func addToGitBom(gb gitbom.ArtifactTree, fileName string) error { } return nil }) - return err } diff --git a/go.mod b/go.mod index 668e6c2..99f0105 100644 --- a/go.mod +++ b/go.mod @@ -2,17 +2,10 @@ module github.com/fkautz/gitbom-go go 1.17 -require ( - github.com/facebookgo/symwalk v0.0.0-20150726040526-42004b9f3222 - github.com/stretchr/testify v1.7.0 -) +require github.com/stretchr/testify v1.7.0 require ( github.com/davecgh/go-spew v1.1.0 // indirect - github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c // indirect - github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect - github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 // indirect - github.com/facebookgo/testname v0.0.0-20150612200628-5443337c3a12 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect ) diff --git a/go.sum b/go.sum index 12b9aaf..acb88a4 100644 --- a/go.sum +++ b/go.sum @@ -1,15 +1,5 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= -github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= -github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= -github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= -github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk= -github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= -github.com/facebookgo/symwalk v0.0.0-20150726040526-42004b9f3222 h1:ivxAxcE9py2xLAqpcEwN7sN711aLfEWgh3cY0aha7uY= -github.com/facebookgo/symwalk v0.0.0-20150726040526-42004b9f3222/go.mod h1:PgrCjL2+FgkITqxQI+erRTONtAv4JkpOzun5ozKW/Jg= -github.com/facebookgo/testname v0.0.0-20150612200628-5443337c3a12 h1:pKeuUgeuL6jk/FpxSr0ZVL1XEiOmrcWBvB2rKXu0mMI= -github.com/facebookgo/testname v0.0.0-20150612200628-5443337c3a12/go.mod h1:IYed2VYeQcs7JTN6KiVXjaz6Rv/Qz092Wjc6o5bCJ9I= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/pkg/util/filepath.go b/pkg/util/filepath.go new file mode 100644 index 0000000..cb1fd16 --- /dev/null +++ b/pkg/util/filepath.go @@ -0,0 +1,120 @@ +// Adopted from https://github.com/grafana/grafana/blob/main/pkg/util/filepath.go + +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() && errors.Is(err, ErrWalkSkipDir) { + err = nil + } + return err + } + + if resolvedPath != "" && info.Mode()&os.ModeSymlink == os.ModeSymlink { + // We only want to lstat on directories. If this entry is a symbolic link to a file, no need to recurse. + statInfo, err := os.Stat(resolvedPath) + if err != nil { + return err + } + if !statInfo.IsDir() { + return nil + } + + path2, err := filepath.EvalSymlinks(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) + } else 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}) + } + + 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 +} +