# HG changeset patch # User Matt Harbison # Date 1514600899 18000 # Node ID 2062f7c2ac83c64f19105a7ed17583aea3a53cc2 # Parent beede158ea8a740efb59b46522f74490381a2086 win32: implement util.getfstype() This will allow NTFS to be added to the hardlink whitelist, and resume creating hardlinks in transactions (which was disabled globally in 07a92bbd02e5; see also e5ce49a30146). I opted to report "cifs" for remote volumes because this shows in `hg debugfs`, which also reports that hardlinks are supported for these volumes. So being able to distinguish it from "unknown" seems useful. The documentation [1] seems to indicate that SMB isn't supported by these functions, but experimenting shows that mapped drives are reported as "NTFS" on Windows 7. I don't have a second Windows machine, but instead shared a temp directory on C:\. In this setup, both of the following were detected as 'cifs' with the explicit GetDriveType() check: Z:\repo>hg ci -A C:\>hg -R \\hostname\temp\repo ci -A # (without Z:\ being mapped) It looks like this is called 6 times to add and commit a single new file, so I'm a little surprised this isn't cached. [1] https://msdn.microsoft.com/en-us/library/windows/desktop/aa364993(v=vs.85).aspx diff -r beede158ea8a -r 2062f7c2ac83 mercurial/win32.py --- a/mercurial/win32.py Sat Dec 30 21:07:03 2017 -0500 +++ b/mercurial/win32.py Fri Dec 29 21:28:19 2017 -0500 @@ -223,6 +223,24 @@ _kernel32.SetFileAttributesA.argtypes = [_LPCSTR, _DWORD] _kernel32.SetFileAttributesA.restype = _BOOL +_DRIVE_UNKNOWN = 0 +_DRIVE_NO_ROOT_DIR = 1 +_DRIVE_REMOVABLE = 2 +_DRIVE_FIXED = 3 +_DRIVE_REMOTE = 4 +_DRIVE_CDROM = 5 +_DRIVE_RAMDISK = 6 + +_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 +428,37 @@ 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 == _DRIVE_REMOTE: + return 'cifs' + elif t not in (_DRIVE_REMOVABLE, _DRIVE_FIXED, _DRIVE_CDROM, + _DRIVE_RAMDISK): + return None + + 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) diff -r beede158ea8a -r 2062f7c2ac83 mercurial/windows.py --- a/mercurial/windows.py Sat Dec 30 21:07:03 2017 -0500 +++ b/mercurial/windows.py Fri Dec 29 21:28:19 2017 -0500 @@ -32,6 +32,7 @@ osutil = policy.importmod(r'osutil') executablepath = win32.executablepath +getfstype = win32.getfstype getuser = win32.getuser hidewindow = win32.hidewindow makedir = win32.makedir @@ -226,13 +227,6 @@ def checklink(path): return False -def getfstype(dirpath): - '''Get the filesystem type name from a directory (best-effort) - - Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc. - ''' - return None - def setbinary(fd): # When run without console, pipes may expose invalid # fileno(), usually set to -1.