Patchwork [1,of,5] pure: implement osutil.getfstype() on Windows

login
register
mail settings
Submitter Matt Harbison
Date Dec. 30, 2017, 6:37 a.m.
Message ID <47921e6734f8f9e1536a.1514615850@Envy>
Download mbox | patch
Permalink /patch/26501/
State Accepted, archived
Headers show

Comments

Matt Harbison - Dec. 30, 2017, 6:37 a.m.
# HG changeset patch
# User Matt Harbison <matt_harbison@yahoo.com>
# Date 1514600899 18000
#      Fri Dec 29 21:28:19 2017 -0500
# Node ID 47921e6734f8f9e1536a8bd9e4b33e77baca7337
# Parent  a210a1a19734a60d52e2b718dfcfccdd5356c03b
pure: implement osutil.getfstype() on Windows
Yuya Nishihara - Dec. 31, 2017, 1:49 a.m.
On Sat, 30 Dec 2017 01:37:30 -0500, Matt Harbison wrote:
> # HG changeset patch
> # User Matt Harbison <matt_harbison@yahoo.com>
> # Date 1514600899 18000
> #      Fri Dec 29 21:28:19 2017 -0500
> # Node ID 47921e6734f8f9e1536a8bd9e4b33e77baca7337
> # Parent  a210a1a19734a60d52e2b718dfcfccdd5356c03b
> pure: implement osutil.getfstype() on Windows

Looks good.

> +def getfstype(path):
> +    """Get the filesystem type name from a directory or file (best-effort)
> +
> +    Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
> +    """
> +    # realpath() calls GetFullPathName()
> +    realpath = os.path.realpath(path)
> +
> +    size = len(realpath) + 1
> +    buf = ctypes.create_string_buffer(size)
> +
> +    if not _kernel32.GetVolumePathNameA(realpath, ctypes.byref(buf), size):
> +        raise ctypes.WinError() # Note: WinError is a function
> +
> +    t = _kernel32.GetDriveTypeA(buf.value)
> +
> +    if t == 4:  # DRIVE_REMOTE
> +        return 'cifs'
> +    elif t not in (2, 3, 5, 6):
> +        # DRIVE_UNKNOWN, DRIVE_NO_ROOT_DIR, unknown

Perhaps named constants are preferred.

_DRIVE_REMOTE = 4
...

Patch

diff --git a/mercurial/pure/osutil.py b/mercurial/pure/osutil.py
--- a/mercurial/pure/osutil.py
+++ b/mercurial/pure/osutil.py
@@ -154,6 +154,9 @@ 
 
 else:
     import msvcrt
+    from .. import win32
+
+    getfstype = win32.getfstype
 
     _kernel32 = ctypes.windll.kernel32
 
diff --git a/mercurial/win32.py b/mercurial/win32.py
--- a/mercurial/win32.py
+++ b/mercurial/win32.py
@@ -223,6 +223,16 @@ 
 _kernel32.SetFileAttributesA.argtypes = [_LPCSTR, _DWORD]
 _kernel32.SetFileAttributesA.restype = _BOOL
 
+_kernel32.GetDriveTypeA.argtypes = [_LPCSTR]
+_kernel32.GetDriveTypeA.restype = _UINT
+
+_kernel32.GetVolumeInformationA.argtypes = [_LPCSTR, ctypes.c_void_p, _DWORD,
+    ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, _DWORD]
+_kernel32.GetVolumeInformationA.restype = _BOOL
+
+_kernel32.GetVolumePathNameA.argtypes = [_LPCSTR, ctypes.c_void_p, _DWORD]
+_kernel32.GetVolumePathNameA.restype = _BOOL
+
 _kernel32.OpenProcess.argtypes = [_DWORD, _BOOL, _DWORD]
 _kernel32.OpenProcess.restype = _HANDLE
 
@@ -410,6 +420,39 @@ 
         raise ctypes.WinError(_ERROR_INSUFFICIENT_BUFFER)
     return buf.value
 
+def getfstype(path):
+    """Get the filesystem type name from a directory or file (best-effort)
+
+    Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
+    """
+    # realpath() calls GetFullPathName()
+    realpath = os.path.realpath(path)
+
+    size = len(realpath) + 1
+    buf = ctypes.create_string_buffer(size)
+
+    if not _kernel32.GetVolumePathNameA(realpath, ctypes.byref(buf), size):
+        raise ctypes.WinError() # Note: WinError is a function
+
+    t = _kernel32.GetDriveTypeA(buf.value)
+
+    if t == 4:  # DRIVE_REMOTE
+        return 'cifs'
+    elif t not in (2, 3, 5, 6):
+        # DRIVE_UNKNOWN, DRIVE_NO_ROOT_DIR, unknown
+        return None
+
+    # DRIVE_REMOVABLE, DRIVE_FIXED, DRIVE_CDROM, DRIVE_RAMDISK
+
+    size = 256
+    name = ctypes.create_string_buffer(size)
+
+    if not _kernel32.GetVolumeInformationA(buf.value, None, 0, None, None, None,
+            ctypes.byref(name), size):
+        raise ctypes.WinError() # Note: WinError is a function
+
+    return name.value
+
 def getuser():
     '''return name of current user'''
     size = _DWORD(300)