diff options
Diffstat (limited to 'utils/filesystem.go')
-rw-r--r-- | utils/filesystem.go | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/utils/filesystem.go b/utils/filesystem.go new file mode 100644 index 0000000..bb8c53e --- /dev/null +++ b/utils/filesystem.go @@ -0,0 +1,124 @@ +// Package utils contains various utility functions to support the +// main tools-golang packages. +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +package utils + +import ( + "crypto/md5" + "crypto/sha1" + "crypto/sha256" + "fmt" + "io" + "os" + "path/filepath" + "strings" +) + +// GetAllFilePaths takes a path to a directory (including an optional slice of +// path patterns to ignore), and returns a slice of relative paths to all files +// in that directory and its subdirectories (excluding those that are ignored). +func GetAllFilePaths(dirRoot string, pathsIgnored []string) ([]string, error) { + // paths is a _pointer_ to a slice -- not just a slice. + // this is so that it can be appropriately modified by append + // in the sub-function. + paths := &[]string{} + prefix := strings.TrimSuffix(dirRoot, "/") + + err := filepath.Walk(dirRoot, func(path string, fi os.FileInfo, err error) error { + if err != nil { + return err + } + // don't include path if it's a directory + if fi.IsDir() { + return nil + } + // don't include path if it's a symbolic link + if fi.Mode()&os.ModeSymlink == os.ModeSymlink { + return nil + } + + shortPath := strings.TrimPrefix(path, prefix) + + // don't include path if it should be ignored + if pathsIgnored != nil && ShouldIgnore(shortPath, pathsIgnored) { + return nil + } + + // if we got here, record the path + *paths = append(*paths, shortPath) + return nil + }) + + return *paths, err +} + +// GetHashesForFilePath takes a path to a file on disk, and returns +// SHA1, SHA256 and MD5 hashes for that file as strings. +func GetHashesForFilePath(p string) (string, string, string, error) { + f, err := os.Open(p) + if err != nil { + return "", "", "", err + } + defer f.Close() + + var ssha1, ssha256, smd5 string + hSHA1 := sha1.New() + hSHA256 := sha256.New() + hMD5 := md5.New() + hMulti := io.MultiWriter(hSHA1, hSHA256, hMD5) + + if _, err := io.Copy(hMulti, f); err != nil { + f.Close() + return "", "", "", err + } + ssha1 = fmt.Sprintf("%x", hSHA1.Sum(nil)) + ssha256 = fmt.Sprintf("%x", hSHA256.Sum(nil)) + smd5 = fmt.Sprintf("%x", hMD5.Sum(nil)) + + return ssha1, ssha256, smd5, nil +} + +// ShouldIgnore compares a file path to a slice of file path patterns, +// and determines whether that file should be ignored because it matches +// any of those patterns. +func ShouldIgnore(fileName string, pathsIgnored []string) bool { + fDirs, fFile := filepath.Split(fileName) + + for _, pattern := range pathsIgnored { + // split into dir(s) and filename + patternDirs, patternFile := filepath.Split(pattern) + patternDirStars := strings.HasPrefix(patternDirs, "**") + if patternDirStars { + patternDirs = patternDirs[2:] + } + + // case 1: specific file + if !patternDirStars && patternDirs == fDirs && patternFile != "" && patternFile == fFile { + return true + } + + // case 2: all files in specific directory + if !patternDirStars && strings.HasPrefix(fDirs, patternDirs) && patternFile == "" { + return true + } + + // case 3: specific file in any dir + if patternDirStars && patternDirs == "/" && patternFile != "" && patternFile == fFile { + return true + } + + // case 4: specific file in any matching subdir + if patternDirStars && strings.Contains(fDirs, patternDirs) && patternFile != "" && patternFile == fFile { + return true + } + + // case 5: any file in any matching subdir + if patternDirStars && strings.Contains(fDirs, patternDirs) && patternFile == "" { + return true + } + + } + + // if no match, don't ignore + return false +} |