0

I am attempting to follow this tutorial: Defragmenting Files.

I call DeviceIoControl() with FSCTL_GET_VOLUME_BITMAP on a handle to the C: volume, and I get a proper response.

I then open a handle to another file (I tried files from 10KB to a few MB) successfully, then I call DeviceIoControl() with FSCTL_GET_RETRIEVAL_POINTERS, and it succeeds with no last error or failed result, but the RETRIEVAL_POINTERS_BUFFER is not filled.

I also tried calling it on the C: volume handle, but it keeps returning ERROR_HANDLE_EOF even after trying to set the OVERLAPPED offset to 0, and setting the file pointer with SetFilePointer() to 0 relative to the beginning of the file.

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, &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: %d\n", 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, &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, &rpb, &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 (0x%016X)\n", i, cluster_cnt, rpb.Extents[i].Lcn.QuadPart);
    }
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Carol Victor
  • 331
  • 1
  • 7

1 Answers1

0

You are not checking the first HeapAlloc() for failure. And HeapFree() can wipe the last error code from DeviceIoControl() before you print it.

But more importantly, you are not passing the out data back to the caller correctly, so you are leaking the allocated memory, and the caller ends up with garbage for output.

Since the caller is passing in their own RETRIEVAL_POINTERS_BUFFER to receive the data, you need to copy the contents of the allocate memory into that buffer, eg:

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, &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: %u\n", 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, &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, &rpb, &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 (0x%016X)\n", i, cluster_cnt, rpb.Extents[i].Lcn.QuadPart);
        }
    }

    closefile(fi);
}

Alternatively, you can return the pointer to the allocated memory to the caller, and the caller will have to free the memory when done using it, eg:

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, &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: %u\n", 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, &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, &rpb, &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 (0x%016X)\n", i, cluster_cnt, rpb->Extents[i].Lcn.QuadPart);
        }

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

    closefile(fi);
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770