다음을 통해 공유


변경 저널 레코드 버퍼 안내

USN(업데이트 시퀀스 번호) 변경 저널 레코드를 반환하는 제어 코드인 FSCTL_READ_USN_JOURNALFSCTL_ENUM_USN_DATA는 출력 버퍼에서 유사한 데이터를 반환합니다. 둘 다 USN을 반환하고 각각 USN_RECORD_V2 또는 USN_RECORD_V3 구조에서 0개 이상의 변경 저널 레코드를 반환합니다.

USN 작업의 대상 볼륨은 ReFS 또는 NTFS 3.0 이상이어야 합니다. 볼륨의 NTFS 버전을 가져오려면 관리자 액세스 권한으로 명령 프롬프트를 열고 다음 명령을 실행합니다.

FSUtil.exe FSInfo NTFSInfo X**:**

여기서 X는 볼륨의 드라이브 문자입니다.

다음 목록에서는 변경 저널 레코드를 가져오는 방법을 식별합니다.

  • FSCTL_ENUM_USN_DATA를 사용하여 두 USN 간의 모든 변경 저널 레코드의 목록(열거형)을 가져옵니다.
  • FSCTL_READ_USN_JOURNAL을 사용하여 변경에 대한 특정 이유를 선택하거나 파일이 닫힐 때 반환하는 것과 같이 더 선택적으로 사용할 수 있습니다.

참고

이러한 두 작업은 모두 지정된 조건을 충족하는 변경 저널 레코드의 하위 집합만 반환합니다.

 

출력 버퍼의 첫 번째 항목으로 반환된 USN은 검색할 다음 레코드 번호의 USN입니다. 이 값을 사용하여 끝 경계에서 앞으로 레코드를 계속 읽습니다.

USN_RECORD_V2 또는 USN_RECORD_V3FileName 멤버에는 해당 레코드가 적용되는 파일의 이름이 포함됩니다. 파일 이름은 길이가 다르므로 USN_RECORD_V2USN_RECORD_V3는 가변 길이 구조입니다. 첫 번째 멤버인 RecordLength는 바이트 단위의 구조체 길이(파일 이름 포함)입니다.

USN_RECORD_V2USN_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 - 1) * Width) + Size입니다. 여기서 MaxComponentLength는 레코드 파일 이름의 최대 문자 길이입니다. 너비는 와이드 문자의 크기이고 Size는 구조체의 크기입니다.

최대 길이를 얻으려면 GetVolumeInformation 함수를 호출하고 lpMaximumComponentLength 매개 변수가 가리키는 값을 검사합니다. MaxComponentLength에서 하나를 빼서 USN_RECORDUSN_RECORD_V3의 정의에 파일 이름의 한 문자가 포함되어 있다는 사실을 설명합니다.

C 프로그래밍 언어에서 가능한 가장 큰 레코드 크기는 다음과 같습니다.

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