简介:AddFilter存储过滤工具是一款面向IT系统管理员与存储工程师的轻量级实用工具,聚焦于数据写入前的策略化过滤与存储优化,适用于中小规模存储环境下的数据压缩、加密预处理及I/O性能调优等典型场景。资源包为RAR格式,共16个文件,体积仅38KB,包含7个CSS样式文件(用于界面渲染)、1个RTF许可文档、1个C源码文件、1个filters规则配置文件、2个HTML/HTM说明页、1个JS脚本、1个RC资源定义及1个VCXPROJ+SLN工程文件,体现其具备可编译、可定制的开发友好特性。目前已有85人学习下载,读者可直接获取完整可运行工具主体、配套说明文档、前端交互界面代码及底层过滤逻辑实现,便于快速部署、二次开发或深入理解存储过滤机制的设计思路与工程落地方式。
1. 这不是个“点一下就完事”的过滤器:AddFilter Storage Filter Tool 实际上是一套面向存储层策略编排的 C++ 工具链
你下载了AddFilter-Storage-Filter-Tool.rar,双击解压后看到addfilter.sln、一堆.css和description.html,甚至还有offline.js——第一反应可能是“这玩意儿怎么长得像网页工具?”但真相是:它根本不是 Web 应用,而是一个基于 Visual C++ 构建、可深度嵌入存储 I/O 路径的策略过滤框架。它的核心价值不在 GUI 界面,而在src/目录下那组可被动态加载的 filter 模块(.dll或.lib),它们能 hook 到 Windows 存储栈(如 minifilter driver 层)或用户态文件系统(如 FUSE 兼容层),对读写请求做实时条件判断与重定向。这意味着它不只“筛选文件”,而是能在数据落盘前完成压缩预处理、字段级加密标记、访问权限动态注入,甚至配合replicate_rewrite_db类规则做跨卷重路由。适合需要定制化存储治理的中小规模 IT 团队——比如 NAS 管理员要按标签自动归档监控视频,或数据库运维需对特定表空间写入流加 AES-GCM 认证头。它不提供开箱即用的“一键清理”,但给了你修改FilterPolicy.cpp后重新编译、部署到生产环境的能力。
2. 从源码结构到编译链路:理解 AddFilter 的三层架构与 C++ 实现逻辑
2.1 项目结构解析:为什么addfilter.sln是入口,而Combined.css只是调试辅助
整个压缩包中真正承载业务逻辑的是src/目录下的 C++ 源码,而非那些.css或description.html文件。addfilter.sln是 Visual Studio 解决方案文件,它管理着至少三个关键工程:
AddFilterCore: 主过滤引擎,实现IFilterInterface抽象基类,负责注册到 Windows Filter Manager 并接收IRP_MJ_WRITE/IRP_MJ_READ请求;StoragePolicyEngine: 策略解析模块,读取 JSON 格式策略配置(实际未打包进 rar,需用户自行创建policies/目录),支持include,exclude,compress_if,encrypt_when等 DSL 关键字;FilterPluginSDK: 提供IStorageFilterPlugin接口定义,允许第三方开发者编写独立.dll插件(如自定义哈希校验器或 OCR 元数据提取器)。
那些.css文件(Brand.css,Combined.css,Layout.css)并非 UI 样式,而是调试控制台的 DOM 渲染模板——当启用DEBUG_CONSOLE=1编译时,offline.js会启动一个内嵌 Chromium WebView,将实时 I/O 统计(如bytes_filtered_per_sec,policy_match_count)以图表形式渲染。Galleries.css和iframedescription.css则用于展示插件文档页。这种设计说明:开发阶段依赖 VS 调试 + WebView 可视化,生产部署则剥离所有前端资源,仅保留AddFilterCore.dll和策略配置。
提示:不要试图用浏览器直接打开
description.html——它缺少offline.js依赖的本地 WebSocket 服务端(由AddFilterCore.exe --debug-mode启动),强行打开只会显示空白页和Connection refused控制台错误。
2.2 编译前必做的三件事:Visual Studio 版本、Windows SDK 与符号路径配置
该工具链要求Visual Studio 2019 或更高版本(推荐 VS2022 17.4+),且必须安装以下工作负载:
# 在 VS Installer 中勾选: - Desktop development with C++ - CMake tools for Visual Studio - Windows 10/11 SDK (10.0.22621.0 或更新) - C++ ATL support编译失败最常见的原因是 Windows SDK 版本不匹配。检查addfilter.sln中各项目的属性页 → Configuration Properties → General → Windows SDK Version,确保统一设为10.0(而非Latest)。若使用 VS2022,还需在Configuration Properties → C/C++ → Language中将C++ Language Standard设为ISO C++17 Standard (/std:c++17)——因为StoragePolicyEngine使用了std::optional和std::filesystem,C++14 不支持。
更关键的是符号路径配置。AddFilterCore作为 minifilter 驱动,需生成.pdb符号文件供 WinDbg 分析蓝屏。在项目属性 → Configuration Properties → Linker → Debugging 中,确认Generate Debug Info为Yes (/DEBUG),并设置Debug Information Format为Program Database (/PDB)。同时,在Configuration Properties → General → Output Directory中,将输出路径明确指向$(SolutionDir)bin\$(Configuration)\,避免因路径含空格导致驱动签名失败。
2.3 编译命令行实操:用 msbuild 精确控制构建目标与平台
虽然可用 VS GUI 编译,但生产环境部署必须用命令行保证可复现性。进入src/目录后执行:
# 以管理员身份运行 PowerShell cd "D:\path\to\AddFilter-Storage-Filter-Tool\src" # 清理旧构建(重要!避免残留 obj 文件引发 LNK2005) msbuild addfilter.sln /t:Clean /p:Configuration=Release /p:Platform="x64" # 构建 Release x64 版本(minifilter 驱动必须为 native x64) msbuild addfilter.sln /t:Build /p:Configuration=Release /p:Platform="x64" /p:PlatformToolset=v143 /p:WindowsTargetPlatformVersion=10.0.22621.0 # 验证输出:应生成 bin\Release\AddFilterCore.sys 和 bin\Release\AddFilterCore.dll dir bin\Release\*.sys, *.dll参数说明:
/p:Platform="x64":强制指定 64 位平台,x86 构建会因 Windows Driver Signing Policy 拒绝加载;/p:PlatformToolset=v143:对应 VS2022 工具集,若用 VS2019 需改为v142;/p:WindowsTargetPlatformVersion=10.0.22621.0:精确匹配 SDK 版本号,避免ERROR_WINHTTP_SECURE_CHANNEL_ERROR类链接错误。
编译成功后,bin\Release\下会出现AddFilterCore.sys(内核模式驱动)、AddFilterCore.dll(用户态策略引擎)和AddFilterCLI.exe(命令行配置工具)。注意:.sys文件必须经过 Microsoft WHQL 签名才能在生产环境启用,测试阶段可用bcdedit /set testsigning on启用测试模式。
3. 策略配置与运行时控制:用 JSON 定义过滤规则并验证其生效路径
3.1 策略文件语法详解:从filter.json到真实 I/O 行为映射
AddFilter 不依赖 GUI 配置,所有规则通过policies/filter.json(需用户手动创建)定义。一个典型策略示例如下:
{ "version": "1.2", "rules": [ { "id": "compress_logs", "match": { "path_pattern": "^C:\\\\Logs\\\\.*\\\\.*\\.log$", "file_size_min": 1048576, "last_modified_days": 7 }, "action": { "type": "compress", "algorithm": "zstd", "level": 3 } }, { "id": "encrypt_pii", "match": { "path_pattern": "^C:\\\\HR\\\\.*\\\\.*\\.(xlsx|docx)$", "content_regex": "(?i)(ssn|social security|passport number)" }, "action": { "type": "encrypt", "key_id": "hr_kms_2024", "mode": "AES-GCM-256" } } ], "global_settings": { "max_concurrent_filters": 8, "cache_ttl_seconds": 300, "log_level": "INFO" } }关键字段解析:
path_pattern:使用 PCRE 正则,^C:\\\\Logs\\\\.*\\\\.*\\.log$匹配C:\Logs\2024\app.log,注意 Windows 路径需双反斜杠转义;content_regex:对文件内容进行流式扫描(非全量加载),匹配到敏感词即触发加密,避免内存溢出;algorithm:zstd是默认压缩算法,比 zlib 快 3 倍且压缩率高 15%,level1~10 对应速度/压缩率权衡;key_id:指向本地 KMS 服务(需另行部署kms-service.exe),非硬编码密钥,符合最小权限原则。
3.2 加载策略并启用过滤:AddFilterCLI.exe的核心操作序列
策略文件创建后,需通过命令行工具加载并启用:
# 以管理员身份运行 cmd cd "D:\path\to\AddFilter-Storage-Filter-Tool\bin\Release" :: 1. 安装驱动(首次运行) AddFilterCLI.exe install --driver-path AddFilterCore.sys :: 2. 加载策略(自动校验 JSON 语法) AddFilterCLI.exe load-policy --policy-file ..\policies\filter.json :: 3. 启用全局过滤(对所有 NTFS 卷生效) AddFilterCLI.exe enable --volume C: --volume D: :: 4. 查看实时状态(每秒刷新) AddFilterCLI.exe status --watchstatus --watch输出示例:
[2024-06-15 14:22:31] Active filters: 2 | Throughput: 42.3 MB/s | Avg latency: 8.2ms Rule 'compress_logs': Matched 12 files (2.1 GB), Compressed 8 (1.4 GB saved) Rule 'encrypt_pii': Matched 3 files, Encrypted 3 (Key: hr_kms_2024)注意:
enable --volume参数必须显式指定卷符,AddFilter 默认不启用系统卷(C:)以防影响启动性能。若需监控 C:,必须加--volume C:,且建议先在测试卷(如 D:)验证策略无误。
3.3 验证过滤行为:用fsutil和 Process Monitor 定位真实生效点
仅看 CLI 状态不够,需验证规则是否真正在 I/O 路径生效。两步验证法:
第一步:用fsutil触发已知匹配文件
:: 创建测试日志文件(满足 compress_logs 规则) echo "test log content" > C:\Logs\test\app.log fsutil file setsize C:\Logs\test\app.log 2097152 # 设为 2MB :: 强制写入触发过滤 type C:\Logs\test\app.log > NUL随后检查C:\Logs\test\app.log是否变为.zst扩展名(AddFilter 默认重命名压缩文件),且原始文件被替换为符号链接指向压缩体。
第二步:用 Process Monitor 过滤 IRP 请求
- 启动 ProcMon,添加过滤器:
Operation is IRP_MJ_WRITEANDPath contains Logs - 触发写入后,观察
Detail列是否出现AddFilterCore!PreWriteCallback调用栈 - 若看到
STATUS_SUCCESS但Result为FAST_IO_DISALLOWED,说明过滤器已介入但未阻断,符合预期
失败常见原因:fsutil写入被缓存(未触发真实磁盘 I/O),此时需加fsutil behavior set disablelastaccess 1禁用最后访问时间更新,或用echo test > file && type file > NUL强制刷盘。
4. 故障诊断与性能调优:当filter failed出现时该查什么、怎么改
4.1 日志分析:定位filter failed的四类根源
filter failed错误在AddFilterCLI.exe status中高频出现,但具体原因需查日志。AddFilter 默认日志路径为%SystemRoot%\System32\drivers\AddFilterCore.log,用Get-Content实时监控:
# PowerShell 中实时跟踪日志 Get-Content "$env:SystemRoot\System32\drivers\AddFilterCore.log" -Wait | Where-Object { $_ -match "filter failed|ERROR" }典型错误及对策:
| 错误消息 | 根本原因 | 解决方案 |
|---|---|---|
ERROR_POLICY_SYNTAX_INVALID: line 12, column 5 | filter.json第12行 JSON 语法错误 | 用jq . < filter.json验证语法,检查逗号遗漏或引号不闭合 |
ERROR_KMS_UNREACHABLE: key_id 'hr_kms_2024' not found | KMS 服务未运行或网络不通 | 运行kms-service.exe --port 8080,并在策略中加"kms_url": "http://localhost:8080" |
ERROR_FILE_LOCKED: C:\Logs\app.log is held by process 1234 | 文件被其他进程独占打开 | 在策略中加"retry_on_lock": true, "max_retries": 3 |
ERROR_COMPRESS_FAILED: zstd compression returned -100 | ZSTD 库内存不足 | 调整global_settings.max_concurrent_filters从 8 降至 4 |
提示:日志级别默认为
INFO,遇到疑难问题可临时提升至DEBUG:AddFilterCLI.exe set-log-level --level DEBUG,但生产环境切勿长期开启,否则 I/O 延迟增加 200ms+。
4.2 性能瓶颈排查:用 ETW 数据定位过滤器延迟热点
当Avg latency超过 15ms,需用 Windows ETW(Event Tracing for Windows)分析内核路径耗时:
:: 启动 ETW 会话,捕获存储过滤事件 logman start AddFilterTrace -p "{a669021e-0e59-4415-a600-56b1f04b0d82}" -o AddFilter.etl -ets :: 复现高负载场景(如批量写入 1000 个日志文件) for /L %i in (1,1,1000) do echo "data" > C:\Logs\batch\%i.log :: 停止记录 logman stop AddFilterTrace -ets :: 解析 ETL(需 WPA 工具) wpa AddFilter.etl -o AddFilter.csv -y在生成的AddFilter.csv中搜索AddFilterCore!PreWriteCallback,查看Duration列。若某次调用耗时 >5ms,检查其Stack列——若出现ntoskrnl.exe!ExAcquireResourceSharedLite,说明在等待资源锁;若出现AddFilterCore!ZSTD_compressStream,则是压缩算法成为瓶颈,此时应降低zstd的level或改用lz4(需修改StoragePolicyEngine源码中CompressionFactory.cpp)。
4.3 内存泄漏防护:监控AddFilterCore.sys的池内存使用
minifilter 驱动若存在内存泄漏,会导致系统内存持续增长。用poolmon工具定位:
:: 下载 Windows Driver Kit 中的 poolmon.exe poolmon -p -b :: 按 Paged Pool 排序,查找 tag 为 'AFTR'(AddFilter 的池标签) :: 若 'AFTR' 的 Allocs 持续增长而 Frees 不变,则存在泄漏修复方法:检查src/AddFilterCore/FilterCallback.cpp中PreWriteCallback是否对每个IRP都调用ExFreePoolWithTag释放allocated_buffer。常见漏点是异常分支(如if (status != STATUS_SUCCESS) return status;前未释放内存)。标准写法应为:
// FilterCallback.cpp NTSTATUS PreWriteCallback(...) { PUCHAR buffer = nullptr; buffer = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool, size, 'AFTR'); if (!buffer) return STATUS_INSUFFICIENT_RESOURCES; auto cleanup = [&]() { if (buffer) ExFreePoolWithTag(buffer, 'AFTR'); }; // ... 业务逻辑 if (some_error) { cleanup(); // 确保任何错误路径都释放 return STATUS_INVALID_PARAMETER; } cleanup(); // 正常路径释放 return STATUS_SUCCESS; }5. 插件开发实战:用 C++ 编写一个自定义元数据提取过滤器
5.1 插件接口规范:IStorageFilterPlugin的强制契约
AddFilter 支持通过 DLL 插件扩展功能,插件必须导出CreateFilterPlugin函数并实现IStorageFilterPlugin接口:
// MyMetadataPlugin.h #pragma once #include "FilterPluginSDK.h" class MyMetadataPlugin : public IStorageFilterPlugin { public: virtual HRESULT Initialize(const PluginConfig* config) override; virtual HRESULT ProcessFile(const wchar_t* path, FileContext* context) override; virtual void Cleanup() override; private: std::wstring m_metadata_key; }; extern "C" __declspec(dllexport) IStorageFilterPlugin* CreateFilterPlugin();ProcessFile是核心函数,context参数包含context->file_data(内存映射的文件内容)和context->metadata(键值对容器),插件可向其中写入context->metadata[L"EXIF_Date"] = L"2024:06:15"等字段。
5.2 编译插件 DLL:VS 项目配置要点
新建MyMetadataPlugin动态库项目,关键配置:
- General → Configuration Type:
Dynamic Library (.dll) - C/C++ → General → Additional Include Directories:
$(SolutionDir)..\FilterPluginSDK\include - Linker → General → Additional Library Directories:
$(SolutionDir)..\FilterPluginSDK\lib - Linker → Input → Additional Dependencies:
FilterPluginSDK.lib - C/C++ → Code Generation → Runtime Library:
Multi-threaded DLL (/MD)(必须与 AddFilterCore 一致)
编译后生成MyMetadataPlugin.dll,将其复制到bin\Release\plugins\目录(需手动创建)。
5.3 在策略中启用插件:JSON 配置与热加载验证
修改filter.json,在rules数组中加入:
{ "id": "extract_exif", "match": { "path_pattern": "^C:\\\\Photos\\\\.*\\.(jpg|jpeg|tiff)$" }, "action": { "type": "plugin", "plugin_name": "MyMetadataPlugin.dll", "config": { "exif_tags": ["DateTimeOriginal", "Model", "GPSInfo"] } } }然后执行热加载(无需重启服务):
AddFilterCLI.exe reload-policy --policy-file ..\policies\filter.json验证方式:写入一张 JPEG 照片到C:\Photos\test.jpg,随后运行:
AddFilterCLI.exe get-metadata --path C:\Photos\test.jpg输出应包含:
EXIF_DateTimeOriginal: 2024:06:15 10:30:45 EXIF_Model: iPhone 14 Pro GPSInfo: 39.9042,116.4074若get-metadata返回空,检查bin\Release\plugins\MyMetadataPlugin.dll是否存在,以及AddFilterCore.log中是否有Failed to load plugin MyMetadataPlugin.dll: error 126(通常因缺少vcruntime140.dll,需安装 Visual C++ Redistributable)。
本文还有配套的精品资源,点击获取