Вы можете перечислить все файлы на томе, используя FSCTL_ENUM_USN_DATA. Это быстрый процесс (мои тесты возвращали лучше, чем 6000 записей в секунду даже на очень старой машине, а более 20000+ более типично) и включают только файлы, которые существуют в настоящее время.
Возвращенные данные включают в себя флаги файлов, а также USN, чтобы вы могли проверять изменения по своему усмотрению.
Вам все равно нужно будет определить полный путь к файлам, сопоставив идентификаторы родителей с идентификаторами файлов каталогов. Один из подходов состоит в том, чтобы использовать буфер, достаточно большой для одновременного хранения всех записей файла, и выполнять поиск в записях, чтобы найти подходящего родителя для каждого файла, для которого необходимо создать резервную копию. Для больших томов вам, вероятно, потребуется обработать записи каталога в более эффективную структуру данных, возможно, в хеш-таблицу.
Кроме того, вы можете читать / перечитывать записи для родительских каталогов по мере необходимости. Это было бы менее эффективно, но производительность все равно могла бы быть удовлетворительной в зависимости от того, сколько файлов было скопировано. Похоже, что 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;
}
}
Дополнительные примечания
Как обсуждалось в комментариях, вам может потребоваться заменить MFT_ENUM_DATA
на MFT_ENUM_DATA_V0
в версиях Windows, более поздних, чем Windows 7. (Это также может зависеть от того, какой компилятор и SDK вы используете.)
Я печатаю 64-битные номера файлов, как если бы они были 32-битными. Это была просто ошибка с моей стороны. Возможно, в рабочем коде вы их не будете печатать, но, к вашему сведению,