Программная дефрагментация файла в Windows

#c #windows #winapi #filesystems #fragmentation

#c #Windows #winapi #файловые системы #Фрагментация

Вопрос:

Я пытаюсь следовать этому руководству: дефрагментация файлов.

Я вызываю DeviceIoControl() с FSCTL_GET_VOLUME_BITMAP помощью дескриптора C: тома и получаю правильный ответ.

Затем я успешно открываю дескриптор другого файла (я пробовал файлы от 10 КБ до нескольких МБ), затем я вызываю DeviceIoControl() with FSCTL_GET_RETRIEVAL_POINTERS , и он завершается успешно без последней ошибки или сбоя, но RETRIEVAL_POINTERS_BUFFER не заполняется.

Я также пытался вызвать его с C: помощью дескриптора тома, но он продолжает возвращаться ERROR_HANDLE_EOF даже после попытки установить OVERLAPPED смещение равным 0 и установить указатель на файл SetFilePointer() равным 0 относительно начала файла.

 BOOL dic(HANDLE dev, DWORD code, LPVOID in, DWORD ins, LPVOID out, LPDWORD outs)
{
    HANDLE h = GetProcessHeap();
    DWORD s = 1000;
    DWORD r = 0;
    out = HeapAlloc(h,HEAP_ZERO_MEMORY,s);
    while (!DeviceIoControl(dev, code, in, ins, out, s, amp;r, 0))
    {
        if (ERROR_INSUFFICIENT_BUFFER == GetLastError() || ERROR_MORE_DATA == GetLastError())
        {
            s *= 10;
            LPVOID t = HeapReAlloc(h, HEAP_ZERO_MEMORY, out, s);
            if(!t){
                HeapFree(h, 0, out);
                return 0;
            }
            out = t;
        }
        else
        {
            HeapFree(h, 0, out);
            printf("dic unk: %dn", GetLastError());
            return 0;
        }
    }
    *outs = s;
    return 1;
}

BOOL getvolptr(HANDLE volh, PRETRIEVAL_POINTERS_BUFFER rpb, LPDWORD rpbs)
{
    STARTING_VCN_INPUT_BUFFER vcn = { 0 };
    return dic(volh, FSCTL_GET_RETRIEVAL_POINTERS, amp;vcn, sizeof(vcn), rpb, rpbs);
}

    RETRIEVAL_POINTERS_BUFFER rpb = { 0 };
    DWORD rpbs = 0;
    ULONGLONG cluster_cnt=0;
    HANDLE fi = openfile("C:\Windows\System32\Kernel32.dll");
    if (INVALID_HANDLE_VALUE == fi)
    {
        printf("failed to open file! (%d)n", GetLastError());
        getchar();
    }
    r = getvolptr(fi, amp;rpb, amp;rpbs);
    if (!r)
    {
        printf("failed to get vol ptrs! (%d)n", GetLastError());
        getchar();
    }
    for (int i = 0; i < rpb.ExtentCount;   i)
    {
        cluster_cnt = (ULONGLONG)(rpb.Extents[i].NextVcn.QuadPart) - (ULONGLONG)(rpb.StartingVcn.QuadPart);
        printf("%d) size: %llu clusters (0x6X)n", i, cluster_cnt, rpb.Extents[i].Lcn.QuadPart);
    }
 

Ответ №1:

Вы не проверяете первый HeapAlloc() на сбой. И HeapFree() может стереть последний код ошибки DeviceIoControl() перед его печатью.

Но что еще более важно, вы неправильно передаете out данные обратно вызывающей стороне, поэтому происходит утечка выделенной памяти, и вызывающая сторона получает мусор для вывода.

Поскольку вызывающий объект передает свои собственные RETRIEVAL_POINTERS_BUFFER данные для получения данных, вам необходимо скопировать содержимое выделенной памяти в этот буфер, например:

 BOOL dic(HANDLE dev, DWORD code, LPVOID in, DWORD ins, LPVOID out, LPDWORD outs)
{
    if (!in || !out || !outs)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    *outs = 0;

    HANDLE h = GetProcessHeap();
    DWORD s = 1000;

    LPVOID buf = HeapAlloc(h, HEAP_ZERO_MEMORY, s);
    if (!buf)
        return FALSE;

    DWORD r = 0;
    while (!DeviceIoControl(dev, code, in, ins, buf, s, amp;r, 0))
    {
        if (ERROR_INSUFFICIENT_BUFFER == GetLastError() || ERROR_MORE_DATA == GetLastError())
        {
            s *= 10;
            LPVOID t = HeapReAlloc(h, HEAP_ZERO_MEMORY, buf, s);
            if (!t)
            {
                HeapFree(h, 0, buf);
                return FALSE;
            }
            buf = t;
        }
        else
        {
            printf("dic unk: %un", GetLastError());
            HeapFree(h, 0, buf);
            return FALSE;
        }
    }

    if (s > *outs)
    {
        HeapFree(h, 0, buf);
        SetLastError(ERROR_INSUFFICIENT_BUFFER);
        return FALSE; 
    }

    CopyMemory(out, buf, s);
    *outs = s;

    HeapFree(h, 0, buf);
    return TRUE;
}

