-1

When calling DeviceIoControl with IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, it fills VOLUME_DISK_EXTENTS structure with pointers to some data (Extents array). While the structure that is created in my code is deallocated by me, an array of pointers seems disturbing.

Should I free that memory? How do I free it?

polkovnikov.ph
  • 6,256
  • 6
  • 44
  • 79

2 Answers2

1

The only pointer involved is a pointer to the buffer that you pass to DeviceIoControl() for it to fill with the content of the VOLUME_DISK_EXTENTS struct. There are no pointers inside of VOLUME_DISK_EXTENTS, its Extents member is a flat array of structs, not an array of pointers. Those structs are completely contained within your allocated buffer. So the only thing that needs to be freed is your buffer, nothing else.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • When you call DeviceIoControl `lpOutBuffe`r should be a pointer to `VOLUME_DISK_EXTENTS` struct, and `nOutBufferSize` should be `sizeof(VOLUME_DISK_EXTENTS`. – Tony J Feb 16 '15 at 22:40
  • @TonyJiang: you pass in a pointer to a single `VOLUME_DISK_EXTENTS`, however `Extents` is declared as `DISK_EXTENT Extents[ANYSIZE_ARRAY]` **NOT** `DISK_EXTENT* Extents[ANYSIZE_ARRAY]`, so it is an array of structs, **NOT** an array of pointers to structs. The documentation says: "An extent is a contiguous run of sectors on one disk. When the number of extents returned is greater than one (1), the error code ERROR_MORE_DATA is returned. You should call DeviceIoControl again, **allocating enough buffer space based on the value of NumberOfDiskExtents after the first DeviceIoControl call.**" – Remy Lebeau Feb 16 '15 at 22:44
  • @RemyLebeau Based on the answer of my friend, I've answered own question. Thanks for support anyway. – polkovnikov.ph Feb 16 '15 at 22:46
  • So the buffer has to be allocated to be large enough to hold X number of extents in a single memory block. – Remy Lebeau Feb 16 '15 at 22:46
0

Actually VOLUME_DISK_EXTENTS is defined to store 1 item.

typedef struct _VOLUME_DISK_EXTENTS {
    DWORD       NumberOfDiskExtents;
    DISK_EXTENT Extents[ANYSIZE_ARRAY]; // ANYSIZE_ARRAY == 1
} VOLUME_DISK_EXTENTS, *PVOLUME_DISK_EXTENTS;

If we call DeviceIoControl in the following way

VOLUME_DISK_EXTENTS vde;
ret = DeviceIoControl(
    h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
    NULL, 0, &vde, sizeof(vde), &bytesReturned, NULL
);

we'll get ERROR_MORE_DATA return code, because there's not enough space in that structure. Extents array is defined of size 1 with the fact that if you'll try to access elements 1 and above, you'll get into the space in the buffer after the VOLUME_DISK_EXTENTS structure. So what we actually need is to create not a VOLUME_DISK_EXTENTS but a buffer of size

sizeof(DISK_EXTENT) * (nextents - 1) + sizeof(VOLUME_DISK_EXTENTS)

where nextents is VOLUME_DISK_EXTENTS::NumberOfDiskExtents value that we get from the previous call. Then we should use

VOLUME_DISK_EXTENTS *ext = (VOLUME_DISK_EXTENTS*) buff;

and work with this structure, assuming that Extents array has nextents elements.

There's no need for extra API calls to free the memory. Everything that is freed is a buffer.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
polkovnikov.ph
  • 6,256
  • 6
  • 44
  • 79
  • What you have described is explained in the [`VOLUME_DISK_EXTENTS` documentation on MSDN](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365727.aspx): "An extent is a contiguous run of sectors on one disk. When the number of extents returned is greater than one (1), the error code ERROR_MORE_DATA is returned. You should call DeviceIoControl again, allocating enough buffer space based on the value of NumberOfDiskExtents after the first DeviceIoControl call." – Remy Lebeau Feb 16 '15 at 22:48
  • So, you allocate a `VOLUME_DISK_EXTENTS` buffer with an initial size of your choosing (must be `>= sizeof(VOLUME_DISK_EXTENTS)`), and then keep re-allocating it to increase its size until `DeviceIoControl()` stops returning `ERROR_MORE_DATA`, then process the buffer as needed and free it when done. – Remy Lebeau Feb 16 '15 at 22:52
  • @RemyLebeau Thanks. I've found an incorrect example on SO, and didn't understand that line after that at all. It's quite counterintuitive what buffer it's telling about, when all you see is `&vde, sizeof(vde)`. – polkovnikov.ph Feb 16 '15 at 22:52
  • @RemyLebeau Why are you editing a C++-styled reference to a C-styled pointer? :) – polkovnikov.ph Feb 16 '15 at 22:55
  • because you can't pass a reference to `DeviceIoControl()`, and you can't free the buffer by reference, either. There is no point (no pun intended) in allocating a buffer (which means you have a pointer to begin with), converting it to a reference, then converting it back to a pointer again. You should just use the pointer as-is for everything. But either way, there is no difference in using a reference vs a pointer to access the struct's members, the compiled machine code is the same. – Remy Lebeau Feb 16 '15 at 22:58