From ec3e20a2d1edddb0558f9d32e2b367904ccdde88 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 28 Jun 2019 18:01:59 +0200 Subject: [PATCH] bpo-37412: Fix os.getcwd() for long path on Windows (GH-14424) * Fix test for integer overflow. * Add an unit test. --- Lib/test/test_os.py | 55 +++++++++++++++++++++++++++++++++++++++++++ Modules/posixmodule.c | 2 +- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 18cd78b55f..2e73906f93 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -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) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 197607c9cb..b848710281 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -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 { -- 2.40.0