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 <eryk...@gmail.com> Sent: Tuesday, February 9, 2021 3:05 PM To: python-win32@python.org <python-win32@python.org> Cc: Doug Campbell <wdouglascampb...@hotmail.com> Subject: Re: [python-win32] DeviceIOControl using IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS fails with 'Incorrect function.' On 2/9/21, Doug Campbell <wdouglascampb...@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.
_______________________________________________ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32