Integrity Streams
An integrity stream is a per-file, opt-in feature that protects a file’s data with per-block
checksums, so the driver can detect — and, on a mirror or parity storage space, repair —
silent corruption or out-of-band tampering of file content. For an analyst the two questions that matter
are: which files were checksum-protected, and where on disk that fact is recorded. Both reduce to a
single bit — FILE_ATTRIBUTE_INTEGRITY_STREAM (0x8000) in the file’s Win32 attribute word — plus a
per-block checksum store. This page explains the mechanism, the one authoritative on-disk marker, and the
several places an analyst is tempted to look that hold no per-file integrity signal.
Per-file opt-in, not per-volume
Integrity is enabled per object, never volume-wide. A file becomes an integrity stream either at format
time (format /fs:ReFS /i enable, i.e. -SetIntegrityStreams 1), per object (Set-FileIntegrity), or by
inheriting the setting from its parent directory at create time. Once a file is marked, the driver keeps a
checksum for every data block; on read, a stored-vs-recomputed mismatch is surfaced as corruption and is
self-healed wherever a redundant copy exists.
Because the setting is inherited at create time rather than applied dynamically, the flag is a per-file historical fact, not a live volume property. Disabling a directory’s integrity does not clear the flag on files already created underneath it, so the only reliable way to know a file’s status is to read that file’s own attribute word — never infer it from the directory or the volume.
The one authoritative marker
The on-disk marker is established by the driver function RefsSetIntegrity, which sets or clears
bit 15 (0x8000) at SCB+0x98 in the stream control block. That bit is then reflected into the file’s
Win32 attribute word at $SI+0x20 as FILE_ATTRIBUTE_INTEGRITY_STREAM (0x8000). The attribute bit in
$STANDARD_INFORMATION is the only authoritative on-disk signal
that a given file is an integrity stream:
RefsSetIntegrity ──sets──▶ SCB+0x98 bit15 (0x8000)
│ reflected to
▼
$SI+0x20 file_attrs |= 0x8000 ← THE marker
(FILE_ATTRIBUTE_INTEGRITY_STREAM)
The decisive test for “was this file checksum-protected?” is therefore $SI+0x20 & 0x8000. A file enabled
this way carries the bit alongside whatever other Win32 attributes it has — an integrity-marked test file,
for example, reads 0x8020 (integrity 0x8000 ORed with archive 0x20).
Where the checksum machinery lives
Two distinct stores hold the actual checksum data, and neither is the per-file roster:
- Per-cluster data checksums. With integrity enabled, the driver checksums each data cluster with the
volume’s data-checksum algorithm — CRC32-C (Castagnoli, poly
0x82f63b78, 4 bytes) on 4 KiB-cluster volumes, CRC64 (8 bytes) on 64 KiB-cluster volumes, SHA-256 (32 bytes) on SHA-256 volumes. On a 4 KiB CRC32-C volume the checksum is stored inline in the file’s own extent map: an extent record whose flags carry bit0x80is followed by one 4-byte CRC32-C per cluster of its run, and the record is padded to an 8-byte boundary (a single-cluster record is therefore 32 bytes in total).forefst extractrecomputes and verifies each one. The selected type is also reflected in the file’s$DATAstream summary: the stream-flagsu32atval+0x38low byte selects the algorithm (0x02 = CRC, 0x04 = SHA-256), with bit 0x10000 = integrity. This selector tracks the volume’s checksum configuration (0x02 on None/CRC64 volumes, 0x04 on SHA-256 volumes), so it follows the format choice rather than identifying individual files — see$DATAfor the stream-summary layout. - Integrity State Table (root #11, schema 0xe080). A volume-level B+-tree (
CmsIntegrityState) that tracks integrity-stream coverage by LCN range, reusing the same row format as the allocator tables. It is present on every volume regardless of whether any file uses integrity streams, and on baseline images carries exactly one row spanning the whole volume. See Integrity State Table for the byte layout. It is not a per-file lookup — per-file status lives only in the 0x8000 attribute bit.
The per-cluster data checksums above live inline in the file’s extent map, separate from the metadata Merkle tree — note the orthogonality: the page-reference checksum-type byte (cktype 0x01/0x02/0x04) governs metadata verification and is entirely separate from whether a file’s data is an integrity stream. See Checksum Architecture for the metadata side of the same coin.
Two places that hold no per-file integrity signal
Both of the structures below look like plausible per-file integrity rosters and are not.
$SI+0x24(internal flags) carries no integrity bit. Its bit 0 (0x01) is the delete-disposition / EFS transient state, derived fromFCB+0x08bit 27 (set in the driver’sDeleteDirectoryOnDiskpath). It is not an integrity flag. The proof that the integrity bit cannot appear in this field is structural —RefsComputeStandardInformationInternalFromFcbbuilds$SI+0x24fromFCB+0x08bits only and never readsSCB+0x98, where the integrity bit lives. On disk, an integrity-enabled volume with ~6,000 integrity files shows that bit set on zero of them. Use$SI+0x20bit 0x8000, never$SI+0x24.- The Integrity State Table is not a per-file roster. Root #11 is volume-level and invariant — the same single whole-volume row whether or not any file is an integrity stream, and across CRC64 versus SHA-256 metadata configurations. Reading it expecting a list of protected files yields nothing.
Forensic consequences
The integrity marker hands the analyst two distinct capabilities. First, it is the authoritative per-file answer to “was this checksum-protected?” — a single bit, read per file. Second, because an integrity stream carries a per-cluster CRC32-C over its content, a stored-vs-recomputed mismatch on an integrity-marked file is strong evidence the data was altered out-of-band: an offline edit, bit-rot, or a write that bypassed the driver. On a non-integrity file there is no per-block data checksum at all, so content tampering leaves no equivalent on-disk trace — the presence of the 0x8000 marker is what makes after-the-fact tamper detection possible in the first place.
Version and state differences
The FILE_ATTRIBUTE_INTEGRITY_STREAM (0x8000) marker and the Integrity State Table (root #11) are present
across all studied versions, from Win10 v3.4 through Insider. The marker test is therefore version-agnostic.
By contrast, the $SI+0x24 internal-flags word — the field one might wrongly key an integrity test on — is
zero on v3.4 through v3.10 and only becomes populated on v3.14+, so a test built on it would produce
no signal at all on older volumes regardless of correctness. The $SI+0x20 bit 0x8000 marker works on every
version.
Finally, whether a file is an integrity stream is a per-file choice (the 0x8000 bit), but the algorithm its data checksums use is volume-wide and tracks the volume’s checksum type and cluster size — CRC32-C (4 bytes) on 4 KiB-cluster volumes, CRC64 (8 bytes) on 64 KiB, SHA-256 (32 bytes) on SHA-256 volumes — the same configuration set at format and reported through VBR offset 0x2A and the checkpoint flags (see Checksum Architecture). The data checksums are a separate store (inline in the file’s extent map) from the metadata page-reference checksums, not a separate algorithm independent of the volume’s choice.
Reading the marker with the tools
The integrity marker is read directly from the $SI attribute word surfaced by the file-enumeration path of
forefst.py / refsanalysis.py — test file_attrs & 0x8000. Do not confuse this with
forefst.py integrity --checksums, which exercises the page-reference metadata checksum machinery
(CRC64, not the per-file data CRC32-C) and verifies root-table metadata — a separate mechanism from per-file
integrity streams.
Cross-references
Driver Transitions — enabling integrity is one of the residency triggers
Integrity State Table — root #11 byte layout, the volume-level tracker (not a per-file roster)
$STANDARD_INFORMATION — the
$SI+0x20attribute word holding the 0x8000 marker, and the corrected$SI+0x24internal flags that do not$DATA — the stream summary whose
val+0x38flags carry the checksum-type / integrity selectorChecksum Architecture — the metadata CRC64 / SHA-256 page references, distinct from per-file data CRC32-C
Page References — the Merkle-tree checksum carriers and the cktype byte
Allocators — the row format the Integrity State Table reuses
Redundancy — the mirror / parity self-healing that integrity checksums enable
Evidence
The marker mechanism is confirmed in the driver: RefsSetIntegrity sets/clears SCB+0x98 bit 0x8000
and reflects it to $SI+0x20, and RefsComputeStandardInformationInternalFromFcb builds $SI+0x24 from
FCB+0x08 bits only — the static proof that integrity can never appear in the internal-flags field. The
CmsIntegrityState class backs the Integrity State Table: Initialize reads root #11 at mount,
SetClearIntegrityState sets/clears a stream’s state, and GetIntegrityStateTable / GetRangeBitmap read
the per-range checksum bitmap. On the raw-disk corpus, the 0x8000 attribute bit was set on every
integrity file across the corpus’s v3.14 integrity images, while the
$SI+0x24 bit 0 was set on none of them, and
the Integrity State Table’s single whole-volume row was invariant across checksum configurations.
The per-cluster data checksum algorithm is CRC32-C (Castagnoli, poly 0x82f63b78), confirmed in the
driver: the crc32c_4096 kernels reached via ComputeOneChecksum (which takes the 4096-byte path only
for a one-cluster span), and recomputed on the raw-disk corpus — 886 inline checksummed clusters across
three v3.14 integrity volumes with 0 mismatches, a cross-algorithm control ruling out plain CRC-32 and CRC64. Also registered for statements on this page:.