Andando em um buffer de registros de diário de alteração

Os códigos de controle que retornam usn (número de sequência de atualização) alteram registros de diário, FSCTL_READ_USN_JOURNAL e FSCTL_ENUM_USN_DATA, retornam dados semelhantes no buffer de saída. Ambos retornam um USN seguido de zero ou mais registros de diário de alteração, cada um em uma estrutura de USN_RECORD_V2 ou USN_RECORD_V3 .

O volume de destino para operações usn deve ser ReFS ou NTFS 3.0 ou posterior. Para obter a versão NTFS de um volume, abra um prompt de comando com direitos de acesso do administrador e execute o seguinte comando:

FSUtil.exe FSInfo NTFSInfoX**:**

em que X é a letra da unidade do volume.

A lista a seguir identifica maneiras de obter registros de diário de alteração:

  • Use FSCTL_ENUM_USN_DATA para obter uma listagem (enumeração) de todos os registros de diário de alteração entre dois USNs.
  • Use FSCTL_READ_USN_JOURNAL para ser mais seletivo, como selecionar motivos específicos para alterações ou retornar quando um arquivo for fechado.

Observação

Ambas as operações retornam apenas o subconjunto de registros de diário de alteração que atendem aos critérios especificados.

 

A USN retornada como o primeiro item no buffer de saída é o USN do próximo número de registro a ser recuperado. Use esse valor para continuar lendo registros do limite final para frente.

O membro FileName de USN_RECORD_V2 ou USN_RECORD_V3 contém o nome do arquivo ao qual o registro em questão se aplica. O nome do arquivo varia de comprimento, portanto , USN_RECORD_V2 e USN_RECORD_V3 são estruturas de comprimento variável. Seu primeiro membro, RecordLength, é o comprimento da estrutura (incluindo o nome do arquivo), em bytes.

Quando você trabalha com o membro FileName das estruturas USN_RECORD_V2 e USN_RECORD_V3 , não suponha que o nome do arquivo contenha um delimitador '\0' à direita. Para determinar o comprimento do nome do arquivo, use o membro FileNameLength .

O exemplo a seguir chama FSCTL_READ_USN_JOURNAL e orienta o buffer de registros de diário de alteração que a operação retorna.

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

}

O tamanho em bytes de qualquer registro especificado por uma estrutura de USN_RECORD_V2 ou USN_RECORD_V3 é, no máximo ((MaxComponentLength - 1) * Width) + Size , em que MaxComponentLength é o comprimento máximo em caracteres do nome do arquivo de registro. A largura é do tamanho de um caractere largo e o Tamanho é o tamanho da estrutura.

Para obter o comprimento máximo, chame a função GetVolumeInformation e examine o valor apontado pelo parâmetro lpMaximumComponentLength . Subtraia um de MaxComponentLength para considerar o fato de que a definição de USN_RECORD e USN_RECORD_V3 inclui um caractere do nome do arquivo.

Na linguagem de programação C, o maior tamanho de registro possível é o seguinte:

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