pub enum VolumeIdentity {
FsUuid([u8; 16]),
Serial32(u32),
Serial64(u64),
}Expand description
A volume’s durable identity, as the platform reports it.
Unlike a mount point or a device node — both of which are session-local and change across remounts, reboots and machines — the value here is stored on the volume itself, so it survives unmounting, re-plugging into another port, and moving the disk to another computer.
The variants differ in strength, and a consumer that builds a registry key from one should keep them apart (for example by prefixing the variant name) rather than mixing their numeric spaces:
FsUuidis a 128-bit filesystem UUID and is normally strong enough to stand alone — with one caveat: on the FAT-class filesystems the platform derives the UUID from a narrower serial (see below), so it is no stronger than the value it was derived from, however wide it looks.Serial64is a 64-bit volume serial. Collisions are unlikely but it carries no structure, so it is only as unique as the formatting tool made it.Serial32is a 32-bit volume serial — the FAT class. It is weak: 32 bits is small, and some formatting tools derive it from the wall clock, so two volumes formatted in the same second can collide. Consumers that need a durable key should widen it with further invariants (volume size, label, filesystem type).
volume_identity() returns None when the platform or the filesystem
genuinely reports no identity at all — a virtual filesystem, a network
mount, or a platform without a durable-identity query — or when the platform
declines to let this caller look: the volume is no longer there, reading it
is not permitted, or the filesystem does not implement the question. None
is an honest “nothing to report”. On Apple platforms, Linux and Windows it
is never a failure to look: a read that failed for any other reason — no
descriptors left, no memory, an I/O error — is returned as the error it is,
not as a volume with no identity. On Linux it is also a directory of
published names that could not be read whole: one entry declined partway
refuses the whole directory, for every device, rather than leave a partial
answer standing. On Windows it is also a volume whose file system declined
FileFsVolumeInformation through the handle the row is read through.
§The value comes with the assurance of the read
The identity is durable on the volume, but not every platform lets an
unprivileged caller read it from the volume: Apple and Windows ask the
mounted filesystem, while Linux recovers it from a name udev published about
the mount’s source device, which can lag the media now behind that device.
So volume_identity() hands back an IdentityReading — this value and
the IdentityAssurance it was read at — rather than the value alone, and
a caller that must not act on a possibly-lagged name can require
Vouched. It is read afresh on every resolve, on every platform: no
backend keeps one.
§The same volume answers the same on every platform
The form is fixed per filesystem, not per platform. Whatever a platform can reach is reduced to the one value that filesystem’s volumes are named by, so a disk carried between macOS, Linux and Windows keeps a single key:
| Filesystem | Canonical identity | How each platform reaches it |
|---|---|---|
| APFS, ext2/3/4, XFS, f2fs | FsUuid — the UUID in the superblock | Apple: getattrlist. Linux: the by-uuid name udev’s record of the device lists |
| btrfs | FsUuid — the filesystem’s FSID, one value however many devices carry it | Linux: /sys/fs/btrfs/<fsid>/devices/, falling back to /dev/disk/by-uuid |
| HFS+ | FsUuid — a version-3 UUID derived from the volume’s 64-bit Finder-info id | Apple derives it in the kernel; blkid derives the identical value and udev publishes it |
| exFAT, with no Volume GUID | FsUuid — a version-3 UUID derived from the 32-bit serial | Apple derives it in the kernel; Linux and Windows compute the same value from the serial they read |
| exFAT, carrying a Volume GUID | FsUuid — the GUID in the root directory (but see below) | Apple only |
| NTFS | Serial64 — the full 64-bit boot-sector serial | Linux: the by-uuid name udev’s record lists. Windows: FSCTL_GET_NTFS_VOLUME_DATA |
| FAT12/16/32 | Serial32 — the 32-bit boot-sector serial (but see below) | Linux: the by-uuid name udev’s record lists. Windows: FileFsVolumeInformation |
Four cases cannot be made to agree. Each is a narrowing — a form poorer than the volume’s own identity, never a value invented in its place — and each is recorded here rather than left as a difference a caller would have to discover. In all four the failure is a missed match: two readings of one volume can differ, and no two volumes are made to look alike.
§FAT12/16/32 on Apple platforms
msdosfs never reports the serial. At mount time it derives a version-3
UUID from it and reports only that:
digest = MD5( b3e20f39-f292-11d6-97a4-00306543ecac, as 16 raw bytes
‖ the 4 serial bytes as they sit in the boot sector
‖ the BPB total-sector count, as 4 little-endian bytes )
digest[6] = (digest[6] & 0x0f) | 0x30 // version 3
digest[8] = (digest[8] & 0x3f) | 0x80 // RFC 4122 variant(msdosfs_generate_volume_uuid; the sector count is the BPB’s 16-bit
bpbSectors, or its 32-bit bpbHugeSectors when that field is zero.)
The sector count is the obstacle. Nothing unprivileged reports it off Apple:
statfs and FileFsFullSizeInformation describe the data area in
clusters, while
the BPB field also covers the reserved sectors, the FATs and the root
directory, so it cannot be recovered from them — and reading the boot sector
directly needs a raw volume handle, which needs elevation. Linux and Windows
therefore report the narrower Serial32, which does not compare equal to
the UUID an Apple platform reports for the same stick. A consumer spanning
both should qualify the key with fs_type() and treat the two as separate
keyspaces. exFAT’s derivation takes the serial alone, so it is unaffected by
the sector count — but see the Volume GUID below.
§NTFS on Windows when the volume FSCTL is unavailable
FileFsVolumeInformation reports only the low 32 bits of the 64-bit
serial. The full width comes from FSCTL_GET_NTFS_VOLUME_DATA, asked
through the one handle a Windows row is read through; where the file system
declines it there, this crate falls back to Serial32 of the low half.
That is a truncation of the Serial64 Linux reports for the same volume —
the same bits, fewer of them — but the two do not compare equal.
§exFAT volumes carrying a native Volume GUID
The exFAT format permits an optional Volume GUID entry in the root
directory, and where one is present it, not the serial, is the volume’s
identity: Apple reports that GUID through getattrlist, and exfat.util -k
documents the rule exactly — “if the root directory contains a Volume GUID
entry, that GUID is the value returned; otherwise, the 32-bit volume serial
number stored in the boot sector is converted to a UUID”.
Nothing off Apple can read it. The entry lives in the root directory rather
than the boot sector, so reaching it means reading the volume’s data through
a raw handle — which needs elevation — and neither FileFsVolumeInformation
nor the by-uuid name udev publishes carries it. Linux and Windows
therefore report the serial-derived UUID for such a volume, which is a
different value from the GUID Apple reports for it. A stamped volume read on
two platforms yields two identities; it is never mistaken for another volume.
Stamping is rare — no format tool writes one by default — but it is real:
exfat.util -s creates the entry, and one such volume is pinned as a test
fixture so this narrowing cannot quietly become untrue.
§exFAT mounted through FUSE
The derivation belongs to the format, so applying it takes proof of the
format. Linux gives that proof for the in-kernel driver (exfat) and for a
FUSE mount that publishes its subtype (fuse.exfat), and this crate derives
the UUID for both. It gives no proof for exfat-fuse mounted as a
block-backed FUSE filesystem, which is reported as bare fuseblk — a name
shared with ntfs-3g and every other block-backed FUSE helper. There the
serial udev published is reported as Serial32 rather than run through a
derivation that may not be the volume’s. NTFS is unaffected either way: its
identity is the serial itself, whatever the mount publishes as its type.
§Not verified: NTFS on an Apple platform
Apple ships a read-only NTFS driver, and its ntfs.util has a “Get UUID
Key” action — so an NTFS volume mounted there may well answer
ATTR_VOL_UUID with a UUID, which would be a fifth case of the same kind,
since Linux and Windows both name NTFS by its Serial64. No NTFS volume
was available to check it against on an Apple host, and an Apple platform is
not a place NTFS is usually read, so the row is left unclaimed rather than
guessed at in either direction.
§Zero is not an identity
A zero serial and the nil UUID are their formats’ “nothing was ever
recorded” sentinels rather than values: every volume that was never given
one carries the same zeros, so accepting them would make all of them
collide. Every platform maps them to None here. Apple’s kernel already
works this way — msdosfs derives no UUID at all from a zero serial.
Variants§
FsUuid([u8; 16])
A 128-bit filesystem UUID (APFS, ext2/3/4, XFS, btrfs, f2fs, …), or the version-3 UUID every platform derives for HFS+ and exFAT.
The bytes are in the canonical order of the textual form: the first byte
is the one rendered by the leading two hex digits of
8f19a253-d450-3090-abf6-e651943998d1.
Serial32(u32)
A 32-bit volume serial — the FAT12/16/32 class, where the on-disk format
has no room for a UUID, and the fallback for an NTFS volume whose full
serial could not be read. Rendered by most tools as two dash-separated
16-bit halves (1a2b-3c4d); the value here is the whole 32-bit number.
Serial64(u64)
A 64-bit volume serial, reported for volumes whose format carries a serial wider than 32 bits but no UUID — NTFS.
Trait Implementations§
Source§impl Clone for VolumeIdentity
impl Clone for VolumeIdentity
impl Copy for VolumeIdentity
Source§impl Debug for VolumeIdentity
impl Debug for VolumeIdentity
Source§impl Display for VolumeIdentity
The spelling the tools that read these values off a volume print: a UUID in
its canonical 8-4-4-4-12 form, a FAT-class serial in the two dash-separated
halves blkid and diskutil show it as, and a 64-bit serial as the sixteen
hex digits they print for NTFS.
impl Display for VolumeIdentity
The spelling the tools that read these values off a volume print: a UUID in
its canonical 8-4-4-4-12 form, a FAT-class serial in the two dash-separated
halves blkid and diskutil show it as, and a 64-bit serial as the sixteen
hex digits they print for NTFS.
Lowercase throughout, where some tools print the serials uppercase; it is the same value either way, and one crate should spell it one way.