def __init__(self, disk_number): super(Disk, self).__init__() self._number = disk_number self._path = r"\\.\PHYSICALDRIVE{}".format(self._number) if not self._is_path_valid(self._path): raise RuntimeError("Invalid disk number: {0}".format(self._number)) self._io = DeviceIoControl(self._path, True)
def _set_disk_attrttributes(self, attributes, mask): set_struct = structures.SET_DISK_ATTRIBUTES(Version=0x28, Persist=constants.TRUE, RelinquishOwnership=0, Attributes=attributes, AttributesMask=mask, Caller=GUID_ZERO) io = DeviceIoControl(self._path, True) io.ioctl_disk_set_disk_attributes(set_struct)
def _filter(volume): try: actual = DeviceIoControl(volume.DeviceID.rstrip( r'\\')).storage_get_device_and_partition_number() except WindowsException as e: if e.winerror == 1: # Floppy/CD/.. return False return actual == expected
def windows(): from infi.devicemanager import DeviceManager from infi.devicemanager.ioctl import DeviceIoControl drive_numbers = [ DeviceIoControl( disk_drive.psuedo_device_object).storage_get_device_number() for disk_drive in DeviceManager().disk_drives ] return [ r"\\.\PHYSICALDRIVE{0}".format(drive_number) for drive_number in drive_numbers if drive_number != -1 ]
def __init__(self, disk_number): super(Disk, self).__init__() self._number = disk_number self._path = r"\\.\PHYSICALDRIVE{}".format(self._number) self._io = DeviceIoControl(self._path, True)
def offline(self): return DeviceIoControl(self._path, True, False).ioctl_volume_offline()
def get_ioctl_interface(self): from infi.devicemanager.ioctl import DeviceIoControl return DeviceIoControl(self.get_pdo())
class Disk(object): def __init__(self, disk_number): super(Disk, self).__init__() self._number = disk_number self._path = r"\\.\PHYSICALDRIVE{}".format(self._number) if not self._is_path_valid(self._path): raise RuntimeError("Invalid disk number: {0}".format(self._number)) self._io = DeviceIoControl(self._path, True) def __repr__(self): return "Disk <{}>".format(self._path) @cached_method def _get_layout(self): """:returns: a DRIVE_LAYOUT_INFORMATION_EX Structure""" return self._io.ioctl_disk_get_drive_layout_ex() def _set_layout(self, layout): self._io.ioctl_disk_set_drive_layout_ex(layout) self.wait_for_all_volumes() self.clear_cached_properties() def _get_disk_drives(self): from infi.diskmanagement import wmi client = wmi.WmiClient() drives = wmi.get_disk_drives(client) return drives def _get_volumes_cluster_sizes(self): from infi.diskmanagement import wmi client = wmi.WmiClient() sizes = wmi.get_volumes_cluster_sizes(client) return sizes def _is_path_valid(self, path): return path in self._get_disk_drives() def wait_for_all_volumes(self): from time import sleep from waiting import wait def predicate(): try: _ = [partition.get_volume().get_volume_guid() for partition in self.get_partitions()] return True except: logger.exception("Exception in wait_for_all_volumes") return False wait(predicate, timeout_seconds=30) def _update_layout(self): self._set_layout(self._get_layout()) @cached_method def get_size_in_bytes(self): import infi.devicemanager.ioctl return infi.devicemanager.ioctl.DeviceIoControl(self._path).disk_get_drive_geometry_ex() def _get_named_type(self): if self.is_gpt(): return 'gpt' elif self.is_mbr(): return 'mbr' else: return 'raw' def is_gpt(self): return self._get_layout().PartitionStyle == PARTITION_STYLE_GPT def is_mbr(self): return self._get_layout().PartitionStyle == PARTITION_STYLE_MBR def is_raw(self): return self._get_layout().PartitionStyle == PARTITION_STYLE_RAW def is_dynamic(self): all_partitions = [Partition(self, struct) for struct in self._get_layout().PartitionEntry] return any(partition.is_ldm() for partition in all_partitions) def clear_cached_properties(self): clear_cache(self) def _has_extended_partition(self): return len(self._get_layout().PartitionEntry) >= 3 and \ not is_zero(self._get_layout().PartitionEntry[3].PartitionLength) def _iter_partitions_mbr(self): for struct in self._get_layout().PartitionEntry[0:3]: # The first three MBR partitions are always primary (can be empty entries) partition = Partition(self, struct) if partition.is_empty(): continue yield partition if not self._has_extended_partition(): return for struct in self._get_layout().PartitionEntry[4::4]: partition = Partition(self, struct) if partition.is_empty(): continue yield partition def _iter_partitions_raw(self): pass def _iter_partitions_gpt(self): # When creating a GPT drive in Windows, the first partition is a hidden partition # When creating a VSS snapshot, Windows moves the reseved partition and creates another one before it # This method needs to return the non-hidden partitions # The only logic I see that fits is to yield those after the reserved parititon: # the reserved partition has a known GUID, I can't find a known guid for the VSS partition key_lambda = lambda item: from_large_integer(item.StartingOffset) sorted_by_offset = sorted(self._get_layout().PartitionEntry, key=key_lambda) reserved_partition = [item for item in sorted_by_offset if is_guid_partition(item)] starting_index = 0 if reserved_partition: starting_index = sorted_by_offset.index(reserved_partition[0]) for struct in sorted_by_offset[starting_index+1:]: partition = Partition(self, struct) yield partition def get_partitions(self): return [partition for partition in self.iter_partitions()] def iter_partitions(self): for partition in getattr(self, "_iter_partitions_{}".format(self._get_named_type()))(): yield partition def destroy_partition_table(self): self._io.ioctl_disk_delete_drive_layout() self.clear_cached_properties() def _create_partition_table_mbr(self, alignment_in_bytes=None): signature = generate_signature() self._io.ioctl_disk_create_disk(partition_style=PARTITION_STYLE_MBR) def _create_partition_table_gpt(self, alignment_in_bytes=None): guid = generate_guid() self._io.ioctl_disk_create_disk(partition_style=PARTITION_STYLE_GPT) minimum_required_size_in_bytes = PARTITION_MSFT_RESERVED_SIZE_MIN \ if self.get_size_in_bytes() < PARTITION_MSFT_RESERVED_BAR \ else PARTITION_MSFT_RESERVED_SIZE_MAX size_in_bytes = minimum_required_size_in_bytes if alignment_in_bytes: size_alignment = size_in_bytes % alignment_in_bytes offset_alignment = PARTITION_MSFT_RESERVED_STARTING_OFFSET % alignment_in_bytes if size_in_bytes % alignment_in_bytes: size_in_bytes += (alignment_in_bytes - size_in_bytes % alignment_in_bytes) if offset_alignment: size_in_bytes += (alignment_in_bytes - offset_alignment) Partition.create_guid(self, 1, PARTITION_MSFT_RESERVED_GUID, PARTITION_MSFT_RESERVED_STARTING_OFFSET, size_in_bytes) def create_partition_table(self, type_name, alignment_in_bytes=None): """:param type_name: either 'gpt' or 'mbr'""" getattr(self, "_create_partition_table_{}".format(type_name))(alignment_in_bytes) self.clear_cached_properties() def create_first_partition(self, alignment_in_bytes=None): if self.is_mbr(): offset_in_bytes = FIRST_PRIMARY_PARTITION_OFFSET if alignment_in_bytes: offset_alignment = FIRST_PRIMARY_PARTITION_OFFSET % alignment_in_bytes if offset_alignment: offset_in_bytes += FIRST_PRIMARY_PARTITION_OFFSET - offset_alignment Partition.create_primary(self, start_offset_in_bytes=offset_in_bytes) elif self.is_gpt(): reserved_partition = self._get_layout().PartitionEntry[0] offset_in_bytes = from_large_integer(reserved_partition.StartingOffset) + from_large_integer(reserved_partition.PartitionLength) offset_in_bytes += GPT_PARTITION_OFFSET if alignment_in_bytes: offset_alignment = GPT_PARTITION_OFFSET % alignment_in_bytes if offset_alignment: offset_in_bytes += GPT_PARTITION_OFFSET - offset_alignment size_in_bytes = self.get_size_in_bytes() - offset_in_bytes - GPT_PARTITION_OFFSET Partition.create_guid(self, 2, PARTITION_BASIC_DATA_GUID, offset_in_bytes, size_in_bytes) def is_offline(self): return self._io.ioctl_disk_get_disk_attributes().Attributes & constants.DISK_ATTRIBUTE_OFFLINE def is_online(self): return not self.is_offline() def is_read_only(self): return self._io.ioctl_disk_get_disk_attributes().Attributes & constants.DISK_ATTRIBUTE_READ_ONLY def _set_disk_attrttributes(self, attributes, mask): set_struct = structures.SET_DISK_ATTRIBUTES(Version=0x28, Persist=constants.TRUE, RelinquishOwnership=0, Attributes=attributes, AttributesMask=mask, Caller=GUID_ZERO) io = DeviceIoControl(self._path, True) io.ioctl_disk_set_disk_attributes(set_struct) def online(self): self._set_disk_attrttributes(0, constants.DISK_ATTRIBUTE_OFFLINE) def offline(self): self._set_disk_attrttributes(constants.DISK_ATTRIBUTE_OFFLINE, constants.DISK_ATTRIBUTE_OFFLINE) def read_only(self): self._set_disk_attrttributes(constants.DISK_ATTRIBUTE_READ_ONLY, constants.DISK_ATTRIBUTE_READ_ONLY) def read_write(self): self._set_disk_attrttributes(0, constants.DISK_ATTRIBUTE_READ_ONLY) def get_volume_number(self): self._io.ioctl_volume_query_volume_number()
class Disk(object): def __init__(self, disk_number): super(Disk, self).__init__() self._number = disk_number self._path = r"\\.\PHYSICALDRIVE{}".format(self._number) self._io = DeviceIoControl(self._path, True) def __repr__(self): return "Disk <{}>".format(self._path) @cached_method def _get_layout(self): """:returns: a DRIVE_LAYOUT_INFORMATION_EX Structure""" return self._io.ioctl_disk_get_drive_layout_ex() def _set_layout(self, layout): self._io.ioctl_disk_set_drive_layout_ex(layout) self.wait_for_all_volumes() self.clear_cached_properties() def wait_for_all_volumes(self): from time import sleep from waiting import wait def predicate(): try: _ = [partition.get_volume().get_volume_guid() for partition in self.get_partitions()] return True except: logger.exception("Exception in wait_for_all_volumes") return False wait(predicate, timeout_seconds=30) def _update_layout(self): self._set_layout(self._get_layout()) @cached_method def get_size_in_bytes(self): import infi.devicemanager.ioctl return infi.devicemanager.ioctl.DeviceIoControl(self._path).disk_get_drive_geometry_ex() def _get_named_type(self): if self.is_gpt(): return 'gpt' elif self.is_mbr(): return 'mbr' else: return 'raw' def is_gpt(self): return self._get_layout().PartitionStyle == PARTITION_STYLE_GPT def is_mbr(self): return self._get_layout().PartitionStyle == PARTITION_STYLE_MBR def is_raw(self): return self._get_layout().PartitionStyle == PARTITION_STYLE_RAW def clear_cached_properties(self): clear_cache(self) def _has_extended_partition(self): return len(self._get_layout().PartitionEntry) >= 3 and \ not is_zero(self._get_layout().PartitionEntry[3].PartitionLength) def _iter_partitions_mbr(self): for struct in self._get_layout().PartitionEntry[0:3]: # The first three MBR partitions are always primary (can be empty entries) partition = Partition(self, struct) if partition.is_empty(): continue yield partition if not self._has_extended_partition(): return for struct in self._get_layout().PartitionEntry[4::4]: partition = Partition(self, struct) if partition.is_empty(): continue yield partition def _iter_partitions_raw(self): pass def _iter_partitions_gpt(self): for struct in self._get_layout().PartitionEntry[1:]: partition = Partition(self, struct) yield partition def get_partitions(self): return [partition for partition in self.iter_partitions()] def iter_partitions(self): for partition in getattr(self, "_iter_partitions_{}".format(self._get_named_type()))(): yield partition def destroy_partition_table(self): self._io.ioctl_disk_delete_drive_layout() self.clear_cached_properties() def _create_partition_table_mbr(self): signature = generate_signature() self._io.ioctl_disk_create_disk(partition_style=PARTITION_STYLE_MBR) def _create_partition_table_gpt(self): guid = generate_guid() self._io.ioctl_disk_create_disk(partition_style=PARTITION_STYLE_GPT) size_in_bytes = PARTITION_MSFT_RESERVED_SIZE_MIN if self.get_size_in_bytes() < PARTITION_MSFT_RESERVED_BAR \ else PARTITION_MSFT_RESERVED_SIZE_MAX Partition.create_guid(self, 1, PARTITION_MSFT_RESERVED_GUID, PARTITION_MSFT_RESERVED_STARTING_OFFSET, size_in_bytes) def create_partition_table(self, type_name): """:param type_name: either 'gpt' or 'mbr'""" getattr(self, "_create_partition_table_{}".format(type_name))() self.clear_cached_properties() def create_first_partition(self): if self.is_mbr(): Partition.create_primary(self) elif self.is_gpt(): reserved_partition_size = from_large_integer(self._get_layout().PartitionEntry[0].PartitionLength) offset_in_bytes = reserved_partition_size + (1024 * 1024) size_in_bytes = self.get_size_in_bytes() - offset_in_bytes Partition.create_guid(self, 2, PARTITION_BASIC_DATA_GUID, offset_in_bytes, size_in_bytes) def is_offline(self): return self._io.ioctl_disk_get_disk_attributes().Attributes & constants.DISK_ATTRIBUTE_OFFLINE def is_online(self): return not self.is_offline() def is_read_only(self): return self._io.ioctl_disk_get_disk_attributes().Attributes & constants.DISK_ATTRIBUTE_READ_ONLY def _set_disk_attrttributes(self, attributes, mask): set_struct = structures.SET_DISK_ATTRIBUTES(Version=0x28, Persist=constants.TRUE, RelinquishOwnership=0, Attributes=attributes, AttributesMask=mask, Caller=GUID_ZERO) io = DeviceIoControl(self._path, True) io.ioctl_disk_set_disk_attributes(set_struct) def online(self): self._set_disk_attrttributes(0, constants.DISK_ATTRIBUTE_OFFLINE) def offline(self): self._set_disk_attrttributes(constants.DISK_ATTRIBUTE_OFFLINE, constants.DISK_ATTRIBUTE_OFFLINE) def read_only(self): self._set_disk_attrttributes(constants.DISK_ATTRIBUTE_READ_ONLY, constants.DISK_ATTRIBUTE_READ_ONLY) def read_write(self): self._set_disk_attrttributes(0, constants.DISK_ATTRIBUTE_READ_ONLY) def get_volume_number(self): self._io.ioctl_volume_query_volume_number()
def __init__(self, disk, partition, mount_manager=None): super(Volume, self).__init__() self._disk = disk self._partition = partition self._mount_manager = mount_manager or MountManager() self._io = DeviceIoControl(self._path, False)
class Disk(object): def __init__(self, disk_number): super(Disk, self).__init__() self._number = disk_number self._path = r"\\.\PHYSICALDRIVE{}".format(self._number) if not self._is_path_valid(self._path): raise RuntimeError("Invalid disk number: {0}".format(self._number)) self._io = DeviceIoControl(self._path, True) def __repr__(self): return "Disk <{}>".format(self._path) @cached_method def _get_layout(self): """:returns: a DRIVE_LAYOUT_INFORMATION_EX Structure""" return self._io.ioctl_disk_get_drive_layout_ex() def _set_layout(self, layout): self._io.ioctl_disk_set_drive_layout_ex(layout) self.wait_for_all_volumes() self.clear_cached_properties() def _get_disk_drives(self): from infi.diskmanagement import wmi client = wmi.WmiClient() drives = wmi.get_disk_drives(client) return drives def _is_path_valid(self, path): return path in self._get_disk_drives() def wait_for_all_volumes(self): from time import sleep from waiting import wait def predicate(): try: _ = [ partition.get_volume().get_volume_guid() for partition in self.get_partitions() ] return True except: logger.exception("Exception in wait_for_all_volumes") return False wait(predicate, timeout_seconds=30) def _update_layout(self): self._set_layout(self._get_layout()) @cached_method def get_size_in_bytes(self): import infi.devicemanager.ioctl return infi.devicemanager.ioctl.DeviceIoControl( self._path).disk_get_drive_geometry_ex() def _get_named_type(self): if self.is_gpt(): return 'gpt' elif self.is_mbr(): return 'mbr' else: return 'raw' def is_gpt(self): return self._get_layout().PartitionStyle == PARTITION_STYLE_GPT def is_mbr(self): return self._get_layout().PartitionStyle == PARTITION_STYLE_MBR def is_raw(self): return self._get_layout().PartitionStyle == PARTITION_STYLE_RAW def clear_cached_properties(self): clear_cache(self) def _has_extended_partition(self): return len(self._get_layout().PartitionEntry) >= 3 and \ not is_zero(self._get_layout().PartitionEntry[3].PartitionLength) def _iter_partitions_mbr(self): for struct in self._get_layout().PartitionEntry[0:3]: # The first three MBR partitions are always primary (can be empty entries) partition = Partition(self, struct) if partition.is_empty(): continue yield partition if not self._has_extended_partition(): return for struct in self._get_layout().PartitionEntry[4::4]: partition = Partition(self, struct) if partition.is_empty(): continue yield partition def _iter_partitions_raw(self): pass def _iter_partitions_gpt(self): # When creating a GPT drive in Windows, the first partition is a hidden partition # When creating a VSS snapshot, Windows moves the reseved partition and creates another one before it # This method needs to return the non-hidden partitions # The only logic I see that fits is to yield those after the reserved parititon: # the reserved partition has a known GUID, I can't find a known guid for the VSS partition key_lambda = lambda item: from_large_integer(item.StartingOffset) sorted_by_offset = sorted(self._get_layout().PartitionEntry, key=key_lambda) reserved_partition = [ item for item in sorted_by_offset if is_guid_partition(item) ] starting_index = 0 if reserved_partition: starting_index = sorted_by_offset.index(reserved_partition[0]) for struct in sorted_by_offset[starting_index + 1:]: partition = Partition(self, struct) yield partition def get_partitions(self): return [partition for partition in self.iter_partitions()] def iter_partitions(self): for partition in getattr( self, "_iter_partitions_{}".format(self._get_named_type()))(): yield partition def destroy_partition_table(self): self._io.ioctl_disk_delete_drive_layout() self.clear_cached_properties() def _create_partition_table_mbr(self, alignment_in_bytes=None): signature = generate_signature() self._io.ioctl_disk_create_disk(partition_style=PARTITION_STYLE_MBR) def _create_partition_table_gpt(self, alignment_in_bytes=None): guid = generate_guid() self._io.ioctl_disk_create_disk(partition_style=PARTITION_STYLE_GPT) minimum_required_size_in_bytes = PARTITION_MSFT_RESERVED_SIZE_MIN \ if self.get_size_in_bytes() < PARTITION_MSFT_RESERVED_BAR \ else PARTITION_MSFT_RESERVED_SIZE_MAX size_in_bytes = minimum_required_size_in_bytes if alignment_in_bytes: size_alignment = size_in_bytes % alignment_in_bytes offset_alignment = PARTITION_MSFT_RESERVED_STARTING_OFFSET % alignment_in_bytes if size_in_bytes % alignment_in_bytes: size_in_bytes += (alignment_in_bytes - size_in_bytes % alignment_in_bytes) if offset_alignment: size_in_bytes += (alignment_in_bytes - offset_alignment) Partition.create_guid(self, 1, PARTITION_MSFT_RESERVED_GUID, PARTITION_MSFT_RESERVED_STARTING_OFFSET, size_in_bytes) def create_partition_table(self, type_name, alignment_in_bytes=None): """:param type_name: either 'gpt' or 'mbr'""" getattr( self, "_create_partition_table_{}".format(type_name))(alignment_in_bytes) self.clear_cached_properties() def create_first_partition(self, alignment_in_bytes=None): if self.is_mbr(): offset_in_bytes = FIRST_PRIMARY_PARTITION_OFFSET if alignment_in_bytes: offset_alignment = FIRST_PRIMARY_PARTITION_OFFSET % alignment_in_bytes if offset_alignment: offset_in_bytes += FIRST_PRIMARY_PARTITION_OFFSET - offset_alignment Partition.create_primary(self, start_offset_in_bytes=offset_in_bytes) elif self.is_gpt(): reserved_partition = self._get_layout().PartitionEntry[0] offset_in_bytes = from_large_integer( reserved_partition.StartingOffset) + from_large_integer( reserved_partition.PartitionLength) offset_in_bytes += GPT_PARTITION_OFFSET if alignment_in_bytes: offset_alignment = GPT_PARTITION_OFFSET % alignment_in_bytes if offset_alignment: offset_in_bytes += GPT_PARTITION_OFFSET - offset_alignment size_in_bytes = self.get_size_in_bytes( ) - offset_in_bytes - GPT_PARTITION_OFFSET Partition.create_guid(self, 2, PARTITION_BASIC_DATA_GUID, offset_in_bytes, size_in_bytes) def is_offline(self): return self._io.ioctl_disk_get_disk_attributes( ).Attributes & constants.DISK_ATTRIBUTE_OFFLINE def is_online(self): return not self.is_offline() def is_read_only(self): return self._io.ioctl_disk_get_disk_attributes( ).Attributes & constants.DISK_ATTRIBUTE_READ_ONLY def _set_disk_attrttributes(self, attributes, mask): set_struct = structures.SET_DISK_ATTRIBUTES(Version=0x28, Persist=constants.TRUE, RelinquishOwnership=0, Attributes=attributes, AttributesMask=mask, Caller=GUID_ZERO) io = DeviceIoControl(self._path, True) io.ioctl_disk_set_disk_attributes(set_struct) def online(self): self._set_disk_attrttributes(0, constants.DISK_ATTRIBUTE_OFFLINE) def offline(self): self._set_disk_attrttributes(constants.DISK_ATTRIBUTE_OFFLINE, constants.DISK_ATTRIBUTE_OFFLINE) def read_only(self): self._set_disk_attrttributes(constants.DISK_ATTRIBUTE_READ_ONLY, constants.DISK_ATTRIBUTE_READ_ONLY) def read_write(self): self._set_disk_attrttributes(0, constants.DISK_ATTRIBUTE_READ_ONLY) def get_volume_number(self): self._io.ioctl_volume_query_volume_number()
def resize(self, size_in_bytes): return DeviceIoControl(self._path, True, True).ioctl_extend_volume(size_in_bytes)
def _filter(volume): actual = DeviceIoControl(volume.DeviceID.rstrip(r'\\')).storage_get_device_and_partition_number() return actual == expected
def _get_device_and_partition_number(self): from infi.devicemanager.ioctl import DeviceIoControl return DeviceIoControl( self._path).storage_get_device_and_partition_number()