]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] GH-118447: Fix handling of unreadable symlinks in `os.path.realpath()` (GH...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Sat, 18 May 2024 23:12:19 +0000 (01:12 +0200)
committerGitHub <noreply@github.com>
Sat, 18 May 2024 23:12:19 +0000 (23:12 +0000)
(cherry picked from commit caf6064a1bc15ac344afd78b780188e60b9c628e)

Co-authored-by: Barney Gale <barney.gale@gmail.com>
Co-authored-by: Nice Zombies <nineteendo19d0@gmail.com>
Lib/posixpath.py
Lib/test/test_posixpath.py
Misc/NEWS.d/next/Library/2024-05-01-22-24-05.gh-issue-110863.GjYBbq.rst [new file with mode: 0644]

index b4547d7893b1cd8a7aee43ffc5e4e75402fa3de6..c04c628de55ee260385e278119c45761cbeace87 100644 (file)
@@ -471,26 +471,26 @@ symbolic links encountered in the path."""
             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
index 32a20efbb64e1d14e10973a8fbcc70fedee19e52..5c27b7bee8f60e1d4ff6d074a54560a0815433b5 100644 (file)
@@ -660,6 +660,23 @@ class PosixPathTest(unittest.TestCase):
             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 (file)
index 0000000..37e27a6
--- /dev/null
@@ -0,0 +1,2 @@
+:func:`os.path.realpath` now suppresses any :exc:`OSError` from
+:func:`os.readlink` when *strict* mode is disabled (the default).