Получение размера каталога путем чтения MFT в C ++ (например, TreeSize)

Пожалуйста, прочтите, прежде чем цитировать «репост» - я знаю, что задавались похожие вопросы, но я еще не нашел удовлетворительного ответа

Моя цель - предоставить древовидную структуру каталогов использования дискового пространства, позволяющую пользователю детализировать иерархию, чтобы найти папки значительного размера.

Программа TreeSize является прекрасным примером этого, и я хочу получить такое же время отклика, что и эта программа.

Мой текущий код может перебирать мои 480 ГБ файлов примерно за 25 секунд с использованием MFT. Я ищу с этого момента, чтобы начать создание размеров каталогов, получив информацию о файле (MFT содержит только fileName и 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();
}

Большое спасибо


person Malcolm Swaine    schedule 27.08.2019    source источник


Ответы (2)


После нескольких проб и ошибок запуск

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

person Malcolm Swaine    schedule 28.08.2019

Вы можете повторить атрибут $ INDEX_ALLOCATION, чтобы получить список дочерних узлов.

person Kamaal    schedule 31.10.2019