usn日志不能用标准文件api打开,必须通过卷设备路径调用createfile和deviceiocontrol配合fsctl_query_usn_journal与fsctl_read_usn_journal访问;记录为变长二进制结构,需依recordlength跳转解析;须动态维护startusn并处理日志覆盖、权限冲突及缓冲区大小敏感问题。

USN日志不是普通文件,不能用 fopen 或 std::ifstream 直接打开
NTFS 的 USN 日志(Update Sequence Number Journal)是卷级元数据结构,存储在隐藏的系统文件 $Extend$UsnJrnl 中,但操作系统不允许以常规文件方式读取。直接尝试 std::ifstream("C:\$Extend\$UsnJrnl") 会失败(errno = 5:拒绝访问),哪怕以管理员权限运行也一样。
正确路径是通过卷设备路径 + DeviceIoControl 访问,例如 \\?\Volume{xxx-xxx}\,而非盘符路径。
- 必须使用 Windows API:
CreateFile打开卷句柄(需FILE_SHARE_WRITE和GENERIC_READ权限) - 调用
DeviceIoControl配合FSCTL_QUERY_USN_JOURNAL获取日志基本信息(如起始 USN、最大 USN、日志 ID) - 再用
FSCTL_READ_USN_JOURNAL提取具体记录,每次请求需指定StartUsn和ReasonMask
FSCTL_READ_USN_JOURNAL 返回的是二进制块,不是结构化数据流
USN 记录以链式结构拼接在返回缓冲区中,每个记录开头是 USN_RECORD_V2 或 USN_RECORD_V3(取决于 Windows 版本),但长度不固定——RecordLength 字段必须用来跳转到下一个记录,不能简单按 sizeof() 步进。
常见错误是把整个缓冲区当数组遍历,结果解析错位、读出乱码或崩溃。
- 必须从
Buffer起始地址开始,读USN_RECORD_Vx::RecordLength,然后偏移该长度找下一条 -
FileNameOffset和FileNameLength是相对于记录起始地址的偏移,不是绝对地址 - 文件名是 UTF-16LE 编码,需用
wstring接收,不能直接转string - Windows 10 1809+ 默认启用
USN_RECORD_V3,含Timestamp和SecurityId,旧代码只处理 V2 会漏字段
USN 日志可能被清空或覆盖,StartUsn 必须动态维护
USN 日志大小有限(默认约 32MB),写满后旧记录会被覆盖;管理员也可能手动调用 FSCTL_DELETE_USN_JOURNAL 清除。这意味着你上次记住的 LowestValidUsn 或 NextUsn 下次可能已失效。
硬编码起始 USN(比如设成 0)会导致重复读、漏读,甚至触发 STATUS_LOG_FILE_FULL 错误。
- 首次运行应先调用
FSCTL_QUERY_USN_JOURNAL拿到当前FirstUsn和NextUsn - 每次读完一批记录后,保存最大的
Usn值(不是NextUsn!),作为下次StartUsn - 若
DeviceIoControl返回ERROR_JOURNAL_ENTRY_DELETED,说明日志已被截断,需重新查询FirstUsn并重置跟踪位置 - 建议将最新 USN 存在本地文件或注册表,避免重启后丢失上下文
权限和稳定性问题比想象中更敏感
即使以管理员身份运行,仍可能遇到 ERROR_ACCESS_DENIED ——原因常是卷被其他进程独占(如杀毒软件、备份工具正扫描 $UsnJrnl),或卷处于“正在检查”状态(CHKDSK 运行中)。
另外,FSCTL_READ_USN_JOURNAL 对缓冲区大小非常挑剔:太小会返回 ERROR_INSUFFICIENT_BUFFER;太大又可能因内核分配失败而返回 ERROR_INVALID_PARAMETER(尤其在低内存机器上)。
- 推荐初始缓冲区设为 64KB(
65536),根据实际单条记录平均大小动态调整 - 务必检查
GetLastError()而非仅靠返回值判断成败;ERROR_MORE_DATA表示还有更多记录可读 - 不要在 GUI 线程长时间阻塞调用;USN 读取可能耗时数百毫秒,建议用独立线程 + 异步 I/O
- 注意:某些企业环境(如启用了 Windows Defender Application Control)会拦截对
$Extend的底层访问,此时需策略白名单
真正难的不是解析结构,而是应对日志滚动、权限抖动和跨版本字段差异——这些细节不处理好,程序跑几天就 silently 失效。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











