Partager via


Découverte d'un tampon d'enregistrements du journal des modifications

Les codes de contrôle qui renvoient le numéro de séquence de mise à jour (USN) modifient les enregistrements de journal, FSCTL_READ_USN_JOURNAL and FSCTL_ENUM_USN_DATA, renvoie des données similaires dans le tampon de sortie. Les deux renvoient un USN suivi ou non de plusieurs enregistrements de journal de modification, chacun dans une structure USN_RECORD_V2 ou USN_RECORD_V3.

Le volume cible pour les opérations USN doit être ReFS ou NTFS 3.0 ou version ultérieure. Pour obtenir la version NTFS d’un volume, ouvrez une invite de commandes avec les droits d’accès administrateur, puis exécutez la commande suivante :

FSUtil.exe FSInfo NTFSInfo X**:**

où X X correspond à la lettre du lecteur.

La liste suivante identifie les façons d’obtenir les enregistrements de journal des modifications :

  • Utilisez FSCTL_ENUM_USN_DATA pour obtenir une liste (énumération) de tous les enregistrements de journal de modification entre deux USN.
  • Utilisez FSCTL_READ_USN_JOURNAL pour être plus sélectif, comme la sélection de raisons spécifiques pour les modifications ou le retour lorsqu'un dossier est clôturé.

Remarque

Ces deux opérations renvoient uniquement le sous-ensemble d’enregistrements de journal de modification qui répondent aux critères indiqués.

 

L'USN renvoyé en tant que premier élément de la mémoire tampon de sortie est l'USN du numéro d'enregistrement suivant à récupérer. Cette valeur permet de poursuivre la lecture des enregistrements à partir de la frontière finale.

Le membre FileName de USN_RECORD_V2 or USN_RECORD_V3 contient le nom du dossier auquel s'applique l'enregistrement en question. Le nom de fichier varie en longueur : USN_RECORD_V2 et USN_RECORD_V3 sont des structures de longueur variable. Leur premier membre, RecordLength, correspond à la longueur de la structure (y compris le nom de fichier), en octets.

Lorsque vous travaillez avec le membre FileName des structures USN_RECORD_V2 et USN_RECORD_V3 ne partez pas du principe que le nom du fichier contient un délimiteur « \0 » à la fin. Pour déterminer la longueur du nom de fichier, utilisez le membre FileNameLength.

L’exemple suivant appelle FSCTL_READ_USN_JOURNAL et parcourt la mémoire tampon des enregistrements du journal des modifications que l'opération renvoie.

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

}

La taille en octet d'un enregistrement spécifié par une structure USN_RECORD_V2 ou USN_RECORD_V3 est la plus importante ((MaxComponentLength - 1) * Width) + Size lorsque MaxComponentLength atteint le nombre maximum de caractères pour le nom du fichier d'enregistrement. La largeur est la taille d’un caractère large, et la taille est la taille de la structure.

Pour obtenir la longueur maximale, appelez la fonction GetVolumeInformation, puis consultez la valeur indiquée par le paramètre lpMaximumComponentLength. Retranchez 1 à MaxComponentLength pour prendre en compte le fait que la définition de USN_RECORD et de USN_RECORD_V3 inclut un caractère du nom de fichier.

Dans le langage de programmation C, la plus grande taille d’enregistrement possible est la suivante :

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