scan: skip OS-appropriate default excludes (/proc, /sys, /tmp, /run, /var/tmp on Linux) (closes #124)
check / check (push) Canceled after 0s

Below an operand, the walk no longer enters the default excludes: on
Linux /proc, /sys, /dev, /run, /tmp, /var/tmp and every mount point of a
kernel or memory-backed filesystem, read once from /proc/self/mountinfo;
on macOS /dev, /private/tmp, /private/var/tmp, /private/var/vm,
/System/Volumes/VM and /System/Volumes/Data. The lists live in the new
exclude.go. An operand at or inside one is still walked, also under
another operand, as with -x. Each skipped directory gets one stderr line
through the warning path, is not counted as skipped, and its records
count as outside the scanned operands. --no-default-excludes turns them
off; scan --help lists them.

Judgement call: an unreadable /proc/self/mountinfo fails the scan.
Judgement call: where filesystems are mounted over one another, the last decides.

Model: opus-5-5
This commit is contained in:
2026-10-16 23:28:07 +00:00
parent 68f6bde5ee
commit 5af53914e8
9 changed files with 693 additions and 120 deletions
+16 -8
View File
@@ -782,7 +782,7 @@ func collectWalk(t *testing.T, roots []string, oneFS bool,
errs int
)
for ev := range startWalk(t.Context(), roots, oneFS, workers) {
for ev := range startWalk(t.Context(), roots, oneFS, nil, workers) {
if ev.fail {
errs++
@@ -1004,7 +1004,7 @@ func callSubdirJob(t *testing.T, p string, e fs.DirEntry,
t.Helper()
events := make(chan walkEvent, 1)
job, ok := subdirJob(t.Context(), p, e, parent, oneFS, events)
job, ok := subdirJob(t.Context(), p, e, parent, oneFS, nil, events)
close(events)
var evs []walkEvent
@@ -1152,7 +1152,7 @@ func syncOneFilesystem(t *testing.T, db *sql.DB, roots []string,
return dev, ok
}
roots = pruneRoots(roots, true, device)
roots = pruneRoots(roots, true, nil, device)
ctx := t.Context()
s := &scanState{db: db}
@@ -1162,7 +1162,7 @@ func syncOneFilesystem(t *testing.T, db *sql.DB, roots []string,
t.Fatal(err)
}
jobs, subdirs, events := startWalkWorkers(ctx, 2, true)
jobs, subdirs, events := startWalkWorkers(ctx, 2, true, nil)
// seedRoot emits a regular-file operand itself and returns a job
// only for a directory operand.
@@ -1463,7 +1463,7 @@ const smokeTreeFiles = 15
func syncTree(t *testing.T, db *sql.DB, roots ...string) scanStats {
t.Helper()
st, err := syncScan(t.Context(), db, roots, 4, false)
st, err := syncScan(t.Context(), db, roots, 4, false, nil)
if err != nil {
t.Fatalf("syncScan: %v", err)
}
@@ -2412,7 +2412,7 @@ func TestPruneRoots(t *testing.T) {
// Duplicates and operands under other operands are dropped; /cc is
// not under /c (sibling with a shared prefix).
got := pruneRoots([]string{"/a/b", "/a", "/c", "/a", "/a/b/c", "/cc"},
false, device)
false, nil, device)
want := []string{"/a", "/c", "/cc"}
if !slices.Equal(got, want) {
@@ -2421,7 +2421,7 @@ func TestPruneRoots(t *testing.T) {
nested := []string{"/p/q/r", pqRoot, "/p", "/p", "/p/s", "/p/t"}
got = pruneRoots(nested, false, device)
got = pruneRoots(nested, false, nil, device)
want = []string{"/p"}
if !slices.Equal(got, want) {
@@ -2429,12 +2429,20 @@ func TestPruneRoots(t *testing.T) {
}
// With -x, /p/q, on another filesystem than /p, is kept as well.
got = pruneRoots(nested, true, device)
got = pruneRoots(nested, true, nil, device)
want = []string{"/p", pqRoot}
if !slices.Equal(got, want) {
t.Fatalf("pruneRoots with -x = %q, want %q", got, want)
}
// The walk of /p does not enter /p/q when it is a default exclude, so
// /p/q is kept, without -x too; /p/q/r lies under it and is dropped.
got = pruneRoots(nested, false,
map[string]string{pqRoot: defaultExcludeWhy}, device)
if !slices.Equal(got, want) {
t.Fatalf("pruneRoots excluding %s = %q, want %q", pqRoot, got, want)
}
}
func TestReportsNeverTouchFilesystem(t *testing.T) {