Skip to content

$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.

Published on 7 min read

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:

OffsetSizeField
0x004Signature: RSTR, or CHKD if chkdsk modified the log
0x042Update sequence array offset
0x062Update sequence array count
0x088Chkdsk last LSN
0x104System page size
0x144Log page size
0x182Restart area offset
0x1A2Minor version
0x1C2Major 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:

OffsetSizeFieldWhy it matters
0x008Current LSNNewest LSN at the time this restart area was written
0x082Log clientsNumber of client slots (normally one: NTFS)
0x0A / 0x0C2 + 2Free / in-use client list0xFFFF = empty
0x0E2Flags0x1 single-page I/O (ntfs3); the in-browser parser reads 0x2 as "cleanly dismounted"
0x104Sequence number bitsNeeded for LSN ↔ offset
0x142Restart area length
0x162Client array offsetRelative to the restart area
0x188Log file sizeCompare with the copy you have: shorter means truncated
0x204Last LSN data length
0x242Record header length
0x262Log page data offsetWhere records start inside each RCRD page
0x284Open log count

The NTFS client record

Each client slot is 0xA0 bytes; NTFS is normally the only client:

OffsetFieldMeaning
0x00Oldest LSNOldest record recovery may still need
0x08Client restart LSNLSN of the latest checkpoint record
0x1CName length (bytes)
0x20NameUTF-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:

OffsetField
0x00 / 0x04Client major / minor version (0.0 or 1.0)
0x08Checkpoint start LSN
0x10Open attribute table dump LSN
0x18Attribute names dump LSN
0x20Dirty page table dump LSN
0x28Transaction table dump LSN
0x30–0x3CLengths 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 sizefile_data_bitsseq_bits
256 KiB (the site's synthetic sample)1549
64 MiB (a common size)2341

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 $MFT stores, at header offset 0x08, 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

ObservationMeaning
Lowest LSN ≈ highest LSNVery little history (recently resized log, or heavy churn)
Restart page is CHKDchkdsk has worked on this log
Current LSN in restart area > highest record LSN foundNewest page missing: check the tail / fast pages, or the copy is stale
Log size in restart area > file sizeTruncated copy
"Cleanly dismounted" on a capture described as liveCheck 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

Related articles