如何获取USN期刊查询的完整路径?

时间:2015-08-01 14:58:28

标签: c++ visual-studio winapi msdn ntfs-mft

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

但是,在此示例代码中,USN_RECORD结构仅返回文件引用号和文件名。它不会返回文件的完整路径。有谁知道如何查询USN日志返回完整路径?或者有一种方法可以从文件引用号中获取完整路径?

感谢。

3 个答案:

答案 0 :(得分:2)

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

您可以使用FSCTL_ENUM_USN_DATA按参考编号查找文件(或目录!)。您需要迭代树以构建完整路径。 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;
}

答案 1 :(得分:1)

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

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

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

我目前的解决方案如下。

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的一次调用,这将是获得get的有效方法家长信息。

答案 2 :(得分:1)

为了成为一个Wiki,并为下一个搜索此问题的人提供帮助,以及我希望他们为后代进行归档的事实,这些是:MSDN上的页面,解释了更新序列号(USN) )日记,它是什么,它是如何工作的以及如何查询它。