6

我正在尝试通过 MSDN 上的示例(https://msdn.microsoft.com/en-us/library/windows/desktop/aa365736%28v=vs.85%29.aspx)了解如何查询 USN 日志为了跟踪 NTFS 驱动器上的文件更改。示例代码运行良好。

然而,在这个示例代码中,USN_RECORD 结构只返回一个文件参考号和文件名。它不会返回文件的完整路径。有谁知道如何查询 USN 日志以返回完整路径?或者有一种方法可以从文件参考号中获取完整路径?

谢谢。

4

3 回答 3

3

该结构的ParentFileReferenceNumber成员USN_RECORD是包含该文件的目录的参考号。

您可以使用FSCTL_ENUM_USN_DATA参考号来查找文件(或目录!)。您将需要迭代树以构建完整路径。此答案中有一些代码作为示例可能会有所帮助。

此代码查找根目录的参考编号,以便您知道何时完成:

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;
}
于 2015-08-03T01:23:00.780 回答
3

我一直在尝试避免递归父目录搜索以获取完整路径,因为我的初始测试增加了解析路径所需的总时间。

在使用了几个小时的 windbg 和一些 OSR Onlinefourm 的帮助之后,我终于明白了。

发布答案以帮助其他遇到同样问题的人。

我目前的解决方案如下。

USN_RECORD-> FileReferenceNumber 完全依赖于 USN_RECORD 的版本,一旦从 FileReferenceNumber 中提取 FILE_ID_DESCRIPTOR,就可以调用 OpenFileById() 并传递 FILE_ID_DESCRIPTOR 来获取父文件夹的句柄。

然后您可以调用 GetFinalPathNameByHandle() 来获取 ParentDirectory 路径。

下面是我最终用于提取 FILE_ID_DESCRIPTOR 的代码

如果是 USN_RECORD_V2 中的 FileId,则 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的一次调用,这将是获取父信息的有效方法。

于 2018-03-27T17:47:57.317 回答
1

以下是 MSDN 上解释更新序列号 (USN) 日志、它是什么、它如何工作以及如何查询它的页面。

于 2020-05-13T18:40:43.843 回答