4


My goal is to read write operations from a chosen drive (usually C), using USN journal. In the next code I've written, I made a small class that processes USN records by using DeviceIoControl
with the FSCTL_QUERY_USN_JOURNAL and FSCTL_ENUM_USN_DATA codes.

#include "stdafx.h"
#include <stdio.h> 
#include <assert.h>
#include <vector>
#include <system_error>
#include <Windows.h>

[[noreturn]] void throw_system_error(int error_code) {
    throw std::system_error(error_code, std::system_category());
}

class usn_journal {

private:
    HANDLE m_drive_handle;
    std::vector<uint8_t> m_buffer;
    USN_JOURNAL_DATA* m_usn_journal_data;
    USN m_next_usn_record_id;

public:
    usn_journal(const wchar_t* driver_name) {
        m_next_usn_record_id = 0;
        m_drive_handle = ::CreateFileW(
            driver_name,
            GENERIC_READ,
            FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
            nullptr,
            OPEN_ALWAYS,
            FILE_FLAG_NO_BUFFERING,
            nullptr);

        if (m_drive_handle == INVALID_HANDLE_VALUE) {
            throw_system_error(::GetLastError());
        }

        m_buffer.resize(1024 * 1024);
    }

    ~usn_journal() {
        ::CloseHandle(m_drive_handle);
    }

    void refresh_jounral() {
        assert(m_buffer.size() == 1024 * 1024);

        DWORD buffer_count = 0;
        if (!DeviceIoControl(
            m_drive_handle,
            FSCTL_QUERY_USN_JOURNAL,
            nullptr,
            0,
            m_buffer.data(),
            m_buffer.size(),
            &buffer_count,
            nullptr)) {
            throw_system_error(::GetLastError());
        }

        m_usn_journal_data =
            reinterpret_cast<decltype(m_usn_journal_data)>(m_buffer.data());
    }

    void process_entries() {
        DWORD bytes_read = 0;
        MFT_ENUM_DATA_V0 mft_enum_data = {};
        mft_enum_data.StartFileReferenceNumber = m_next_usn_record_id;
        mft_enum_data.LowUsn = 0;
        mft_enum_data.HighUsn = m_usn_journal_data->MaxUsn;

        assert(m_buffer.size() == 1024 * 1024);

        for (;;){       
            auto buffer = m_buffer.data();
            if (!DeviceIoControl(
                m_drive_handle,
                FSCTL_ENUM_USN_DATA,
                &mft_enum_data,
                sizeof(mft_enum_data),
                buffer,
                m_buffer.size(),
                &bytes_read,
                nullptr)){

                auto error_code = ::GetLastError();
                if (error_code == ERROR_HANDLE_EOF) {
                    return;
                }
                else {
                    throw_system_error(::GetLastError());
                }
            }

            m_next_usn_record_id = *reinterpret_cast<USN*>(buffer); 
            auto buffer_real_begin = buffer + sizeof(USN);
            auto usn_cursor = reinterpret_cast<USN_RECORD*>(buffer_real_begin);
            int64_t total_usn_buffer_number = bytes_read - sizeof(USN);

            while (total_usn_buffer_number >= 0){
                total_usn_buffer_number -= usn_cursor->RecordLength;
                buffer = reinterpret_cast<uint8_t*>(usn_cursor) + usn_cursor->RecordLength;
                usn_cursor = reinterpret_cast<USN_RECORD*>(usn_cursor);
                if (usn_cursor->Reason != 0) {
                    printf("%d\n", (int)usn_cursor->Reason);
                }
            }

            mft_enum_data.StartFileReferenceNumber = m_next_usn_record_id;
        }
    }
};

int main(int argc, char ** argv){
    usn_journal my_journal(L"\\\\?\\c:");
    while (true) {
        my_journal.refresh_jounral();
        my_journal.process_entries();
    }

    return 0;
}

Here is my problem, after a while, the records are exhausted, and calling DeviceIoControl and FSCTL_ENUM_USN_DATA DeviceIoControl fails and the error code I get is ERROR_HANDLE_EOF, even if I refresh the journal, I get the same error.
I want to be able to stream any new USN record, and handle write events. I know for sure it's possible as there are
third party tools which present USN records non-stop.
how can reproduce this state of non-stop streaming?

David Haim
  • 25,446
  • 3
  • 44
  • 78
  • but you not reset to 0 `m_next_usn_record_id`. as result and `ERROR_HANDLE_EOF`. in `process_entries()` function `mft_enum_data.StartFileReferenceNumber = 0;` must be before loop. – RbMm Nov 29 '17 at 16:33
  • read the code again. the constructor sets this variable to zero. this is the first value that `process_entries` uses – David Haim Nov 29 '17 at 16:35
  • yes, it will be zero in first call `process_entries()` but if second time call `process_entries()` this already will be not 0 at begin. and unclear why this variable is member of class but not local variable of function ?currently it used only as local variable. move this variable in class exist sense only if `process_entries()` can break enumeration, and then start it again from the `m_next_usn_record_id` but not from 0 – RbMm Nov 29 '17 at 16:45
  • this is what I did in the beginning. it didn't help. – David Haim Nov 29 '17 at 16:47
  • i not understand code for `refresh_jounral()` - `FSCTL_QUERY_USN_JOURNAL` - out buffer must be `USN_JOURNAL_DATA` - fixed size structure , but you what do here ? – RbMm Nov 29 '17 at 16:49
  • why you not declare `USN_JOURNAL_DATA m_usn_journal_data;` (not as pointer) and simply not pass `&m_usn_journal_data, sizeof(m_usn_journal_data)` to `FSCTL_QUERY_USN_JOURNAL` ? and what is output data ? `MaxUsn` is what ? – RbMm Nov 29 '17 at 16:52
  • It doesn't really matter. the function expects a `void*` with some buffer big enough to hold that struct. it's a C api, so they both POD types. – David Haim Nov 29 '17 at 16:52
  • yes, but for what do this ? when can be much more simply and effective ? and code be less and clear – RbMm Nov 29 '17 at 16:55
  • sure, I agree, but it doesn't solve my problem. – David Haim Nov 29 '17 at 16:56
  • Have you read these docs : https://www.microsoft.com/msj/0999/journal/journal.aspx and https://www.microsoft.com/msj/1099/journal2/journal2.aspx ? – engf-010 Nov 29 '17 at 16:56
  • are `MaxUsn` not 0 at begin ? jornal not empty ? – RbMm Nov 29 '17 at 16:59

0 Answers0