BOOL getvolptr(HANDLE volh, PRETRIEVAL_POINTERS_BUFFER rpb, LPDWORD rpbs)
{
    STARTING_VCN_INPUT_BUFFER vcn = { 0 };
    return dic(volh, FSCTL_GET_RETRIEVAL_POINTERS, amp;vcn, sizeof(vcn), rpb, rpbs);
}
 
 HANDLE fi = openfile("C:\Windows\System32\Kernel32.dll");
if (INVALID_HANDLE_VALUE == fi)
{
    printf("failed to open file! (%u)n", GetLastError());
    getchar();
}
else
{
    RETRIEVAL_POINTERS_BUFFER rpb = { 0 };
    DWORD rpbs = sizeof(rpb);

    if (!getvolptr(fi, amp;rpb, amp;rpbs))
    {
        printf("failed to get vol ptrs! (%u)n", GetLastError());
        getchar();
    }
    else
    {
        ULONGLONG cluster_cnt = 0;
        for (int i = 0; i < rpb.ExtentCount;   i)
        {
            cluster_cnt = (ULONGLONG)(rpb.Extents[i].NextVcn.QuadPart) - (ULONGLONG)(rpb.StartingVcn.QuadPart);
            printf("%d) size: %llu clusters (0x6X)n", i, cluster_cnt, rpb.Extents[i].Lcn.QuadPart);
        }
    }

    closefile(fi);
}
 

В качестве альтернативы, вы можете вернуть указатель на выделенную память вызывающей стороне, и вызывающей стороне придется освободить память после завершения ее использования, например:

 BOOL dic(HANDLE dev, DWORD code, LPVOID in, DWORD ins, LPVOID* out, LPDWORD outs)
{
    if (!in || !out || !outs)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    *out = NULL;
    *outs = 0;

    HANDLE h = GetProcessHeap();
    DWORD s = 1000;

    LPVOID buf = HeapAlloc(h, HEAP_ZERO_MEMORY, s);
    if (!buf)
        return FALSE;

    DWORD r = 0;
    while (!DeviceIoControl(dev, code, in, ins, buf, s, amp;r, 0))
    {
        if (ERROR_INSUFFICIENT_BUFFER == GetLastError() || ERROR_MORE_DATA == GetLastError())
        {
            s *= 10;
            LPVOID t = HeapReAlloc(h, HEAP_ZERO_MEMORY, buf, s);
            if (!t)
            {
                HeapFree(h, 0, buf);
                return FALSE;
            }
            buf = t;
        }
        else
        {
            printf("dic unk: %un", GetLastError());
            HeapFree(h, 0, buf);
            return FALSE;
        }
    }

    *out = buf;
    *outs = s;

    return TRUE;
}

BOOL getvolptr(HANDLE volh, PRETRIEVAL_POINTERS_BUFFER* rpb, LPDWORD rpbs)
{
    STARTING_VCN_INPUT_BUFFER vcn = { 0 };
    return dic(volh, FSCTL_GET_RETRIEVAL_POINTERS, amp;vcn, sizeof(vcn), (void**)rpb, rpbs);
}
 
 HANDLE fi = openfile("C:\Windows\System32\Kernel32.dll");
if (INVALID_HANDLE_VALUE == fi)
{
    printf("failed to open file! (%u)n", GetLastError());
    getchar();
}
else
{
    PRETRIEVAL_POINTERS_BUFFER rpb = NULL;
    DWORD rpbs = 0;

    if (!getvolptr(fi, amp;rpb, amp;rpbs))
    {
        printf("failed to get vol ptrs! (%u)n", GetLastError());
        getchar();
    }
    else
    {
        ULONGLONG cluster_cnt = 0;
        for (int i = 0; i < rpb->ExtentCount;   i)
        {
            cluster_cnt = (ULONGLONG)(rpb->Extents[i].NextVcn.QuadPart) - (ULONGLONG)(rpb->StartingVcn.QuadPart);
            printf("%d) size: %llu clusters (0x6X)n", i, cluster_cnt, rpb->Extents[i].Lcn.QuadPart);
        }

        HeapFree(GetProcessHeap(), 0, rpb);
    }

    closefile(fi);
}