[python-win32] DeviceIOControl using IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS fails with 'Incorrect function.'
Doug Campbell
wdouglascampbell at hotmail.com
Wed Feb 10 00:24:28 EST 2021
Eryk,
Again, you expand my knowledge! It seems so obvious now after reading what you wrote that I would not be able to get volume disk extents for a physical partition but yet this is what I wanted to do because I was attempting to find out the partition's offset on the disk.
Now, I think I see that I need to figure out the partition's volume device name using the \\?\Volume{GUID} format and then I will be able to determine what its offset is on the disk. The only way I can find to accomplish this is to iterate through each volume and using IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS to get its disk and IOCTL_DISK_GET_PARTITION_INFO_EX to get its partition and then see if I have a match for the disk/partition combination that I am looking for.
Thanks for sharing the extra details on how to handle the wintype.ERROR_MORE_DATA error. That was likely to be my next issue :) I did find an alternative method for handling this that is used in the multibootusb script/win32.py<https://github.com/mbusb/multibootusb/blob/master/scripts/win32.py> function findVolumeGuids(). They use BytesIO to create an io stream and then unpack the bytes on-the-fly. I think I likely go with your method.
Anyway, thanks again for your excellent explanations.
Doug
________________________________
From: Eryk Sun <eryksun at gmail.com>
Sent: Tuesday, February 9, 2021 3:05 PM
To: python-win32 at python.org <python-win32 at python.org>
Cc: Doug Campbell <wdouglascampbell at hotmail.com>
Subject: Re: [python-win32] DeviceIOControl using IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS fails with 'Incorrect function.'
On 2/9/21, Doug Campbell <wdouglascampbell at hotmail.com> wrote:
>
> win32file.DeviceIoControl(hDevice,
> winioctlcon.IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
> None, extents, None)
> pywintypes.error: (1, 'DeviceIoControl', 'Incorrect function.')
>
> I have tried with all three of the disks on my system: \\.\PhysicalDrive0,
> \\.\PhysicalDrive1, and \\.\PhyscialDrive2 but the results are always the
> same.
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS requests the disk extents of a
volume device. Disk devices do not implement this request. A volume
device such as "\\.\C:" can span multiple extents across one or more
physical disks such as "\\.\PhysicalDrive0" and "\\.\PhysicalDrive1".
If the request fails with winerror.ERROR_MORE_DATA, you need to be
prepared to resize the structure according to NumberOfDiskExtents.
ctypes doesn't make this all that easy or convenient at a high level.
What I do is to make the array field private and implement a property
that takes the dynamic length into account. For example:
ANYSIZE_ARRAY = 1
class VOLUME_DISK_EXTENTS(ctypes.Structure):
_fields_ = (('NumberOfDiskExtents', ctypes.c_ulong),
('_Extents', DISK_EXTENT * ANYSIZE_ARRAY))
@property
def Extents(self):
offset = type(self)._Extents.offset
array_t = DISK_EXTENT * self.NumberOfDiskExtents
return array_t.from_buffer(self, offset)
def resize(self):
if self.NumberOfDiskExtents < 1:
self.NumberOfDiskExtents = 1
offset = type(self)._Extents.offset
array_size = ctypes.sizeof(DISK_EXTENT) * self.NumberOfDiskExtents
ctypes.resize(self, offset + array_size)
If you passed `vde = VOLUME_DISK_EXTENTS()`, and the request fails
with winerror.ERROR_MORE_DATA, the call should have set
NumberOfDiskExtents to the required length. Call vde.resize(), and try
again. It's a bit clumsy, but it works.
> hDevice = win32file.CreateFile(
> disk, win32con.GENERIC_READ,
> win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
> None, win32con.OPEN_EXISTING, win32con.FILE_ATTRIBUTE_NORMAL,
> None)
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS is defined to require
FILE_ANY_ACCESS. This means the desired access and access sharing can
both be 0, and should be since there's no reason to request access
that you don't need that could cause the call to fail with a
permission error (e.g. access denied, or a sharing violation if read
access isn't shared). For example:
volume = r'\\.\C:'
hDevice = win32file.CreateFile(volume, 0, 0, None,
win32file.OPEN_EXISTING, 0, None)
---
Volume Device Names
The base name of a volume device is typically something like
"\Device\HarddiskVolume<N>" or "\Device\CdRom<N>", but these are
automatic names that can vary in the device number N each time a
volume comes online. The mountpoint manager usually will also assign
persistent names to a volume device, which are stored in the registry
if necessary (HKLM\System\MountedDevices) and are set globally in the
"\GLOBAL??" device-alias directory when the volume comes online.
Usually it assigns a GUID name of the form
"Volume{12345678-0000-0000-0000-123456789ABC}". A GUID name is used
when mounting a volume on an existing directory (e.g. mounting a
volume as "C:\Mount\BackupDrive"). It usually also assigns a DOS
drive-letter name such as "C:", which provides the classic DOS volume
mountpoint, such as "C:\".
The "\GLOBAL??" object directory is rarely accessed directly, even in
native NT API programs that have full access to the NT object
namespace. Instead, native NT programs use "\??", a virtual directory
that allows accessing global device symlinks in addition to, and
shadowed by, device symlinks in the caller's logon session device
directory. The SYSTEM logon uses "\GLOBAL??" for its logon session
devices, so it always creates and accesses global device aliases.
In the Windows API, the NT API "\??\" prefix maps to "\\.\" or "\\?\"
UNC-style paths. The former is more common if just the device is
accessed, and the latter is more common for a filesystem path. Thus to
access the "C:" volume device directly, programs typically use
"\\.\C:". Note that just "C:" by itself is a DOS drive-relative path
that expands to the working directory in the mounted filesystem, which
defaults to the root directory. So passing "C:" as the path to open is
actually a reference to a filesystem directory, not to the volume
device, which obviously won't work.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.python.org/pipermail/python-win32/attachments/20210210/e64a756f/attachment-0001.html>
More information about the python-win32
mailing list