変更ジャーナル レコードのバッファーの探索

更新シーケンス番号 (USN) 変更ジャーナル・レコード ( FSCTL_READ_USN_JOURNAL および FSCTL_ENUM_USN_DATA) を返す制御コードは、出力バッファー内の類似データを返します。 どちらの場合も、USN の後に 0 個以上の変更履歴レコードが返され、それぞれが USN_RECORD_V2 または USN_RECORD_V3 構造で返されます。

USN 操作のターゲット ボリュームは、ReFS または NTFS 3.0 以降である必要があります。 ボリュームの NTFS バージョンを取得するには、管理者権限でコマンド プロンプトを開き、次のコマンドを実行します。

FSUtil.exe FSInfo NTFSInfoX**:**

ここで 、X はボリュームのドライブ文字です。

次の一覧は、変更履歴レコードを取得する方法を示しています。

  • FSCTL_ENUM_USN_DATAを使用して、2 つの USN 間のすべての変更履歴レコードの一覧 (列挙) を取得します。
  • FSCTL_READ_USN_JOURNALを使用して、変更の特定の理由を選択したり、ファイルを閉じたときにを返したりするなど、より選択的になります。

Note

どちらの操作も、指定した条件を満たす変更履歴レコードのサブセットのみを返します。

 

出力バッファーの最初の項目として返される USN は、取得する次のレコード番号の USN です。 この値を使用して、終了境界からレコードの読み取りを続行します。

USN_RECORD_V2 または USN_RECORD_V3FileName メンバーには、該当するレコードが適用されるファイルの名前が含まれています。 ファイル名は長さが異なるため、 USN_RECORD_V2USN_RECORD_V3 は可変長構造体です。 最初のメンバー RecordLength は、構造体の長さ (ファイル名を含む) です (バイト単位)。

USN_RECORD_V2 および USN_RECORD_V3 構造体の FileName メンバーを操作する場合、ファイル名に末尾の '\0' 区切り記号が含まれているとは想定しないでください。 ファイル名の長さを確認するには、 FileNameLength メンバーを使用します。

次の例では 、FSCTL_READ_USN_JOURNAL を呼び出し、操作によって返される変更ジャーナル レコードのバッファーをウォークします。

#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);

}

USN_RECORD_V2またはUSN_RECORD_V3構造体で指定されたレコードのサイズ (バイト単位) は、MaxComponentLength((MaxComponentLength - 1) * Width) + Size がレコード ファイル名の最大文字数です。 幅はワイド文字のサイズ、 Size は構造体のサイズです。

最大長を取得するには、 GetVolumeInformation 関数を呼び出し、 lpMaximumComponentLength パラメーターが指す値を調べます。 MaxComponentLength から 1 つを減算して、USN_RECORDUSN_RECORD_V3の定義にファイル名の 1 文字が含まれていることを考慮します。

C プログラミング言語では、可能な最大のレコード サイズは次のとおりです。

(MaxComponentLength - 1) * sizeof(WCHAR) + sizeof(USN_RECORD)