// 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 }