Add support for path-mounted drives.
This commit is contained in:
parent
03256e2a79
commit
e23bd1131f
1 changed files with 103 additions and 22 deletions
|
@ -1,28 +1,63 @@
|
||||||
'''
|
'''
|
||||||
NOTE: I have not yet figured out how to get a list of volumes that are mounted
|
This module provides functions for getting information about logical drives
|
||||||
to folders instead of drive letters. I think this is the API I need:
|
on Windows.
|
||||||
https://docs.microsoft.com/en-us/windows/win32/fileio/enumerating-volume-mount-points
|
|
||||||
'''
|
'''
|
||||||
import ctypes
|
import ctypes
|
||||||
|
import re
|
||||||
import string
|
import string
|
||||||
|
kernel32 = ctypes.windll.kernel32
|
||||||
|
|
||||||
def get_drive_info(letter):
|
def get_all_volumes():
|
||||||
'''
|
'''
|
||||||
Given a drive letter, return a dictionary containing its attributes.
|
Return a list of volume paths like \\?\Volume{GUID}\ for all volumes,
|
||||||
|
whether they are mounted or not.
|
||||||
|
|
||||||
|
Note: This will include recovery / EFI partitions, which may not be what
|
||||||
|
you're looking for. Also see get_drive_letters and get_drive_mounts.
|
||||||
|
|
||||||
|
Thank you Duncan.
|
||||||
|
https://stackoverflow.com/a/3075879
|
||||||
|
'''
|
||||||
|
volumes = []
|
||||||
|
buf = ctypes.create_unicode_buffer(1024)
|
||||||
|
length = ctypes.c_int32()
|
||||||
|
handle = kernel32.FindFirstVolumeW(buf, ctypes.sizeof(buf))
|
||||||
|
if handle:
|
||||||
|
volumes.append(buf.value)
|
||||||
|
while kernel32.FindNextVolumeW(handle, buf, ctypes.sizeof(buf)):
|
||||||
|
volumes.append(buf.value)
|
||||||
|
kernel32.FindVolumeClose(handle)
|
||||||
|
return volumes
|
||||||
|
|
||||||
|
def get_drive_info(path):
|
||||||
|
'''
|
||||||
|
Given a drive path as either:
|
||||||
|
- a letter like C or C:\, or
|
||||||
|
- a mount path, or
|
||||||
|
- a volume path like \\?\Volume{GUID},
|
||||||
|
return a dictionary containing its attributes.
|
||||||
|
|
||||||
Thanks Nicholas Orlowski
|
Thanks Nicholas Orlowski
|
||||||
http://stackoverflow.com/a/12056414
|
http://stackoverflow.com/a/12056414
|
||||||
'''
|
'''
|
||||||
kernel32 = ctypes.windll.kernel32
|
letter_match = re.match(r'^([A-Z])(|:|:\\)$', path)
|
||||||
|
if letter_match:
|
||||||
|
letter = letter_match.group(1)
|
||||||
|
path = f'{letter}:\\'
|
||||||
|
|
||||||
|
if path.startswith('\\\\?\\Volume{'):
|
||||||
|
mount = get_volume_mount(path)
|
||||||
|
else:
|
||||||
|
mount = path
|
||||||
|
|
||||||
name_buffer = ctypes.create_unicode_buffer(1024)
|
name_buffer = ctypes.create_unicode_buffer(1024)
|
||||||
filesystem_buffer = ctypes.create_unicode_buffer(1024)
|
filesystem_buffer = ctypes.create_unicode_buffer(1024)
|
||||||
serial_number = None
|
serial_number = None
|
||||||
max_component_length = None
|
max_component_length = None
|
||||||
file_system_flags = None
|
file_system_flags = None
|
||||||
|
|
||||||
letter = letter.rstrip(':\\/')
|
|
||||||
drive_active = kernel32.GetVolumeInformationW(
|
drive_active = kernel32.GetVolumeInformationW(
|
||||||
ctypes.c_wchar_p(f'{letter}:\\'),
|
ctypes.c_wchar_p(path),
|
||||||
name_buffer,
|
name_buffer,
|
||||||
ctypes.sizeof(name_buffer),
|
ctypes.sizeof(name_buffer),
|
||||||
serial_number,
|
serial_number,
|
||||||
|
@ -35,21 +70,25 @@ def get_drive_info(letter):
|
||||||
'active': bool(drive_active),
|
'active': bool(drive_active),
|
||||||
'filesystem': filesystem_buffer.value,
|
'filesystem': filesystem_buffer.value,
|
||||||
'name': name_buffer.value,
|
'name': name_buffer.value,
|
||||||
|
'mount': mount,
|
||||||
}
|
}
|
||||||
return info
|
return info
|
||||||
|
|
||||||
def get_drive_letters():
|
def get_drive_letters():
|
||||||
'''
|
'''
|
||||||
Return a list of all connected drive letters.
|
Return a list of all connected drive letters as single-character strings.
|
||||||
|
|
||||||
|
Drives which are mounted to paths instead of letters will not be returned.
|
||||||
|
Use get_drive_mounts instead.
|
||||||
|
|
||||||
Thanks RichieHindle
|
Thanks RichieHindle
|
||||||
http://stackoverflow.com/a/827398
|
http://stackoverflow.com/a/827398
|
||||||
|
|
||||||
"If the function succeeds, the return value is a bitmask representing the
|
|
||||||
currently available disk drives. Bit position 0 (the least-significant bit)
|
|
||||||
is drive A, bit position 1 is drive B, bit position 2 is drive C, and so on."
|
|
||||||
https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getlogicaldrives
|
|
||||||
'''
|
'''
|
||||||
|
# "If the function succeeds, the return value is a bitmask representing the
|
||||||
|
# currently available disk drives. Bit position 0 (the least-significant
|
||||||
|
# bit) is drive A, bit position 1 is drive B, bit position 2 is drive C,
|
||||||
|
# and so on."
|
||||||
|
# https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getlogicaldrives
|
||||||
letters = []
|
letters = []
|
||||||
bitmask = ctypes.windll.kernel32.GetLogicalDrives()
|
bitmask = ctypes.windll.kernel32.GetLogicalDrives()
|
||||||
for letter in string.ascii_uppercase:
|
for letter in string.ascii_uppercase:
|
||||||
|
@ -58,22 +97,64 @@ def get_drive_letters():
|
||||||
bitmask >>= 1
|
bitmask >>= 1
|
||||||
return letters
|
return letters
|
||||||
|
|
||||||
def get_drive_letter_by_name(name):
|
def get_drive_map():
|
||||||
|
'''
|
||||||
|
Return a dict of {mount: info} for all connected drives, where mount is
|
||||||
|
either a drive letter or mount path, and info is the dict returned
|
||||||
|
by get_drive_info.
|
||||||
|
'''
|
||||||
|
drives = {mount: get_drive_info(mount) for mount in get_drive_mounts()}
|
||||||
|
return drives
|
||||||
|
|
||||||
|
def get_drive_mounts():
|
||||||
|
'''
|
||||||
|
Return a list of all connected drives as either:
|
||||||
|
- a letter like C:\ if the volume has a letter, or
|
||||||
|
- a mount path if the volume does not have a letter.
|
||||||
|
'''
|
||||||
|
mounts = []
|
||||||
|
for volume in get_all_volumes():
|
||||||
|
mount = get_volume_mount(volume)
|
||||||
|
if mount:
|
||||||
|
mounts.append(mount)
|
||||||
|
return mounts
|
||||||
|
|
||||||
|
def get_drive_mount_by_name(name):
|
||||||
'''
|
'''
|
||||||
Given the name of a drive (the user-customizable name seen in Explorer),
|
Given the name of a drive (the user-customizable name seen in Explorer),
|
||||||
return the letter of that drive.
|
return the letter or mount path of that drive.
|
||||||
|
|
||||||
Raises KeyError if it is not found.
|
Raises KeyError if it is not found.
|
||||||
'''
|
'''
|
||||||
drives = get_drive_map()
|
drives = get_drive_map()
|
||||||
for (letter, info) in drives.items():
|
for (mount, info) in drives.items():
|
||||||
if info['name'] == name:
|
if info['name'] == name:
|
||||||
return letter
|
return mount
|
||||||
raise KeyError(name)
|
raise KeyError(name)
|
||||||
|
|
||||||
def get_drive_map():
|
def get_volume_mount(volume):
|
||||||
'''
|
'''
|
||||||
Return a dict of {letter: info}.
|
Given a volume path like \\?\Volume{GUID}\, return either:
|
||||||
|
- a letter like C:\ if the volume has a letter, or
|
||||||
|
- a mount path if the volume does not have a letter, or
|
||||||
|
- emptystring if the volume is not mounted at all.
|
||||||
|
|
||||||
|
Thank you Duncan.
|
||||||
|
https://stackoverflow.com/a/3075879
|
||||||
|
|
||||||
|
Note: The API function is named "GetVolumePathNames..." in the plural,
|
||||||
|
and Duncan's original answer uses .split('\0'), but in my testing the
|
||||||
|
drives always contain only a single name.
|
||||||
|
If the drive has a letter and mount path, only the letter is returned.
|
||||||
|
If it has two mount paths, only the first one is returned.
|
||||||
|
So, I'll just use a single return value until further notice.
|
||||||
'''
|
'''
|
||||||
drives = {letter: get_drive_info(letter) for letter in get_drive_letters()}
|
buf = ctypes.create_unicode_buffer(1024)
|
||||||
return drives
|
length = ctypes.c_int32()
|
||||||
|
kernel32.GetVolumePathNamesForVolumeNameW(
|
||||||
|
ctypes.c_wchar_p(volume),
|
||||||
|
buf,
|
||||||
|
ctypes.sizeof(buf),
|
||||||
|
ctypes.pointer(length),
|
||||||
|
)
|
||||||
|
return buf.value
|
||||||
|
|
Loading…
Reference in a new issue