4

How do I get the size from a block device or raw disk in Windows if I only know the device name is "\.\PhysicalDrive0" with no file system on it or volume label?

I tried the following:

fd = os.open(r"\.\PhysicalDrive0", os.O_RDONLY)

os.lseek(fd, 0, os.SEEK_END)

it works fine on Linux but always return "OSError: [Errno 22] Invalid argument" on Windows.

I also tried the ctypes.windll.kernel32.GetDiskFreeSpaceExW(), but it seems only work for disk with filesystem and assigned volume label.

What's the proper way to do this for raw disk or block device?

Thanks in advance.

kli
  • 283
  • 1
  • 5
  • 16
  • http://stackoverflow.com/questions/7135398/is-it-possible-to-get-writing-access-to-raw-devices-using-python-with-windows might answere your question. – ojs Jun 28 '13 at 22:45
  • Thanks, it's great info. Now I can write to it, but when I use .seek(0,2) or .seek(0,os.SEEK_END) to get the size, it returns nothing, it seems it doesn't know where is the end of the device. – kli Jun 28 '13 at 23:16
  • Now you seem to be using the seek method associated with a file object (seek, instead of lseek), have you changed the file descriptor returned by os.open into a file object? You do that by using os.fdopen. – ojs Jun 29 '13 at 00:36
  • yea, like fo=os.fdopen(os.open("\\\\.\\PhysicalDrive3", os.O_RDONLY|os.O_BINARY), "rb+"), and fo.seek(0,2) doesn't work – kli Jun 29 '13 at 05:41

2 Answers2

2

Using the wmi module

import wmi
c = wmi.WMI()
[drive] = c.Win32_DiskDrive(Index=0)
print("The disk has %s bytes" % drive.size)
print("Or %s GB" % int(int(drive.size) / 1024**3))

The disk has 320070320640 bytes
Or 298 GB

This code queries the WMI interface for Win32_DiskDrive objects with the Index equal to 0 (so that there is only one result and that result is PHYSICALDRIVE0). The drive object has an attribute called size, which is a string containing the size of the drive in bytes.

Bryce Guinta
  • 3,456
  • 1
  • 35
  • 36
0

I have a solution.. but it's not pretty. Use diskpart. Unfortunately it doesn't give you an exact byte size, if that's needed, but it gives you a human readable string.

import tempfile
import subprocess
import re
import os

def query_diskpart(command):
    """Run command script for diskpart.

    Args:
        command(str): String of all commands to run against diskpart
    Returns:
        String of all diskpart standard output
    Size Effects:
        Creates a temporary file(and then deletes it)
        Creates a subprocess to run diskpart
    """
    with tempfile.NamedTemporaryFile(mode='w', delete=False) as temp_handle:
        temp_handle.write(command)
    # The temporary file needs to be closed before opening with diskpart
    diskpart_handle = subprocess.Popen(["diskpart", '/s', temp_handle.name], stdout=subprocess.PIPE)
    output, _ = diskpart_handle.communicate()

    os.remove(temp_handle.name)
    return output

def match_drive_size(diskpart_output, disk_num):
    """Get drive size from diskpart output.

    Args:
        diskpart_output(str): String of diskpart standard output
        disk_num(int): Number of PhysicalDrive to match against
    Returns:
        Human readable size of drive.
    Raises:
        ValueError if drive doesn't exist.
        """

    # Break up gigantic output string
    output_lines = diskpart_output.decode().split(os.linesep)
    # Apply regular expression to every line, but it should only match one
    matches = [re.match(".*Disk %s\s*(.*)" % disk_num, line) for line in output_lines]
    size = None
    for match in matches:
        if match:
            # Get first subgroup (parens above)
            size_line = match.group(1)
            # Split by whitespace
            size_list = re.split("\s*", size_line)
            # Merge numerical value + units
            # ['256', 'GB'] becomes 256GB
            size = ''.join(size_list[1:3])
            break
    else:
        raise ValueError("PHYSICALDRIVE%s does not exist", disk_num)
    return size

def get_drive_size(disk_num):
    """Get Windows Drive size.

    Args:
        disk_num(int): The Physical Drive Number
            e.g. for PHYSICALDRIVE0 put 0
    Returns:
        Human readable string of the drive size
    """
    output = query_diskpart("list disk\n")
    drive_size = match_drive_size(output, disk_num)
    return drive_size

if __name__ == "__main__":
    print(get_drive_size(0))
Bryce Guinta
  • 3,456
  • 1
  • 35
  • 36