[issue9035] os.path.ismount on windows doesn't support windows mount points

Tim Golden report at bugs.python.org
Tue Jul 30 23:36:49 CEST 2013


Tim Golden added the comment:

issue9035.3.patch has switched to the new memory management API and has 
tweaked the tests slightly for robustness.

This approach does introduce a behavioural change: the root of a SUBSTed 
drive (essentially a symlink into the Dos namespace) will raise an 
OSError because GetVolumePathName returns error 87: invalid parameter. 
So os.path.ismount("F:\\") will fail where F: is the result of running, 
eg, "SUBST F: C:\temp".

I think the simplest thing is to special-case drive roots (which are 
always mount points) and then to apply the new GetVolumePathName logic.

----------
Added file: http://bugs.python.org/file31092/issue9035.3.patch

_______________________________________
Python tracker <report at bugs.python.org>
<http://bugs.python.org/issue9035>
_______________________________________
-------------- next part --------------
diff --git a/Lib/ntpath.py b/Lib/ntpath.py
--- a/Lib/ntpath.py
+++ b/Lib/ntpath.py
@@ -336,15 +336,26 @@
     return True
 
 # Is a path a mount point?  Either a root (with or without drive letter)
-# or an UNC path with at most a / or \ after the mount point.
+# or an UNC path with at most a / or \ after the mount point or a mounted
+# drive. The canonical approach which detects the IO_REPARSE_TAG_MOUNT_POINT
+# fails for drive roots.
 
-def ismount(path):
-    """Test whether a path is a mount point (defined as root of drive)"""
-    seps = _get_bothseps(path)
-    root, rest = splitdrive(path)
-    if root and root[0] in seps:
-        return (not rest) or (rest in seps)
-    return rest in seps
+try:
+    from nt import _getvolumepathname
+    def ismount(path):
+        #
+        # GetVolumePathName will return the directory itself
+        # if it is a mount point (including a drive root).
+        #
+        return abspath(path).rstrip(sep) == abspath(_getvolumepathname(path)).rstrip(sep)
+except ImportError:
+    def ismount(path):
+        """Test whether a path is a mount point (defined as root of drive)"""
+        seps = _get_bothseps(path)
+        root, rest = splitdrive(path)
+        if root and root[0] in seps:
+            return (not rest) or (rest in seps)
+        return rest in seps
 
 
 # Expand paths beginning with '~' or '~user'.
diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py
--- a/Lib/test/test_ntpath.py
+++ b/Lib/test/test_ntpath.py
@@ -256,6 +256,29 @@
                     # dialogs (#4804)
                     ntpath.sameopenfile(-1, -1)
 
+    def test_ismount(self):
+        self.assertTrue(ntpath.ismount("c:\\"))
+        self.assertTrue(ntpath.ismount("C:\\"))
+        self.assertTrue(ntpath.ismount("c:/"))
+        self.assertTrue(ntpath.ismount("C:/"))
+        self.assertTrue(ntpath.ismount("\\\\.\\c:\\"))
+        self.assertTrue(ntpath.ismount("\\\\.\\C:\\"))
+
+        with support.temp_dir() as d:
+            self.assertFalse(ntpath.ismount(d))
+
+        #
+        # Make sure the current folder isn't the root folder
+        # (or any other volume root). The drive-relative
+        # locations below cannot then refer to mount points
+        #
+        drive, path = ntpath.splitdrive(sys.executable)
+        with support.change_cwd(os.path.dirname(sys.executable)):
+            self.assertFalse(ntpath.ismount(drive.lower()))
+            self.assertFalse(ntpath.ismount(drive.upper()))
+
+        self.assertTrue(ntpath.ismount("\\\\localhost\\c$"))
+        self.assertTrue(ntpath.ismount("\\\\localhost\\c$\\"))
 
 class NtCommonTest(test_genericpath.CommonTest, unittest.TestCase):
     pathmodule = ntpath
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -3711,6 +3711,47 @@
     else
         Py_RETURN_FALSE;
 }
+
+PyDoc_STRVAR(posix__getvolumepathname__doc__,
+"Return volume mount point of the specified path.");
+
+/* A helper function for ismount on windows */
+static PyObject *
+posix__getvolumepathname(PyObject *self, PyObject *args)
+{
+    PyObject *po, *result;
+    wchar_t *path, *mountpath=NULL;
+    size_t bufsize;
+    BOOL ret;
+
+    if (!PyArg_ParseTuple(args, "U|:_getvolumepathname", &po))
+        return NULL;
+    path = PyUnicode_AsUnicode(po);
+    if (path == NULL)
+        return NULL;
+
+    /* Volume path should be shorter than entire path */
+    bufsize = max(MAX_PATH, wcslen(path) * 2 * sizeof(wchar_t)+1);
+    mountpath = (wchar_t *)PyMem_Malloc(bufsize);
+    if (mountpath == NULL)
+        return PyErr_NoMemory();
+
+    Py_BEGIN_ALLOW_THREADS
+    ret = GetVolumePathNameW(path, mountpath, bufsize);
+    Py_END_ALLOW_THREADS
+
+    if (!ret) {
+        result = win32_error_object("_getvolumepathname", po);
+        goto exit;
+    }
+    result = PyUnicode_FromWideChar(mountpath, wcslen(mountpath));
+
+exit:
+    PyMem_Free(mountpath);
+    return result;
+}
+/* end of posix__getvolumepathname */
+
 #endif /* MS_WINDOWS */
 
 PyDoc_STRVAR(posix_mkdir__doc__,
@@ -10884,6 +10925,7 @@
     {"_getfinalpathname",       posix__getfinalpathname, METH_VARARGS, NULL},
     {"_isdir",                  posix__isdir, METH_VARARGS, posix__isdir__doc__},
     {"_getdiskusage",           win32__getdiskusage, METH_VARARGS, win32__getdiskusage__doc__},
+    {"_getvolumepathname",      posix__getvolumepathname, METH_VARARGS, posix__getvolumepathname__doc__},
 #endif
 #ifdef HAVE_GETLOADAVG
     {"getloadavg",      posix_getloadavg, METH_NOARGS, posix_getloadavg__doc__},


More information about the Python-bugs-list mailing list