docs(media): stat through the overlay can outlive the file it points at
Measured 2026-11-02 while a TV dedupe pass moved folders on the raw path: the merged tree still answered stat at the old coordinates, link count 0, while ls of the same parent no longer listed the name. Overlayfs keeps a dentry for a lower file moved underneath it, so a tool that tests existence with [ -e ] against the view can believe a file its own readdir denies — the media servers walk by readdir, so this bites a script and never a user. Recorded with it: every pass that moves or measures media walks /mnt/entropy, and the one reader allowed to point at the view is the tool that needs show identity.
This commit is contained in:
1 parent
9f9043c037
commit
51ed5c0b2a
1 file changed
+15
-1
@@ -3,7 +3,7 @@ type: reference
|
|||||||
title: Media Metadata View
|
title: Media Metadata View
|
||||||
description: "The SSD-backed overlay the two media servers read and write: shape, guards, commands, laws, rollback."
|
description: "The SSD-backed overlay the two media servers read and write: shape, guards, commands, laws, rollback."
|
||||||
tags: [dankserver, media-server, jellyfin, plex, storage]
|
tags: [dankserver, media-server, jellyfin, plex, storage]
|
||||||
timestamp: 2026-11-01T01:00:00Z
|
timestamp: 2026-11-02T01:00:00Z
|
||||||
---
|
---
|
||||||
|
|
||||||
# Media Metadata View
|
# Media Metadata View
|
||||||
@@ -134,6 +134,19 @@ the defaults are the host values above.
|
|||||||
upper layer*, not for changes on the array underneath. After any relayout or
|
upper layer*, not for changes on the array underneath. After any relayout or
|
||||||
externally-written metadata, trigger a library refresh — see
|
externally-written metadata, trigger a library refresh — see
|
||||||
[[Media Relayout]] for the refresh procedure.
|
[[Media Relayout]] for the refresh procedure.
|
||||||
|
- **`stat` through the view can outlive the file.** Measured 2026-11-02 while a TV
|
||||||
|
dedupe pass moved folders on the raw path: the merged tree still answered `stat`
|
||||||
|
at the old coordinates — link count `0`, and `ls` of the same parent no longer
|
||||||
|
listed the name. Overlayfs keeps a dentry for a lower file that was moved
|
||||||
|
underneath it, so a script that tests existence with `[ -e ]` against the view can
|
||||||
|
believe a file its own `readdir` denies. Prove existence by listing the parent.
|
||||||
|
The media servers walk by `readdir`, so ghosts never reach the library — this bites
|
||||||
|
a tool, never a user, which is why no server symptom accompanies it.
|
||||||
|
- **Tools that move or measure media walk the raw array, never the view** — every
|
||||||
|
relayout, prune and detritus pass, and every journal it writes. The view is for
|
||||||
|
Jellyfin's writes and Plex's reads only. One deliberate exception: `tvshow.nfo`
|
||||||
|
sidecars live in the upper layer, so a tool reading show identity points at the
|
||||||
|
view (`media-tvdupes.py --meta /var/lib/media-meta/view/TVShows`).
|
||||||
|
|
||||||
## Failure signatures
|
## Failure signatures
|
||||||
|
|
||||||
@@ -148,6 +161,7 @@ the defaults are the host values above.
|
|||||||
| A media server's scan record reads `"Status": "Aborted"` | Its container restarted (a cutover, an image bump, an OOM) and the walk was discarded; systemd's `Restart=always` restarts the *server*, never the scan. Trigger a refresh — see [[Media Relayout]]. |
|
| A media server's scan record reads `"Status": "Aborted"` | Its container restarted (a cutover, an image bump, an OOM) and the walk was discarded; systemd's `Restart=always` restarts the *server*, never the scan. Trigger a refresh — see [[Media Relayout]]. |
|
||||||
| The Jellyfin API answers 401 to every token header | Almost always the header **form**, not the key: 12.4 authenticates only `Authorization: MediaBrowser Token="…"` and refuses a bare `X-Emby-Token` for every key, valid or not — and one collapsed backslash in the curl config produces the same 401. `[[Jellyfin]]` owns the block that works. Before concluding anything, prove the key against the server's own table: `select count(*) from ApiKeys where AccessToken = ?` on `jellyfin.db` opened `mode=ro&immutable=1`. |
|
| The Jellyfin API answers 401 to every token header | Almost always the header **form**, not the key: 12.4 authenticates only `Authorization: MediaBrowser Token="…"` and refuses a bare `X-Emby-Token` for every key, valid or not — and one collapsed backslash in the curl config produces the same 401. `[[Jellyfin]]` owns the block that works. Before concluding anything, prove the key against the server's own table: `select count(*) from ApiKeys where AccessToken = ?` on `jellyfin.db` opened `mode=ro&immutable=1`. |
|
||||||
| `systemctl status` finds no such unit | Look under the escaped name, `var-lib-media\x2dmeta-view.mount`, not the repo file name. |
|
| `systemctl status` finds no such unit | Look under the escaped name, `var-lib-media\x2dmeta-view.mount`, not the repo file name. |
|
||||||
|
| Stale dentry through the view: `stat` succeeds with link count `0` while the parent's `ls` omits the name | The lower file was moved on the raw path after the view cached its dentry. Nothing is lost; existence through the view is proved by the parent listing. |
|
||||||
|
|
||||||
## Rollback
|
## Rollback
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user