You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
file locking: keep the log on a failed roll, fix the mutex name - #319
Four findings from an audit, all in file locking. None is a vulnerability; audit da18b6fd-*
cites them for provenance.
f036: RollingFileAppender reported a failed rename, which a backup or antivirus agent
holding the file without FILE_SHARE_DELETE is enough to cause, then reopened the log without
appending and destroyed everything it held: 21 consecutive events lost in the Windows run. It
appends now, and only that rename is retried, once per tenth of MaxFileSize of growth, so the
file can exceed MaxFileSize while the rename keeps failing.
f032: FileAppender.LockingStream's recursion counter went below zero, because the footer,
close and open paths released a lock they had failed to take, after which every later acquisition
on that stream failed and the footer and buffered tail were dropped in silence.
f031: FileAppender.InterProcessLock named its mutex from the configured path before ConvertToFullPath ran, so a relative and an absolute spelling of one file took two mutexes and
excluded nothing. That resolution is all it covers: symbolic links, 8.3 names and letter case
still name differently.
f010: RollingFileAppender's rolling lock and FileAppender.InterProcessLock both name their
mutex after the log path, and Unix rejects one over 255 UTF-8 bytes, which 104 characters can
already exceed, throwing out of ActivateOptions before anything is written. Such names are
hashed now; Windows has no limit and keeps its names.
The Local\ prefix, per-user scoping and ACL the scan proposed are deliberately absent: measured on
Windows, cross-session exclusion never existed, and a bare Global\ prefix throws for whichever
process starts second.
Two more came out of review, both the same failure one step from the fix: a pending rename was
never retried in Date mode, because the check sat inside the size-rolling guard, and the rollover
at startup still truncated the file it could not move when AppendToFile was false.
Also here: a pre-Vista branch in EventLogAppender that could not be false, fixtures that keep no
state between tests, and three older ones that stopped leaving temp files behind.
- A failed rename was reported, then the file reopened without appending, which
destroyed it. A backup agent holding a read handle is enough. It appends now,
and only that rename is retried, once per MaxFileSize of growth, so the
backups are never rotated twice and a retry that succeeds keeps the generation
it recovers.
- The footer, close and open paths released the file lock even when acquiring it
had failed. Only what was taken is released now.
audit da18b6f-f036, da18b6f-f032
FreeAndNil
changed the title
keep the log when a roll cannot rename it #319
keep the log when a roll cannot rename it
Sep 11, 2026
- InterProcessLock named its mutex before the path was resolved, so a relative
and an absolute spelling of one file took two mutexes and excluded nothing.
- A name over 255 characters throws on Unix, out of ActivateOptions, so a deep
log path took the appender down. Those are hashed now. Windows has no limit,
measured, so its names are left alone and keep excluding older versions.
- Both mutexes take their name from one helper, which carries why there is no
ACL, no Global\ prefix and no user component.
audit da18b6f-f031, da18b6f-f010
Windows 7 SP1 is the floor for the net462 build this file compiles into, so the
version test could not fail and the 32766 constant behind it was dead. The
surviving constant keeps its measured value and loses the superseded lore.
FreeAndNil
changed the title
keep the log when a roll cannot rename it
keep the log when a roll cannot rename it, and name the file lock after the resolved path
Sep 14, 2026
FreeAndNil
changed the title
keep the log when a roll cannot rename it, and name the file lock after the resolved path
file locking: keep the log on a failed roll, fix the mutex name
Sep 14, 2026
- Release the file lock with _stream?.ReleaseLock() wherever the acquire was
already null-conditional.
- One CountingWriter property instead of five QuietWriter casts, through
EnsureIs<> so a wrong writer names itself. The two sites after a reopen keep
their null check: a refused lock leaves no writer there.
- The new fixtures keep no state between tests: AutoTempFolder from
PeanutButter.Utils and a per-test error handler. Its namespace collides with
our test helper Utils, which is internal TestUtils now.
- SmtpPickupDirAppenderTest kept its pickup directory in a fixture field, shared
by every test, and created it inside the build output.
- FileAppenderTest leaked two files per run: Path.GetTempFileName creates them
and nothing deleted them.
- PatternStringTest tracked its config file by hand to delete it in a finally.
All three use AutoTempFolder now, which disposes what it made.
- the message printed 0 bytes of growth where the threshold was really 1
- the retry-count assertion allowed 2 to 19, so a wrong threshold passed it.
Two attempts on Unix, three on Windows, and the range says which is which
- record what the mutex suffix collision costs
- drop the empty else, rewrap a long changelog line
This contradicts both the failure mode and the PR description: after the footer or close path underflowed the existing stream's counter, later acquisitions on that stream failed, so the footer and buffered tail could be dropped. The appender only reopened automatically when no writer had been installed during the open path. Please describe the actual data-loss impact rather than stating that nothing was lost.
- ExistingInit rolled again for AppendToFile=false, moving the file out from
under the pending rename, so the first retry archived the new file under the
previous date.
- The forced append reached AppendToFile through base.OpenFile and replaced the
configured value. Every successful roll did this, not just a failed one, so
RollingCombinedWithPreserveExtension now asserts it too.
- The changelog entries now describe what a user sees.
- TheDeadlineCoversTheWholeSendAndNotOneOperation timed the whole send against a
5 s bound, and failed on a slow Windows runner at 6289 ms
- the fake transport now records which call found the token cancelled
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Four findings from an audit, all in file locking. None is a vulnerability;
audit da18b6fd-*cites them for provenance.
RollingFileAppenderreported a failed rename, which a backup or antivirus agentholding the file without
FILE_SHARE_DELETEis enough to cause, then reopened the log withoutappending and destroyed everything it held: 21 consecutive events lost in the Windows run. It
appends now, and only that rename is retried, once per tenth of
MaxFileSizeof growth, so thefile can exceed
MaxFileSizewhile the rename keeps failing.FileAppender.LockingStream's recursion counter went below zero, because the footer,close and open paths released a lock they had failed to take, after which every later acquisition
on that stream failed and the footer and buffered tail were dropped in silence.
FileAppender.InterProcessLocknamed its mutex from the configured path beforeConvertToFullPathran, so a relative and an absolute spelling of one file took two mutexes andexcluded nothing. That resolution is all it covers: symbolic links, 8.3 names and letter case
still name differently.
RollingFileAppender's rolling lock andFileAppender.InterProcessLockboth name theirmutex after the log path, and Unix rejects one over 255 UTF-8 bytes, which 104 characters can
already exceed, throwing out of
ActivateOptionsbefore anything is written. Such names arehashed now; Windows has no limit and keeps its names.
The
Local\prefix, per-user scoping and ACL the scan proposed are deliberately absent: measured onWindows, cross-session exclusion never existed, and a bare
Global\prefix throws for whicheverprocess starts second.
Two more came out of review, both the same failure one step from the fix: a pending rename was
never retried in
Datemode, because the check sat inside the size-rolling guard, and the rolloverat startup still truncated the file it could not move when
AppendToFilewas false.Also here: a pre-Vista branch in
EventLogAppenderthat could not be false, fixtures that keep nostate between tests, and three older ones that stopped leaving temp files behind.