Open files to hash without blocking and reject non-regular ones (closes #100)
check / check (push) Waiting to run
check / check (push) Waiting to run
A regular file replaced by a FIFO after the walk statted it made the hash phase's open wait forever for a writer, so the scan never ended and kept the lock. The hash and content phases now open each file with O_NONBLOCK and check with fstat that it is still a regular file; anything else is a per-path error, warned about and skipped. The new test replaces a walked file with a FIFO before the hash phase reads it. Model: opus-5-5
This commit was merged in pull request #107.
This commit is contained in:
@@ -1797,6 +1797,54 @@ func TestScanHardlinkRunFailsTogether(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestHashPhaseSkipsFileReplacedByFIFO replaces a walked regular file
|
||||
// with a FIFO before the hash phase reads it. Opening a FIFO that has
|
||||
// no writer waits for one, so a blocking open would hang the phase. The
|
||||
// path must be warned about once and skipped, with no record written.
|
||||
//
|
||||
//nolint:paralleltest // captureStderr replaces the process-wide os.Stderr
|
||||
func TestHashPhaseSkipsFileReplacedByFIFO(t *testing.T) {
|
||||
stderr := captureStderr(t)
|
||||
dir := t.TempDir()
|
||||
p := writeFile(t, dir, "a.bin", pattern(1, 300))
|
||||
|
||||
walked, _ := collectWalk(t, []string{dir}, false, 1)
|
||||
|
||||
err := os.Remove(p)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = unix.Mkfifo(p, 0o600)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
s := &scanState{db: openTestDB(t), toHash: walked}
|
||||
|
||||
err = s.hashPhase(t.Context(), 1)
|
||||
if err != nil {
|
||||
t.Fatalf("hashPhase = %v, want nil", err)
|
||||
}
|
||||
|
||||
if s.st.skipped != 1 || len(s.batch) != 0 {
|
||||
t.Errorf("skipped = %d, records = %d, want 1 skipped and none",
|
||||
s.st.skipped, len(s.batch))
|
||||
}
|
||||
|
||||
warning := "hash " + p + ": not a regular file\n"
|
||||
if got := stderr(); strings.Count(got, warning) != 1 {
|
||||
t.Errorf("stderr = %q, want %q once", got, warning)
|
||||
}
|
||||
|
||||
// The content phase opens files the same way.
|
||||
_, _, content, err := hashContentOnly(p, headTailMin)
|
||||
if content != "" || !errors.Is(err, errNotRegular) {
|
||||
t.Errorf("hashContentOnly on a FIFO = %q, %v; want no hash and %v",
|
||||
content, err, errNotRegular)
|
||||
}
|
||||
}
|
||||
|
||||
// injectedWriteFailure is the message the injected database trigger
|
||||
// aborts with, so the test can recognize its own failure in the error
|
||||
// the scan reports.
|
||||
|
||||
Reference in New Issue
Block a user