windows - USN NFTS 变化通知事件中断

标签 windows ntfs usn

我正在尝试找到一种方法,让系统在 USN Change Journal 中有新条目时告诉我,以跟踪对文件和目录所做的修改NTFS 卷(服务器 2008/2012)。

这样我就不必不断地轮询日志,只需让我的线程休眠,直到我在有新的更改事件时收到通知。

但是,有没有这样的中断呢?

FSCTL_QUERY_USN_JOURNAL 函数没有具体提及中断(事件、通知),我也无法找到另一种使用不太密集的轮询和比较技术来实现此目的的方法。

我不是铁杆程序员,所以可能有更简单的方法将这些函数与我不知道的中断联系起来。

我能否找出 USN 变更日志的存储位置,并使用另一个可以生成并在变更时中断的进程查看该文件?

https://msdn.microsoft.com/en-us/library/aa365729(v=vs.85).aspx

最佳答案

此处发布的代码会阻塞执行线程,直到在日志中创建新的 USN 记录。当新记录到达时,线程被唤醒,您可以处理更改和/或通过文件系统已更改的回调通知监听器(在示例中它只是将消息打印到控制台)。然后线程再次阻塞。此示例对每个卷使用一个线程(因此对于每个卷,需要单独的 NTFSChangesWatcher 类实例)。

没有具体说明你用的是什么工具和语言,我就照着写吧。要运行此代码,请创建一个 Visual Studio C++ Win32 控制台应用程序。 创建 NTFSChangesWatcher 类。将此代码粘贴到 NTFSChangesWatcher.h 文件中(替换自动生成的代码):

#pragma once

#include <windows.h>
#include <memory>

class NTFSChangesWatcher
{
public:
    NTFSChangesWatcher(char drive_letter);
    ~NTFSChangesWatcher() = default;

    // Method which runs an infinite loop and waits for new update sequence number in a journal.
    // The thread is blocked till the new USN record created in the journal.
    void WatchChanges();

private:
    HANDLE OpenVolume(char drive_letter);

    bool CreateJournal(HANDLE volume);

    bool LoadJournal(HANDLE volume, USN_JOURNAL_DATA* journal_data);

    bool NTFSChangesWatcher::WaitForNextUsn(PREAD_USN_JOURNAL_DATA read_journal_data) const;

    std::unique_ptr<READ_USN_JOURNAL_DATA> GetWaitForNextUsnQuery(USN start_usn);

    bool NTFSChangesWatcher::ReadJournalRecords(PREAD_USN_JOURNAL_DATA journal_query, LPVOID buffer,
        DWORD& byte_count) const;

    std::unique_ptr<READ_USN_JOURNAL_DATA> NTFSChangesWatcher::GetReadJournalQuery(USN low_usn);


    char drive_letter_;

    HANDLE volume_;

    std::unique_ptr<USN_JOURNAL_DATA> journal_;

    DWORDLONG journal_id_;

    USN last_usn_;

    // Flags, which indicate which types of changes you want to listen.
    static const int FILE_CHANGE_BITMASK;

    static const int kBufferSize;
};

NTFSChangesWatcher.cpp 文件中的这段代码:

#include "NTFSChangesWatcher.h"

#include <iostream>

using namespace std;

const int NTFSChangesWatcher::kBufferSize = 1024 * 1024 / 2;

const int NTFSChangesWatcher::FILE_CHANGE_BITMASK =
   USN_REASON_RENAME_NEW_NAME | USN_REASON_SECURITY_CHANGE | USN_REASON_BASIC_INFO_CHANGE | USN_REASON_DATA_OVERWRITE |
   USN_REASON_DATA_TRUNCATION | USN_REASON_DATA_EXTEND | USN_REASON_CLOSE;


NTFSChangesWatcher::NTFSChangesWatcher(char drive_letter) :
    drive_letter_(drive_letter)
{
    volume_ = OpenVolume(drive_letter_);

    journal_ = make_unique<USN_JOURNAL_DATA>();

    bool res = LoadJournal(volume_, journal_.get());

    if (!res) {
        cout << "Failed to load journal" << endl;
        return;
    }

    journal_id_ = journal_->UsnJournalID;
    last_usn_ = journal_->NextUsn;
}

HANDLE NTFSChangesWatcher::OpenVolume(char drive_letter) {

wchar_t pattern[10] = L"\\\\?\\a:";

pattern[4] = static_cast<wchar_t>(drive_letter);
HANDLE volume = nullptr;

volume = CreateFile(
    pattern,  // lpFileName
    // also could be | FILE_READ_DATA | FILE_READ_ATTRIBUTES | SYNCHRONIZE
    GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,              // dwDesiredAccess
    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,  // share mode
    NULL,                                                    // default security attributes
    OPEN_EXISTING,                                           // disposition
    // It is always set, no matter whether you explicitly specify it or not. This means, that access
    // must be aligned with sector size so we can only read a number of bytes that is a multiple of the sector size.
    FILE_FLAG_NO_BUFFERING,  // file attributes
    NULL                     // do not copy file attributes
    );

    if (volume == INVALID_HANDLE_VALUE) {
        // An error occurred!
        cout << "Failed to open volume" << endl;
        return nullptr;
    }

    return volume;
}


