我正在阅读 NTFS 更改日志以监视驱动器上的任何文件更改,但返回的 USN_RECORD 仅包括关联文件的文件名,而不包括完整路径。
为了获得完整路径,我使用带有 OpenFileById API 的 FileReferenceNumber(也在 USN_RECORD 中)来获取关联文件的句柄,然后使用 GetFinalPathNameByHandleA 来获取完整路径。
只要该文件仍然存在,这很好用,但是如果文件已被删除(例如,如果我正在处理 USN_RECORD 以删除文件),那么显然 OpenFileById 失败,因此我无法获得完整路径。
虽然 USN_RECORD 确实包含一个 ParentFileReferenceNumber 成员(我大概可以使用 OpenFileById 打开),它应该允许我获取父目录的名称,但同样的问题存在,即如果父目录已被删除怎么办?
此外,即使父目录没有被删除,我如何向上走到下一个目录(即父目录的父目录)等等,直到我到达卷根?
这是我的示例代码:
#include "stdafx.h"
#include <Windows.h>
#include <WinIoCtl.h>
#include <stdio.h>
#include <string>
using std::string;
#define BUF_LEN 4096
std::string GetFullPath(HANDLE hVol, DWORDLONG fileRefNum)
{
FILE_ID_DESCRIPTOR fid;
ZeroMemory(&fid, sizeof(fid));
fid.dwSize = sizeof(fid);
fid.Type = FileIdType;
fid.FileId.QuadPart = fileRefNum;
HANDLE handle = OpenFileById(hVol, &fid, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, FILE_FLAG_BACKUP_SEMANTICS);
if (handle == INVALID_HANDLE_VALUE)
return "Error: " + std::to_string(GetLastError());
char buffer[1024];
GetFinalPathNameByHandleA(handle, &buffer[0], 1024, 0);
return string(&buffer[0]);
}
void main()
{
// Open the volume
DWORD dwBytes;
DWORD dwRetBytes;
HANDLE hVol = CreateFile(TEXT("\\\\.\\c:"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (hVol == INVALID_HANDLE_VALUE)
{
printf("CreateFile failed (%d)\n", GetLastError());
return;
}
// Query the Journal
USN_JOURNAL_DATA_V0 JournalData;
if (!DeviceIoControl(hVol, FSCTL_QUERY_USN_JOURNAL, NULL, 0, &JournalData, sizeof(JournalData), &dwBytes, NULL))
{
printf("Query journal failed (%d)\n", GetLastError());
return;
}
printf("Journal ID: %I64x\n", JournalData.UsnJournalID);
printf("FirstUsn: %I64x\n\n", JournalData.FirstUsn);
READ_USN_JOURNAL_DATA_V0 ReadData;
ZeroMemory(&ReadData, sizeof(ReadData));
ReadData.ReasonMask = 0xFFFFFFFF;
ReadData.UsnJournalID = JournalData.UsnJournalID;
PUSN_RECORD UsnRecord;
CHAR Buffer[BUF_LEN];
for (int loop = 0; loop <= 100; loop++)
{
// Read the journal
ZeroMemory(Buffer, BUF_LEN);
if (!DeviceIoControl(hVol, FSCTL_READ_USN_JOURNAL, &ReadData, sizeof(ReadData), &Buffer, BUF_LEN, &dwBytes, NULL))
{
printf("Read journal failed (%d)\n", GetLastError());
return;
}
dwRetBytes = dwBytes - sizeof(USN);
// Find the first record
UsnRecord = (PUSN_RECORD)(((PUCHAR)Buffer) + sizeof(USN));
printf("****************************************\n");
// This loop could go on for a long time, given the current buffer size.
while (dwRetBytes > 0)
{
printf("USN: %I64x\n", UsnRecord->Usn);
printf("Filename: %.*S\n", UsnRecord->FileNameLength / 2, UsnRecord->FileName);
printf("Full filename: %s\n", GetFullPath(hVol, UsnRecord->FileReferenceNumber).c_str());
printf("Reason: %x\n", UsnRecord->Reason);
printf("\n");
dwRetBytes -= UsnRecord->RecordLength;
// Find the next record
UsnRecord = (PUSN_RECORD)(((PCHAR)UsnRecord) + UsnRecord->RecordLength);
}
// Update starting USN for next call
ReadData.StartUsn = *(USN *)&Buffer;
}
CloseHandle(hVol);
}
示例输出:
USN: 8ebc423a8
Filename: 999000000167237.xml
Full filename: Error: 87
Reason: 80000200
USN: 8ebc42410
Filename: 999000000167238.xml
Full filename: Error: 87
Reason: 80000200
USN: 8ebc42478
Filename: MobilityDB.db
Full filename: \\?\C:\ProgramData\NGC\Open Mobile\Profiles\12316\MobilityDB.db
Reason: 1