如何获取USN期刊查询的完整路径?
How to get the full path for USN journal query?
我正在尝试通过 MSDN (https://msdn.microsoft.com/en-us/library/windows/desktop/aa365736%28v=vs.85%29.aspx) 上的示例了解如何查询 USN 日志以跟踪 NTFS 驱动器上的文件更改。示例代码运行良好。
但是,在此示例代码中,USN_RECORD 结构仅 return 提供了文件引用号和文件名。它没有 return 文件的完整路径。有谁知道如何将 USN 日志查询到 return 完整路径?或者有办法从文件参考号获取完整路径吗?
谢谢。
USN_RECORD
结构的 ParentFileReferenceNumber
成员是包含该文件的目录的引用号。
您可以使用 FSCTL_ENUM_USN_DATA
to look up a file (or directory!) by reference number. You will need to iterate up the tree to build the complete path. There is some code in this answer 作为示例,这可能会有所帮助。
此代码查找根目录的参考编号,因此您可以知道何时完成:
HANDLE rootdir_handle;
USN_RECORD * rootdir_usn;
printf("Opening root directory.\n");
rootdir_handle = CreateFile(L"\\?\C:\", GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS,
FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (rootdir_handle == INVALID_HANDLE_VALUE)
{
printf("CreateFile: %u\n", GetLastError());
return 0;
}
if (!DeviceIoControl(rootdir_handle, FSCTL_READ_FILE_USN_DATA, NULL, 0,
buffer, BUFFER_SIZE, &bytecount, NULL))
{
printf("FSCTL_READ_FILE_USN_DATA: %u\n", GetLastError());
}
else
{
rootdir_usn = (USN_RECORD *)buffer;
show_record(rootdir_usn, FALSE);
rootdir = rootdir_usn->FileReferenceNumber;
}
我一直在努力避免递归父目录搜索来获取完整路径,因为我的初始测试增加了解析路径所需的总时间。
在 windbg 上花了几个小时并从 OSR Online fourm 获得了一些帮助后,我终于明白了。
发布答案以帮助遇到同样问题的其他人。
我目前的解决方案如下
USN_RECORD-> FileReferenceNumber 完全依赖于 USN_RECORD 的版本,一旦从 FileReferenceNumber 中提取 FILE_ID_DESCRIPTOR,就可以调用 OpenFileById() 并传递 FILE_ID_DESCRIPTOR 获取父文件夹的句柄。
然后您可以调用 GetFinalPathNameByHandle() 来获取 ParentDirectory 路径。
下面是我最终用于提取的代码 FILE_ID_DESCRIPTOR
如果FileId在USN_RECORD_V2, FileReferenceNu DWORDLONG.
FILE_ID_DESCRIPTOR getFileIdDescriptor(const DWORDLONG fileId)
{
FILE_ID_DESCRIPTOR fileDescriptor;
fileDescriptor.Type = FileIdType;
fileDescriptor.FileId.QuadPart = fileId;
fileDescriptor.dwSize = sizeof(fileDescriptor);
return fileDescriptor;
}
如果你以 UNS_RECORD_V3 结尾,fileId 的类型是 FILE_ID_128,这里是提取 FileId 的代码。
FILE_ID_DESCRIPTOR getFileIdDescriptor(const FILE_ID_128& fileId)
{
FILE_ID_DESCRIPTOR fileDescriptor;
fileDescriptor.Type = ExtendedFileIdType;
fileDescriptor.ExtendedFileId = fileId;
fileDescriptor.dwSize = sizeof(fileDescriptor);
return fileDescriptor;
}
提取 FileId 后,可以通过以下方式获取父路径。
TCHAR filePath[MAX_PATH];
HANDLE hh= OpenFileById(volume_, &(getFileIdDescriptor(UsnRecord->FileReferenceNumber)), 0, 0, 0, 0);
GetFinalPathNameByHandle(hh,filePath, MAX_PATH, 0);
你可以找到参考实现@https://github.com/kirankumarcelestial/NTFSChangeJournalUserMode
不过我发现GetFilePathNameByHandle()
其实很慢,这个API最终会调用GetFileInformationByHandleEx()
,而GetFileInformationByHandleEx()
是单次调用KernelMode,这样会是获得家长信息的有效方式。
以下是 MSDN 上解释更新序列号 (USN) 日志、它是什么、它如何工作以及如何查询它的页面。
我正在尝试通过 MSDN (https://msdn.microsoft.com/en-us/library/windows/desktop/aa365736%28v=vs.85%29.aspx) 上的示例了解如何查询 USN 日志以跟踪 NTFS 驱动器上的文件更改。示例代码运行良好。
但是,在此示例代码中,USN_RECORD 结构仅 return 提供了文件引用号和文件名。它没有 return 文件的完整路径。有谁知道如何将 USN 日志查询到 return 完整路径?或者有办法从文件参考号获取完整路径吗?
谢谢。
USN_RECORD
结构的 ParentFileReferenceNumber
成员是包含该文件的目录的引用号。
您可以使用 FSCTL_ENUM_USN_DATA
to look up a file (or directory!) by reference number. You will need to iterate up the tree to build the complete path. There is some code in this answer 作为示例,这可能会有所帮助。
此代码查找根目录的参考编号,因此您可以知道何时完成:
HANDLE rootdir_handle;
USN_RECORD * rootdir_usn;
printf("Opening root directory.\n");
rootdir_handle = CreateFile(L"\\?\C:\", GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS,
FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (rootdir_handle == INVALID_HANDLE_VALUE)
{
printf("CreateFile: %u\n", GetLastError());
return 0;
}
if (!DeviceIoControl(rootdir_handle, FSCTL_READ_FILE_USN_DATA, NULL, 0,
buffer, BUFFER_SIZE, &bytecount, NULL))
{
printf("FSCTL_READ_FILE_USN_DATA: %u\n", GetLastError());
}
else
{
rootdir_usn = (USN_RECORD *)buffer;
show_record(rootdir_usn, FALSE);
rootdir = rootdir_usn->FileReferenceNumber;
}
我一直在努力避免递归父目录搜索来获取完整路径,因为我的初始测试增加了解析路径所需的总时间。
在 windbg 上花了几个小时并从 OSR Online fourm 获得了一些帮助后,我终于明白了。
发布答案以帮助遇到同样问题的其他人。
我目前的解决方案如下
USN_RECORD-> FileReferenceNumber 完全依赖于 USN_RECORD 的版本,一旦从 FileReferenceNumber 中提取 FILE_ID_DESCRIPTOR,就可以调用 OpenFileById() 并传递 FILE_ID_DESCRIPTOR 获取父文件夹的句柄。
然后您可以调用 GetFinalPathNameByHandle() 来获取 ParentDirectory 路径。
下面是我最终用于提取的代码 FILE_ID_DESCRIPTOR
如果FileId在USN_RECORD_V2, FileReferenceNu DWORDLONG.
FILE_ID_DESCRIPTOR getFileIdDescriptor(const DWORDLONG fileId)
{
FILE_ID_DESCRIPTOR fileDescriptor;
fileDescriptor.Type = FileIdType;
fileDescriptor.FileId.QuadPart = fileId;
fileDescriptor.dwSize = sizeof(fileDescriptor);
return fileDescriptor;
}
如果你以 UNS_RECORD_V3 结尾,fileId 的类型是 FILE_ID_128,这里是提取 FileId 的代码。
FILE_ID_DESCRIPTOR getFileIdDescriptor(const FILE_ID_128& fileId)
{
FILE_ID_DESCRIPTOR fileDescriptor;
fileDescriptor.Type = ExtendedFileIdType;
fileDescriptor.ExtendedFileId = fileId;
fileDescriptor.dwSize = sizeof(fileDescriptor);
return fileDescriptor;
}
提取 FileId 后,可以通过以下方式获取父路径。
TCHAR filePath[MAX_PATH];
HANDLE hh= OpenFileById(volume_, &(getFileIdDescriptor(UsnRecord->FileReferenceNumber)), 0, 0, 0, 0);
GetFinalPathNameByHandle(hh,filePath, MAX_PATH, 0);
你可以找到参考实现@https://github.com/kirankumarcelestial/NTFSChangeJournalUserMode
不过我发现GetFilePathNameByHandle()
其实很慢,这个API最终会调用GetFileInformationByHandleEx()
,而GetFileInformationByHandleEx()
是单次调用KernelMode,这样会是获得家长信息的有效方式。
以下是 MSDN 上解释更新序列号 (USN) 日志、它是什么、它如何工作以及如何查询它的页面。