Skip to content

快速遍历磁盘

Published: at 02:26 AM | 3 min read

快速遍历磁盘,不用遍历每个目录,主要使用DeviceIoControl 函数
需要用管理员权限运行

参考链接

使用命令行工具也可以遍历出所有文件:
注意C盘需要管理员权限

dir c:\*.* /b /s /a-d >>c:\allfile.txt
// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <Windows.h>
#include <string>
#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include <algorithm>

struct Record {
	uint64_t parentNumber;	// 父目录编号
	std::wstring name;		// 文件名
	uint32_t attr;			// 文件属性
};

// driveletter	- 磁盘,如:"E:"
// fileRecord	- 文件记录
// dirRecord	- 目录记录
// ignores		- 忽略的文件
void GetRecord(const std::wstring &driveletter, std::unordered_map<uint64_t, Record> &fileRecord, std::unordered_map<uint64_t, Record> &dirRecord, std::unordered_set<uint64_t> &ignores)
{
	HANDLE hVol = CreateFile(driveletter.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
	USN_JOURNAL_DATA journalData;
	PUSN_RECORD usnRecord;
	DWORD dwBytes;
	DWORD dwRetBytes;
	char buffer[USN_PAGE_SIZE];
	BOOL bDioControl = DeviceIoControl(hVol, FSCTL_QUERY_USN_JOURNAL, NULL, 0, &journalData, sizeof(journalData), &dwBytes, NULL);
	if (!bDioControl)
	{
		printf("[!]DeviceIoControl error\n");
		return;
	}

	MFT_ENUM_DATA med;
	med.StartFileReferenceNumber = 0;
	med.LowUsn = 0;
	med.HighUsn = journalData.NextUsn;
	int count = 0;
	while (dwBytes > sizeof(USN))
	{
		memset(buffer, 0, sizeof(USN_PAGE_SIZE));
		bDioControl = DeviceIoControl(hVol, FSCTL_ENUM_USN_DATA, &med, sizeof(med), &buffer, USN_PAGE_SIZE, &dwBytes, NULL);
		if (!bDioControl)
			break;
		dwRetBytes = dwBytes - sizeof(USN);
		usnRecord = (PUSN_RECORD)(((PUCHAR)buffer) + sizeof(USN));
		while (dwRetBytes > 0)
		{
			count++;
			int len = usnRecord->FileNameLength / 2;
			if (len > 0) {
				std::wstring name;
				name.resize(len, 0);
				wmemcpy_s(&name[0], len, usnRecord->FileName, len);
				if (usnRecord->FileAttributes & FILE_ATTRIBUTE_SYSTEM) {
					ignores.emplace(usnRecord->FileReferenceNumber);
					std::wcout << "ignore system:" << name << std::endl;
				}
				else if (usnRecord->FileAttributes & FILE_ATTRIBUTE_HIDDEN) {
					ignores.emplace(usnRecord->FileReferenceNumber);
					std::wcout << "ignore hidden:" << name << std::endl;
				}
				else {
					if (usnRecord->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
						dirRecord[usnRecord->FileReferenceNumber] = Record{ usnRecord->ParentFileReferenceNumber, name, usnRecord->FileAttributes };
					}
					else {
						fileRecord[usnRecord->FileReferenceNumber] = Record{ usnRecord->ParentFileReferenceNumber, name, usnRecord->FileAttributes };
					}
				}
			}

			//printf("%.*ws\n", (int)(usnRecord->FileNameLength / 2), usnRecord->FileName);
			dwRetBytes -= usnRecord->RecordLength;
			usnRecord = (PUSN_RECORD)(((PCHAR)usnRecord) + usnRecord->RecordLength);
		}
		med.StartFileReferenceNumber = *(DWORDLONG*)buffer;
	}
	CloseHandle(hVol);
	std::cout << "total:" << count << std::endl;
}

void GetAllFiles(const std::wstring driver, std::vector<std::wstring> &allFiles)
{
	std::unordered_map<uint64_t, Record> files;
	std::unordered_map<uint64_t, std::wstring> fullDirs;
	{
		std::unordered_map<uint64_t, Record> dirs;
		std::unordered_set<uint64_t> ignores;
		GetRecord(L"\\\\.\\" + driver, files, dirs, ignores);

// 拼装完整目录
		for (auto &dir : dirs) {
			uint64_t number = dir.second.parentNumber;
			std::wstring full = dir.second.name;
			bool ignore = false;
			while (1) {
				if (ignores.find(number) != ignores.end()) {
					ignore = true;
					break;
				}
				auto i = dirs.find(number);
				if (i == dirs.end()) {
					break;
				}
				full.insert(0, i->second.name + L"\\");
				number = i->second.parentNumber;
			}
			if (ignore) {
				continue;
			}
			full.insert(0, driver + L"\\");
			fullDirs[dir.first] = full;
		}
	}

	for (auto &file : files) {
		auto iter = fullDirs.find(file.second.parentNumber);
		if (iter != fullDirs.end()) {
			allFiles.emplace_back(iter->second + L"\\" + file.second.name);
		}
	}
}

int wmain(int argc, wchar_t *argv[])
{
	setlocale(LC_ALL, "");

	if (argc != 2) {
		std::wcout << L"please input driver name!" << std::endl;
		return 1;
	}

	std::wstring driver(argv[1]);
	if (driver.empty()) {
		std::wcout << L"params error" << std::endl;
		return 1;
	}

	if (driver[driver.size() - 1] != L':') {
		driver += L":";
	}

	std::wcout << L"fetch dir " << driver << std::endl;
	std::vector<std::wstring> allFiles;
	GetAllFiles(driver, allFiles);

	std::wcout << L"----------------------------------------" << std::endl;
	std::wcout << L"files:" << allFiles.size() << std::endl;

	system("pause");
    return 0;
}