5

我正在尝试阅读 NTFS 更改日志,但我注意到我能找到的所有示例代码在 Windows 10 上都失败了,即使它适用于 Windows 7。

例如,Microsoft 自己的示例“Walking a Buffer of Change Journal Records”在 Windows 7 上工作,但是当我在 Windows 10 上运行相同的代码时,当我使用 FSCTL_READ_USN_JOURNAL 调用 DeviceIoControl 时出现错误 87(参数不正确)(请注意,之前的调用使用 FSCTL_QUERY_USN_JOURNAL 到 DeviceIoControl 成功完成并返回有效数据。)。

我什至已经将在 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);
}
4

3 回答 3

5

我已经设法找出问题所在。

示例 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;

请注意新的 Min 和 Max Major 版本成员。

因此,Microsoft 代码定义了一个名为 ReadData 的局部变量,它实际上是一个 V1 结构,但它似乎在填充数据时假设它是一个 V0 结构。即它没有设置Min 和Max 元素。

看起来Win7对此很好,但Win10拒绝它并返回错误87(参数不正确)。

果然,如果我将 ReadData 变量明确定义为 READ_USN_JOURNAL_DATA_V0,则代码适用于 Win7 和 Win10,而如果我明确将其定义为 READ_USN_JOURNAL_DATA_V1,则它继续适用于 Win7,但不适用于 Win10。

奇怪的是,READ_USN_JOURNAL_DATA_V1 结构的 API 文档声明它仅在 Windows 8 上受支持,所以它在 Windows 7 上工作很奇怪。我猜它只是将其解释为 READ_USN_JOURNAL_DATA_V0 结构,因为 V1 版本是 V0 结构的扩展。如果是这样,那么它必须忽略传递给 DeviceIOControl 的大小参数。

无论如何,现在一切正常。我希望将来有人会发现这是一个有用的参考。

于 2017-10-27T16:32:46.223 回答
1

我在示例代码中遇到了与 OP 完全相同的问题。在示例代码的开头,您将在声明点看到结构的部分初始化。稍后在代码中,就在有问题的调用之前,有一行将 UsnJournalID 分配给读取的数据结构。

但是,对于 Windows 10,V1 结构的其他两个成员未初始化。我在 UsnJournalID 初始化后立即初始化了它们:

#if (NTDDI_VERSION >= NTDDI_WIN8)
ReadData.MinMajorVersion = JournalData.MinSupportedMajorVersion;
ReadData.MaxMajorVersion = JournalData.MaxSupportedMajorVersion;
#endif

当我这样做后运行我的代码时,它可以正常工作而没有错误代码。我记得在 Volume Management API 讨论中读到需要设置 Min 和 Max 版本。我忘记了确切的位置,因为我已经阅读和测试这些东西几天了。

无论如何,我希望这能为任何追随我的人澄清这个问题。

于 2020-01-23T21:54:22.587 回答
0

将 READ_USN_JOURNAL_DATA 结构替换为 READ_USN_JOURNAL_DATA_V0 数据结构并对其进行初始化。

这对我有用

READ_USN_JOURNAL_DATA_V0 ReadData;
ZeroMemory(&ReadData, sizeof(ReadData));
ReadData.ReasonMask = 0xFFFFFFFF;
于 2020-06-01T06:52:56.683 回答