1

请在引用“转发”之前阅读 - 我知道有人问过类似的问题,但我还没有找到满意的答案

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

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

我当前的代码可以使用 MFT 在大约 25 秒内遍历我的 480GB 文件。我从这一点开始通过获取文件信息来构建目录大小(MFT 只包含文件名和 parentId,而不是完整的文件路径)

要从 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();
}

非常感谢

4

2 回答 2

0

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

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

于 2019-08-28T14:58:46.687 回答
0

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

于 2019-10-31T22:47:44.867 回答