# HG changeset patch # User Matt Harbison # Date 2017-12-28 06:35:41 # Node ID 4be2befbec25e1dfa1fe66abdbe93b64ca7aab70 # Parent 9b3f95d9783d99298232a6721a3e5fef5b277155 cext: implement osutil.getfstype() on Windows 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'll wait until this is accepted before implementing the pure version. It isn't clear to me if the API version should be bumped, but it's new to Windows, so I assume the answer is "yes". 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 --git a/mercurial/cext/osutil.c b/mercurial/cext/osutil.c --- a/mercurial/cext/osutil.c +++ b/mercurial/cext/osutil.c @@ -1268,6 +1268,84 @@ bail: PyMem_Free(name); return file_obj; } + +static PyObject *getfstype(PyObject *self, PyObject *args) +/* given a directory path, return filesystem type name (best-effort) */ +{ + const char *path = NULL; + char *fullpath = NULL; + char *volume = NULL; + DWORD size = 0; + char fstype[MAX_PATH + 1]; + + if (!PyArg_ParseTuple(args, "s", &path)) + return NULL; + + size = GetFullPathName(path, 0, NULL, NULL); + + if (size == 0) { + PyErr_SetFromWindowsErrWithFilename(GetLastError(), path); + return NULL; + } + + fullpath = calloc(size, sizeof(fullpath[0])); + volume = calloc(size, sizeof(volume[0])); + + if (!fullpath || !volume) { + PyErr_SetString(PyExc_MemoryError, "out of memory"); + goto bail; + } + + if (!GetFullPathName(path, size, fullpath, NULL)) { + PyErr_SetFromWindowsErrWithFilename(GetLastError(), path); + goto bail; + } + + if (!GetVolumePathName(fullpath, volume, size)) { + PyErr_SetFromWindowsErrWithFilename(GetLastError(), path); + goto bail; + } + + /* Even though the documentation says SMB doesn't support volume management + * functions, passing an SMB path to GetVolumeInformation() returns 'NTFS'. + * So bail unless this is known to _not_ be a network drive. */ + switch (GetDriveType(volume)) { + case DRIVE_FIXED: + case DRIVE_REMOVABLE: + case DRIVE_CDROM: + case DRIVE_RAMDISK: + break; + + case DRIVE_REMOTE: + free(fullpath); + free(volume); + return Py_BuildValue("s", "cifs"); + + case DRIVE_UNKNOWN: + case DRIVE_NO_ROOT_DIR: + default: + free(fullpath); + free(volume); + Py_RETURN_NONE; + } + + if (!GetVolumeInformation(volume, NULL, 0, NULL, NULL, NULL, fstype, + sizeof(fstype))) { + PyErr_SetFromWindowsErrWithFilename(GetLastError(), volume); + goto bail; + } + + free(fullpath); + free(volume); + return Py_BuildValue("s", fstype); + +bail: + if (fullpath) + free(fullpath); + if (volume) + free(volume); + return NULL; +} #endif #ifdef __APPLE__ @@ -1307,13 +1385,13 @@ static PyMethodDef methods[] = { {"setprocname", (PyCFunction)setprocname, METH_VARARGS, "set process title (best-effort)\n"}, #endif -#if defined(HAVE_BSD_STATFS) || defined(HAVE_LINUX_STATFS) + {"unblocksignal", (PyCFunction)unblocksignal, METH_VARARGS, + "change signal mask to unblock a given signal\n"}, +#endif /* ndef _WIN32 */ +#if defined(HAVE_BSD_STATFS) || defined(HAVE_LINUX_STATFS) || defined(_WIN32) {"getfstype", (PyCFunction)getfstype, METH_VARARGS, "get filesystem type (best-effort)\n"}, #endif - {"unblocksignal", (PyCFunction)unblocksignal, METH_VARARGS, - "change signal mask to unblock a given signal\n"}, -#endif /* ndef _WIN32 */ #ifdef __APPLE__ { "isgui", (PyCFunction)isgui, METH_NOARGS, @@ -1323,7 +1401,7 @@ static PyMethodDef methods[] = { {NULL, NULL} }; -static const int version = 2; +static const int version = 3; #ifdef IS_PY3K static struct PyModuleDef osutil_module = { diff --git a/mercurial/policy.py b/mercurial/policy.py --- a/mercurial/policy.py +++ b/mercurial/policy.py @@ -74,7 +74,7 @@ def _importfrom(pkgname, modname): (r'cext', r'bdiff'): 1, (r'cext', r'diffhelpers'): 1, (r'cext', r'mpatch'): 1, - (r'cext', r'osutil'): 2, + (r'cext', r'osutil'): 3, (r'cext', r'parsers'): 4, }