$LogFile Restart Area and LSNs Explained
Inside the NTFS $LogFile restart pages: restart area fields, the NTFS client record, checkpoints, and how to turn a log sequence number into a file offset.
TL;DR. The first two pages of $LogFile are RSTR restart pages. Each holds a restart area: the current LSN, the log size, the number of sequence-number bits, flags, and a client record for NTFS pointing to the latest checkpoint. An LSN is a 64-bit number whose low bits are the record's offset in 8-byte units and whose high bits are a wrap counter; with seq_bits from the restart area, offset = (LSN << seq_bits) >> (seq_bits − 3). LSNs only grow, so they order everything in the log — and the same LSNs appear in $MFT record headers.
If you ever need to explain why a parser says "LSN 115720 → 117880" or check a record by hand in a hex editor, this is the part of the format you need. The page-level layout is in $LogFile format 1.1 vs 2.0.
The restart page header
Two copies at offsets 0 and one system page later (usually 0x1000). They are not necessarily in sync (dfir.ru); a reader validates both and keeps the one with the higher current LSN. Offsets per libfsntfs and ntfs3:
| Offset | Size | Field |
|---|---|---|
0x00 | 4 | Signature: RSTR, or CHKD if chkdsk modified the log |
0x04 | 2 | Update sequence array offset |
0x06 | 2 | Update sequence array count |
0x08 | 8 | Chkdsk last LSN |
0x10 | 4 | System page size |
0x14 | 4 | Log page size |
0x18 | 2 | Restart area offset |
0x1A | 2 | Minor version |
0x1C | 2 | Major version (1 or 2 in practice) |
Apply the update sequence fix-ups before reading anything past the first sector.
The restart area
At the offset given in 0x18:
| Offset | Size | Field | Why it matters |
|---|---|---|---|
0x00 | 8 | Current LSN | Newest LSN at the time this restart area was written |
0x08 | 2 | Log clients | Number of client slots (normally one: NTFS) |
0x0A / 0x0C | 2 + 2 | Free / in-use client list | 0xFFFF = empty |
0x0E | 2 | Flags | 0x1 single-page I/O (ntfs3); the in-browser parser reads 0x2 as "cleanly dismounted" |
0x10 | 4 | Sequence number bits | Needed for LSN ↔ offset |
0x14 | 2 | Restart area length | |
0x16 | 2 | Client array offset | Relative to the restart area |
0x18 | 8 | Log file size | Compare with the copy you have: shorter means truncated |
0x20 | 4 | Last LSN data length | |
0x24 | 2 | Record header length | |
0x26 | 2 | Log page data offset | Where records start inside each RCRD page |
0x28 | 4 | Open log count |
The NTFS client record
Each client slot is 0xA0 bytes; NTFS is normally the only client:
| Offset | Field | Meaning |
|---|---|---|
0x00 | Oldest LSN | Oldest record recovery may still need |
0x08 | Client restart LSN | LSN of the latest checkpoint record |
0x1C | Name length (bytes) | |
0x20 | Name | UTF-16: NTFS |
Checkpoints: record type 2
The client restart LSN points to a log record of type 2 (client restart). For NTFS, its payload (NTFS_RESTART in ntfs3) lists the LSNs and lengths of the table dumps written at that checkpoint:
| Offset | Field |
|---|---|
0x00 / 0x04 | Client major / minor version (0.0 or 1.0) |
0x08 | Checkpoint start LSN |
0x10 | Open attribute table dump LSN |
0x18 | Attribute names dump LSN |
0x20 | Dirty page table dump LSN |
0x28 | Transaction table dump LSN |
0x30–0x3C | Lengths of the four dumps |
Recovery starts from here: it reloads the open attribute table and friends, then scans forward. For an analyst, the checkpoint explains why OpenAttributeTableDump and AttributeNamesDump records appear regularly, and why a parser needs them to name the attribute a record targets.
LSN anatomy
An LSN is 64 bits split in two:
63 (64 - seq_bits) 0
+-------------------------+-----------------------------------+
| sequence number | offset in 8-byte units |
+-------------------------+-----------------------------------+
The sequence number grows each time the log wraps, which keeps LSNs strictly increasing even though offsets repeat. The split depends on the log size. ntfs3 derives it as file_data_bits = log2(log_size) − 3 and seq_bits = 64 − file_data_bits:
| Log size | file_data_bits | seq_bits |
|---|---|---|
| 256 KiB (the site's synthetic sample) | 15 | 49 |
| 64 MiB (a common size) | 23 | 41 |
Always read seq_bits from the restart area rather than computing it.
From LSN to file offset
offset = (LSN << seq_bits) >> (seq_bits - 3)
The left shift drops the sequence number; the right shift by seq_bits − 3 brings the offset field back and multiplies it by 8. dfir.ru gives a worked example: with 44 sequence bits, LSN 2124332 has sequence number 2 and an offset of 27180 eight-byte units, i.e. byte 217440.
A robust parser can use this in reverse. Instead of walking page chains, it scans each RCRD page for 8-byte-aligned headers whose LSN maps exactly to their own offset. That check rejects random bytes and keeps working when pages in between are damaged — it is how the in-browser $LogFile parser finds records. Records that do not fit in the rest of a page continue after the header of the next page (and wrap from the end of the file to the start of the circular area); a continuation page older than the record means the rest was overwritten, and the record is flagged incomplete.
LSNs outside the $LogFile
- Every FILE record in the
$MFTstores, at header offset0x08, the LSN of the last logged change to that record — one of the links in David Cowen's NTFS TriForce. If that LSN is inside the log's current range, the change that produced the current FILE record is still in the log. - Index buffers (
INDX) also carry an LSN in their header.
Comparing an $MFT record's LSN with the log's lowest LSN tells you immediately whether its latest change can still be found.
What the numbers tell you in practice
| Observation | Meaning |
|---|---|
| Lowest LSN ≈ highest LSN | Very little history (recently resized log, or heavy churn) |
Restart page is CHKD | chkdsk has worked on this log |
| Current LSN in restart area > highest record LSN found | Newest page missing: check the tail / fast pages, or the copy is stale |
| Log size in restart area > file size | Truncated copy |
| "Cleanly dismounted" on a capture described as live | Check the collection notes: the file may come from an image or a snapshot |
Frequently asked questions
What is an LSN in NTFS?
A log sequence number is a 64-bit identifier given to every $LogFile record. Its low bits encode the record's position in the file in 8-byte units and its high bits a sequence number that grows each time the log wraps, so LSNs always increase.
How do I convert a $LogFile LSN to a file offset?
Read the sequence-number bit count from the restart area, then compute (LSN << seq_bits) >> (seq_bits - 3). That keeps the low 64 - seq_bits bits and multiplies them by 8, which gives the byte offset of the record header in the file.
Further reading
- Linux kernel,
fs/ntfs3/fslog.c—RESTART_HDR,RESTART_AREA,CLIENT_REC,NTFS_RESTART,lsn_to_vbo. - Maxim Suhanov, How the $LogFile works?