fix(files): file registration records the proc-fd link size (64 bytes) instead of the real file length - #901
Open
kevinheneveld wants to merge 1 commit into
Open
Conversation
…) instead of the real file length Since Listenarrs#717, file registration reads sizes from the lease's metadata path, which on Linux is a /proc/self/fd magic link. FileSystemInfo.Length reads the link inode itself (lstat), and proc magic symlinks report a constant st_size of 64 — so every file registered through a pinned lease persists Size = 64 regardless of the actual file. The UI then shows 0 KB wherever sizes are rendered (duplicate comparison, library size totals, quality scoring inputs). Open the metadata path instead and take the stream length: opening follows the magic link to the exact pinned object the lease holds, so the length is read from the real file without reintroducing the rename race the metadata path exists to avoid. Applies to both initial registration and physical-generation replacement snapshots (the latter falls back to the prior recorded size when the open fails). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
kevinheneveld
added a commit
to kevinheneveld/Listenarr
that referenced
this pull request
Aug 26, 2026
…fd link (PR Listenarrs#901) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
m4bard
added a commit
to m4bard/Listenarr
that referenced
this pull request
Aug 26, 2026
…rrs#901 This PR fixed two things behind the same /proc descriptor link: the probe's extension guard rejecting the candidate, and FileInfo reporting the link's own 64 bytes instead of the file's length. Listenarrs#901 fixes the size half, and fixes it better. It opens the metadata path and takes the stream length unconditionally. The version here read the length through the lease's generation-bound stream but fell back to FileInfo on the metadata path when the lease did not expose one, which on Linux is the original 64-byte bug again. That half also had no test of its own here; both test files in this branch cover the scan boundary. So the size change is dropped and the two shared files go back to canary. What remains is the half Listenarrs#901 does not touch: the scan passed the lease's metadata path as both the byte source and the media identity, and on Linux that path is an extensionless /proc link, so the audio-extension guard rejected the candidate before ffprobe ever ran. Passing the candidate as the identity alongside the metadata path as the byte source keeps the guard working on the real filename. The two PRs no longer touch a file in common.
m4bard
added a commit
to m4bard/Listenarr
that referenced
this pull request
Aug 27, 2026
…rrs#901 This PR fixed two things behind the same /proc descriptor link: the probe's extension guard rejecting the candidate, and FileInfo reporting the link's own 64 bytes instead of the file's length. Listenarrs#901 fixes the size half, and fixes it better. It opens the metadata path and takes the stream length unconditionally. The version here read the length through the lease's generation-bound stream but fell back to FileInfo on the metadata path when the lease did not expose one, which on Linux is the original 64-byte bug again. That half also had no test of its own here; both test files in this branch cover the scan boundary. So the size change is dropped and the two shared files go back to canary. What remains is the half Listenarrs#901 does not touch: the scan passed the lease's metadata path as both the byte source and the media identity, and on Linux that path is an extensionless /proc link, so the audio-extension guard rejected the candidate before ffprobe ever ran. Passing the candidate as the identity alongside the metadata path as the byte source keeps the guard working on the real filename. The two PRs no longer touch a file in common. (cherry picked from commit 2af63b9)
m4bard
added a commit
to m4bard/Listenarr
that referenced
this pull request
Aug 28, 2026
…rrs#901 This PR fixed two things behind the same /proc descriptor link: the probe's extension guard rejecting the candidate, and FileInfo reporting the link's own 64 bytes instead of the file's length. Listenarrs#901 fixes the size half, and fixes it better. It opens the metadata path and takes the stream length unconditionally. The version here read the length through the lease's generation-bound stream but fell back to FileInfo on the metadata path when the lease did not expose one, which on Linux is the original 64-byte bug again. That half also had no test of its own here; both test files in this branch cover the scan boundary. So the size change is dropped and the two shared files go back to canary. What remains is the half Listenarrs#901 does not touch: the scan passed the lease's metadata path as both the byte source and the media identity, and on Linux that path is an extensionless /proc link, so the audio-extension guard rejected the candidate before ffprobe ever ran. Passing the candidate as the identity alongside the metadata path as the byte source keeps the guard working on the real filename. The two PRs no longer touch a file in common. (cherry picked from commit 2af63b9)
m4bard
added a commit
to m4bard/Listenarr
that referenced
this pull request
Aug 28, 2026
…rrs#901 This PR fixed two things behind the same /proc descriptor link: the probe's extension guard rejecting the candidate, and FileInfo reporting the link's own 64 bytes instead of the file's length. Listenarrs#901 fixes the size half, and fixes it better. It opens the metadata path and takes the stream length unconditionally. The version here read the length through the lease's generation-bound stream but fell back to FileInfo on the metadata path when the lease did not expose one, which on Linux is the original 64-byte bug again. That half also had no test of its own here; both test files in this branch cover the scan boundary. So the size change is dropped and the two shared files go back to canary. What remains is the half Listenarrs#901 does not touch: the scan passed the lease's metadata path as both the byte source and the media identity, and on Linux that path is an extensionless /proc link, so the audio-extension guard rejected the candidate before ffprobe ever ran. Passing the candidate as the identity alongside the metadata path as the byte source keeps the guard working on the real filename. The two PRs no longer touch a file in common. (cherry picked from commit 2af63b9)
m4bard
added a commit
to m4bard/Listenarr
that referenced
this pull request
Aug 31, 2026
…rrs#901 This PR fixed two things behind the same /proc descriptor link: the probe's extension guard rejecting the candidate, and FileInfo reporting the link's own 64 bytes instead of the file's length. Listenarrs#901 fixes the size half, and fixes it better. It opens the metadata path and takes the stream length unconditionally. The version here read the length through the lease's generation-bound stream but fell back to FileInfo on the metadata path when the lease did not expose one, which on Linux is the original 64-byte bug again. That half also had no test of its own here; both test files in this branch cover the scan boundary. So the size change is dropped and the two shared files go back to canary. What remains is the half Listenarrs#901 does not touch: the scan passed the lease's metadata path as both the byte source and the media identity, and on Linux that path is an extensionless /proc link, so the audio-extension guard rejected the candidate before ffprobe ever ran. Passing the candidate as the identity alongside the metadata path as the byte source keeps the guard working on the real filename. The two PRs no longer touch a file in common. (cherry picked from commit 2af63b9)
m4bard
added a commit
to m4bard/Listenarr
that referenced
this pull request
Aug 31, 2026
…rrs#901 This PR fixed two things behind the same /proc descriptor link: the probe's extension guard rejecting the candidate, and FileInfo reporting the link's own 64 bytes instead of the file's length. Listenarrs#901 fixes the size half, and fixes it better. It opens the metadata path and takes the stream length unconditionally. The version here read the length through the lease's generation-bound stream but fell back to FileInfo on the metadata path when the lease did not expose one, which on Linux is the original 64-byte bug again. That half also had no test of its own here; both test files in this branch cover the scan boundary. So the size change is dropped and the two shared files go back to canary. What remains is the half Listenarrs#901 does not touch: the scan passed the lease's metadata path as both the byte source and the media identity, and on Linux that path is an extensionless /proc link, so the audio-extension guard rejected the candidate before ffprobe ever ran. Passing the candidate as the identity alongside the metadata path as the byte source keeps the guard working on the real filename. The two PRs no longer touch a file in common. (cherry picked from commit 2af63b9)
m4bard
added a commit
to m4bard/Listenarr
that referenced
this pull request
Sep 1, 2026
…rrs#901 This PR fixed two things behind the same /proc descriptor link: the probe's extension guard rejecting the candidate, and FileInfo reporting the link's own 64 bytes instead of the file's length. Listenarrs#901 fixes the size half, and fixes it better. It opens the metadata path and takes the stream length unconditionally. The version here read the length through the lease's generation-bound stream but fell back to FileInfo on the metadata path when the lease did not expose one, which on Linux is the original 64-byte bug again. That half also had no test of its own here; both test files in this branch cover the scan boundary. So the size change is dropped and the two shared files go back to canary. What remains is the half Listenarrs#901 does not touch: the scan passed the lease's metadata path as both the byte source and the media identity, and on Linux that path is an extensionless /proc link, so the audio-extension guard rejected the candidate before ffprobe ever ran. Passing the candidate as the identity alongside the metadata path as the byte source keeps the guard working on the real filename. The two PRs no longer touch a file in common. (cherry picked from commit 2af63b9)
m4bard
added a commit
to m4bard/Listenarr
that referenced
this pull request
Sep 1, 2026
…rrs#901 This PR fixed two things behind the same /proc descriptor link: the probe's extension guard rejecting the candidate, and FileInfo reporting the link's own 64 bytes instead of the file's length. Listenarrs#901 fixes the size half, and fixes it better. It opens the metadata path and takes the stream length unconditionally. The version here read the length through the lease's generation-bound stream but fell back to FileInfo on the metadata path when the lease did not expose one, which on Linux is the original 64-byte bug again. That half also had no test of its own here; both test files in this branch cover the scan boundary. So the size change is dropped and the two shared files go back to canary. What remains is the half Listenarrs#901 does not touch: the scan passed the lease's metadata path as both the byte source and the media identity, and on Linux that path is an extensionless /proc link, so the audio-extension guard rejected the candidate before ffprobe ever ran. Passing the candidate as the identity alongside the metadata path as the byte source keeps the guard working on the real filename. The two PRs no longer touch a file in common. (cherry picked from commit 2af63b9)
m4bard
added a commit
to m4bard/Listenarr
that referenced
this pull request
Sep 1, 2026
…rrs#901 This PR fixed two things behind the same /proc descriptor link: the probe's extension guard rejecting the candidate, and FileInfo reporting the link's own 64 bytes instead of the file's length. Listenarrs#901 fixes the size half, and fixes it better. It opens the metadata path and takes the stream length unconditionally. The version here read the length through the lease's generation-bound stream but fell back to FileInfo on the metadata path when the lease did not expose one, which on Linux is the original 64-byte bug again. That half also had no test of its own here; both test files in this branch cover the scan boundary. So the size change is dropped and the two shared files go back to canary. What remains is the half Listenarrs#901 does not touch: the scan passed the lease's metadata path as both the byte source and the media identity, and on Linux that path is an extensionless /proc link, so the audio-extension guard rejected the candidate before ffprobe ever ran. Passing the candidate as the identity alongside the metadata path as the byte source keeps the guard working on the real filename. The two PRs no longer touch a file in common. (cherry picked from commit 2af63b9)
m4bard
added a commit
to m4bard/Listenarr
that referenced
this pull request
Sep 1, 2026
…rrs#901 This PR fixed two things behind the same /proc descriptor link: the probe's extension guard rejecting the candidate, and FileInfo reporting the link's own 64 bytes instead of the file's length. Listenarrs#901 fixes the size half, and fixes it better. It opens the metadata path and takes the stream length unconditionally. The version here read the length through the lease's generation-bound stream but fell back to FileInfo on the metadata path when the lease did not expose one, which on Linux is the original 64-byte bug again. That half also had no test of its own here; both test files in this branch cover the scan boundary. So the size change is dropped and the two shared files go back to canary. What remains is the half Listenarrs#901 does not touch: the scan passed the lease's metadata path as both the byte source and the media identity, and on Linux that path is an extensionless /proc link, so the audio-extension guard rejected the candidate before ffprobe ever ran. Passing the candidate as the identity alongside the metadata path as the byte source keeps the guard working on the real filename. The two PRs no longer touch a file in common. (cherry picked from commit 2af63b9)
m4bard
added a commit
to m4bard/Listenarr
that referenced
this pull request
Sep 2, 2026
…rrs#901 This PR fixed two things behind the same /proc descriptor link: the probe's extension guard rejecting the candidate, and FileInfo reporting the link's own 64 bytes instead of the file's length. Listenarrs#901 fixes the size half, and fixes it better. It opens the metadata path and takes the stream length unconditionally. The version here read the length through the lease's generation-bound stream but fell back to FileInfo on the metadata path when the lease did not expose one, which on Linux is the original 64-byte bug again. That half also had no test of its own here; both test files in this branch cover the scan boundary. So the size change is dropped and the two shared files go back to canary. What remains is the half Listenarrs#901 does not touch: the scan passed the lease's metadata path as both the byte source and the media identity, and on Linux that path is an extensionless /proc link, so the audio-extension guard rejected the candidate before ffprobe ever ran. Passing the candidate as the identity alongside the metadata path as the byte source keeps the guard working on the real filename. The two PRs no longer touch a file in common. (cherry picked from commit 2af63b9)
m4bard
added a commit
to m4bard/Listenarr
that referenced
this pull request
Sep 2, 2026
…rrs#901 This PR fixed two things behind the same /proc descriptor link: the probe's extension guard rejecting the candidate, and FileInfo reporting the link's own 64 bytes instead of the file's length. Listenarrs#901 fixes the size half, and fixes it better. It opens the metadata path and takes the stream length unconditionally. The version here read the length through the lease's generation-bound stream but fell back to FileInfo on the metadata path when the lease did not expose one, which on Linux is the original 64-byte bug again. That half also had no test of its own here; both test files in this branch cover the scan boundary. So the size change is dropped and the two shared files go back to canary. What remains is the half Listenarrs#901 does not touch: the scan passed the lease's metadata path as both the byte source and the media identity, and on Linux that path is an extensionless /proc link, so the audio-extension guard rejected the candidate before ffprobe ever ran. Passing the candidate as the identity alongside the metadata path as the byte source keeps the guard working on the real filename. The two PRs no longer touch a file in common. (cherry picked from commit 2af63b9)
m4bard
added a commit
to m4bard/Listenarr
that referenced
this pull request
Sep 2, 2026
…rrs#901 This PR fixed two things behind the same /proc descriptor link: the probe's extension guard rejecting the candidate, and FileInfo reporting the link's own 64 bytes instead of the file's length. Listenarrs#901 fixes the size half, and fixes it better. It opens the metadata path and takes the stream length unconditionally. The version here read the length through the lease's generation-bound stream but fell back to FileInfo on the metadata path when the lease did not expose one, which on Linux is the original 64-byte bug again. That half also had no test of its own here; both test files in this branch cover the scan boundary. So the size change is dropped and the two shared files go back to canary. What remains is the half Listenarrs#901 does not touch: the scan passed the lease's metadata path as both the byte source and the media identity, and on Linux that path is an extensionless /proc link, so the audio-extension guard rejected the candidate before ffprobe ever ran. Passing the candidate as the identity alongside the metadata path as the byte source keeps the guard working on the real filename. The two PRs no longer touch a file in common. (cherry picked from commit 2af63b9)
m4bard
added a commit
to m4bard/Listenarr
that referenced
this pull request
Sep 2, 2026
…rrs#901 This PR fixed two things behind the same /proc descriptor link: the probe's extension guard rejecting the candidate, and FileInfo reporting the link's own 64 bytes instead of the file's length. Listenarrs#901 fixes the size half, and fixes it better. It opens the metadata path and takes the stream length unconditionally. The version here read the length through the lease's generation-bound stream but fell back to FileInfo on the metadata path when the lease did not expose one, which on Linux is the original 64-byte bug again. That half also had no test of its own here; both test files in this branch cover the scan boundary. So the size change is dropped and the two shared files go back to canary. What remains is the half Listenarrs#901 does not touch: the scan passed the lease's metadata path as both the byte source and the media identity, and on Linux that path is an extensionless /proc link, so the audio-extension guard rejected the candidate before ffprobe ever ran. Passing the candidate as the identity alongside the metadata path as the byte source keeps the guard working on the real filename. The two PRs no longer touch a file in common. (cherry picked from commit 2af63b9)
m4bard
added a commit
to m4bard/Listenarr
that referenced
this pull request
Sep 2, 2026
…rrs#901 This PR fixed two things behind the same /proc descriptor link: the probe's extension guard rejecting the candidate, and FileInfo reporting the link's own 64 bytes instead of the file's length. Listenarrs#901 fixes the size half, and fixes it better. It opens the metadata path and takes the stream length unconditionally. The version here read the length through the lease's generation-bound stream but fell back to FileInfo on the metadata path when the lease did not expose one, which on Linux is the original 64-byte bug again. That half also had no test of its own here; both test files in this branch cover the scan boundary. So the size change is dropped and the two shared files go back to canary. What remains is the half Listenarrs#901 does not touch: the scan passed the lease's metadata path as both the byte source and the media identity, and on Linux that path is an extensionless /proc link, so the audio-extension guard rejected the candidate before ffprobe ever ran. Passing the candidate as the identity alongside the metadata path as the byte source keeps the guard working on the real filename. The two PRs no longer touch a file in common. (cherry picked from commit 2af63b9)
m4bard
added a commit
to m4bard/Listenarr
that referenced
this pull request
Sep 2, 2026
…rrs#901 This PR fixed two things behind the same /proc descriptor link: the probe's extension guard rejecting the candidate, and FileInfo reporting the link's own 64 bytes instead of the file's length. Listenarrs#901 fixes the size half, and fixes it better. It opens the metadata path and takes the stream length unconditionally. The version here read the length through the lease's generation-bound stream but fell back to FileInfo on the metadata path when the lease did not expose one, which on Linux is the original 64-byte bug again. That half also had no test of its own here; both test files in this branch cover the scan boundary. So the size change is dropped and the two shared files go back to canary. What remains is the half Listenarrs#901 does not touch: the scan passed the lease's metadata path as both the byte source and the media identity, and on Linux that path is an extensionless /proc link, so the audio-extension guard rejected the candidate before ffprobe ever ran. Passing the candidate as the identity alongside the metadata path as the byte source keeps the guard working on the real filename. The two PRs no longer touch a file in common. (cherry picked from commit 2af63b9)
m4bard
added a commit
to m4bard/Listenarr
that referenced
this pull request
Sep 2, 2026
…rrs#901 This PR fixed two things behind the same /proc descriptor link: the probe's extension guard rejecting the candidate, and FileInfo reporting the link's own 64 bytes instead of the file's length. Listenarrs#901 fixes the size half, and fixes it better. It opens the metadata path and takes the stream length unconditionally. The version here read the length through the lease's generation-bound stream but fell back to FileInfo on the metadata path when the lease did not expose one, which on Linux is the original 64-byte bug again. That half also had no test of its own here; both test files in this branch cover the scan boundary. So the size change is dropped and the two shared files go back to canary. What remains is the half Listenarrs#901 does not touch: the scan passed the lease's metadata path as both the byte source and the media identity, and on Linux that path is an extensionless /proc link, so the audio-extension guard rejected the candidate before ffprobe ever ran. Passing the candidate as the identity alongside the metadata path as the byte source keeps the guard working on the real filename. The two PRs no longer touch a file in common. (cherry picked from commit 2af63b9)
m4bard
added a commit
to m4bard/Listenarr
that referenced
this pull request
Sep 2, 2026
…rrs#901 This PR fixed two things behind the same /proc descriptor link: the probe's extension guard rejecting the candidate, and FileInfo reporting the link's own 64 bytes instead of the file's length. Listenarrs#901 fixes the size half, and fixes it better. It opens the metadata path and takes the stream length unconditionally. The version here read the length through the lease's generation-bound stream but fell back to FileInfo on the metadata path when the lease did not expose one, which on Linux is the original 64-byte bug again. That half also had no test of its own here; both test files in this branch cover the scan boundary. So the size change is dropped and the two shared files go back to canary. What remains is the half Listenarrs#901 does not touch: the scan passed the lease's metadata path as both the byte source and the media identity, and on Linux that path is an extensionless /proc link, so the audio-extension guard rejected the candidate before ffprobe ever ran. Passing the candidate as the identity alongside the metadata path as the byte source keeps the guard working on the real filename. The two PRs no longer touch a file in common. (cherry picked from commit 2af63b9)
m4bard
added a commit
to m4bard/Listenarr
that referenced
this pull request
Sep 2, 2026
…rrs#901 This PR fixed two things behind the same /proc descriptor link: the probe's extension guard rejecting the candidate, and FileInfo reporting the link's own 64 bytes instead of the file's length. Listenarrs#901 fixes the size half, and fixes it better. It opens the metadata path and takes the stream length unconditionally. The version here read the length through the lease's generation-bound stream but fell back to FileInfo on the metadata path when the lease did not expose one, which on Linux is the original 64-byte bug again. That half also had no test of its own here; both test files in this branch cover the scan boundary. So the size change is dropped and the two shared files go back to canary. What remains is the half Listenarrs#901 does not touch: the scan passed the lease's metadata path as both the byte source and the media identity, and on Linux that path is an extensionless /proc link, so the audio-extension guard rejected the candidate before ffprobe ever ran. Passing the candidate as the identity alongside the metadata path as the byte source keeps the guard working on the real filename. The two PRs no longer touch a file in common. (cherry picked from commit 2af63b9)
m4bard
added a commit
to m4bard/Listenarr
that referenced
this pull request
Sep 3, 2026
…rrs#901 This PR fixed two things behind the same /proc descriptor link: the probe's extension guard rejecting the candidate, and FileInfo reporting the link's own 64 bytes instead of the file's length. Listenarrs#901 fixes the size half, and fixes it better. It opens the metadata path and takes the stream length unconditionally. The version here read the length through the lease's generation-bound stream but fell back to FileInfo on the metadata path when the lease did not expose one, which on Linux is the original 64-byte bug again. That half also had no test of its own here; both test files in this branch cover the scan boundary. So the size change is dropped and the two shared files go back to canary. What remains is the half Listenarrs#901 does not touch: the scan passed the lease's metadata path as both the byte source and the media identity, and on Linux that path is an extensionless /proc link, so the audio-extension guard rejected the candidate before ffprobe ever ran. Passing the candidate as the identity alongside the metadata path as the byte source keeps the guard working on the real filename. The two PRs no longer touch a file in common. (cherry picked from commit 2af63b9)
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
The bug
Since #717,
AudiobookFileServicereads the registered file's size from the lease's metadata path (new FileInfo(metadataPath).Length). On Linux that path is a/proc/self/fd/<n>magic link, andFileSystemInfostats the link inode itself — proc magic symlinks report a constantst_sizeof 64. Result: every file registered through a pinned lease persistsSize = 64, regardless of the real file.Live evidence from my instance (v1.3.0): 361 of 27,912
AudiobookFilesrows haveSize = 64exactly — every one created after the deploy that brought in #717 (sources:download,LibraryScan,MetadataRescan). The UI shows "0 KB" wherever sizes render (the duplicate-ASIN comparison table is where I caught it).The fix
Open the metadata path and take the stream length. Opening follows the magic link to the exact pinned object the lease holds open, so the size is read from the real file without reintroducing the rename race the metadata path exists to avoid. Applied at both write sites:
AudiobookFileService.cs— initial registration (Size = nullwhen the open fails, as before)AudiobookFileService.PhysicalGeneration.cs— generation-replacement snapshots (falls back to the previously recorded size)Notes
ExtractMetadataAsync's cache key usesLastWriteTimeUtcof the same metadata path, which has the same lstat behavior on Linux (the link's mtime, not the file's). It only weakens cache invalidation — the key still includes the physical object identity — so I left it out of scope here; happy to fold it in if you'd like.Size = 64stay wrong until re-registered; on my instance I'm backfilling them from disk. If you want, a follow-up startup reconciliation could repairSize = 64rows whose path resolves, but I didn't want to bundle that policy decision into this fix.🤖 Generated with Claude Code