亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何僅檢測卷上已刪除,更改和創建的文件?

如何僅檢測卷上已刪除,更改和創建的文件?

C++
當年話下 2019-11-18 10:42:33
我需要知道是否有一種簡單的方法來僅檢測在NTFS卷上已刪除,修改或創建的文件。我已經編寫了一個用C ++進行異地備份的程序。第一次備份后,我檢查每個文件的存檔位以查看是否進行了任何更改,并僅備份已更改的文件。另外,它從VSS快照備份以防止文件鎖定。這似乎在大多數文件系統上都可以正常工作,但是對于某些文件和目錄很多的文件系統,此過程將花費很長時間,并且備份通常需要一天以上的時間才能完成備份。我嘗試使用更改日志來輕松檢測對NTFS卷所做的更改,但是更改日志將顯示很多記錄,其中大多數與創建和銷毀的小型臨時文件有關。另外,我可以使用文件名,文件參考號和父文件參考號,但無法獲取完整的文件路徑。父文件參考號應該以某種方式為您提供父目錄路徑。編輯:這需要每天運行,因此在每次掃描開始時,它應僅記錄自上次掃描以來發生的更改。至少,應該有一種方法可以這樣說,因為時間和日期是這樣。
查看完整描述

3 回答

?
慕斯709654

TA貢獻1840條經驗 獲得超5個贊

您可以使用FSCTL_ENUM_USN_DATA枚舉卷上的所有文件。這是一個快速的過程(即使在非常舊的計算機上,我的測試每秒也能返回6000條記錄,并且更典型的是20000+),并且僅包括當前存在的文件。


返回的數據包括文件標志以及USN,因此您可以根據自己的喜好檢查更改。


您仍然需要通過將父ID與目錄的文件ID匹配來確定文件的完整路徑。一種方法是使用足夠大的緩沖區來同時保存所有文件記錄,并在記錄中搜索以找到需要備份的每個文件的匹配父對象。對于大容量卷,您可能需要將目錄記錄處理為更有效的數據結構,可能是哈希表。


或者,您可以根據需要讀取/重新讀取父目錄的記錄。這會降低效率,但是性能可能仍然令人滿意,具體取決于要備份的文件數。Windows似乎確實緩存了FSCTL_ENUM_USN_DATA返回的數據。


該程序在C卷中搜索名為test.txt的文件,并返回有關找到的任何文件及其父目錄的信息。


#include <Windows.h>


#include <stdio.h>


#define BUFFER_SIZE (1024 * 1024)


HANDLE drive;

USN maxusn;


void show_record (USN_RECORD * record)

{

    void * buffer;

    MFT_ENUM_DATA mft_enum_data;

    DWORD bytecount = 1;

    USN_RECORD * parent_record;


    WCHAR * filename;

    WCHAR * filenameend;


    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);


    filename = (WCHAR *)(((BYTE *)record) + record->FileNameOffset);

    filenameend= (WCHAR *)(((BYTE *)record) + record->FileNameOffset + record->FileNameLength);


    printf("FileName: %.*ls\n", filenameend - filename, filename);


    buffer = VirtualAlloc(NULL, BUFFER_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);


    if (buffer == NULL)

    {

        printf("VirtualAlloc: %u\n", GetLastError());

        return;

    }


    mft_enum_data.StartFileReferenceNumber = record->ParentFileReferenceNumber;

    mft_enum_data.LowUsn = 0;

    mft_enum_data.HighUsn = maxusn;


    if (!DeviceIoControl(drive, FSCTL_ENUM_USN_DATA, &mft_enum_data, sizeof(mft_enum_data), buffer, BUFFER_SIZE, &bytecount, NULL))

    {

        printf("FSCTL_ENUM_USN_DATA (show_record): %u\n", GetLastError());

        return;

    }


    parent_record = (USN_RECORD *)((USN *)buffer + 1);


    if (parent_record->FileReferenceNumber != record->ParentFileReferenceNumber)

    {

        printf("=================================================================\n");

        printf("Couldn't retrieve FileReferenceNumber %u\n", record->ParentFileReferenceNumber);

        return;

    }


    show_record(parent_record);

}


void check_record(USN_RECORD * record)

{

    WCHAR * filename;

    WCHAR * filenameend;


    filename = (WCHAR *)(((BYTE *)record) + record->FileNameOffset);

    filenameend= (WCHAR *)(((BYTE *)record) + record->FileNameOffset + record->FileNameLength);


    if (filenameend - filename != 8) return;


    if (wcsncmp(filename, L"test.txt", 8) != 0) return;


    show_record(record);

}


