aboutsummaryrefslogtreecommitdiff
path: root/utils/filesystem.go
blob: bb8c53eea176f56f667181eb24ce3b5152ef4f5a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
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
}