1// Package utils contains various utility functions to support the 2// main tools-golang packages. 3// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later 4package utils 5 6import ( 7 "crypto/md5" 8 "crypto/sha1" 9 "crypto/sha256" 10 "fmt" 11 "io" 12 "os" 13 "path/filepath" 14 "strings" 15) 16 17// GetAllFilePaths takes a path to a directory (including an optional slice of 18// path patterns to ignore), and returns a slice of relative paths to all files 19// in that directory and its subdirectories (excluding those that are ignored). 20func GetAllFilePaths(dirRoot string, pathsIgnored []string) ([]string, error) { 21 // paths is a _pointer_ to a slice -- not just a slice. 22 // this is so that it can be appropriately modified by append 23 // in the sub-function. 24 paths := &[]string{} 25 prefix := strings.TrimSuffix(dirRoot, "/") 26 27 err := filepath.Walk(dirRoot, func(path string, fi os.FileInfo, err error) error { 28 if err != nil { 29 return err 30 } 31 // don't include path if it's a directory 32 if fi.IsDir() { 33 return nil 34 } 35 // don't include path if it's a symbolic link 36 if fi.Mode()&os.ModeSymlink == os.ModeSymlink { 37 return nil 38 } 39 40 shortPath := strings.TrimPrefix(path, prefix) 41 42 // don't include path if it should be ignored 43 if pathsIgnored != nil && ShouldIgnore(shortPath, pathsIgnored) { 44 return nil 45 } 46 47 // if we got here, record the path 48 *paths = append(*paths, shortPath) 49 return nil 50 }) 51 52 return *paths, err 53} 54 55// GetHashesForFilePath takes a path to a file on disk, and returns 56// SHA1, SHA256 and MD5 hashes for that file as strings. 57func GetHashesForFilePath(p string) (string, string, string, error) { 58 f, err := os.Open(p) 59 if err != nil { 60 return "", "", "", err 61 } 62 defer f.Close() 63 64 var ssha1, ssha256, smd5 string 65 hSHA1 := sha1.New() 66 hSHA256 := sha256.New() 67 hMD5 := md5.New() 68 hMulti := io.MultiWriter(hSHA1, hSHA256, hMD5) 69 70 if _, err := io.Copy(hMulti, f); err != nil { 71 f.Close() 72 return "", "", "", err 73 } 74 ssha1 = fmt.Sprintf("%x", hSHA1.Sum(nil)) 75 ssha256 = fmt.Sprintf("%x", hSHA256.Sum(nil)) 76 smd5 = fmt.Sprintf("%x", hMD5.Sum(nil)) 77 78 return ssha1, ssha256, smd5, nil 79} 80 81// ShouldIgnore compares a file path to a slice of file path patterns, 82// and determines whether that file should be ignored because it matches 83// any of those patterns. 84func ShouldIgnore(fileName string, pathsIgnored []string) bool { 85 fDirs, fFile := filepath.Split(fileName) 86 87 for _, pattern := range pathsIgnored { 88 // split into dir(s) and filename 89 patternDirs, patternFile := filepath.Split(pattern) 90 patternDirStars := strings.HasPrefix(patternDirs, "**") 91 if patternDirStars { 92 patternDirs = patternDirs[2:] 93 } 94 95 // case 1: specific file 96 if !patternDirStars && patternDirs == fDirs && patternFile != "" && patternFile == fFile { 97 return true 98 } 99 100 // case 2: all files in specific directory 101 if !patternDirStars && strings.HasPrefix(fDirs, patternDirs) && patternFile == "" { 102 return true 103 } 104 105 // case 3: specific file in any dir 106 if patternDirStars && patternDirs == "/" && patternFile != "" && patternFile == fFile { 107 return true 108 } 109 110 // case 4: specific file in any matching subdir 111 if patternDirStars && strings.Contains(fDirs, patternDirs) && patternFile != "" && patternFile == fFile { 112 return true 113 } 114 115 // case 5: any file in any matching subdir 116 if patternDirStars && strings.Contains(fDirs, patternDirs) && patternFile == "" { 117 return true 118 } 119 120 } 121 122 // if no match, don't ignore 123 return false 124} 125