int main(int argc, char ** argv)

{

    MFT_ENUM_DATA mft_enum_data;

    DWORD bytecount = 1;

    void * buffer;

    USN_RECORD * record;

    USN_RECORD * recordend;

    USN_JOURNAL_DATA * journal;

    DWORDLONG nextid;

    DWORDLONG filecount = 0;

    DWORD starttick, endtick;


    starttick = GetTickCount();


    printf("Allocating memory.\n");


    buffer = VirtualAlloc(NULL, BUFFER_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);


    if (buffer == NULL)

    {

        printf("VirtualAlloc: %u\n", GetLastError());

        return 0;

    }


    printf("Opening volume.\n");


    drive = CreateFile(L"\\\\?\\c:", GENERIC_READ, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_FLAG_NO_BUFFERING, NULL);


    if (drive == INVALID_HANDLE_VALUE)

    {

        printf("CreateFile: %u\n", GetLastError());

        return 0;

    }


    printf("Calling FSCTL_QUERY_USN_JOURNAL\n");


    if (!DeviceIoControl(drive, FSCTL_QUERY_USN_JOURNAL, NULL, 0, buffer, BUFFER_SIZE, &bytecount, NULL))

    {

        printf("FSCTL_QUERY_USN_JOURNAL: %u\n", GetLastError());

        return 0;

    }


    journal = (USN_JOURNAL_DATA *)buffer;


    printf("UsnJournalID: %lu\n", journal->UsnJournalID);

    printf("FirstUsn: %lu\n", journal->FirstUsn);

    printf("NextUsn: %lu\n", journal->NextUsn);

    printf("LowestValidUsn: %lu\n", journal->LowestValidUsn);

    printf("MaxUsn: %lu\n", journal->MaxUsn);

    printf("MaximumSize: %lu\n", journal->MaximumSize);

    printf("AllocationDelta: %lu\n", journal->AllocationDelta);


    maxusn = journal->MaxUsn;


    mft_enum_data.StartFileReferenceNumber = 0;

    mft_enum_data.LowUsn = 0;

    mft_enum_data.HighUsn = maxusn;


    for (;;)

    {

//      printf("=================================================================\n");

//      printf("Calling FSCTL_ENUM_USN_DATA\n");


        if (!DeviceIoControl(drive, FSCTL_ENUM_USN_DATA, &mft_enum_data, sizeof(mft_enum_data), buffer, BUFFER_SIZE, &bytecount, NULL))

        {

            printf("=================================================================\n");

            printf("FSCTL_ENUM_USN_DATA: %u\n", GetLastError());

            printf("Final ID: %lu\n", nextid);

            printf("File count: %lu\n", filecount);

            endtick = GetTickCount();

            printf("Ticks: %u\n", endtick - starttick);

            return 0;

        }


//      printf("Bytes returned: %u\n", bytecount);


        nextid = *((DWORDLONG *)buffer);

//      printf("Next ID: %lu\n", nextid);


        record = (USN_RECORD *)((USN *)buffer + 1);

        recordend = (USN_RECORD *)(((BYTE *)buffer) + bytecount);


        while (record < recordend)

        {

            filecount++;

            check_record(record);

            record = (USN_RECORD *)(((BYTE *)record) + record->RecordLength);

        }


        mft_enum_data.StartFileReferenceNumber = nextid;

    }

}

補充筆記


如評論中所述,您可能需要在Windows 7以后的Windows版本上替換MFT_ENUM_DATA為MFT_ENUM_DATA_V0(這也取決于您使用的編譯器和SDK。)


我正在打印64位文件參考號,就好像它們是32位一樣。那只是我的一個錯誤。也許在生產代碼中您無論如何都不會打印它們,而是供您參考。


查看完整回答
反對 回復 2019-11-18
?
慕村9548890

TA貢獻1884條經驗 獲得超4個贊

零錢日記是您最好的選擇。您可以使用文件參考號來匹配文件創建/刪除對,從而忽略臨時文件,而無需進一步處理它們。

我認為您必須掃描主文件表才能理解ParentFileReferenceNumber。當然,您在執行此操作時只需要跟蹤目錄,并使用可以快速查找信息的數據結構,因此只需要掃描一次MFT。


查看完整回答
反對 回復 2019-11-18
  • 3 回答
  • 0 關注
  • 665 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號