Diskio
Answer "what is actually reading this disk?" on a Linux host whose storage sits behind device-mapper and FUSE — where the obvious tools all lie.
Model:
@magistr/diskio— probes a host over SSH. Methods:device-map(resolve every dm/md/loop layer to its physical spindle and Unraid array slot, plus the alias groups a per-device metric would double-count),readers(rank processes by disk I/O from /proc//io, container-attributed, reporting requested bytes and block I/O separately),open-files(walk /proc/*/fd and re-attribute FUSE-held descriptors to the containers that really hold them), andattribute(all three for one disk, plus a one-line summary).
2026.08.31.1
First release.
Answers "what is actually reading this disk?" on a Linux host whose storage sits
behind device-mapper and FUSE. Built after a homelab disk-saturation alert named
dm-3 and sdl as two failing drives when they are one 14.6TB spindle, and
lsof /mnt/disk4 could name only shfs while the real reader was a torrent
daemon seeding a large library.
device-map— resolves every dm/md/loop layer to its physical spindle and Unraid array slot, and returnsaliasGroups: the layer sets a per-device metric double-counts.readers— samples/proc/<pid>/ioover a window and ranks processes by disk I/O with container attribution, reporting requested bytes (rchar) and block I/O (read_bytes) separately. The gap between them is the FUSE indirection made visible.open-files— walks/proc/<pid>/fdfor every process and re-attributes a FUSE-held descriptor to the container holding the same file under its own mount path, matched on basename and size.attribute— all three for one device, array slot or mountpoint, plus a one-linesummary.
Every probe is one SSH round trip: the script is generated locally,
base64-encoded and piped to bash, so no caller-supplied value ever becomes a
remote shell word. path and target are additionally allow-listed. Helpers
take an injected SshRunner, so no test spawns ssh.
Three host-behaviour traps are encoded here because each one shipped green and was only caught against the live host:
md4p1 -> sdlexists only in/var/local/emhttp/disks.ini. Unraid's md driver is not Linux md —/sys/block/md4exposes noslaves, so walking/sys/blockalone returnsphysical: nullfor every encrypted array slot.stat -Lc %nechoes its argument.cd /proc; stat "$pid"/fd/*emits relative keys, so every size parsed as null — which sorted real media files below zero-length rows and silently disabled the size check that stops two files sharing a basename being conflated (Postgres relfilenode2688exists under every database on the host).rcharcounts sockets and pipes. A websocket-heavy service outranked the real disk reader, soattributesetsonTargetper reader — true only when that process, or a proxy acting for it, holds a descriptor under the disk's mountpoint — and the summary credits nobody else.null(not evaluated) is kept distinct fromfalse.
The fd scan costs two forks per process (ls -l plus stat -L), not per
descriptor: ~13 000 open fds in ~25s, against over two minutes for the
per-descriptor version.
Global Arguments
| Argument | Type | Description |
|---|---|---|
| sshHost | string | SSH address of the host whose disks to read |
| sshUser | string | SSH username (default: root) |
| fuseProxies | string | Comma-separated process names that hold file descriptors on behalf of other processes. I/O charged to one of these is a proxy for a real consumer, never the consumer itself. |
Resources
- Has README or module doc2/2earned
- README has a code example1/1earned
- README is substantive1/1earned
- Most symbols documented1/1earned
- No slow types (deprecated)1/1earned
- Dependencies pass trust audit2/2earned
- Has description1/1earned
- Platform support declared (or universal)2/2earned
- License declared1/1earned
- Verified public repository2/2earned