C++如何检测除junction points、目录符号链接、挂载点外的其他ReparsePoint
问题解答
未识别的近似重解析点类型
Windows 下除了你已经识别的 junction、目录符号链接、挂载驱动器之外,还有多种功能和表现近似的重解析点类型,常见的包括:
- 云同步目录重解析点:OneDrive、SharePoint、第三方同步盘等工具创建的同步目录,使用
IO_REPARSE_TAG_CLOUD系列标签,外观和访问逻辑和 junction 高度一致,仅会在资源管理器里显示额外的云状态标识 - WSL 跨系统重解析点:用于在 Windows 环境下访问 WSL Linux 分区的目录,使用
IO_REPARSE_TAG_LX_SYMLINK标签,访问时会自动转发到 WSL 内部文件系统 - 分层存储重解析点:企业级存储场景下的冷数据归档目录,使用
IO_REPARSE_TAG_HSM系列标签,访问时会自动从后备存储拉取数据,用户侧感知和普通 junction 无差异 - UWP 应用入口重解析点:部分 UWP 应用创建的目录入口,使用
IO_REPARSE_TAG_APPEXECLINK标签,用于转发到应用的实际安装路径
现有代码的缺陷
你当前的实现仅判断了 FILE_ATTRIBUTE_REPARSE_POINT 属性位,存在两个明显问题:
- 缺少错误处理:
GetFileAttributesA调用失败时会返回INVALID_FILE_ATTRIBUTES,如果目标目录没有读取权限、路径格式错误,都会导致判断失效 - 没有读取重解析点标签做校验:部分特殊重解析点可能因为第三方实现的问题,属性位标记不标准,需要读取实际的重解析点数据才能确认
改进后的 C++ 检测实现
你需要调用 DeviceIoControl 发送 FSCTL_GET_REPARSE_POINT 控制码读取重解析点的标签,实现更精准的检测,参考代码如下:
#include <windows.h> #include <winioctl.h> #include <jni.h> // 列出所有你需要匹配的重解析点标签,可根据需求扩展 #define IS_TARGET_REPARSE_TAG(tag) \ (tag == IO_REPARSE_TAG_MOUNT_POINT || /* junction/挂载驱动器 */ \ tag == IO_REPARSE_TAG_SYMLINK || /* 符号链接 */ \ tag == IO_REPARSE_TAG_CLOUD || /* 云同步目录 */ \ tag == IO_REPARSE_TAG_LX_SYMLINK || /* WSL 跨系统链接 */ \ tag == IO_REPARSE_TAG_HSM || /* 分层存储链接 */ \ tag == IO_REPARSE_TAG_APPEXECLINK) /* UWP 应用链接 */ BOOL FileReports::IsReparsePoint(jstring jsrvName, jobject ErrHdlr) { const char *path = env->GetStringUTFChars(jsrvName, 0); BOOL flag = FALSE; HANDLE hDir = NULL; BYTE reparseBuffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE] = {0}; DWORD bytesReturned = 0; // 先校验属性,快速过滤非重解析点 DWORD attrs = GetFileAttributesA(path); if (attrs == INVALID_FILE_ATTRIBUTES) { goto Cleanup; } if (!(attrs & FILE_ATTRIBUTE_REPARSE_POINT)) { goto Cleanup; } // 打开目录获取句柄 hDir = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL); if (hDir == INVALID_HANDLE_VALUE) { goto Cleanup; } // 读取重解析点数据 if (DeviceIoControl(hDir, FSCTL_GET_REPARSE_POINT, NULL, 0, reparseBuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytesReturned, NULL)) { REPARSE_DATA_BUFFER *pReparse = (REPARSE_DATA_BUFFER *)reparseBuffer; if (IS_TARGET_REPARSE_TAG(pReparse->ReparseTag)) { flag = TRUE; } } Cleanup: if (hDir != INVALID_HANDLE_VALUE) { CloseHandle(hDir); } env->ReleaseStringUTFChars(jsrvName, path); log(NORMAL, "cleaning up"); return flag; }
注意事项
- 如果需要覆盖更多特殊重解析点类型,只需要在
IS_TARGET_REPARSE_TAG宏里添加对应的标签值即可,所有官方重解析点标签定义可以在winioctl.h头文件里查到 - 调用
CreateFileA时必须带上FILE_FLAG_OPEN_REPARSE_POINT标志,否则会直接打开重解析点指向的目标目录,无法读取原重解析点的数据 - 部分受系统保护的目录需要程序以管理员权限运行才能读取重解析点数据
内容的提问来源于stack exchange,提问作者sparrow
相关产品推荐
相关产品推荐

