基于NTFS磁盘快速查找

c++

Posted by YiMiTuMi on August 19, 2021

基于NTFS磁盘快速查找

关于NTFS查找的原理建议自行百度查找,这里写了一个类进行查找,一个盘一个线程。

后面可以将类写成一个单例,然后将除了线程外的函数写到类里。(这里懒了)

TraverSalFile.h

TraverSalFile.h类的头文件:

#include <Windows.h> 
#include <WinIoCtl.h>
#include <string>
#include <vector>

using namespace std;


typedef struct _FILE_INFO_DATA
{
	wstring strDrives;


}FILE_INFO_DATA, *PFILE_INFO_DATA;


static HANDLE hScanFileThread[26];                                 //用于保存线程结构体
DWORD WINAPI StartOverallScanThread(LPVOID lpParam);                //开始扫描线程,并等待扫描完成
DWORD WINAPI FileScanThread(LPVOID lpParam);                        //每个盘一个线程扫描
BOOL JudgeDrivesNTFS(const wstring &strDrives);                     //判断是否是NTFS
BOOL FileScanFunction(PFILE_INFO_DATA pFileInfo);                   //全盘扫描函数
BOOL ActivateUSNState(HANDLE& hVol, USN_JOURNAL_DATA& journalData); //激活USN状态
void GetDrivesInfo(vector<wstring> &VecDrives);                     //获取盘符

class CTraverSalFile
{

public:
	CTraverSalFile();
	virtual ~CTraverSalFile();

	BOOL StartOverallScan();


public:
	
};

TraverSalFile.CPP

类的cpp文件:

#include "stdafx.h"
#include "TraverSalFile.h"
#include <vector>
#include <string>
#include <map>
#include <iostream>
#include <fstream>

using namespace std;

#define BUF_LEN 4096  

CTraverSalFile::CTraverSalFile()
{
	
}

CTraverSalFile::~CTraverSalFile()
{
	
}

//类对外的接口 
BOOL CTraverSalFile::StartOverallScan()
{
	//起一个线程用于调用
	HANDLE hThread = ::CreateThread(NULL, NULL, StartOverallScanThread, NULL, NULL, NULL);  
	if (hThread != NULL)
	{
		CloseHandle(hThread);
		hThread = NULL;
	}

	return TRUE;
}

//用于开启各个盘的线程,并等待结束
DWORD WINAPI StartOverallScanThread(LPVOID lpParam)
{
	//初始化数组
	for (int i = 0; i < 26; i++)
	{
		hScanFileThread[i] = NULL;
	}

	vector<wstring> vecDrives; //保存盘符
	GetDrivesInfo(vecDrives);

	//vecDrives.push_back(L"E:");

	if (vecDrives.empty())
	{
		return FALSE;
	}

	vector<wstring>::iterator iterDrives = vecDrives.begin();
	int iLetterCount = 0;

	for (; iterDrives != vecDrives.end(); iterDrives++, iLetterCount++)  //每有一个盘符创建一个线程
	{
		PFILE_INFO_DATA pFileInfo = new FILE_INFO_DATA;
		pFileInfo->strDrives = *iterDrives;


		hScanFileThread[iLetterCount] = ::CreateThread(NULL, NULL, FileScanThread, pFileInfo, NULL, NULL);
	}

	//等待线程结束
	DWORD dwWait = WaitForMultipleObjects(iLetterCount, hScanFileThread, TRUE, INFINITE);
	while (iLetterCount != 0)
	{
		if (hScanFileThread[iLetterCount - 1] != INVALID_HANDLE_VALUE)
		{
			CloseHandle(hScanFileThread[iLetterCount - 1]);
			hScanFileThread[iLetterCount - 1] = INVALID_HANDLE_VALUE;
		}

		iLetterCount--;
	}
	
	wstring strEnd = L"End File";
	wcout << strEnd << endl;

	return 0;
}

//判断盘符是否是 NTFS格式
DWORD WINAPI FileScanThread(LPVOID lpParam)
{
	PFILE_INFO_DATA pFileInfo = (PFILE_INFO_DATA)lpParam;

	//判断是否是NTFS格式
	if (!JudgeDrivesNTFS(pFileInfo->strDrives))
	{
		return 0;
	}


	FileScanFunction(pFileInfo);
	


	//释放结构体
	if (pFileInfo != NULL)
	{
		delete pFileInfo;
		pFileInfo = NULL;
	}

	return 0;
}

