File activity (kernel file I/O ETW)¶
This source records completed file creates, writes, renames, deletes and metadata changes. Each event is attributed to the process that issued it.
| Collector ID | freedr.file-etw |
| Windows mechanism | Classic kernel FileIo provider ({90CBDC39-4A3E-11D1-84F4-0000F80464E3}) in the shared FreeDR-Unified system logger. Kernel flags DISK_IO, DISK_FILE_IO, FILE_IO, FILE_IO_INIT. |
| Hunt | events view, event_type LIKE 'file.%' |
| Rule SQL source name | file_etw |
| Hot table | file_etw_v5 |
| Code | crates/freedr-collect-file-etw |
Raw events use two 256 MiB-maximum rings: a control lane (name, cleanup,
close, set-info, delete, rename, operation-end) and a bulk lane (create,
write). See ring buffers.
Completed-operation model¶
FreeDR does not store raw I/O requests. Each request (Create, Write, SetInfo, Delete, Rename) is matched to its OperationEnd event by IRP pointer. The source keeps the request only when Windows reports that it completed.
| Rule | Detail |
|---|---|
| Matching deadline | 30 s. Correlation state is bounded (about 2,000,000 entries / about 1 GiB). |
| Opens of existing files | An open with FILE_OPEN disposition, or a create that only opened an existing file, updates the path cache and is not stored |
| Create results kept | Superseded, created, overwritten |
| Failed or unknown outcome | Not stored, except writes, which have no status |
| SetInfo | Kept only for mutation information classes (32 classes, for example rename, disposition, basic info/timestamps, end-of-file). SetInfo from PID 4 (System) is dropped. |
| Reads | Not collected. The read opcode is rejected at the callback. |
| Short-window compaction | Repeats of the same kind, process and target within 1 s (writes) or 500 ms (other kinds) are merged before storage |
Event types¶
| event_type | Produced | Notes |
|---|---|---|
file.create_open |
Yes | The created column is 1 only when the file was newly created (FILE_CREATED) |
file.write |
Yes | byte_offset, byte_count of the first write in the window |
file.rename |
Yes | See gaps |
file.delete |
Yes | |
file.set_info |
Yes | Mutation classes only |
file.read |
No | Registered but never produced |
file.name, file.operation_end, file.cleanup and file.close are
internal and never stored.
Columns¶
| Column | Notes |
|---|---|
pid |
Attributed process. NULL when attribution was not proven. |
created |
0/1 (see above) |
path |
Drive-letter path, or the raw device path if it cannot be normalized |
byte_offset, byte_count |
Writes only |
old_path, volume_serial, file_id |
Present in the schema but always NULL in the current version |
actor_image_path, actor_command_line, actor_start_at, actor_user_sid, actor_user_name |
Lineage of the attributed process |
Process attribution (TTID resolver)¶
The FileIo provider reports the issuing thread ID (TTID). FreeDR does not trust the ETW header process ID for file events. It resolves the thread instead:
- Thread history. The process source maintains a history of
thread lifetimes from thread start, stop and rundown events. A file event is
attributed to the live thread with that TID if it started before the event.
Otherwise it is attributed to a thread lifetime that stopped within the last
10 s and whose
[start, stop]covers the event time. At most two lifetimes are kept per TID, so memory follows thread churn, not uptime. - Hold. The thread history comes from a different ETW session and can arrive late. A request whose TID is still unknown is held for up to 2 s (order preserved) and retried.
- One OS check. If the TID is still unknown, FreeDR calls
OpenThreadonce. If the thread is alive and was created before the event, its process is used, provided that process is in FreeDR's process table. The handle is closed immediately. Nothing is cached and no thread handles are held. - If the header PID disagrees with the resolved thread's process, no actor is assigned.
Unattributed events are still stored with pid NULL. Counters:
resolver_attributed_observations, resolver_unattributed_observations,
resolver_refreshes (OS checks), resolver_failures, and
resolver_failure_thread_query, resolver_failure_process_query,
resolver_failure_thread_reused.
Short-lived threads
An event is left unattributed when its thread exited and the thread's start or stop record was lost or is older than the 10 s window.
Noise controls (in pipeline order)¶
| # | Filter | Dropped | Counter |
|---|---|---|---|
| 1 | Own process | Every file event issued by the FreeDR agent process | callback_self_filtered |
| 2 | Unresolved path | Unresolved or ambiguous path, empty, \FI_UNKNOWN |
terminal_path_discarded |
| 3 | Fixed noise paths | C:\ProgramData\FreeDR\…, CBS.log, WER temp CSVs, CatRoot2 edbtmp.log, Prefetch\Layout.ini, Windows Update / EdgeUpdate / Edge temp logs, CbsTemp, WinSxS Temp\PendingRenames and InFlight, MRT, Defender definition caches, AppX / SoftwareDistribution / DeliveryOptimization caches, OneDrive logs |
terminal_noise_discarded |
| 4 | Internal I/O | FreeDR's own completed I/O under its machine root, when attribution is confirmed. Any uncertain case is kept and counted by reason. | internal_io_suppressed, internal_io_retained_* |
| 5 | Measured process and path pairs (ADR-0028) | wuaucltcore.exe under C:\$WinREAgent; OneDriveSetup.exe, OneDriveStandaloneUpdater.exe, OneDrive.Sync.Service.exe, OneDrive.exe under local AppData Microsoft\OneDrive; MpSigStub.exe on Windows\Temp\<id>\mpengine.dll; TiWorker.exe under Windows\WinSxS. Applied only when the actor is known. |
terminal_noise_discarded |
| 6 | Defender definitions | Verified, Microsoft-signed WinDefend reading or opening its own .vdm/.vdf definition files. Everything else from Defender is kept. |
defender_filtered, defender_* |
| 7 | First-only per minute | See below | repeated_discarded |
Per-minute first-only dedupe¶
| Key | (pid, event_type, created, lower-case path, lower-case old_path) |
| Window | 60 s from the first kept record, by event time |
| Rule | Keep the first record. Drop any record with the same key within the next 60 s. After that, the next one is kept and starts a new window. |
| Memory | 4,096 keys, oldest evicted first. A burst with more than 4,096 distinct keys can let repeats through. The state is in memory and resets when the ETW session restarts. |
| Effect | One stored row stands for up to a minute of identical activity. The row stores no repeat count. |
The USN source uses the same first-only filter.
Known gaps¶
| Gap | |
|---|---|
| Rename target | file.rename stores no new name (old_path is always NULL) |
| File identity | volume_serial and file_id are always NULL. Use USN or Artifacts for file IDs. |
| Reads, opens of existing files, failed operations | Not collected |
| Repeats | Hidden for 60 s per key |
| Own process | All FreeDR agent file I/O is dropped |
| Measured exclusions | Remove some real create and write evidence by design (ADR-0028) |
Artifact triggers¶
file.create_open, file.write, file.rename and file.set_info rows
queue the path for artifact acquisition. A file.write to an
already-inventoried path triggers a new collection.
Health counters¶
Main groups: callback_*, terminal_path_discarded,
terminal_noise_discarded, repeated_discarded, decode_failures,
mapping_failures, validation_failures, correlation_* (for example
correlation_failed_requests, correlation_outcome_unknown_requests,
correlation_unresolved_paths, correlation_actor_missing),
internal_io_*, retention_*, resolver_*, defender_*, plus shared ETW
ring, loss and TDH counters. See Storage & health.
Verification status
ADR-0055, ADR-0072 and ADR-0077 list native Windows qualification as pending.