scan: never record its own database files (closes #115)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
When the database lay under a scanned operand, the walk recorded the database file, its -wal and -shm files and its .lock file like any other file, so every rescan updated them and report paired them with empty files. Once the database and the lock file are open, the scan now notes their device and inode, and the walk passes over a file with one of them, as an operand or below one, printing no line and counting nothing. Matching by device and inode holds through a symlinked directory, where comparing paths would not. A record an earlier scan left for one of them is deleted as unverified. README's smoke-test note on keeping the database out of the tree is gone, as it no longer holds. Judgement call: walkRules, in exclude.go, carries the noted files to the walk. Model: opus-5-5
This commit was merged in pull request #128.
This commit is contained in:
@@ -118,7 +118,8 @@ func scanPhase(name string) string {
|
||||
// rejects anything less. Before the lock is taken, the scan lists the
|
||||
// filesystems that are not persistent, on which the walk records
|
||||
// nothing; unless noDefaultExcludes is set, the walk does not enter the
|
||||
// default excludes either.
|
||||
// default excludes either. Once the database is open, the scan notes
|
||||
// its files, which the walk passes over.
|
||||
func runScan(ctx context.Context, roots []string, workers int,
|
||||
oneFS, noDefaultExcludes bool,
|
||||
) error {
|
||||
@@ -173,6 +174,11 @@ func runScan(ctx context.Context, roots []string, workers int,
|
||||
|
||||
defer closeScanDatabase(ctx, db, dbPath)
|
||||
|
||||
rules.databaseFiles, err = databaseFiles(dbPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
st, err := syncScan(ctx, db, roots, workers, oneFS, rules)
|
||||
if errors.Is(err, context.Canceled) {
|
||||
return interrupted(st.walked)
|
||||
@@ -710,6 +716,15 @@ func sameInode(a, b fileRec) bool {
|
||||
return (a.dev != 0 || a.ino != 0) && a.dev == b.dev && a.ino == b.ino
|
||||
}
|
||||
|
||||
// isDatabaseFile reports whether f, a file the walk reached, is one of
|
||||
// the scan's own database files in rules, which the walk passes over
|
||||
// without a line and without counting it.
|
||||
func isDatabaseFile(f fileRec, rules walkRules) bool {
|
||||
return slices.ContainsFunc(rules.databaseFiles, func(d fileRec) bool {
|
||||
return sameInode(d, f)
|
||||
})
|
||||
}
|
||||
|
||||
// hashPhase hashes every queued file with hashSignature — the head and
|
||||
// tail of a file of headTailMin or more, the whole file below that —
|
||||
// committing completed records to the database in batches as results
|
||||
@@ -1143,7 +1158,7 @@ func startWalk(ctx context.Context, roots []string, oneFS bool,
|
||||
go func() {
|
||||
initial := make([]dirJob, 0, len(roots))
|
||||
for _, root := range roots {
|
||||
initial = append(initial, seedRoot(ctx, root, events)...)
|
||||
initial = append(initial, seedRoot(ctx, root, rules, events)...)
|
||||
}
|
||||
|
||||
dispatchDirs(ctx, initial, jobs, subdirs)
|
||||
@@ -1197,13 +1212,14 @@ func operandWarning(root string, fi fs.FileInfo) string {
|
||||
}
|
||||
|
||||
// seedRoot turns one PATH operand into the walk's starting state: a
|
||||
// regular-file operand is statted and emitted directly, and a directory
|
||||
// regular-file operand is statted and emitted directly, unless it is
|
||||
// one of the scan's own database files in rules, and a directory
|
||||
// operand becomes an initial job. walkableRoots has already dropped
|
||||
// every other operand. One that has changed into something else since
|
||||
// is warned about and skipped here; it is still a root, so the records
|
||||
// stored beneath it, other than those under another root, are deleted
|
||||
// as unverified.
|
||||
func seedRoot(ctx context.Context, root string,
|
||||
func seedRoot(ctx context.Context, root string, rules walkRules,
|
||||
events chan<- walkEvent,
|
||||
) []dirJob {
|
||||
fi, err := os.Lstat(root)
|
||||
@@ -1230,14 +1246,17 @@ func seedRoot(ctx context.Context, root string,
|
||||
}
|
||||
|
||||
dev, ino := inodeOfInfo(fi)
|
||||
|
||||
sendEvent(ctx, events, walkEvent{rec: fileRec{
|
||||
rec := fileRec{
|
||||
path: root,
|
||||
size: fi.Size(),
|
||||
mtime: fi.ModTime(),
|
||||
dev: dev,
|
||||
ino: ino,
|
||||
}})
|
||||
}
|
||||
|
||||
if !isDatabaseFile(rec, rules) {
|
||||
sendEvent(ctx, events, walkEvent{rec: rec})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1372,7 +1391,8 @@ func walkOneDir(ctx context.Context, job dirJob, oneFS bool,
|
||||
// regular file) between the directory read and the lstat is warned
|
||||
// about and skipped. A file on a filesystem in rules that is not
|
||||
// persistent, as through a bind mount of one file, is not recorded: it
|
||||
// gets an event naming it instead.
|
||||
// gets an event naming it instead. One of the scan's own database files
|
||||
// in rules gets no event at all.
|
||||
func emitFile(ctx context.Context, p string, e fs.DirEntry,
|
||||
rules walkRules, events chan<- walkEvent,
|
||||
) {
|
||||
@@ -1398,14 +1418,17 @@ func emitFile(ctx context.Context, p string, e fs.DirEntry,
|
||||
}
|
||||
|
||||
dev, ino := inodeOfInfo(info)
|
||||
|
||||
sendEvent(ctx, events, walkEvent{rec: fileRec{
|
||||
rec := fileRec{
|
||||
path: p,
|
||||
size: info.Size(),
|
||||
mtime: info.ModTime(),
|
||||
dev: dev,
|
||||
ino: ino,
|
||||
}})
|
||||
}
|
||||
|
||||
if !isDatabaseFile(rec, rules) {
|
||||
sendEvent(ctx, events, walkEvent{rec: rec})
|
||||
}
|
||||
}
|
||||
|
||||
// subdirJob applies the descent rules to directory p: never enter
|
||||
|
||||
Reference in New Issue
Block a user