USN change journal¶
This source reads the NTFS USN change journal of each fixed NTFS volume. It records that a file changed and why (the reason bitmask), but not which process made the change. Use it together with file ETW. USN is durable and resumes from a stored cursor. File ETW gives process attribution.
| Collector ID | freedr.usn (health: usn) |
| Windows mechanism | FSCTL_READ_USN_JOURNAL (non-blocking, 256 KiB buffer, V2 records only, all 24 reason bits) |
| Hunt | events view, event_type = 'usn.change' |
| Rule SQL source name | usn |
| Hot table | usn_v3 |
| Code | crates/freedr-collect-usn |
Volumes¶
| Rule | Detail |
|---|---|
| Eligible | Mounted volumes with a fixed-drive mount and the NTFS file system |
| Excluded | Removable, network, ReFS/FAT and unmounted volumes (reported as unsupported_volumes or unavailable_volumes) |
| Limit | 32 active volumes, chosen in volume GUID order |
| Isolation | Each volume has its own state, cursor and failure domain |
| Polling | Re-discovers volumes each loop. Sleeps 500 ms only when all volumes are caught up. |
Event type¶
There is exactly one event type: usn.change. The reason column holds
the raw USN_REASON_* bitmask, for example:
| Bit | Name |
|---|---|
0x1 / 0x2 / 0x4 |
DATA_OVERWRITE / DATA_EXTEND / DATA_TRUNCATION |
0x100 |
FILE_CREATE |
0x200 |
FILE_DELETE |
0x800 |
SECURITY_CHANGE |
0x1000 / 0x2000 |
RENAME_OLD_NAME / RENAME_NEW_NAME |
0x8000 |
BASIC_INFO_CHANGE |
0x200000 |
STREAM_CHANGE |
0x80000000 |
CLOSE |
Filter with bitwise SQL, for example reason & 256 <> 0 for creates.
Columns¶
| Column | Notes |
|---|---|
volume_serial |
Volume serial number |
journal_id, usn |
Journal instance and record position |
file_id |
8-byte file reference number (FRN) |
reason |
Raw reason bitmask |
path |
Resolved path (see below) |
The volume GUID, parent FRN, source info, security ID and file attributes are not stored.
Path resolution¶
- Open by file ID. FreeDR calls
OpenFileByIdand thenGetFinalPathNameByHandleW. This gives the file's current path when the journal is read (form\\?\C:\…), which may be newer than the name at the time of the change. - Parent + leaf fallback. If the file is gone (for example, deleted), the
path is built from the parent directory's FRN and the record's file name.
For
RENAME_OLD_NAMEthe parent is tried first. A 1,024-entry parent cache per volume is cleared when a directory is renamed or deleted. - Unresolvable. If neither the file nor its parent resolves, the record
is dropped (
path_unresolved_dropped). This happens, for example, when a whole directory tree was deleted.
USN does not use the Live MFT reader. Live MFT is a separate on-demand lookup (up to 64 exact paths or FRNs) used from Hunt, not an event source.
Cursor, restart and gaps¶
| Situation | Behaviour |
|---|---|
| First install | Starts at the journal's current end (NextUsn). History is not imported. |
| Commit | Batches of at most 256 events / 8 MiB. The per-volume cursor (journal_id, next USN) is stored in the same transaction as the rows. |
| Agent restart | Downtime is not replayed. The source starts again at NextUsn and records the jump as a coverage gap. |
| Journal overrun while running | Resumes at the oldest retained record (FirstUsn), records a gap, and counts a read_failures |
| Journal deleted or recreated | journal_resets +1 |
| Malformed page | Skipped as a gap if the next position is valid. Otherwise the volume is marked faulted. |
| Filtered records | Still advance the cursor |
Coverage gaps appear only in health counters. No row is written for them.
Noise controls¶
| Control | Detail | Counter |
|---|---|---|
| Close-only records | A record whose only reason is CLOSE is dropped | filtered_close_only |
| First-only per minute | Key (file reference number, reason mask), 60 s window, 4,096 keys per volume, in memory |
repeated_records |
| Fixed paths | C:\ProgramData\FreeDR\…, CBS.log, EdgeUpdate Download/Install/Log, Edge Temp, msedge_installer.log |
|
| OS maintenance paths | Everything under C:\$WinREAgent\, SoftwareDistribution\Download\, WinSxS\Manifests\, WinSxS\FileMaps\, DriverStore\FileRepository\, assembly\NativeImages_*; INF\setupapi.*.log; Prefetch\*.pf; .NET ngen*.log |
|
| Generated directories | CbsTemp transactions, WinSxS component directories, WindowsApps\<package> and WindowsApps\Deleted, AppRepository, versioned Edge / EdgeCore / EdgeWebView application directories |
|
| Measured pairs (ADR-0028) | SoftwareDistribution\Download\*.manifest; WinSxS\Temp\PendingRenames\*.cdf-ms and *.manifest; servicing\Packages\*.cat and *.mum; WinSxS\Catalogs\*.cat; local AppData Microsoft\OneDrive *.js/*.svg/*.css/*.json/*.png; System32\catroot2\dberr.txt |
Warning
These exclusions remove real evidence, including WinSxS, WindowsApps and
Prefetch .pf changes. ADR-0028 states that they are not lossless.
Artifact triggers¶
Every retained USN record queues its path for artifact acquisition. For an already-inventoried path, only content-changing reasons trigger a new collection: data overwrite, extend or truncation (including named streams), file create and stream change.
Health counters¶
Counters: reads, decoded_records, retained_records,
filtered_close_only, repeated_records, path_unresolved_dropped,
commit_acknowledgements, journal_resets, coverage_gaps,
read_failures, decode_failures, checkpoint_failures,
backpressure_pauses, integration_failures, fatal_worker_failures,
commit_* failure counters. (replays exists but is always 0.)
Gauges: active_volumes, unavailable_volumes, unsupported_volumes,
pending_volumes, faulted_volumes, failed_workers, cursor_lag_usn.
The source is degraded while the worker has failed or faulted_volumes
or failed_workers is non-zero, and whenever the lifetime total of
read_failures, decode_failures, checkpoint_failures and
integration_failures grows. Like every collector, it then stays degraded
for 30 minutes after its last fault, clears on its own, and counts
degraded_episodes.
See Storage & health.
Known gaps¶
- No process attribution.
- Changes made while FreeDR is stopped, and history from before installation, are not collected.
- A journal overrun loses records older than
FirstUsn. - Paths reflect the name when the journal is read, not when the change happened.
- A delete is lost when its parent directory is also gone.
- Only fixed NTFS volumes are read, at most 32.