Search code examples
pythonwindowsfilesystemsctypesflags

Python and GetVolumeInformationW and GetDriveTypeW: list all disks and get disk info and filesystem flags


On python3 using the windows API: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumeinformationw

How to get these values (file_system_flags, max_component_length and serial_number), ​through this function GetVolumeInformationW, without installing any other external modules?

import ctypes

kernel32 = ctypes.windll.kernel32
volumeNameBuffer = ctypes.create_unicode_buffer(1024)
fileSystemNameBuffer = ctypes.create_unicode_buffer(1024)
serial_number = None
max_component_length = None
file_system_flags = None

target_disk = 'C:\\'
rc = kernel32.GetVolumeInformationW(
    ctypes.c_wchar_p(target_disk),
    volumeNameBuffer, ctypes.sizeof(volumeNameBuffer),
    serial_number,
    max_component_length,
    file_system_flags, 
    fileSystemNameBuffer, ctypes.sizeof(fileSystemNameBuffer)
)

mount_point = target_disk[:-1]
disk_label = volumeNameBuffer.value
fs_type = fileSystemNameBuffer.value
max_length = max_component_length
flags = file_system_flags
serial = serial_number

print(mount_point, disk_label, fs_type, max_length, flags, serial)

Complement: And how do I convert filesystem flags into human readable format?

my_disk_flag = 0x3e706ff

hex_flags = [0x00000002, 0x00000001, 0x20000000, 0x00000010, 0x00040000, 0x00000008, 0x00080000, 0x00100000, 0x00020000, 0x00800000, 0x00400000, 0x00010000, 0x01000000, 0x00000080, 0x00000040, 0x00200000, 0x02000000, 0x00000004, 0x00008000, 0x00000020, 0x08000000]

str_flags = ['FILE_CASE_PRESERVED_NAMES', 'FILE_CASE_SENSITIVE_SEARCH', 'FILE_DAX_VOLUME', 'FILE_FILE_COMPRESSION', 'FILE_NAMED_STREAMS', 'FILE_PERSISTENT_ACLS', 'FILE_READ_ONLY_VOLUME', 'FILE_SEQUENTIAL_WRITE_ONCE', 'FILE_SUPPORTS_ENCRYPTION', 'FILE_SUPPORTS_EXTENDED_ATTRIBUTES', 'FILE_SUPPORTS_HARD_LINKS', 'FILE_SUPPORTS_OBJECT_IDS', 'FILE_SUPPORTS_OPEN_BY_FILE_ID', 'FILE_SUPPORTS_REPARSE_POINTS', 'FILE_SUPPORTS_SPARSE_FILES', 'FILE_SUPPORTS_TRANSACTIONS', 'FILE_SUPPORTS_USN_JOURNAL', 'FILE_UNICODE_ON_DISK', 'FILE_VOLUME_IS_COMPRESSED', 'FILE_VOLUME_QUOTAS', 'FILE_SUPPORTS_BLOCK_REFCOUNTING']

