scan: skip OS default excludes, and record nothing on filesystems that are not persistent (closes #124)
check / check (push) Canceled after 0s

scan never records anything on a filesystem that is not persistent
(tmpfs, proc, sysfs and the other kernel or memory-backed types). As it
starts it notes the device ID of each such mount point, from
/proc/self/mountinfo on Linux and the mounted-filesystem list on macOS,
and checks every operand, directory and file the walk meets against
them; no flag turns this off. Below an operand the walk also skips the
default excludes, a path list per system (Linux, macOS, and Windows,
which is not used yet), and --no-default-excludes turns off only those.
A default exclude that is itself an operand gets no skip line. The tests
stop at once, saying why, when TMPDIR is not on a persistent filesystem.

Judgement call: an unreadable mount list fails the scan, and no flag gets round it.
Judgement call: a file on such a filesystem below an operand gets the same line as a directory.
Judgement call: an operand on such a filesystem that another operand's walk also reaches is named twice.
Unverified: the macOS mount list is built for macOS but never run; bind mounts need root to test.

Model: opus-5-5
This commit is contained in:
2026-10-17 00:31:52 +00:00
parent 68f6bde5ee
commit 18aa3a383d
11 changed files with 1187 additions and 135 deletions
+15 -10
View File
@@ -196,7 +196,7 @@ func TestSyncScanCancelledMidWalkKeepsRecords(t *testing.T) {
base := baselineGoroutines(t)
st, err := syncScan(newWalkClock(walkCancelAtDone), db,
[]string{dir}, walkCancelWorkers, false)
[]string{dir}, walkCancelWorkers, false, walkRules{})
assertWalkGuardAborted(t, st, err)
assertRecordsIntact(t, db, before)
@@ -268,7 +268,8 @@ func TestSyncScanCancelledBeforeLoadIndex(t *testing.T) {
ctx, cancel := context.WithCancel(t.Context())
cancel()
st, err := syncScan(ctx, db, []string{dir}, walkCancelWorkers, false)
st, err := syncScan(ctx, db, []string{dir}, walkCancelWorkers, false,
walkRules{})
if !errors.Is(err, context.Canceled) {
t.Fatalf("syncScan on a cancelled context = %v, want %v",
err, context.Canceled)
@@ -301,7 +302,7 @@ func TestSyncScanCancelledMidHashKeepsHashedRecords(t *testing.T) {
db := openTestDB(t)
st, err := syncScan(newWalkClock(hashCancelAtDone), db,
[]string{dir}, walkCancelWorkers, false)
[]string{dir}, walkCancelWorkers, false, walkRules{})
if !errors.Is(err, context.Canceled) {
t.Fatalf("syncScan cancelled mid-hash = %v, want %v",
err, context.Canceled)
@@ -356,7 +357,8 @@ func TestRunScanInterrupted(t *testing.T) {
stderr := captureStderr(t)
dir := buildSmokeTree(t)
err := runScan(t.Context(), []string{dir}, walkCancelWorkers, false)
err := runScan(t.Context(), []string{dir}, walkCancelWorkers, false,
false)
if err != nil {
t.Fatal(err)
}
@@ -380,7 +382,7 @@ func TestRunScanInterrupted(t *testing.T) {
defer close(done)
err = runScan(cancelledContext(t), []string{dir}, walkCancelWorkers,
false)
false, false)
}()
awaitReturn(t, done, "runScan")
@@ -407,7 +409,8 @@ func TestRunScanInterrupted(t *testing.T) {
got, before)
}
err = runScan(t.Context(), []string{dir}, walkCancelWorkers, false)
err = runScan(t.Context(), []string{dir}, walkCancelWorkers, false,
false)
if err != nil {
t.Fatal(err)
}
@@ -433,7 +436,7 @@ func TestRunScanInterruptedMidHash(t *testing.T) {
dir := buildWalkCancelTree(t)
err := runScan(newWalkClock(hashCancelAtDone), []string{dir},
walkCancelWorkers, false)
walkCancelWorkers, false, false)
if !errors.Is(err, errInterrupted) {
t.Fatalf("runScan interrupted mid-hash = %v, want %v",
err, errInterrupted)
@@ -617,7 +620,8 @@ func TestWalkOneDirStopsWhenCancelled(t *testing.T) {
// Unbuffered and unread: on a cancelled scan every send gives up.
events := make(chan walkEvent)
subs := walkOneDir(cancelledContext(t), dirJob{path: dir}, false, events)
subs := walkOneDir(cancelledContext(t), dirJob{path: dir}, false,
walkRules{}, events)
if len(subs) != 0 {
t.Errorf("cancelled walkOneDir returned %+v to descend into, "+
"want none", subs)
@@ -637,7 +641,8 @@ func TestWalkWorkersDropQueuedDirs(t *testing.T) {
missing := filepath.Join(t.TempDir(), "missing")
jobs, _, events := startWalkWorkers(cancelledContext(t), 2, false)
jobs, _, events := startWalkWorkers(cancelledContext(t), 2, false,
walkRules{})
for range 64 {
jobs <- dirJob{path: missing}
@@ -668,7 +673,7 @@ func TestWalkWorkerAbandonsSubdirHandoff(t *testing.T) {
ctx, cancel := context.WithCancel(t.Context())
defer cancel()
jobs, subdirs, events := startWalkWorkers(ctx, 1, false)
jobs, subdirs, events := startWalkWorkers(ctx, 1, false, walkRules{})
// Fill the hand-back channel to its capacity — one slot per worker
// — so the worker's own hand-back is certain to block.