]> granicus.if.org Git - python/commitdiff
bpo-37412: Fix os.getcwd() for long path on Windows (GH-14424)
authorVictor Stinner <vstinner@redhat.com>
Fri, 28 Jun 2019 16:01:59 +0000 (18:01 +0200)
committerGitHub <noreply@github.com>
Fri, 28 Jun 2019 16:01:59 +0000 (18:01 +0200)
* Fix test for integer overflow.
* Add an unit test.

Lib/test/test_os.py
Modules/posixmodule.c

index 18cd78b55f601906aa15fa524abbf9a70e701364..2e73906f93c3ee3e1cb5a3eeeaaccc69d1688d59 100644 (file)
@@ -22,6 +22,7 @@ import stat
 import subprocess
 import sys
 import sysconfig
+import tempfile
 import threading
 import time
 import unittest
@@ -87,6 +88,60 @@ class MiscTests(unittest.TestCase):
         cwd = os.getcwd()
         self.assertIsInstance(cwd, str)
 
+    def test_getcwd_long_path(self):
+        # bpo-37412: On Linux, PATH_MAX is usually around 4096 bytes. On
+        # Windows, MAX_PATH is defined as 260 characters, but Windows supports
+        # longer path if longer paths support is enabled. Internally, the os
+        # module uses MAXPATHLEN which is at least 1024.
+        #
+        # Use a directory name of 200 characters to fit into Windows MAX_PATH
+        # limit.
+        #
+        # On Windows, the test can stop when trying to create a path longer
+        # than MAX_PATH if long paths support is disabled:
+        # see RtlAreLongPathsEnabled().
+        min_len = 2000   # characters
+        dirlen = 200     # characters
+        dirname = 'python_test_dir_'
+        dirname = dirname + ('a' * (dirlen - len(dirname)))
+
+        with tempfile.TemporaryDirectory() as tmpdir:
+            with support.change_cwd(tmpdir):
+                path = tmpdir
+                expected = path
+
+                while True:
+                    cwd = os.getcwd()
+                    self.assertEqual(cwd, expected)
+
+                    need = min_len - (len(cwd) + len(os.path.sep))
+                    if need <= 0:
+                        break
+                    if len(dirname) > need and need > 0:
+                        dirname = dirname[:need]
+
+                    path = os.path.join(path, dirname)
+                    try:
+                        os.mkdir(path)
+                        # On Windows, chdir() can fail
+                        # even if mkdir() succeeded
+                        os.chdir(path)
+                    except FileNotFoundError:
+                        # On Windows, catch ERROR_PATH_NOT_FOUND (3) and
+                        # ERROR_FILENAME_EXCED_RANGE (206) errors
+                        # ("The filename or extension is too long")
+                        break
+                    except OSError as exc:
+                        if exc.errno == errno.ENAMETOOLONG:
+                            break
+                        else:
+                            raise
+
+                    expected = path
+
+                if support.verbose:
+                    print(f"Tested current directory length: {len(cwd)}")
+
     def test_getcwdb(self):
         cwd = os.getcwdb()
         self.assertIsInstance(cwd, bytes)
index 197607c9cb101c10a1e03c3ab1aa355ce0ae9ea4..b848710281e85c85aea3dc409777a5a48d14c988 100644 (file)
@@ -3334,7 +3334,7 @@ posix_getcwd(int use_bytes)
        terminating \0. If the buffer is too small, len includes
        the space needed for the terminator. */
     if (len >= Py_ARRAY_LENGTH(wbuf)) {
-        if (len >= PY_SSIZE_T_MAX / sizeof(wchar_t)) {
+        if (len <= PY_SSIZE_T_MAX / sizeof(wchar_t)) {
             wbuf2 = PyMem_RawMalloc(len * sizeof(wchar_t));
         }
         else {