如何仅检测卷上已删除,更改和创建的文件?

我需要知道是否有一种简单的方法来仅检测在NTFS卷上已删除,修改或创建的文件。

我已经编写了一个用C ++进行异地备份的程序。第一次备份后,我检查每个文件的存档位以查看是否进行了任何更改,并仅备份已更改的文件。另外,它从VSS快照备份以防止文件锁定。

这似乎在大多数文件系统上都可以正常工作,但是对于某些文件和目录很多的文件系统,此过程将花费很长时间,并且备份通常需要一天以上的时间才能完成备份。

我尝试使用更改日志来轻松检测对NTFS卷所做的更改,但是更改日志将显示很多记录,其中大多数与创建和销毁的小型临时文件有关。另外,我可以使用文件名,文件参考号和父文件参考号,但无法获取完整的文件路径。父文件参考号应该以某种方式为您提供父目录路径。

编辑:这需要每天运行,因此在每次扫描开始时,它应仅记录自上次扫描以来发生的更改。至少,应该有一种方法可以这样说,因为时间和日期是这样。


当年话下
浏览 623回答 3
3回答

慕斯709654

您可以使用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){&nbsp; &nbsp; void * buffer;&nbsp; &nbsp; MFT_ENUM_DATA mft_enum_data;&nbsp; &nbsp; DWORD bytecount = 1;&nbsp; &nbsp; USN_RECORD * parent_record;&nbsp; &nbsp; WCHAR * filename;&nbsp; &nbsp; WCHAR * filenameend;&nbsp; &nbsp; printf("=================================================================\n");&nbsp; &nbsp; printf("RecordLength: %u\n", record->RecordLength);&nbsp; &nbsp; printf("MajorVersion: %u\n", (DWORD)record->MajorVersion);&nbsp; &nbsp; printf("MinorVersion: %u\n", (DWORD)record->MinorVersion);&nbsp; &nbsp; printf("FileReferenceNumber: %lu\n", record->FileReferenceNumber);&nbsp; &nbsp; printf("ParentFRN: %lu\n", record->ParentFileReferenceNumber);&nbsp; &nbsp; printf("USN: %lu\n", record->Usn);&nbsp; &nbsp; printf("Timestamp: %lu\n", record->TimeStamp);&nbsp; &nbsp; printf("Reason: %u\n", record->Reason);&nbsp; &nbsp; printf("SourceInfo: %u\n", record->SourceInfo);&nbsp; &nbsp; printf("SecurityId: %u\n", record->SecurityId);&nbsp; &nbsp; printf("FileAttributes: %x\n", record->FileAttributes);&nbsp; &nbsp; printf("FileNameLength: %u\n", (DWORD)record->FileNameLength);&nbsp; &nbsp; filename = (WCHAR *)(((BYTE *)record) + record->FileNameOffset);&nbsp; &nbsp; filenameend= (WCHAR *)(((BYTE *)record) + record->FileNameOffset + record->FileNameLength);&nbsp; &nbsp; printf("FileName: %.*ls\n", filenameend - filename, filename);&nbsp; &nbsp; buffer = VirtualAlloc(NULL, BUFFER_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);&nbsp; &nbsp; if (buffer == NULL)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; printf("VirtualAlloc: %u\n", GetLastError());&nbsp; &nbsp; &nbsp; &nbsp; return;&nbsp; &nbsp; }&nbsp; &nbsp; mft_enum_data.StartFileReferenceNumber = record->ParentFileReferenceNumber;&nbsp; &nbsp; mft_enum_data.LowUsn = 0;&nbsp; &nbsp; mft_enum_data.HighUsn = maxusn;&nbsp; &nbsp; if (!DeviceIoControl(drive, FSCTL_ENUM_USN_DATA, &mft_enum_data, sizeof(mft_enum_data), buffer, BUFFER_SIZE, &bytecount, NULL))&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; printf("FSCTL_ENUM_USN_DATA (show_record): %u\n", GetLastError());&nbsp; &nbsp; &nbsp; &nbsp; return;&nbsp; &nbsp; }&nbsp; &nbsp; parent_record = (USN_RECORD *)((USN *)buffer + 1);&nbsp; &nbsp; if (parent_record->FileReferenceNumber != record->ParentFileReferenceNumber)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; printf("=================================================================\n");&nbsp; &nbsp; &nbsp; &nbsp; printf("Couldn't retrieve FileReferenceNumber %u\n", record->ParentFileReferenceNumber);&nbsp; &nbsp; &nbsp; &nbsp; return;&nbsp; &nbsp; }&nbsp; &nbsp; show_record(parent_record);}void check_record(USN_RECORD * record){&nbsp; &nbsp; WCHAR * filename;&nbsp; &nbsp; WCHAR * filenameend;&nbsp; &nbsp; filename = (WCHAR *)(((BYTE *)record) + record->FileNameOffset);&nbsp; &nbsp; filenameend= (WCHAR *)(((BYTE *)record) + record->FileNameOffset + record->FileNameLength);&nbsp; &nbsp; if (filenameend - filename != 8) return;&nbsp; &nbsp; if (wcsncmp(filename, L"test.txt", 8) != 0) return;&nbsp; &nbsp; show_record(record);}int main(int argc, char ** argv){&nbsp; &nbsp; MFT_ENUM_DATA mft_enum_data;&nbsp; &nbsp; DWORD bytecount = 1;&nbsp; &nbsp; void * buffer;&nbsp; &nbsp; USN_RECORD * record;&nbsp; &nbsp; USN_RECORD * recordend;&nbsp; &nbsp; USN_JOURNAL_DATA * journal;&nbsp; &nbsp; DWORDLONG nextid;&nbsp; &nbsp; DWORDLONG filecount = 0;&nbsp; &nbsp; DWORD starttick, endtick;&nbsp; &nbsp; starttick = GetTickCount();&nbsp; &nbsp; printf("Allocating memory.\n");&nbsp; &nbsp; buffer = VirtualAlloc(NULL, BUFFER_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);&nbsp; &nbsp; if (buffer == NULL)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; printf("VirtualAlloc: %u\n", GetLastError());&nbsp; &nbsp; &nbsp; &nbsp; return 0;&nbsp; &nbsp; }&nbsp; &nbsp; printf("Opening volume.\n");&nbsp; &nbsp; drive = CreateFile(L"\\\\?\\c:", GENERIC_READ, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_FLAG_NO_BUFFERING, NULL);&nbsp; &nbsp; if (drive == INVALID_HANDLE_VALUE)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; printf("CreateFile: %u\n", GetLastError());&nbsp; &nbsp; &nbsp; &nbsp; return 0;&nbsp; &nbsp; }&nbsp; &nbsp; printf("Calling FSCTL_QUERY_USN_JOURNAL\n");&nbsp; &nbsp; if (!DeviceIoControl(drive, FSCTL_QUERY_USN_JOURNAL, NULL, 0, buffer, BUFFER_SIZE, &bytecount, NULL))&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; printf("FSCTL_QUERY_USN_JOURNAL: %u\n", GetLastError());&nbsp; &nbsp; &nbsp; &nbsp; return 0;&nbsp; &nbsp; }&nbsp; &nbsp; journal = (USN_JOURNAL_DATA *)buffer;&nbsp; &nbsp; printf("UsnJournalID: %lu\n", journal->UsnJournalID);&nbsp; &nbsp; printf("FirstUsn: %lu\n", journal->FirstUsn);&nbsp; &nbsp; printf("NextUsn: %lu\n", journal->NextUsn);&nbsp; &nbsp; printf("LowestValidUsn: %lu\n", journal->LowestValidUsn);&nbsp; &nbsp; printf("MaxUsn: %lu\n", journal->MaxUsn);&nbsp; &nbsp; printf("MaximumSize: %lu\n", journal->MaximumSize);&nbsp; &nbsp; printf("AllocationDelta: %lu\n", journal->AllocationDelta);&nbsp; &nbsp; maxusn = journal->MaxUsn;&nbsp; &nbsp; mft_enum_data.StartFileReferenceNumber = 0;&nbsp; &nbsp; mft_enum_data.LowUsn = 0;&nbsp; &nbsp; mft_enum_data.HighUsn = maxusn;&nbsp; &nbsp; for (;;)&nbsp; &nbsp; {//&nbsp; &nbsp; &nbsp; printf("=================================================================\n");//&nbsp; &nbsp; &nbsp; printf("Calling FSCTL_ENUM_USN_DATA\n");&nbsp; &nbsp; &nbsp; &nbsp; if (!DeviceIoControl(drive, FSCTL_ENUM_USN_DATA, &mft_enum_data, sizeof(mft_enum_data), buffer, BUFFER_SIZE, &bytecount, NULL))&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf("=================================================================\n");&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf("FSCTL_ENUM_USN_DATA: %u\n", GetLastError());&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf("Final ID: %lu\n", nextid);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf("File count: %lu\n", filecount);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; endtick = GetTickCount();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf("Ticks: %u\n", endtick - starttick);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return 0;&nbsp; &nbsp; &nbsp; &nbsp; }//&nbsp; &nbsp; &nbsp; printf("Bytes returned: %u\n", bytecount);&nbsp; &nbsp; &nbsp; &nbsp; nextid = *((DWORDLONG *)buffer);//&nbsp; &nbsp; &nbsp; printf("Next ID: %lu\n", nextid);&nbsp; &nbsp; &nbsp; &nbsp; record = (USN_RECORD *)((USN *)buffer + 1);&nbsp; &nbsp; &nbsp; &nbsp; recordend = (USN_RECORD *)(((BYTE *)buffer) + bytecount);&nbsp; &nbsp; &nbsp; &nbsp; while (record < recordend)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; filecount++;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; check_record(record);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; record = (USN_RECORD *)(((BYTE *)record) + record->RecordLength);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; mft_enum_data.StartFileReferenceNumber = nextid;&nbsp; &nbsp; }}补充笔记如评论中所述,您可能需要在Windows 7以后的Windows版本上替换MFT_ENUM_DATA为MFT_ENUM_DATA_V0(这也取决于您使用的编译器和SDK。)我正在打印64位文件参考号,就好像它们是32位一样。那只是我的一个错误。也许在生产代码中您无论如何都不会打印它们,而是供您参考。

慕村9548890

零钱日记是您最好的选择。您可以使用文件参考号来匹配文件创建/删除对,从而忽略临时文件,而无需进一步处理它们。我认为您必须扫描主文件表才能理解ParentFileReferenceNumber。当然,您在执行此操作时只需要跟踪目录,并使用可以快速查找信息的数据结构,因此只需要扫描一次MFT。
打开App,查看更多内容
随时随地看视频慕课网APP