https://github.com/python/cpython/commit/7407267ce47d95d41acd2e7e6a5cc9c0e70d2ccd
commit: 7407267ce47d95d41acd2e7e6a5cc9c0e70d2ccd
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: barneygale <[email protected]>
date: 2024-05-18T23:12:19Z
summary:

[3.13] GH-118447: Fix handling of unreadable symlinks in `os.path.realpath()` 
(GH-118489) (#119163)

(cherry picked from commit caf6064a1bc15ac344afd78b780188e60b9c628e)

Co-authored-by: Barney Gale <[email protected]>
Co-authored-by: Nice Zombies <[email protected]>

files:
A Misc/NEWS.d/next/Library/2024-05-01-22-24-05.gh-issue-110863.GjYBbq.rst
M Lib/posixpath.py
M Lib/test/test_posixpath.py

diff --git a/Lib/posixpath.py b/Lib/posixpath.py
index b4547d7893b1cd..c04c628de55ee2 100644
--- a/Lib/posixpath.py
+++ b/Lib/posixpath.py
@@ -471,26 +471,26 @@ def realpath(filename, *, strict=False):
             if not stat.S_ISLNK(st.st_mode):
                 path = newpath
                 continue
+            if newpath in seen:
+                # Already seen this path
+                path = seen[newpath]
+                if path is not None:
+                    # use cached value
+                    continue
+                # The symlink is not resolved, so we must have a symlink loop.
+                if strict:
+                    # Raise OSError(errno.ELOOP)
+                    os.stat(newpath)
+                path = newpath
+                continue
+            target = os.readlink(newpath)
         except OSError:
             if strict:
                 raise
             path = newpath
             continue
         # Resolve the symbolic link
-        if newpath in seen:
-            # Already seen this path
-            path = seen[newpath]
-            if path is not None:
-                # use cached value
-                continue
-            # The symlink is not resolved, so we must have a symlink loop.
-            if strict:
-                # Raise OSError(errno.ELOOP)
-                os.stat(newpath)
-            path = newpath
-            continue
         seen[newpath] = None # not resolved symlink
-        target = os.readlink(newpath)
         if target.startswith(sep):
             # Symlink target is absolute; reset resolved path.
             path = sep
diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py
index 32a20efbb64e1d..5c27b7bee8f60e 100644
--- a/Lib/test/test_posixpath.py
+++ b/Lib/test/test_posixpath.py
@@ -660,6 +660,23 @@ def test_realpath_resolve_first(self):
             safe_rmdir(ABSTFN + "/k")
             safe_rmdir(ABSTFN)
 
+    @os_helper.skip_unless_symlink
+    @skip_if_ABSTFN_contains_backslash
+    @unittest.skipIf(os.chmod not in os.supports_follow_symlinks, "Can't set 
symlink permissions")
+    def test_realpath_unreadable_symlink(self):
+        try:
+            os.symlink(ABSTFN+"1", ABSTFN)
+            os.chmod(ABSTFN, 0o000, follow_symlinks=False)
+            self.assertEqual(realpath(ABSTFN), ABSTFN)
+            self.assertEqual(realpath(ABSTFN + '/foo'), ABSTFN + '/foo')
+            self.assertEqual(realpath(ABSTFN + '/../foo'), dirname(ABSTFN) + 
'/foo')
+            self.assertEqual(realpath(ABSTFN + '/foo/..'), ABSTFN)
+            with self.assertRaises(PermissionError):
+                realpath(ABSTFN, strict=True)
+        finally:
+            os.chmod(ABSTFN, 0o755, follow_symlinks=False)
+            os.unlink(ABSTFN)
+
     def test_relpath(self):
         (real_getcwd, os.getcwd) = (os.getcwd, lambda: r"/home/user/bar")
         try:
diff --git 
a/Misc/NEWS.d/next/Library/2024-05-01-22-24-05.gh-issue-110863.GjYBbq.rst 
b/Misc/NEWS.d/next/Library/2024-05-01-22-24-05.gh-issue-110863.GjYBbq.rst
new file mode 100644
index 00000000000000..37e27a6e37c7d0
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-05-01-22-24-05.gh-issue-110863.GjYBbq.rst
@@ -0,0 +1,2 @@
+:func:`os.path.realpath` now suppresses any :exc:`OSError` from
+:func:`os.readlink` when *strict* mode is disabled (the default).

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to