ReFS Forensics Reference

$DATA

The descriptor’s form — not the record’s placement, and not the file’s size — decides data residency. See Record placement and data residency.

$DATA is a file’s default data stream (embedded type 0x80, schema 0x180). Its bytes are stored either inline in the B+-tree row or in on-disk extents, and which is a property of the volume format:

formatmain $DATA
≤ 3.10never inline. Every file’s data is in extents — a 5-byte file still holds a whole cluster
≥ 3.11inline while the stream is under 2 KiB (0x800), extents at or above it

Two further states exist on ≥ 3.11 and are neither of the above: a live stream whose descriptor is the extent form but which owns no allocation (disk_alloc at +0x48 is 0). If a snapshot sub-record exists for the same stream, its bytes are the snapshot’s — snapshot-shared, and still recoverable; if none does, nothing was ever written — reported as unallocated. (The word sparse is reserved for the FILE_ATTRIBUTE_SPARSE_FILE bit, which is a different fact.) Both were previously indistinguishable.

Where the record sits (embedded in the name row, or split into a type-0x40 backing) is a separate question — see Resident vs Non-Resident Storage. On disk it appears as embedded sub-records inside the type-0x10 / type-0x30 rows: one single-instance (SI) entry carrying the stream summary and any inline content, plus one or more multi-instance (MI) entries carrying the extent / allocation metadata.

Resident content — the SI sub-record

The single-instance entry stores the stream-summary header and the inline file content.

Key (16 bytes):

OffsetSizeFieldDescription
0x008Value length (u64)total value byte count
0x084Marker (u32)0x80000001 (single-instance)
0x0C2Type code (u16)0x0080
0x0E2Subtype (u16)0x000E

Value:

OffsetSizeFieldDescription
0x004Padding0
0x044Data-area sizevalue length − 12
0x084Content offset0x0C
0x0C4Summary size0x30 (48)
0x108Reserved0
0x188Allocated sizestream allocated size (≈ file size, but not a fixed 8-byte round-up — e.g. a 129-byte file reads 129, not 136)
0x208File sizeexact stream length in bytes
0x288Valid data lengthusually equals file size
0x308Total allocatedusually equals allocated size
0x384Stream flagschecksum-type / integrity selector (not a residency flag). Low byte = checksum type: 0x02 = CRC (CRC32-C / CRC64), 0x04 = SHA-256 (mutually exclusive). Bit 0x10000 = integrity stream enabled (per-block checksums + CoW), so 0x10002 = CRC + integrity. 0 on v3.4. Proven by GetChecksumTypeForStreams / SetResidentStreamSummary
0x3CvarInline contentthe raw file bytes (file_size long)

Size relationships: value_length = 0x3C + allocated_size (or 0x3C for an empty file); key[0:8] = value_length. The minimum value is 60 bytes (0x3C) for an empty file.

Extent metadata — the MI sub-records

Each file also has one or more multi-instance entries holding the stream allocation metadata (not inline content). The MI key is 40 bytes and carries the extent/allocation identifiers; the MI value does not use the common 12-byte sub-record header.

Value (extent record). Offsets below are the v3.7+ layout:

Offset (v3.7+)SizeFieldDescription
0x004Inner header size0x88 (136) — all versions
0x0C4Summary size0x200 on v3.14+; 0x1A0 on v3.4–v3.10
0x308Total allocated
0x388Stream sizethe stream size in MI records (not the stream-flags field)
0x408Valid data length
0x488Disk allocated
0x504Version count + sparse flaglow 31 bits = version count (1 for a single-version stream); bit 31 = sparse flag

On ReFS v3.4 the four size fields sit +4 later — total allocated at 0x34, stream size at 0x3C, valid data length at 0x44 — and the disk-allocated slot (0x4C) is 0, with the allocation carried by the total-allocated field alone. The inner header size (0x88) and the extent sub-record at +0x88 are the same on every version.

The extent sub-record header sits at offset = inner_header_size (0x88): +0x00 sub_rec_size (0x28), +0x0C flags (0xe00 = non-resident extents), +0x14 extent_count, then 24-byte extents.

CoW version namespace (in the key, not the value): the sub_id at key[16:20] selects the version — 0x8 = stream-set metadata / next-sub-id counter, 0x1000 = the live data version, 0x1001+ = CoW / snapshot versions.

Extracting resident content

  1. Find the SI $DATA sub-record (marker 0x80000001, type 0x80).
  2. Read the file size: le64(value, 0x20).
  3. Content starts at value[0x3C], length = file_size bytes.

Note: the $SI+0x38 DataSize slot in the type-0x10 own-row is unpopulated — the size lives on the resident type-0x30 index entry, at value+0x58 (FileSize) and value+0x60 (AllocatedSize); see the cross-references.

Cross-references

Evidence

Type 0x80 / schema 0x180 and the SI/MI layouts are confirmed in the decompiled driver (RefsCheckValidAttributeAccess, GetChecksumTypeForStreams, SetResidentStreamSummary) and raw-disk decoded across the corpus.