ホームページ >バックエンド開発 >C++ >FSCTL_ENUM_USN_DATA 関数を使用して、NTFS ボリューム上のファイル変更を効率的に検出するにはどうすればよいですか?

FSCTL_ENUM_USN_DATA 関数を使用して、NTFS ボリューム上のファイル変更を効率的に検出するにはどうすればよいですか?

DDD
DDDオリジナル
2024-10-29 08:14:30611ブラウズ

How can I efficiently detect file changes on an NTFS volume using the FSCTL_ENUM_USN_DATA function?

ボリューム上の変更の検出: 詳細なソリューション

NTFS ボリューム上のファイルの削除、変更、および作成を効果的に検出するには、次のことができます。 FSCTL_ENUM_USN_DATA 関数を利用します。このアプローチにはいくつかの利点があります。

  • 高速列挙: ボリュームを効率的にスキャンし、1 秒あたり 6000 レコードを超えるパフォーマンスで既存のファイルのみを取得します。
  • 詳細情報: ファイル フラグや USN を含む包括的なデータを提供し、正確な変更検出方法を可能にします。
  • 階層ファイル データ: 親 ID とファイル ID を照合することで、検出された各ファイルの完全なファイル パスを再構築できます。

実装手順:

  1. ファイルの列挙: FSCTL_ENUM_USN_DATA を使用します。既存のすべてのファイルのレコードを取得します。
  2. 変更の識別: ファイル フラグと USN を分析して、どのファイルが変更、作成、または削除されたかを判断します。
  3. ファイル パスの再構築: 親 ID をファイル ID と照合して、影響を受けるファイルのフル パスを取得します。

このアプローチを示す C プログラムの例を以下に示します。「test」という名前のファイルを検索します。 .txt" とその変更内容と親ディレクトリに関する情報を表示します:

<code class="c++">#include <Windows.h>
#include <stdio.h>

#define BUFFER_SIZE (1024 * 1024)

int main() {
  HANDLE drive = CreateFileW(L"\\?\c:", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_FLAG_NO_BUFFERING, NULL);

  MFT_ENUM_DATA mft_enum_data;
  USN maxusn;
  USN_RECORD *record;

  // Query USN journal for information
  if (DeviceIoControl(drive, FSCTL_QUERY_USN_JOURNAL, NULL, 0, &maxusn, sizeof(USN), NULL, NULL)) {
    mft_enum_data.StartFileReferenceNumber = 0;
    mft_enum_data.LowUsn = 0;
    mft_enum_data.HighUsn = maxusn;
    
    DWORDLONG nextid, filecount = 0;

    for (;;) {
      void *buffer = VirtualAlloc(NULL, BUFFER_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

      if (DeviceIoControl(drive, FSCTL_ENUM_USN_DATA, &mft_enum_data, sizeof(mft_enum_data), buffer, BUFFER_SIZE, NULL, NULL)) {
        nextid = *((DWORDLONG *)buffer);

        record = (USN_RECORD *)((USN *)buffer + 1);
        while (record < (USN_RECORD *)(((BYTE *)buffer) + BUFFER_SIZE)) {
          filecount++;
          WCHAR *filename = (WCHAR *)(((BYTE *)record) + record->FileNameOffset);
          if (wcsncmp(filename, L"test.txt", 8) == 0) {
            printf("=================================================================\n");
            printf("RecordLength: %u\n", record->RecordLength);
            printf("MajorVersion: %u\n", (DWORD)record->MajorVersion);
            printf("MinorVersion: %u\n", (DWORD)record->MinorVersion);
            printf("FileReferenceNumber: %lu\n", record->FileReferenceNumber);
            printf("ParentFRN: %lu\n", record->ParentFileReferenceNumber);
            printf("USN: %lu\n", record->Usn);
            printf("Timestamp: %lu\n", record->TimeStamp);
            printf("Reason: %u\n", record->Reason);
            printf("SourceInfo: %u\n", record->SourceInfo);
            printf("SecurityId: %u\n", record->SecurityId);
            printf("FileAttributes: %x\n", record->FileAttributes);
            printf("FileNameLength: %u\n", (DWORD)record->FileNameLength);
            printf("FileName: %.*ls\n", record->FileNameLength, filename);
            
            // Reconstruct file path by matching parent file reference numbers
            DWORD bytecount;
            if (DeviceIoControl(drive, FSCTL_ENUM_USN_DATA, &mft_enum_data, sizeof(mft_enum_data), buffer, BUFFER_SIZE, &bytecount, NULL)) {
              USN_RECORD *parent_record = (USN_RECORD *)((USN *)buffer + 1);
              if (parent_record->FileReferenceNumber == record->ParentFileReferenceNumber) {
                printf("Parent File:\n");
                printf("=================================================================\n");
                printf("FileName: %.*ls\n", parent_record->FileNameLength, (WCHAR *)(((BYTE *)parent_record) + parent_record->FileNameOffset));
              }
            }
          }
          record = (USN_RECORD *)(((BYTE *)record) + record->RecordLength);
        }
        mft_enum_data.StartFileReferenceNumber = nextid;
      } else {
        printf("FSCTL_ENUM_USN_DATA failed\n");
        break;
      }
      if (nextid == 0) break;
    }
    printf("Total Files: %lu\n", filecount);
  } else {
    printf("FSCTL_QUERY_USN_JOURNAL failed\n");
  }

  if (drive != INVALID_HANDLE_VALUE)
    CloseHandle(drive);

  return 0;
}</code>

以上がFSCTL_ENUM_USN_DATA 関数を使用して、NTFS ボリューム上のファイル変更を効率的に検出するにはどうすればよいですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。