From 04bb38e3059dc584d143a7c55a1c86c72d827c59 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 27 Jun 2019 17:10:02 +0200 Subject: [PATCH 1/2] bpo-37412: Fix os.getcwd() for long path on Windows * 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 18cd78b55f6019..997bee99fa07d2 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 the universal + # naming convention (UNC) allows longer paths. 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 197607c9cb101c..b848710281e85c 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 { From 9aad484c9fc9882da82548d2f569c659fe838eef Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 28 Jun 2019 16:25:54 +0200 Subject: [PATCH 2/2] Rephrase comment --- Lib/test/test_os.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 997bee99fa07d2..2e73906f93c3ee 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -90,8 +90,8 @@ def test_getcwd(self): 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 the universal - # naming convention (UNC) allows longer paths. Internally, the os + # 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