通过读取 MFT C++(如 TreeSize)来获取目录大小

Getting Directory Size by reading through the MFT in C++ (like TreeSize)

本文关键字:获取 TreeSize 读取 MFT C++      更新时间:2023-10-16

请在引用"repost"之前阅读 - 我知道有人问过类似的问题,但我还没有找到一个满意的答案

我的目标是提供磁盘空间使用情况的树状目录结构,允许用户向下钻取层次结构以找到相当大的文件夹。

程序TreeSize就是一个很好的例子,我希望获得与该程序相同的响应时间。

我当前的代码可以使用 MFT 在 aprx 25 秒内循环访问我的 480GB 文件。我正在从这一点开始通过获取文件信息来开始构建目录大小(MFT 仅包含文件名和父 ID,而不是完整的文件路径(

从 MFT 日志条目中获取文件信息,我当前的代码调用

TCHAR filePath[MAX_PATH];
HANDLE hh = OpenFileById(hDevice, &(getFileIdDescriptor(pRecord->FileReferenceNumber)), 0, 0, 0, 0);
GetFinalPathNameByHandle(hh, filePath, MAX_PATH, 0);

不幸的是,此代码将程序的总执行时间从 25 秒增加到 5 分钟。

有没有更好的方法来获取文件信息?

非常感谢,如果您建议FindFirstFile和FindNextFile,但是对于处理大型目录,这些选项太慢了

代码如下(我不是你可能注意到的 C 程序员!

#include <iostream>
#include <string>
#include <fstream>
#include <windows.h>
#include <fstream>
#include <atlbase.h>
#include <windows.h> 
#include <stdio.h>
using namespace std;
typedef std::basic_string<TCHAR> tstring;
FILE_ID_DESCRIPTOR getFileIdDescriptor(const DWORDLONG fileId)
{
FILE_ID_DESCRIPTOR fileDescriptor;
fileDescriptor.Type = FileIdType;
fileDescriptor.FileId.QuadPart = fileId;
fileDescriptor.dwSize = sizeof(fileDescriptor);
return fileDescriptor;
}
bool ReadMFT()
{
HANDLE hDevice = CreateFile(TEXT("\\.\C:"),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
0);
if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive
{
printf("Error %d", GetLastError());
return (FALSE);
}
USN_JOURNAL_DATA ujd = { 0 };
DWORD cb = 0;
BYTE pData[sizeof(DWORDLONG) + 0x10000] = { 0 };
if (!DeviceIoControl(hDevice, FSCTL_QUERY_USN_JOURNAL, NULL, 0, &ujd, sizeof(USN_JOURNAL_DATA), &cb, NULL))
{
printf("Error %d", GetLastError());
return (FALSE);
}
MFT_ENUM_DATA med = { 0 };
med.StartFileReferenceNumber = 0;
med.LowUsn = 0;
med.HighUsn = ujd.NextUsn;
while (TRUE)
{
if (!DeviceIoControl(hDevice, FSCTL_ENUM_USN_DATA, &med, sizeof(med), pData, sizeof(pData), &cb, NULL))
{
printf("Error %d", GetLastError());
break;
}
PUSN_RECORD pRecord = (PUSN_RECORD)&pData[sizeof(USN)];
//Inner Loop
while ((PBYTE)pRecord < (pData + cb))
{
tstring sz((LPCWSTR)
((PBYTE)pRecord + pRecord->FileNameOffset),
pRecord->FileNameLength / sizeof(WCHAR));
pRecord = (PUSN_RECORD)((PBYTE)pRecord + pRecord->RecordLength);
// *******************************************************************************
// APPROACH 1
// Adding these lines of code increases the time from 25 seconds to 340 seconds
// Although it may be possible to push this onto a queue and run these in parrallel
// I still think it's an expensive option
/*TCHAR filePath[MAX_PATH];
HANDLE hh = OpenFileById(hDevice, &(getFileIdDescriptor(pRecord->FileReferenceNumber)), 0, 0, 0, 0);
GetFinalPathNameByHandle(hh, filePath, MAX_PATH, 0);*/
}
med.StartFileReferenceNumber = *(DWORDLONG *)pData;
}
}

int main()
{
ReadMFT();
}

非常感谢

经过几次试验和错误,运行

FILE_ID_DESCRIPTOR f = getFileIdDescriptor(pRecord->FileReferenceNumber);
q.Dispatch(f, [f] 
{ 
TCHAR filePath[MAX_PATH];
HANDLE hh = OpenFileById(hDevice, (LPFILE_ID_DESCRIPTOR)&(f), 0, 0, 0, 0);
GetFinalPathNameByHandle(hh, filePath, MAX_PATH, 0);
});

在帕拉莱尔将时间缩短到1:30

看看这家伙的调度队列实现

https://github.com/embeddedartistry/embedded-resources/blob/master/examples/cpp/dispatch.cpp

您可能希望迭代 $INDEX_ALLOCATION 属性以获取子节点的列表。