-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwalk.go
More file actions
76 lines (64 loc) · 1.96 KB
/
walk.go
File metadata and controls
76 lines (64 loc) · 1.96 KB
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
package main
import (
"log/slog"
"math/rand/v2"
"os"
"path/filepath"
)
// WalkSizes traverses the directory tree rooted at root, recording each
// regular file's size in the SizeMap. Symlinks are ignored. Directory
// entry order is randomized so repeated runs explore different parts of
// the tree before the bounded map fills up.
// The optional onFile callback is called for every regular file encountered.
func WalkSizes(root string, sm *SizeMap, includeSnapshots bool, minSize int64, onFile func(path string, size int64)) (int64, error) {
var count int64
err := walkRandom(root, includeSnapshots, minSize, func(path string, size int64) {
sm.Add(size)
count++
if onFile != nil {
onFile(path, size)
}
})
return count, err
}
// walkRandom recursively walks the directory tree at dir, calling fn for
// each regular file found. Directory entries are shuffled to randomize
// traversal order. Symlinks, special files, and empty files are skipped.
// Errors reading individual directories are logged and skipped.
func walkRandom(dir string, includeSnapshots bool, minSize int64, fn func(path string, size int64)) error {
entries, err := os.ReadDir(dir)
if err != nil {
slog.Debug("skipping unreadable directory", "path", dir, "error", err)
return nil
}
rand.Shuffle(len(entries), func(i, j int) {
entries[i], entries[j] = entries[j], entries[i]
})
for _, entry := range entries {
// Skip symlinks entirely.
if entry.Type()&os.ModeSymlink != 0 {
continue
}
path := filepath.Join(dir, entry.Name())
if entry.IsDir() {
if !includeSnapshots && entry.Name() == ".snapshots" {
continue
}
_ = walkRandom(path, includeSnapshots, minSize, fn)
continue
}
if !entry.Type().IsRegular() {
continue
}
info, err := entry.Info()
if err != nil {
slog.Debug("skipping unreadable file", "path", path, "error", err)
continue
}
if info.Size() == 0 || info.Size() < minSize {
continue
}
fn(path, info.Size())
}
return nil
}