bool NTFSChangesWatcher::CreateJournal(HANDLE volume) {

    DWORD byte_count;
    CREATE_USN_JOURNAL_DATA create_journal_data;

    bool ok = DeviceIoControl(volume, // handle to volume
        FSCTL_CREATE_USN_JOURNAL,     // dwIoControlCode
        &create_journal_data,         // input buffer
        sizeof(create_journal_data),  // size of input buffer
        NULL,                         // lpOutBuffer
        0,                            // nOutBufferSize
        &byte_count,                  // number of bytes returned
        NULL) != 0;                   // OVERLAPPED structure

    if (!ok) {
        // An error occurred!
    }

    return ok;
}


bool NTFSChangesWatcher::LoadJournal(HANDLE volume, USN_JOURNAL_DATA* journal_data) {

    DWORD byte_count;

    // Try to open journal.
    if (!DeviceIoControl(volume, FSCTL_QUERY_USN_JOURNAL, NULL, 0, journal_data, sizeof(*journal_data), &byte_count,
        NULL)) {

        // If failed (for example, in case journaling is disabled), create journal and retry.

        if (CreateJournal(volume)) {
            return LoadJournal(volume, journal_data);
        }

        return false;
    }

    return true;
}

void NTFSChangesWatcher::WatchChanges() {

    auto u_buffer = make_unique<char[]>(kBufferSize);

    auto read_journal_query = GetWaitForNextUsnQuery(last_usn_);

    while (true) {

        // This function does not return until new USN record created.
        WaitForNextUsn(read_journal_query.get());

        cout << "New entry created in the journal!" << endl;

        auto journal_query = GetReadJournalQuery(read_journal_query->StartUsn);

        DWORD byte_count;
        if (!ReadJournalRecords(journal_query.get(), u_buffer.get(), byte_count)) {
            // An error occurred.
            cout << "Failed to read journal records" << endl;
        }

        last_usn_ = *(USN*)u_buffer.get();
        read_journal_query->StartUsn = last_usn_;

        // If you need here you can:
        // Read and parse Journal records from the buffer.
        // Notify an NTFSChangeObservers about journal changes.
    }
}

bool NTFSChangesWatcher::WaitForNextUsn(PREAD_USN_JOURNAL_DATA read_journal_data) const {

    DWORD bytes_read;
    bool ok = true;

    // This function does not return until new USN record created.
    ok = DeviceIoControl(volume_, FSCTL_READ_USN_JOURNAL, read_journal_data, sizeof(*read_journal_data),
        &read_journal_data->StartUsn, sizeof(read_journal_data->StartUsn), &bytes_read,
        nullptr) != 0;

    return ok;
   }

   unique_ptr<READ_USN_JOURNAL_DATA> NTFSChangesWatcher::GetWaitForNextUsnQuery(USN start_usn) {

    auto query = make_unique<READ_USN_JOURNAL_DATA>();

    query->StartUsn = start_usn;
    query->ReasonMask = 0xFFFFFFFF;     // All bits.
    query->ReturnOnlyOnClose = FALSE;   // All entries.
    query->Timeout = 0;                 // No timeout.
    query->BytesToWaitFor = 1;          // Wait for this.
    query->UsnJournalID = journal_id_;  // The journal.
    query->MinMajorVersion = 2;
    query->MaxMajorVersion = 2;

    return query;
}


bool NTFSChangesWatcher::ReadJournalRecords(PREAD_USN_JOURNAL_DATA journal_query, LPVOID buffer,
    DWORD& byte_count) const {

    return DeviceIoControl(volume_, FSCTL_READ_USN_JOURNAL, journal_query, sizeof(*journal_query), buffer, kBufferSize,
        &byte_count, nullptr) != 0;
}

unique_ptr<READ_USN_JOURNAL_DATA> NTFSChangesWatcher::GetReadJournalQuery(USN low_usn) {

    auto query = make_unique<READ_USN_JOURNAL_DATA>();

    query->StartUsn = low_usn;
    query->ReasonMask = 0xFFFFFFFF;  // All bits.
    query->ReturnOnlyOnClose = FALSE;
    query->Timeout = 0;  // No timeout.
    query->BytesToWaitFor = 0;
    query->UsnJournalID = journal_id_;
    query->MinMajorVersion = 2;
    query->MaxMajorVersion = 2;

    return query;
}

现在您可以使用它了(例如在用于测试的 main 函数中):

#include "NTFSChangesWatcher.h"

int _tmain(int argc, _TCHAR* argv[])
{
    auto watcher = new NTFSChangesWatcher('z');
    watcher->WatchChanges();
    return 0;
}

每次文件系统发生变化时,控制台输出应该是这样的:

enter image description here

此代码经过轻微修改以删除不相关的细节,并且是 Indexer++ 的一部分项目。所以更多的细节可以引用original code .

关于windows - USN NFTS 变化通知事件中断,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38885286/

相关文章:

windows - Windows 7 上的 Cygwin 死于等待 dll 加载

windows - 如何强制 Robocopy 覆盖文件?

c++ - 第二次运行应用程序时文件加载速度较慢,带有重现代码

windows - 估计 NTFS 卷上的 USN 记录数

windows - NTFS 文件系统的 USN Journal 是否可以大于其声明的大小?

java - install4j 生成的可执行文件不启动

c++ - 为什么 Qt .pro 文件会为平台提供错误,即使未选择或提及它也是如此?

c# - 如何设置我的服务器 exe 继续运行

python - 如何在 Linux 上使用 python 检查 NTFS 分区上的隐藏文件和文件夹?