Home > Enterprise >  Python and GetVolumeInformationW and GetDriveTypeW: list all disks and get disk info and filesystem
Python and GetVolumeInformationW and GetDriveTypeW: list all disks and get disk info and filesystem

Time:10-25

On python3 using the windows API: https://docs.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']

CodePudding user response:

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

CodePudding user response:

For future reference, I'll leave the complete code here on how to list all disks and get their fstab-like information

import ctypes as ct
import string
from ctypes import wintypes as w
from enum import IntFlag
from pathlib import Path

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()

lst_available_disks = [f'{d}:\\' for d in string.ascii_uppercase if Path(f'{d}:\\').exists()]
lst_disk_types = ['DRIVE_UNKNOWN', 'DRIVE_NO_ROOT_DIR', 'DRIVE_REMOVABLE', 'DRIVE_FIXED', 'DRIVE_REMOTE', 'DRIVE_CDROM', 'DRIVE_RAMDISK']

for target_disk in lst_available_disks:
    disk_type_index = dll.GetDriveTypeW(target_disk)

    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
    disk_label = volumeNameBuffer.value
    fs_type = fileSystemNameBuffer.value
    max_length = max_component_length.value
    flags = FSFlags(file_system_flags.value)
    serial = serial_number.value

    if 'FILE_READ_ONLY_VOLUME' in str(flags):
        read_write_status = 'ro'
    else:
        read_write_status = 'rw'

    extra_tab_label = ''
    if len(disk_label) < 8: extra_tab_label = '\t'
    extra_tab_type = ''
    if len(lst_disk_types[disk_type_index]) < 12: extra_tab_type = '\t'

    print(f'{disk_label}{extra_tab_label}\t{mount_point}\t{fs_type}\t{max_length}\t{serial}\t{read_write_status},{lst_disk_types[disk_type_index]}{extra_tab_type}\t{flags=}\n')

Output (Just replaces disk serial with zeros):

Win10           C:\     NTFS    255     0000000000      rw,DRIVE_FIXED          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>     

                D:\     FAT32   255     0000000000      rw,DRIVE_FIXED          flags=<FSFlags.FILE_SUPPORTS_ENCRYPTION|512|FILE_UNICODE_ON_DISK|FILE_CASE_PRESERVED_NAMES: 131590>

DRV061107       E:\     CDFS    110     0000000000      ro,DRIVE_CDROM          flags=<FSFlags.FILE_SUPPORTS_OPEN_BY_FILE_ID|FILE_READ_ONLY_VOLUME|FILE_UNICODE_ON_DISK|FILE_CASE_SENSITIVE_SEARCH: 17301509>

Ventoy          F:\     exFAT   255     0000000000      rw,DRIVE_REMOVABLE      flags=<FSFlags.FILE_SUPPORTS_ENCRYPTION|512|FILE_UNICODE_ON_DISK|FILE_CASE_PRESERVED_NAMES: 131590>

VTOYEFI         G:\     FAT     255     0000000000      rw,DRIVE_REMOVABLE      flags=<FSFlags.FILE_SUPPORTS_ENCRYPTION|512|FILE_UNICODE_ON_DISK|FILE_CASE_PRESERVED_NAMES: 131590>
  • Related