Caminar un búfer de registros de diario de cambios

Los códigos de control que devuelven registros de diario de cambios de número de secuencia de actualización (USN), FSCTL_READ_USN_JOURNAL y FSCTL_ENUM_USN_DATA, devuelven datos similares en el búfer de salida. Ambos devuelven un USN seguido de cero o más registros del diario de cambios, cada uno de ellos en una estructura de USN_RECORD_V2 o USN_RECORD_V3 .

El volumen de destino para las operaciones USN debe ser ReFS o NTFS 3.0 o posterior. Para obtener la versión NTFS de un volumen, abra un símbolo del sistema con derechos de acceso de administrador y ejecute el siguiente comando:

FSUtil.exe FSInfo NTFSInfoX**:**

donde X es la letra de unidad del volumen.

En la lista siguiente se identifican las formas de obtener registros de diario de cambios:

  • Use FSCTL_ENUM_USN_DATA para obtener una lista (enumeración) de todos los registros de diario de cambios entre dos USN.
  • Use FSCTL_READ_USN_JOURNAL para ser más selectivo, como la selección de motivos específicos de los cambios o la devolución cuando se cierra un archivo.

Nota

Ambas operaciones devuelven solo el subconjunto de registros del diario de cambios que cumplen los criterios especificados.

 

El USN devuelto como el primer elemento del búfer de salida es el USN del siguiente número de registro que se va a recuperar. Use este valor para continuar leyendo registros desde el límite final hacia delante.

El miembro FileName de USN_RECORD_V2 o USN_RECORD_V3 contiene el nombre del archivo al que se aplica el registro en cuestión. El nombre de archivo varía en longitud, por lo que USN_RECORD_V2 y USN_RECORD_V3 son estructuras de longitud variable. Su primer miembro, RecordLength, es la longitud de la estructura (incluido el nombre de archivo), en bytes.

Al trabajar con el miembro FileName de USN_RECORD_V2 y USN_RECORD_V3 estructuras, no suponga que el nombre de archivo contiene un delimitador "\0" final. Para determinar la longitud del nombre de archivo, use el miembro FileNameLength .

En el ejemplo siguiente se llama a FSCTL_READ_USN_JOURNAL y se recorre el búfer de registros del diario de cambios que devuelve la operación.

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

}

El tamaño en bytes de cualquier registro especificado por una estructura USN_RECORD_V2 o USN_RECORD_V3 es, como máximo ((MaxComponentLength - 1) * Width) + Size , donde MaxComponentLength es la longitud máxima en caracteres del nombre de archivo de registro. El ancho es el tamaño de un carácter ancho y el tamaño es el tamaño de la estructura.

Para obtener la longitud máxima, llame a la función GetVolumeInformation y examine el valor al que apunta el parámetro lpMaximumComponentLength . Resta uno de MaxComponentLength para tener en cuenta el hecho de que la definición de USN_RECORD y USN_RECORD_V3 incluya un carácter del nombre de archivo.

En el lenguaje de programación C, el tamaño de registro más grande posible es el siguiente:

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