Bit rot, checksums, and the copy nobody checked
Silent data corruption is real, it has been measured across more than a million drives, and per drive per year it is roughly twenty times rarer than the read errors your disk already reports out loud. The way a file usually breaks without anyone noticing is duller than decay: a copy or a sync ran, something went wrong partway through, and nobody compared the result against the original. A checksum is the cheap fix for that second problem and the only detector for the first, and most people who worry about bit rot have never once verified a transfer.
What silent corruption is, precisely
The reference definition comes from Bairavasundaram, Goodson, Schroeder, Arpaci-Dusseau and Arpaci-Dusseau at USENIX FAST '08. Ordinary bad sectors are not silent: "Latent sector errors are detected by a drive's internal error-correcting codes (ECC) and are reported to the storage system." Silent corruption is the case where data is "silently corrupted with no indication from the drive that an error has occurred."
Their Storage Developer Conference 2008 talk on the same data splits it into four shapes: bit corruption, lost writes where "data not written but completion is reported", misdirected writes landing on the wrong block, and torn writes partially written and reported complete. The slide's closing line matters: "In all cases, data passes disk's internal ECC."
Three of those four are not media decay at all. They are firmware or software claiming a write happened that did not. Magnetism quietly flipping on a platter is the story people tell; a controller lying about a write is what the field data mostly shows.
The two studies everyone cites, and what they leave out
The FAST '08 paper analysed 1.53 million drives over 41 months from January 2004: "We find more than 400,000 instances of checksum mismatches over the 41-month period." That sentence travels the internet on its own. The denominators do not.
Of those 1.53 million disks, 3,855 developed any mismatch at all, 3,088 of the 358,000 nearline SATA disks (0.86%) and 767 of the 1.17 million enterprise Fibre Channel disks (0.065%). "On average, each disk developed 0.26 checksum mismatches." The distribution is brutally uneven: "1% of the corrupt disks (the top 1% corrupt disks with the largest number of mismatches) produce more than half of all mismatches recorded in the data", and "The maximum number of mismatches observed for any single drive is 33,000." A handful of sick drives, not a slow universal rot.
The CERN measurement gets the same treatment. Bernd Panzer-Steindel's Data integrity note, draft 1.3 dated 8 April 2007, checked stored files against previously computed adler32 values: "During a test 33700 files were checked (~8.7 TB) and 22 mismatches found. That translates into an error rate of one bad file in 1500 files." That ratio is quoted constantly. The next paragraphs of the same document are not. A separate probe writing and re-reading 2 GB patterns found that 80% of its errors were "64k regions of corrupted data" with "large correlation with the 3ware-WD disk drop-out problem", which "was identified as a problem in the WD disk firmware", and CERN was "currently updating the firmware of about 3000 disks". Single bit errors were 10%. One in 1500 was a snapshot of a specific broken controller and drive pairing in 2007, not a decay constant for your photo library.
What the drive already does for you
Every sector is stored with error-correcting code, and the drive reports what it cannot fix. Seagate's IronWolf Pro data sheet quantifies that as "Nonrecoverable Read Errors per Bits Read, Max" of "1 per 10E15". Note the word nonrecoverable. Those errors are loud: the read fails and you get an I/O error, which is exactly why they are not the thing to be afraid of.
The FAST '08 comparison table puts both classes on the same scale, as the average percentage of disks affected per year. Latent sector errors hit 9.5% of nearline and 1.4% of enterprise disks. Checksum mismatches hit 0.466% and 0.042%.
Why a checksum is the only proof that a copy worked
Copy tools compare far less than most people assume. The rsync manual is explicit about its default: "rsync uses a 'quick check' that (by default) checks if each file's size and time of last modification match between the sender and receiver." Matching size and timestamp means the file is skipped and its contents are never read on either side. Rsync does verify what it decides to send, and says so: "rsync normally verifies that each transferred file was correctly reconstructed on the receiving side by checking a whole-file checksum that is generated as the file is transferred."
On Windows the trap is a letter. Robocopy's /v is a logging switch, documented as "Produces verbose output, and shows all skipped files." The older xcopy is the one whose /v actually means verify: "Verifies each file as it is written to the destination file to make sure that the destination files are identical to the source files."
Fast hash or cryptographic hash
Against accidental damage, any decent hash works, and a 64 bit digest leaves about one chance in 1.8 x 10^19 that a random change produces the same value. Speed is then the only thing that matters, and the gap is enormous. On the xxHash project's own benchmark, an Intel i7-9700K running clang 10 with -O3:
| Algorithm | Digest | Bandwidth | Project's own note |
|---|---|---|---|
| XXH3 (SSE2) | 64 bit | 31.5 GB/s | faster than the 28.0 GB/s reference RAM read |
| XXH128 (SSE2) | 128 bit | 29.6 GB/s | |
| XXH64 | 64 bit | 19.4 GB/s | |
| BLAKE2 | 256 bit | 1.1 GB/s | "Cryptographic" |
| SHA1 | 160 bit | 0.8 GB/s | "Cryptographic but broken" |
| MD5 | 128 bit | 0.6 GB/s | "Cryptographic but broken" |
The cryptographic column buys one extra property: resistance to somebody deliberately constructing a different file with your hash. MD5 and SHA-1 no longer have it. NIST announced on 15 December 2022 that SHA-1 "should be phased out by Dec. 31, 2030", because "Today's more powerful computers can create fraudulent messages that result in the same hash as the original". Microsoft draws the same line in the Get-FileHash documentation: MD5 and SHA1 "should only be used for simple change validation, and should not be used to generate hash values for files that require protection from attack or tampering".
So: verifying your own copy of your own drive, use xxHash and finish in a fraction of the time. Verifying a download against a hash a stranger published, use SHA-256 or BLAKE3, which its authors describe as "Secure, unlike MD5 and SHA-1. And secure against length extension, unlike SHA-2."
Verifying a transfer on Windows, macOS and Linux
The pattern is a manifest written at the source and checked at the destination.
Linux, with coreutils: find . -type f -print0 | xargs -0 sha256sum > SHA256SUMS on the source, then sha256sum -c SHA256SUMS at the destination. The -c flag will "Read file names and checksum information (not data) from each file ... and report whether the checksums match the contents of the named files." Add --quiet, which suppresses the per-file OK line so only failures print.
macOS has shasum, whose -a takes "1 (default), 224, 256, 384, 512, 512224, 512256". The default is SHA-1, so always pass -a 256: shasum -a 256 file to generate, shasum -a 256 -c SHA256SUMS to "read SHA sums from the FILEs and check them".
Windows: Get-FileHash -Path file already defaults to SHA256. For a one-off from cmd, certutil's -hashfile "Generates and displays a cryptographic hash over a file", invoked as certutil -hashfile InFile [HashAlgorithm].
For a whole tree, a second rsync pass with -c is less work than a manifest. It "changes this to compare a checksum for each file that has a matching size", which reads both trees end to end. Pin the algorithm with --checksum-choice, which accepts xxh128, xxh3, xxh64, md5, md4, sha1 and none. Never none: the manual warns that then "the --whole-file option is forced on and no checksum verification is performed on the transferred data."
What ZFS and Btrfs buy, and what they do not
Both compute a checksum for every block and validate it on every read, which converts silent corruption into a loud error. Neither can repair anything on its own.
| ZFS | Btrfs | |
|---|---|---|
| Default algorithm | fletcher4 | crc32c |
| Others available | sha256, sha512, skein, edonr, blake3 | xxhash (64 bit), sha256, blake2b, since kernel 5.5 |
| Checksum stored | in the block pointer, away from the data | in a separate checksum tree |
| Repair requires | RAID protection or the copies property | a replicated profile such as RAID1 |
OpenZFS states the condition plainly: corrupt blocks "are automatically repaired if possible, by using the RAID protection in suitably configured pools, or redundant copies". The Btrfs manual is blunter about the limit: scrub "can only repair filesystem damage by copying from other known good replicas". On a single disk with a single profile you get detection and a clear error, which is a real gain over a file that opens and looks wrong, but it is not recovery.
Scrubbing, and how often
A scrub reads everything and checks it while you still have redundancy to fix it with. zpool scrub "examines all data in the specified pools and verifies each block's checksum", and "For replicated (mirror, raidz, or draid) devices, ZFS automatically repairs any damage discovered during the scrub." Btrfs describes scrub as "a validation pass over all filesystem data and metadata" and gives a schedule: "The recommended period is a month but it could be less."
That cadence is not arbitrary. In the FAST '08 data, scrubbing was the single largest detector: "on the average data scrubbing discovers about 49% of checksum mismatches in nearline disks", and 73% in enterprise ones, on systems where "an entire RAID group is scrubbed approximately once every two weeks on an average". The mismatches a scrub does not find surface during RAID reconstruction instead, when the redundancy that would have repaired them is already spent.
An unverified backup is not yet a backup
Backup programs default to cheap consistency checks that never read your data. Restic says so: "By default, the check command does not verify that the actual pack files on disk in the repository are unmodified, because doing so requires reading a copy of every pack file in the repository." Only restic check --read-data reads it. Borg is the same shape: --verify-data "will perform a full integrity verification (as opposed to checking the CRC32 of the segment) of data, which means reading the data from the repository, decrypting and decompressing it."
Reading an entire off-site repository every month is expensive in time and, on metered storage, in egress. Restic's --read-data-subset takes an n/t form that splits the repository into t groups and reads only group n. Run 1/12 this month through 12/12 next December and every byte has been read once a year at a twelfth of the cost per run. Put that on a schedule, because the failure mode of every technique above is never that it was wrong, only that nobody ran it.
Sources
- corruption fast08.pdf
- 4ecef2f528498a7486555a0725a9bb63bb4a.pdf
- Data integrity v3.pdf
- ironwolf pro 16tb DS1914 10 1905US en US.pdf
- rsync(1) manpage
- Robocopy | Microsoft Learn
- xcopy | Microsoft Learn
- Get-FileHash (Microsoft.PowerShell.Utility) - PowerShell | Microsoft Learn
- certutil | Microsoft Learn
- cksum common options (GNU Coreutils 9.11)
- Ubuntu Manpage: shasum - Print or Check SHA Checksums
- GitHub - Cyan4973/xxHash: Extremely fast non-cryptographic hash algorithm · GitHub
- GitHub - BLAKE3-team/BLAKE3: the official Rust and C implementations of the BLAKE3 cryptographic hash function · GitHub
- NIST Retires SHA-1 Cryptographic Algorithm | NIST
- Checksums and Their Use in ZFS — OpenZFS documentation
- zpool-scrub.8 — OpenZFS documentation
- Checksumming — BTRFS documentation
- Scrub — BTRFS documentation
- Working with repositories — restic 0.19.1 documentation
- borg check — Borg - Deduplicating Archiver 1.4.5 documentation