scan: skip OS default excludes, and record nothing on filesystems that are not persistent (closes #124)
check / check (push) Canceled after 0s
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:
+29
-8
@@ -782,7 +782,8 @@ 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, walkRules{},
|
||||
workers) {
|
||||
if ev.fail {
|
||||
errs++
|
||||
|
||||
@@ -1004,7 +1005,8 @@ 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, walkRules{},
|
||||
events)
|
||||
close(events)
|
||||
|
||||
var evs []walkEvent
|
||||
@@ -1152,7 +1154,7 @@ func syncOneFilesystem(t *testing.T, db *sql.DB, roots []string,
|
||||
return dev, ok
|
||||
}
|
||||
|
||||
roots = pruneRoots(roots, true, device)
|
||||
roots = pruneRoots(roots, true, walkRules{}, device)
|
||||
|
||||
ctx := t.Context()
|
||||
s := &scanState{db: db}
|
||||
@@ -1162,7 +1164,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, walkRules{})
|
||||
|
||||
// seedRoot emits a regular-file operand itself and returns a job
|
||||
// only for a directory operand.
|
||||
@@ -1463,7 +1465,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, walkRules{})
|
||||
if err != nil {
|
||||
t.Fatalf("syncScan: %v", err)
|
||||
}
|
||||
@@ -2412,7 +2414,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, walkRules{}, device)
|
||||
|
||||
want := []string{"/a", "/c", "/cc"}
|
||||
if !slices.Equal(got, want) {
|
||||
@@ -2421,7 +2423,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, walkRules{}, device)
|
||||
|
||||
want = []string{"/p"}
|
||||
if !slices.Equal(got, want) {
|
||||
@@ -2429,12 +2431,31 @@ 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, walkRules{}, 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, walkRules{excludes: []string{pqRoot}},
|
||||
device)
|
||||
if !slices.Equal(got, want) {
|
||||
t.Fatalf("pruneRoots excluding %s = %q, want %q", pqRoot, got, want)
|
||||
}
|
||||
|
||||
// Nor does it enter /p/q when the filesystem of /p/q is not
|
||||
// persistent, so /p/q/r/m, of no known device, is kept.
|
||||
got = pruneRoots([]string{"/p", "/p/q/r/m"}, false,
|
||||
walkRules{nonPersistent: map[uint64]string{2: "tmpfs"}}, device)
|
||||
|
||||
want = []string{"/p", "/p/q/r/m"}
|
||||
if !slices.Equal(got, want) {
|
||||
t.Fatalf("pruneRoots with %s not persistent = %q, want %q",
|
||||
pqRoot, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReportsNeverTouchFilesystem(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user