基于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;
}