//判断是否是NTFS
BOOL JudgeDrivesNTFS(const wstring &strDrives)
{
	BOOL bIsNTFSDisk = FALSE;

	if (DRIVE_FIXED == GetDriveTypeW(strDrives.c_str()))  //判断是否是本地硬盘
	{
		DWORD dwMaxComLen, dwFileSysFlag;
		WCHAR szVolumeName[64] = { 0 };
		WCHAR fileSysBuf[64] = { 0 };

		wstring wstrDrives = strDrives;
		wstrDrives += L"\\";

		GetVolumeInformationW(wstrDrives.c_str(), szVolumeName, 32, NULL, &dwMaxComLen, &dwFileSysFlag, fileSysBuf, 8);
		if (fileSysBuf[0] == L'N' && fileSysBuf[1] == L'T' && fileSysBuf[2] == L'F' && fileSysBuf[3] == L'S')
		{
			bIsNTFSDisk = TRUE;
		}
	}

	return bIsNTFSDisk;
}

//全盘扫描函数
BOOL FileScanFunction(PFILE_INFO_DATA pFileInfo)
{
	wstring driveletter = L"\\\\.\\";
	driveletter += pFileInfo->strDrives;

	HANDLE hVol = CreateFileW(driveletter.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
	NTFS_VOLUME_DATA_BUFFER ntfsVolData;
	DWORD dwWritten = 0;

	BOOL bDioControl = DeviceIoControl(hVol, FSCTL_GET_NTFS_VOLUME_DATA, NULL, 0, &ntfsVolData, sizeof(ntfsVolData), &dwWritten, NULL);
	if (!bDioControl)
	{
		CloseHandle(hVol);
		return FALSE;
	}

	USN_JOURNAL_DATA journalData;
	READ_USN_JOURNAL_DATA readData = { 0, 0xFFFFFFFF, FALSE, 0, 0 };

	PUSN_RECORD usnRecord;
	DWORD dwBytes = 0;
	DWORD dwRetBytes = 0;
	char buffer[BUF_LEN];
	bDioControl = DeviceIoControl(hVol, FSCTL_QUERY_USN_JOURNAL, NULL, 0, &journalData, sizeof(journalData), &dwBytes, NULL);
	if (!bDioControl)
	{
		int iError = GetLastError();
		if (iError == 1179)     //1179错误 USN状态未激活,需要激活一下
		{
			if (!ActivateUSNState(hVol, journalData))
			{
				CloseHandle(hVol);
				return FALSE;
			}
		}
		else
		{
			CloseHandle(hVol);
			return FALSE;
		}
	}
	readData.UsnJournalID = journalData.UsnJournalID;

	MFT_ENUM_DATA med;   //vs 2017 以上版本请使用 MFT_ENUM_DATA_V0  否则DeviceIoControl会报87错误
	memset(&med, 0, sizeof(MFT_ENUM_DATA));
	med.StartFileReferenceNumber = 0;
	med.LowUsn = 0;
	med.HighUsn = journalData.NextUsn;

	dwBytes = 0;
	ULONGLONG iFileNumber = 0;

	map<ULONGLONG, wstring> namemap;
	map<ULONGLONG, ULONGLONG> frnmap;
	
	map<ULONGLONG, wstring> mapFilePath;     //用来保存路径
	map<ULONGLONG, wstring> mapFile;         //只保存文件

	//获取 USN日志
	while (TRUE)
	{
		memset(buffer, 0, BUF_LEN);
		bDioControl = DeviceIoControl(hVol, FSCTL_ENUM_USN_DATA, &med, sizeof(med), &buffer, BUF_LEN, &dwBytes, NULL); 
		int i = GetLastError();
		if (!bDioControl) 
		{ 
			break; 
		}

		if (dwBytes <= sizeof(USN))
		{
			break;                                  //结束!
		}

		dwRetBytes = dwBytes - sizeof(USN);                                 //跳过了1个USN,此USN是下一论查询起点
		usnRecord = (PUSN_RECORD)(((PUCHAR)buffer) + sizeof(USN));

		while (dwRetBytes > 0)
		{
			iFileNumber++;

			//usnRecord->FileReferenceNumber; //本身的文件簇
			//usnRecord->ParentFileReferenceNumber; //父级的文件簇

			DWORD dwFileAttribute = usnRecord->FileAttributes;   //文件属性
			
			//获取文件名
			WCHAR szfileName[1024] = {0};
			swprintf(szfileName,L"%.*ws", (int)(usnRecord->FileNameLength/2),usnRecord->FileName);
			wstring wstrFileName = szfileName;

			if (!(dwFileAttribute & FILE_ATTRIBUTE_DIRECTORY))  //判断是否是文件夹
			{
				//文件则写入文件map

				mapFile.insert(pair<ULONGLONG, wstring>(usnRecord->FileReferenceNumber, wstrFileName));
			}
			else
			{
				//路径map
	
				namemap.insert(pair<ULONGLONG, wstring>(usnRecord->FileReferenceNumber, wstrFileName));
				//mapFilePath.insert(pair<ULONGLONG, wstring>(usnRecord->FileReferenceNumber, wstrFileName));
			}

			frnmap.insert(pair<ULONGLONG, ULONGLONG>(usnRecord->FileReferenceNumber, usnRecord->ParentFileReferenceNumber));	

			
			dwRetBytes -= usnRecord->RecordLength;
			usnRecord = (PUSN_RECORD)(((PCHAR)usnRecord) + usnRecord->RecordLength);
		}

		med.StartFileReferenceNumber = *(DWORDLONG*)buffer; 
	}
	
	wstring strTxtPath = L"C:\\Users\\WGH\\Desktop\\";
	strTxtPath += pFileInfo->strDrives.substr(0, 1);

	wofstream oTxtFile(strTxtPath.c_str(), ios::app);  //输出为文件

	//mapFile 输出所有文件, namemap 可以用来输出所有路径	
	wstring wstrBackSlash = L"\\";
	map<ULONGLONG, wstring>::iterator mapIter = mapFile.begin();
	
	for (; mapIter != mapFile.end(); mapIter++)
	{	
		wstring wstrFilePath = mapIter->second; 
		ULONGLONG self = mapIter->first;

		while (frnmap.find(self) != frnmap.end())
		{
			ULONGLONG parent = frnmap[self];
			wstring wstrParent = namemap[parent];

			wstrFilePath.insert(0, wstrBackSlash);
			wstrFilePath.insert(0, wstrParent);

			self = parent;
		}

		//wstrFilePath.insert(0, wstrBackSlash);
		wstrFilePath.insert(0, pFileInfo->strDrives);
		
		wprintf(L"%s \n", wstrFilePath.c_str());
		oTxtFile << wstrFilePath <<endl;

	}
	
	oTxtFile.close();

	CloseHandle(hVol);
	return TRUE;
}

BOOL ActivateUSNState(HANDLE &hVol, USN_JOURNAL_DATA &journalData)   //激活USN状态
{
	BOOL bUSN = TRUE;
	DWORD br;
	CREATE_USN_JOURNAL_DATA cujd;
	DWORD dwBytes = 0;
	cujd.MaximumSize = 0; // 0表示使用默认值  
	cujd.AllocationDelta = 0; // 0表示使用默认值  
	BOOL status = DeviceIoControl(hVol, FSCTL_CREATE_USN_JOURNAL, &cujd, sizeof(cujd), NULL, 0, &br, NULL);

	if (0 != status)
	{
		BOOL bDioControl = DeviceIoControl(hVol, FSCTL_QUERY_USN_JOURNAL, NULL, 0, &journalData, sizeof(journalData), &dwBytes, NULL);
		if (!bDioControl)
		{
			bUSN = FALSE;
		}
	}
	else
	{
		//USN状态激活失败
		bUSN = FALSE;
	}

	return bUSN;
}

void GetDrivesInfo(vector<wstring> &VecDrives)
{
	TCHAR LogicalDrives[MAX_PATH] = { 0 };
	int nLen = GetLogicalDriveStringsW(MAX_PATH-1, LogicalDrives);
	int i = 0;

	do 
	{
		TCHAR wcharDrives[MAX_PATH];
		wstring strDrive;
		wmemcpy(wcharDrives, LogicalDrives + i, 4);

		strDrive = wcharDrives;

		//判断当前磁盘类型
		int nType = GetDriveTypeW(strDrive.c_str());

		strDrive = strDrive.substr(0, 2);

		if (nType == DRIVE_FIXED) //硬盘
		{
			VecDrives.push_back(strDrive);
		}

		i += 4;

	} while (i<nLen);

	return;
}

Mian.c

类的调用:

#include "stdafx.h"
#include "TraverSalFile.h"
#include <iostream>
#include <string>

using namespace std;


int _tmain(int argc, _TCHAR* argv[])
{

	CTraverSalFile cTraverSalFile;

	cTraverSalFile.StartOverallScan();

	
	//暂定黑框
	string str;
	cin >> str;

	return 0;
}

蓝色妖姬 – 相守