在 Windows 10 上浏览 NTFS 更改日志
Walking the NTFS Change Journal on Windows 10
我正在尝试阅读 NTFS 变更日志,但我注意到我能找到的所有示例代码在 Windows 10 上都失败了,尽管它在 Windows 7 上运行。
例如,Microsoft 自己的示例 Walking a Buffer of Change Journal Records 在 Windows 7 上运行,但是当我 运行 在 Windows 10 上运行相同的代码时,我收到错误 87(参数不正确)当我用 FSCTL_READ_USN_JOURNAL 调用 DeviceIoControl 时(请注意,之前用 FSCTL_QUERY_USN_JOURNAL 调用 DeviceIoControl 成功完成并且 return 有效数据。)。
我什至把在 Windows 7 上编译和工作的 EXE 复制到 Windows 10 机器上,但它仍然失败,所以我相信 Windows 10 可能对参数验证或类似的东西更严格?
我运行以管理员身份运行代码,所以这不是问题所在。
我找不到关于此问题的任何其他参考资料,但如果我使用其他人的示例代码并尝试在 Windows 10 上 运行 它,我会遇到同样的问题。
编辑:
代码本身:
#include <Windows.h>
#include <WinIoCtl.h>
#include <stdio.h>
#define BUF_LEN 4096
void main()
{
HANDLE hVol;
CHAR Buffer[BUF_LEN];
USN_JOURNAL_DATA JournalData;
READ_USN_JOURNAL_DATA ReadData = {0, 0xFFFFFFFF, FALSE, 0, 0};
PUSN_RECORD UsnRecord;
DWORD dwBytes;
DWORD dwRetBytes;
int I;
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;
}
if( !DeviceIoControl( hVol,
FSCTL_QUERY_USN_JOURNAL,
NULL,
0,
&JournalData,
sizeof(JournalData),
&dwBytes,
NULL) )
{
printf( "Query journal failed (%d)\n", GetLastError());
return;
}
ReadData.UsnJournalID = JournalData.UsnJournalID;
printf( "Journal ID: %I64x\n", JournalData.UsnJournalID );
printf( "FirstUsn: %I64x\n\n", JournalData.FirstUsn );
for(I=0; I<=10; I++)
{
memset( Buffer, 0, 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("File name: %.*S\n",
UsnRecord->FileNameLength/2,
UsnRecord->FileName );
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);
}
我已经设法找出问题所在。
示例 Microsoft 代码创建了一个定义为 READ_USN_JOURNAL_DATA 的局部变量,其定义为:
#if (NTDDI_VERSION >= NTDDI_WIN8)
typedef READ_USN_JOURNAL_DATA_V1 READ_USN_JOURNAL_DATA, *PREAD_USN_JOURNAL_DATA;
#else
typedef READ_USN_JOURNAL_DATA_V0 READ_USN_JOURNAL_DATA, *PREAD_USN_JOURNAL_DATA;
#endif
在我的系统(Win10 和 Win7 系统)上,它的计算结果为 READ_USN_JOURNAL_DATA_V1.
查看 READ_USN_JOURNAL_DATA_V0 和 READ_USN_JOURNAL_DATA_V1 之间的差异,我们可以看到 V0 定义为:
typedef struct {
USN StartUsn;
DWORD ReasonMask;
DWORD ReturnOnlyOnClose;
DWORDLONG Timeout;
DWORDLONG BytesToWaitFor;
DWORDLONG UsnJournalID;
} READ_USN_JOURNAL_DATA_V0, *PREAD_USN_JOURNAL_DATA_V0;
而V1版本定义为:
typedef struct {
USN StartUsn;
DWORD ReasonMask;
DWORD ReturnOnlyOnClose;
DWORDLONG Timeout;
DWORDLONG BytesToWaitFor;
DWORDLONG UsnJournalID;
WORD MinMajorVersion;
WORD MaxMajorVersion;
} READ_USN_JOURNAL_DATA_V1, *PREAD_USN_JOURNAL_DATA_V1;
注意新的最小和最大主要版本成员。
因此,Microsoft 代码正在定义一个名为 ReadData 的局部变量,它实际上是一个 V1 结构,但它似乎是在假设它是一个 V0 结构的情况下填充数据。即它不设置 Min 和 Max 元素。
看来Win7没问题,但Win10拒绝了,returns错误87(参数不正确)。
果然,如果我将 ReadData 变量明确定义为 READ_USN_JOURNAL_DATA_V0,则代码可在 Win7 和 Win10 上运行,而如果我将其明确定义为 READ_USN_JOURNAL_DATA_V1,则它会继续适用于 Win7 但不适用于 Win10。
奇怪的是 READ_USN_JOURNAL_DATA_V1 structure 的 API 文档指出它仅从 Windows 8 开始受支持,所以它在 Windows 7 上工作很奇怪根本。我猜它只是将它解释为 READ_USN_JOURNAL_DATA_V0 结构,因为 V1 版本是 V0 结构的扩展。如果是这样,那么它必须忽略传递到 DeviceIOControl 的大小参数。
无论如何,现在一切正常。我希望有人在将来发现这是一个有用的参考。
我在示例代码中遇到了与 OP 完全相同的问题。在示例代码的开头,您将在声明点看到结构的部分初始化。稍后在代码中,就在有问题的调用之前,有一行将 UsnJournalID 分配到读取数据结构中。
对于Windows10,不过,V1结构的其他两个成员没有被初始化。我在 UsnJournalID 初始化后立即初始化它们:
#if (NTDDI_VERSION >= NTDDI_WIN8)
ReadData.MinMajorVersion = JournalData.MinSupportedMajorVersion;
ReadData.MaxMajorVersion = JournalData.MaxSupportedMajorVersion;
#endif
当我 运行 执行此操作后我的代码时,它可以正常工作而没有错误代码。我记得在卷管理 API 讨论中读到需要设置最小和最大版本。我忘了确切的位置,因为我已经阅读和测试这些东西几天了。
无论如何,我希望这能为任何追随我的人澄清问题。
将READ_USN_JOURNAL_DATA结构替换为READ_USN_JOURNAL_DATA_V0数据结构并初始化。
这对我有用
READ_USN_JOURNAL_DATA_V0 ReadData;
ZeroMemory(&ReadData, sizeof(ReadData));
ReadData.ReasonMask = 0xFFFFFFFF;
我正在尝试阅读 NTFS 变更日志,但我注意到我能找到的所有示例代码在 Windows 10 上都失败了,尽管它在 Windows 7 上运行。
例如,Microsoft 自己的示例 Walking a Buffer of Change Journal Records 在 Windows 7 上运行,但是当我 运行 在 Windows 10 上运行相同的代码时,我收到错误 87(参数不正确)当我用 FSCTL_READ_USN_JOURNAL 调用 DeviceIoControl 时(请注意,之前用 FSCTL_QUERY_USN_JOURNAL 调用 DeviceIoControl 成功完成并且 return 有效数据。)。
我什至把在 Windows 7 上编译和工作的 EXE 复制到 Windows 10 机器上,但它仍然失败,所以我相信 Windows 10 可能对参数验证或类似的东西更严格?
我运行以管理员身份运行代码,所以这不是问题所在。
我找不到关于此问题的任何其他参考资料,但如果我使用其他人的示例代码并尝试在 Windows 10 上 运行 它,我会遇到同样的问题。
编辑:
代码本身:
#include <Windows.h>
#include <WinIoCtl.h>
#include <stdio.h>
#define BUF_LEN 4096
void main()
{
HANDLE hVol;
CHAR Buffer[BUF_LEN];
USN_JOURNAL_DATA JournalData;
READ_USN_JOURNAL_DATA ReadData = {0, 0xFFFFFFFF, FALSE, 0, 0};
PUSN_RECORD UsnRecord;
DWORD dwBytes;
DWORD dwRetBytes;
int I;
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;
}
if( !DeviceIoControl( hVol,
FSCTL_QUERY_USN_JOURNAL,
NULL,
0,
&JournalData,
sizeof(JournalData),
&dwBytes,
NULL) )
{
printf( "Query journal failed (%d)\n", GetLastError());
return;
}
ReadData.UsnJournalID = JournalData.UsnJournalID;
printf( "Journal ID: %I64x\n", JournalData.UsnJournalID );
printf( "FirstUsn: %I64x\n\n", JournalData.FirstUsn );
for(I=0; I<=10; I++)
{
memset( Buffer, 0, 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("File name: %.*S\n",
UsnRecord->FileNameLength/2,
UsnRecord->FileName );
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);
}
我已经设法找出问题所在。
示例 Microsoft 代码创建了一个定义为 READ_USN_JOURNAL_DATA 的局部变量,其定义为:
#if (NTDDI_VERSION >= NTDDI_WIN8)
typedef READ_USN_JOURNAL_DATA_V1 READ_USN_JOURNAL_DATA, *PREAD_USN_JOURNAL_DATA;
#else
typedef READ_USN_JOURNAL_DATA_V0 READ_USN_JOURNAL_DATA, *PREAD_USN_JOURNAL_DATA;
#endif
在我的系统(Win10 和 Win7 系统)上,它的计算结果为 READ_USN_JOURNAL_DATA_V1.
查看 READ_USN_JOURNAL_DATA_V0 和 READ_USN_JOURNAL_DATA_V1 之间的差异,我们可以看到 V0 定义为:
typedef struct {
USN StartUsn;
DWORD ReasonMask;
DWORD ReturnOnlyOnClose;
DWORDLONG Timeout;
DWORDLONG BytesToWaitFor;
DWORDLONG UsnJournalID;
} READ_USN_JOURNAL_DATA_V0, *PREAD_USN_JOURNAL_DATA_V0;
而V1版本定义为:
typedef struct {
USN StartUsn;
DWORD ReasonMask;
DWORD ReturnOnlyOnClose;
DWORDLONG Timeout;
DWORDLONG BytesToWaitFor;
DWORDLONG UsnJournalID;
WORD MinMajorVersion;
WORD MaxMajorVersion;
} READ_USN_JOURNAL_DATA_V1, *PREAD_USN_JOURNAL_DATA_V1;
注意新的最小和最大主要版本成员。
因此,Microsoft 代码正在定义一个名为 ReadData 的局部变量,它实际上是一个 V1 结构,但它似乎是在假设它是一个 V0 结构的情况下填充数据。即它不设置 Min 和 Max 元素。
看来Win7没问题,但Win10拒绝了,returns错误87(参数不正确)。
果然,如果我将 ReadData 变量明确定义为 READ_USN_JOURNAL_DATA_V0,则代码可在 Win7 和 Win10 上运行,而如果我将其明确定义为 READ_USN_JOURNAL_DATA_V1,则它会继续适用于 Win7 但不适用于 Win10。
奇怪的是 READ_USN_JOURNAL_DATA_V1 structure 的 API 文档指出它仅从 Windows 8 开始受支持,所以它在 Windows 7 上工作很奇怪根本。我猜它只是将它解释为 READ_USN_JOURNAL_DATA_V0 结构,因为 V1 版本是 V0 结构的扩展。如果是这样,那么它必须忽略传递到 DeviceIOControl 的大小参数。
无论如何,现在一切正常。我希望有人在将来发现这是一个有用的参考。
我在示例代码中遇到了与 OP 完全相同的问题。在示例代码的开头,您将在声明点看到结构的部分初始化。稍后在代码中,就在有问题的调用之前,有一行将 UsnJournalID 分配到读取数据结构中。
对于Windows10,不过,V1结构的其他两个成员没有被初始化。我在 UsnJournalID 初始化后立即初始化它们:
#if (NTDDI_VERSION >= NTDDI_WIN8)
ReadData.MinMajorVersion = JournalData.MinSupportedMajorVersion;
ReadData.MaxMajorVersion = JournalData.MaxSupportedMajorVersion;
#endif
当我 运行 执行此操作后我的代码时,它可以正常工作而没有错误代码。我记得在卷管理 API 讨论中读到需要设置最小和最大版本。我忘了确切的位置,因为我已经阅读和测试这些东西几天了。
无论如何,我希望这能为任何追随我的人澄清问题。
将READ_USN_JOURNAL_DATA结构替换为READ_USN_JOURNAL_DATA_V0数据结构并初始化。
这对我有用
READ_USN_JOURNAL_DATA_V0 ReadData;
ZeroMemory(&ReadData, sizeof(ReadData));
ReadData.ReasonMask = 0xFFFFFFFF;