Win32 Windows Volume Program and Code Example 12

 

 

 

 

 

Walking a Buffer of Change Journal Records

 

The control codes that return change journal records, FSCTL_READ_USN_JOURNAL and FSCTL_ENUM_USN_DATA, return similar data in the output buffer. Both return a USN followed by 0 (zero) or more change journal records, each in a USN_RECORD structure. The following list identifies ways to get change journal records:

 

  1. Use FSCTL_ENUM_USN_DATA to get a listing (enumeration) of all change journal records between two USNs.
  2. Use FSCTL_READ_USN_JOURNAL to be more selective, such as selecting specific reasons for changes or returning when a file is closed.

 

Both of these operations return only the subset of change journal records that meet the specified criteria. The USN returned as the first item in the output buffer is the USN of the next record number to be retrieved. Use this value to continue reading records from the end boundary forward. The FileName member of USN_RECORD contains the name of the file to which the record in question applies. The file name varies in length, so USN_RECORD is a variable length structure. Its first member, RecordLength, is the length of the structure (including the file name), in bytes. When you work with the FileName member of USN_RECORD, do not assume that the file name contains a trailing '\0' delimiter. To determine the length of the file name, use the FileNameLength member.

 

Walking a Buffer of Change Journal Records Program Example

 

The following example calls FSCTL_READ_USN_JOURNAL and walks the buffer of change journal records that the operation returns. To compile an application that uses this function, you may need to define the _WIN32_WINNT macro as 0x0500 (Refer to Using the Windows Headers).

Create a new Win32 console application project and give a suitable project name.

 

Walking a Buffer of Change Journal Records Program Example - creating a new Win32 console application

 

Add the source file and give a suitable name.

 

Walking a Buffer of Change Journal Records Program Example - adding a new C++ source code using Add New Item menu

 

Add the following source code.

 

#include <Windows.h>

#include <WinIoCtl.h>

#include <stdio.h>

 

#define BUF_LEN 4096

 

// Format the Win32 system error code to string

void ErrorMessage(DWORD dwCode);

 

int wmain(int argc, WCHAR **argv)

{

   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( L"\\\\.\\C:",

               GENERIC_READ | GENERIC_WRITE,

               FILE_SHARE_READ | FILE_SHARE_WRITE,

               NULL,

               OPEN_EXISTING,

               0,

               NULL);

 

   if(hVol == INVALID_HANDLE_VALUE)

   {

      wprintf(L"CreateFile() failed\n");

      ErrorMessage(GetLastError());

      return 1;

   }

 

   wprintf(L"CreateFile() is OK!\n");

 

   if(!DeviceIoControl(hVol,

          FSCTL_QUERY_USN_JOURNAL,

          NULL,

          0,

          &JournalData,

          sizeof(JournalData),

          &dwBytes,

          NULL))

   {

      wprintf(L"DeviceIoControl() - Query journal failed\n");

      ErrorMessage(GetLastError());

      return 1;

   }

 

   ReadData.UsnJournalID = JournalData.UsnJournalID;

 

   wprintf(L"Journal ID: %I64x\n", JournalData.UsnJournalID );

   wprintf(L"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))

      {

         wprintf(L"DeviceIoControl()- Read journal failed\n");

         ErrorMessage(GetLastError());

         return 1;

      }

 

        wprintf(L"DeviceIoControl() is OK!\n");

 

      dwRetBytes = dwBytes - sizeof(USN);

 

      // Find the first record

      UsnRecord = (PUSN_RECORD)(((PUCHAR)Buffer) + sizeof(USN)); 

 

      wprintf(L"****************************************\n");

 

      // This loop could go on for a long time, given the current buffer size.

      while(dwRetBytes > 0)

      {

         wprintf(L"USN: %I64x\n", UsnRecord->Usn );

         wprintf(L"File name: %.*S\n", UsnRecord->FileNameLength/2,UsnRecord->FileName );

         wprintf(L"Reason: %x\n", UsnRecord->Reason );

         wprintf(L"\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;

   }

 

   if(CloseHandle(hVol) != 0)

         wprintf(L"CloseHandle() is OK!\n");

   else

   {

         wprintf(L"CloseHandle() failed\n");

         ErrorMessage(GetLastError());

   }

   return 0;

}

 

void ErrorMessage(DWORD dwCode)

{

    // get the error code...

    DWORD dwErrCode = dwCode;

    DWORD dwNumChar;

 

    LPWSTR szErrString = NULL;  // will be allocated and filled by FormatMessage

 

    dwNumChar = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |

                 FORMAT_MESSAGE_FROM_SYSTEM, // use windows internal message table

                 0,       // 0 since source is internal message table

                 dwErrCode, // this is the error code number

                 0,       // auto-determine language to use

                 (LPWSTR)&szErrString, // the message

                 0,                 // min size for buffer

                 0 );               // since getting message from system tables

      if(dwNumChar == 0)

            wprintf(L"FormatMessage() failed, error %u\n", GetLastError());

      //else

      //    wprintf(L"FormatMessage() should be fine!\n");

 

     wprintf(L"Error code %u:\n  %s\n", dwErrCode, szErrString) ;

 

    // This buffer used by FormatMessage()

    if(LocalFree(szErrString) != NULL)

            wprintf(L"Failed to free up the buffer, error %u\n", GetLastError());

    //else

    //    wprintf(L"Buffer has been freed\n");

 

  }

 

Build and run the project. The following screenshot is an output sample and there is no active journal in the system where the program was run.

 

Walking a Buffer of Change Journal Records Program Example - a sample output on the machine that the Journal is not active

 

The size in bytes of any record specified by a USN_RECORD structure is at most ((MaxComponentLength - 1) * Width) + Size where MaxComponentLength is the maximum length in characters of the record file name. The width is the size of a wide character, and the Size is the size of the structure. To obtain the maximum length, call the GetVolumeInformation() function and examine the value pointed to by the lpMaximumComponentLength parameter. Subtract one from MaxComponentLength to account for the fact that the definition of USN_RECORD includes one character of the file name. In the C programming language, the largest possible record size is the following:

 

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

 

 

 

  < Windows Volume 11 | Win32 Programming Index | Windows Volume Index | Windows Volume 13 >