Get full path to file given its "Reference Number"

1

I want to use Windows' change journal to track some file changes. I managed using an exemple code to access the said journal and read records from it. The issue I face now is that the record only holds the related file name, but not the absolute path to it. I tried using FSCTL_GET_NTFS_FILE_RECORD with DeviceIoControl to check if I could get the absolute path this way, but call to DeviceIoControl always returns me the same file record, with "FILE0" in the buffer and the reference number I gave it without the HighPart of it (eg. I give a reference number of 0x001400000015adec, and when function returns the output has a reference number of 0x000000000015adec).

Here is my code (I'm running on Windows 10)

#include <Windows.h>
#include <WinIoCtl.h>
#include <stdio.h>

#define BUF_LEN (1024 * 1024)

void main()
{
    HANDLE hVol;
    CHAR * pBuffer;
    pBuffer = new CHAR[BUF_LEN];

    USN_JOURNAL_DATA JournalData;
    READ_USN_JOURNAL_DATA_V0 ReadData = {0, 0xFFFFFFFF, FALSE, 0, 0};
    PUSN_RECORD UsnRecord;  

    DWORD dwBytes;
    DWORD dwRetBytes;
    DWORD dwFileRecordSize;
    BYTE * pFileRecord;

    hVol = CreateFile( TEXT("\\\\.\\D:"), 
        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 );

    // get single file record size
    NTFS_VOLUME_DATA_BUFFER volBuffer;
    if( !DeviceIoControl( hVol,
        FSCTL_GET_NTFS_VOLUME_DATA,
        NULL,
        0,
        &volBuffer,
        sizeof(volBuffer),
        &dwBytes,
        NULL))
    {
        printf( "Get volume data size failed (%d)\n", GetLastError());
        return;
    }
    dwFileRecordSize = volBuffer.BytesPerFileRecordSegment + sizeof(NTFS_FILE_RECORD_OUTPUT_BUFFER) - 1;
    pFileRecord = new BYTE[dwFileRecordSize];

    bool bEnd = FALSE;
    WORD lastDay = 0;
    WORD lastMonth = 0;
    WORD lastYear = 0;
    DWORD nbEntries = 0;

    while (!bEnd)
    {
        memset( pBuffer, 0, BUF_LEN );

        if( !DeviceIoControl( hVol, 
            FSCTL_READ_USN_JOURNAL, 
            &ReadData,
            sizeof(ReadData),
            pBuffer,
            BUF_LEN,
            &dwBytes,
            NULL) )
        {
            printf( "Read journal failed (%d)\n", GetLastError());
            return;
        }

        dwRetBytes = dwBytes - sizeof(USN);

        bEnd = (dwRetBytes == 0); // we finished reading the journal

        // Find the first record
        UsnRecord = (PUSN_RECORD)(((PUCHAR)pBuffer) + sizeof(USN));  

        // This loop could go on for a long time, given the current buffer size.
        while( dwRetBytes > 0 )
        {
            SYSTEMTIME systemTime;
            FILETIME fileTime;
            fileTime.dwHighDateTime = UsnRecord->TimeStamp.HighPart;
            fileTime.dwLowDateTime = UsnRecord->TimeStamp.LowPart;
            FileTimeToSystemTime(&fileTime, &systemTime);

            if (lastDay != systemTime.wDay || lastMonth != systemTime.wMonth || lastYear != systemTime.wYear)
            {
                if (nbEntries != 0)
                {
                    printf("%u entries read\n", nbEntries);
                }
                nbEntries = 0;
                lastDay = systemTime.wDay;
                lastMonth = systemTime.wMonth;
                lastYear = systemTime.wYear;
                printf("reading entries for %u/%u/%u...\n", lastDay, lastMonth, lastYear);
            }

            ++nbEntries;

            NTFS_FILE_RECORD_INPUT_BUFFER inputBuff;
            memset(pFileRecord, 0, dwFileRecordSize);
            inputBuff.FileReferenceNumber.QuadPart = UsnRecord->FileReferenceNumber;
            if (!DeviceIoControl( hVol,
                FSCTL_GET_NTFS_FILE_RECORD,
                &inputBuff,
                32,
                pFileRecord,
                dwFileRecordSize,
                &dwBytes,
                NULL))
            {
                printf( "get file record failed (%d)\n", GetLastError());
                return;
            }

            NTFS_FILE_RECORD_OUTPUT_BUFFER * pFileRecordBuff = (NTFS_FILE_RECORD_OUTPUT_BUFFER *)(pFileRecord);
            if (pFileRecordBuff->FileReferenceNumber.QuadPart == inputBuff.FileReferenceNumber.QuadPart)
            {
                printf("file record found\n%.*S\n",
                    pFileRecordBuff->FileRecordLength / 2,
                    pFileRecordBuff->FileRecordBuffer);
            }

            dwRetBytes -= UsnRecord->RecordLength;

            // Find the next record
            UsnRecord = (PUSN_RECORD)(((PCHAR)UsnRecord) + 
                UsnRecord->RecordLength); 
        }
        // Update starting USN for next call
        ReadData.StartUsn = *(USN *)pBuffer; 
    }

    delete[] pBuffer;
    delete[] pFileRecord;

    CloseHandle(hVol);

    system("pause");

}

Is there something I'm doing wrong ? Or is there another way to get the full path to a file using an USN_RECORD instance ?

c++
windows
ntfs
deviceiocontrol
asked on Stack Overflow Nov 27, 2018 by axiagame

2 Answers

3

you need first open file handle by call OpenFileById function with exactly FileReferenceNumber from USN_RECORD (you must not zero it high 16 bits !) and then you can query file path by GetFileInformationByHandleEx with FileNameInfo. you got FILE_NAME_INFO on output. or by call NtQueryInformationFile with FileNameInformation. The caller can query this information as long as the file is open, without any particular requirements for DesiredAccess. so code can look like.

ULONG GepPathById(HANDLE hVolumeHint, ULONGLONG FileReferenceNumber, PFILE_NAME_INFO* ppfni)
{
    FILE_ID_DESCRIPTOR fid = { sizeof(fid), FileIdType };
    fid.FileId.QuadPart = FileReferenceNumber;

    HANDLE hFile = OpenFileById(hVolumeHint, &fid, 0, FILE_SHARE_VALID_FLAGS, 0, 0);

    if (hFile == INVALID_HANDLE_VALUE)
    {
        return GetLastError();
    }

    ULONG dwErrorCode = NOERROR;
    ULONG cbNeed = 0x20;
    do 
    {
        if (PFILE_NAME_INFO pfni = (PFILE_NAME_INFO)LocalAlloc(0, cbNeed))
        {
            dwErrorCode = GetFileInformationByHandleEx(hFile, FileNameInfo, 
                pfni, cbNeed - sizeof(WCHAR)) ? 0 : GetLastError();

            if (!dwErrorCode)
            {
                *(PWSTR)((PBYTE)pfni->FileName + pfni->FileNameLength) = 0;
                *ppfni = pfni;
                break;
            }

            cbNeed = FIELD_OFFSET(FILE_NAME_INFO, FileName) + pfni->FileNameLength + sizeof(WCHAR);

            LocalFree(pfni);
        }
        else
        {
            dwErrorCode = GetLastError();
            break;
        }

    } while (dwErrorCode == ERROR_MORE_DATA);

    CloseHandle(hFile);

    return dwErrorCode;
}

and usage

    PFILE_NAME_INFO pfni;
    if (!GepPathById(hVol, UsnRecord->FileReferenceNumber, &pfni))
    {
        DbgPrint("%S\n", pfni->FileName);
        LocalFree(pfni);
    }
answered on Stack Overflow Nov 27, 2018 by RbMm
0
static FILE_ID_DESCRIPTOR getFileIdDescriptor(const DWORDLONG fileId)
{
    FILE_ID_DESCRIPTOR fileDescriptor;
    fileDescriptor.Type = FileIdType;
    fileDescriptor.FileId.QuadPart = fileId;
    fileDescriptor.dwSize = sizeof(fileDescriptor);
    return fileDescriptor;
}


    FILE_ID_DESCRIPTOR f = getFileIdDescriptor(pRecord->FileReferenceNumber);
    TCHAR wFilePath[MAX_PATH];

    HANDLE handle = OpenFileById(hDevice, (LPFILE_ID_DESCRIPTOR) & (f), 0, 0, 0, 0);
    GetFinalPathNameByHandle(handle, wFilePath, MAX_PATH, 0);
    CloseHandle(handle);
answered on Stack Overflow Oct 7, 2020 by Doug Rogers

User contributions licensed under CC BY-SA 3.0