[Python-checkins] bpo-37412: Fix os.getcwd() for long path on Windows (GH-14424) (GH-14451)

Victor Stinner webhook-mailer at python.org
Fri Jun 28 12:23:11 EDT 2019


https://github.com/python/cpython/commit/68c1c398f3534af16309a86ab815b61d2487e6db
commit: 68c1c398f3534af16309a86ab815b61d2487e6db
branch: 3.8
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: Victor Stinner <vstinner at redhat.com>
date: 2019-06-28T18:23:06+02:00
summary:

bpo-37412: Fix os.getcwd() for long path on Windows (GH-14424) (GH-14451)

* Fix test for integer overflow.
* Add an unit test.
(cherry picked from commit ec3e20a2d1edddb0558f9d32e2b367904ccdde88)

Co-authored-by: Victor Stinner <vstinner at redhat.com>

files:
M Lib/test/test_os.py
M Modules/posixmodule.c

diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index 18cd78b55f60..2e73906f93c3 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -22,6 +22,7 @@
 import subprocess
 import sys
 import sysconfig
+import tempfile
 import threading
 import time
 import unittest
@@ -87,6 +88,60 @@ def test_getcwd(self):
         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)
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 4301e876c7ce..bceee89917cb 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -3333,7 +3333,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 {



More information about the Python-checkins mailing list