Solution

  • Create an instance of the type and pass it by reference. It is equivalent to declaring a local variable in C and passing its address, e.g. DWORD flags; and passing as &flags to be an output parameter.

    It also help ctypes error checking by declaring the .argtypes and .restype of the function call, which is similar to declaring a C prototype. ctypes.wintypes has many pre-defined types for Windows.

    I've also added an .errcheck attribute that automatically checks the return value and raises Windows exception based on the GetLastError() code, and an enumeration to process the flags into a readable format:

    import ctypes as ct
    from ctypes import wintypes as w
    from enum import IntFlag
    
    class FSFlags(IntFlag):
        FILE_CASE_SENSITIVE_SEARCH        = 0x00000001
        FILE_CASE_PRESERVED_NAMES         = 0x00000002
        FILE_UNICODE_ON_DISK              = 0x00000004
        FILE_PERSISTENT_ACLS              = 0x00000008
        FILE_FILE_COMPRESSION             = 0x00000010
        FILE_VOLUME_QUOTAS                = 0x00000020
        FILE_SUPPORTS_SPARSE_FILES        = 0x00000040
        FILE_SUPPORTS_REPARSE_POINTS      = 0x00000080
        FILE_VOLUME_IS_COMPRESSED         = 0x00008000
        FILE_SUPPORTS_OBJECT_IDS          = 0x00010000
        FILE_SUPPORTS_ENCRYPTION          = 0x00020000
        FILE_NAMED_STREAMS                = 0x00040000
        FILE_READ_ONLY_VOLUME             = 0x00080000
        FILE_SEQUENTIAL_WRITE_ONCE        = 0x00100000
        FILE_SUPPORTS_TRANSACTIONS        = 0x00200000
        FILE_SUPPORTS_HARD_LINKS          = 0x00400000
        FILE_SUPPORTS_EXTENDED_ATTRIBUTES = 0x00800000
        FILE_SUPPORTS_OPEN_BY_FILE_ID     = 0x01000000
        FILE_SUPPORTS_USN_JOURNAL         = 0x02000000
        FILE_SUPPORTS_BLOCK_REFCOUNTING   = 0x08000000
        FILE_DAX_VOLUME                   = 0x20000000
    
    def validate(result,func,args):
        if not result:
            raise ct.WinError(ct.get_last_error())
        return None
    
    dll = ct.WinDLL('kernel32',use_last_error=True)
    dll.GetVolumeInformationW.argtypes = w.LPCWSTR,w.LPWSTR,w.DWORD,w.LPDWORD,w.LPDWORD,w.LPDWORD,w.LPWSTR,w.DWORD
    dll.GetVolumeInformationW.restype = w.BOOL
    dll.GetVolumeInformationW.errcheck = validate
    
    volumeNameBuffer = ct.create_unicode_buffer(w.MAX_PATH + 1)
    fileSystemNameBuffer = ct.create_unicode_buffer(w.MAX_PATH + 1)
    serial_number = w.DWORD()
    max_component_length = w.DWORD() 
    file_system_flags = w.DWORD()
    
    target_disk = 'c:\\'
    
    dll.GetVolumeInformationW(target_disk,
                              volumeNameBuffer, ct.sizeof(volumeNameBuffer),
                              ct.byref(serial_number),
                              ct.byref(max_component_length),
                              ct.byref(file_system_flags),
                              fileSystemNameBuffer, ct.sizeof(fileSystemNameBuffer))
    
    mount_point = target_disk[:-1]
    disk_label = volumeNameBuffer.value
    fs_type = fileSystemNameBuffer.value
    max_length = max_component_length.value
    flags = FSFlags(file_system_flags.value)
    serial = serial_number.value
    
    print(f'{mount_point=}\n{disk_label=}\n{fs_type=}\n{max_length=}\n{flags=}\n{serial=}')
    

    Output example:

    mount_point='c:'
    disk_label=''
    fs_type='NTFS'
    max_length=255
    flags=<FSFlags.FILE_SUPPORTS_USN_JOURNAL|FILE_SUPPORTS_OPEN_BY_FILE_ID|FILE_SUPPORTS_EXTENDED_ATTRIBUTES|FILE_SUPPORTS_HARD_LINKS|FILE_SUPPORTS_TRANSACTIONS|FILE_NAMED_STREAMS|FILE_SUPPORTS_ENCRYPTION|FILE_SUPPORTS_OBJECT_IDS|1024|512|FILE_SUPPORTS_REPARSE_POINTS|FILE_SUPPORTS_SPARSE_FILES|FILE_VOLUME_QUOTAS|FILE_FILE_COMPRESSION|FILE_PERSISTENT_ACLS|FILE_UNICODE_ON_DISK|FILE_CASE_PRESERVED_NAMES|FILE_CASE_SENSITIVE_SEARCH: 65472255>
    serial=3465270344