delphidelphi-xe2ntfsntfs-mft

Implement Change Journal in Delphi (Step 2)


Continuing my previous question, I was able to use this delphi unit to implement crazy FAST drive scan using this EnumMFTEntries() function, but I have trouble doing the following tasks:

Task #1: Query the change journal to get the newly modified files (changed, renamed, deleted, etc...)

I adapted this broken routine after reading both the EnumMFTEntries() function as well as StCroixSkipper's USN Journal Explorer (in C#).

For some reason reported file names are always #0

function EnumUsnEntries(ARootHandle : Cardinal; AMFTEnumBuff : Pointer; EnumCallBack : TMFTEnumCallback) : Boolean;
var
   P         : Pointer;
   UsnEnum   : Pointer;
   BytesRet  : Cardinal;

   PUSN           : PUSNRecord;
   ReadUSN        : TReadUSNJournalData;
   PReadUSN       : PReadUSNJournalData;
   UsnJournalData : TUSNJournalData;
   BUF_SIZE       : Integer;
begin
     Result            := False;
     if (ARootHandle = INVALID_HANDLE_VALUE) OR (AMFTEnumBuff = nil) then
        Exit;

     QueryUSNJournal(ARootHandle, UsnJournalData);
     with ReadUSN do
     begin
          StartUsn          := UsnJournalData.NextUsn;
          ReasonMask        := USN_REASON_DATA_OVERWRITE OR USN_REASON_DATA_EXTEND OR USN_REASON_NAMED_DATA_OVERWRITE OR USN_REASON_NAMED_DATA_TRUNCATION OR
                               USN_REASON_FILE_CREATE OR USN_REASON_FILE_DELETE OR USN_REASON_EA_CHANGE OR USN_REASON_SECURITY_CHANGE OR
                               USN_REASON_RENAME_OLD_NAME OR USN_REASON_RENAME_NEW_NAME OR USN_REASON_INDEXABLE_CHANGE OR USN_REASON_BASIC_INFO_CHANGE OR
                               USN_REASON_HARD_LINK_CHANGE OR USN_REASON_COMPRESSION_CHANGE OR USN_REASON_ENCRYPTION_CHANGE OR
                               USN_REASON_OBJECT_ID_CHANGE OR USN_REASON_REPARSE_POINT_CHANGE OR USN_REASON_STREAM_CHANGE OR USN_REASON_CLOSE;
          ReturnOnlyOnClose := 0;
          Timeout           := 0;
          BytesToWaitFor    := 0;
          UsnJournalID      := UsnJournalData.UsnJournalID;
     end;    // with

     BUF_SIZE          := SizeOf(ReadUSN);
     GetMem(P, BUF_SIZE);
     try
        ZeroMemory(P, BUF_SIZE);
        while DeviceIoControl(ARootHandle, FSCTL_READ_USN_JOURNAL, Addr(ReadUSN){UsnEnum}, SizeOf(ReadUSN{TReadUSNJournalData}), P, BUF_SIZE, BytesRet, nil) do
        begin
             PUSN      := PUSNRecord{PReadUSNJournalData}(Integer(P) + SizeOf(Int64));
             while (BytesRet > 0{60}) do
             begin
                  if Not EnumCallBack(PUSN, nil{Extra}) then
                     Exit;

                  if PUSN.RecordLength > 0 then
                     Dec(BytesRet, PUSN.RecordLength)
                  else
                      Break;
                  PUSN := PUSNRecord(Cardinal(PUSN) + PUSN.RecordLength);
             end;    // while
             CopyMemory(UsnEnum{MFTEnum}, P, SizeOf(Int64));
        end;    // while

        Result         := True;
     finally
            FreeMem(P);
     end;    // try/finally
end;

Task #2: Restrict scan to a certain folder?

If I'm not mistaken, it seems to be possible to partially do so by defining the StartUsn in the EnumMFTEntries() function, but it's not clear how to do that.

Task #3: Get the full path of the file name?

For example, the EnumMFTEntries() always return the name only, along with its parent folder reference number, it's not clear what's the fastest way to get the full path.


I hope I'm not asking too much, these tasks are really closely related, I really hope the community will help here to make easy for Delphi developers achieve crazy fast folder scan. Despite its usefulness, change journal / MFT is one of the most interesting-yet-forgotten technologies. This has to change!


Solution

  • On task #2: you can restrict usn data search to a particular file/dir by first emitting FSCTL_READ_FILE_USN_DATA to obtain usn number of last change and then FSTCL_READ_USN_JOURNAL with StartUsn member assigned to that usn number to get valid info on the change (timrstamp, reason). However, only the last change can be obtained this way. To get all changes, I think it is only possible to filter read usn journal data by a particular file reference number. More info in MSDN Library.