Обход буфера записей журнала изменений

Коды элементов управления, возвращающие записи журнала изменений с порядком обновления (USN), FSCTL_READ_USN_JOURNAL и FSCTL_ENUM_USN_DATA, возвращают аналогичные данные в выходном буфере. Оба возвращают USN, за которым следует ноль или более записей журнала изменений, каждая из которых содержит USN_RECORD_V2 или USN_RECORD_V3 структуру.

Целевой том для операций USN должен быть ReFS или NTFS 3.0 или более поздней версии. Чтобы получить версию NTFS тома, откройте командную строку с правами доступа администратора и выполните следующую команду:

FSUtil.exe FSInfo NTFSInfoX**:**

где X — буква диска тома.

В следующем списке указаны способы получения записей журнала изменений.

  • Используйте FSCTL_ENUM_USN_DATA , чтобы получить перечисление (перечисление) всех записей журнала изменений между двумя usN.
  • Используйте FSCTL_READ_USN_JOURNAL , чтобы быть более выборочным, например при выборе конкретных причин изменений или возврате при закрытии файла.

Примечание

Обе эти операции возвращают только подмножество записей журнала изменений, соответствующих указанным критериям.

 

UsN, возвращаемое в качестве первого элемента в выходном буфере, является USN следующего номера записи, который требуется извлечь. Используйте это значение, чтобы продолжить чтение записей с конечной границы вперед.

Элемент FileNameUSN_RECORD_V2 или USN_RECORD_V3 содержит имя файла, к которому применяется соответствующая запись. Имя файла зависит от длины, поэтому USN_RECORD_V2 и USN_RECORD_V3 являются структурами переменной длины. Их первый член, RecordLength, представляет собой длину структуры (включая имя файла) в байтах.

При работе с элементом FileNameUSN_RECORD_V2 и USN_RECORD_V3 структурами не следует предполагать, что имя файла содержит конечный разделитель "\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 - 1) * Width) + Size , где MaxComponentLength — это максимальная длина в символах имени файла записи. Ширина — это размер широкого символа, а размер — это размер структуры.

Чтобы получить максимальную длину, вызовите функцию GetVolumeInformation и проверьте значение, указанное параметром lpMaximumComponentLength . Вычтите один из MaxComponentLength , чтобы учесть тот факт, что определение USN_RECORD и USN_RECORD_V3 включает один символ имени файла.

На языке программирования C максимальный размер записи:

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