From 8e3e6cceaefaf63acdf1762baac38c88af9fd15b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= <mgorny@gentoo.org>
Date: Sat, 2 Oct 2021 15:42:17 +0200
Subject: [PATCH 1/7] Skip directories when symlinking libraries for PyPy3

The PyPy3 logic creates symlinks for all files from the library
directory existing alongside the PyPy executable.  This is meant
to ensure that the bundled libraries to which PyPy is linked can also
be found from inside the virtualenv.  However, this logic also symlinks
all directories which is unnecessary and causes library directory
collisions with the new install layout.  Change to logic to symlink
non-directories only.

A similar fix has been applied to the internal venv module in PyPy3.8:
https://foss.heptapod.net/pypy/pypy/-/commit/713b2af9abd2b9453e12c60143e17431a1aefb33

Fixes #2182
---
 docs/changelog/2182.bugfix.txt                             | 2 ++
 src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py | 2 ++
 2 files changed, 4 insertions(+)
 create mode 100644 docs/changelog/2182.bugfix.txt

diff --git a/docs/changelog/2182.bugfix.txt b/docs/changelog/2182.bugfix.txt
new file mode 100644
index 000000000..0f26a202b
--- /dev/null
+++ b/docs/changelog/2182.bugfix.txt
@@ -0,0 +1,2 @@
+Fixed path collision that could lead to a PermissionError or writing to system
+directories when using PyPy3.8 - by :user:`mgorny`.
diff --git a/src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py b/src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py
index be5319a2b..f740de963 100644
--- a/src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py
+++ b/src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py
@@ -44,6 +44,8 @@ def sources(cls, interpreter):
         host_lib = Path(interpreter.system_prefix) / "lib"
         if host_lib.exists() and host_lib.is_dir():
             for path in host_lib.iterdir():
+                if path.is_dir():
+                    continue
                 yield PathRefToDest(path, dest=cls.to_lib)
 
 

From d7ec9269f38dee462c8b3012128aacbbe18894f5 Mon Sep 17 00:00:00 2001
From: mattip <matti.picus@gmail.com>
Date: Mon, 4 Oct 2021 12:01:41 +0300
Subject: [PATCH 2/7] fix stdlib path

---
 src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py b/src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py
index f740de963..0cb83ed9d 100644
--- a/src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py
+++ b/src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py
@@ -28,7 +28,7 @@ class PyPy3Posix(PyPy3, PosixSupports):
     @property
     def stdlib(self):
         """PyPy3 respects sysconfig only for the host python, virtual envs is instead lib/pythonx.y/site-packages"""
-        return self.dest / "lib" / "python{}".format(self.interpreter.version_release_str) / "site-packages"
+        return self.dest / "lib" / "pypy{}".format(self.interpreter.version_release_str) / "site-packages"
 
     @classmethod
     def _shared_libs(cls):

From e1f065f703fe4b506cbc575a5a7c9d5fb6079095 Mon Sep 17 00:00:00 2001
From: mattip <matti.picus@gmail.com>
Date: Mon, 4 Oct 2021 16:06:04 +0300
Subject: [PATCH 3/7] skip linking/copying the stdlib specifically, extensively
 comment

---
 .../create/via_global_ref/builtin/pypy/pypy3.py        | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py b/src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py
index 0cb83ed9d..0c26b79f0 100644
--- a/src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py
+++ b/src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py
@@ -41,10 +41,18 @@ def to_lib(self, src):
     def sources(cls, interpreter):
         for src in super(PyPy3Posix, cls).sources(interpreter):
             yield src
+        # Also copy/symlink anything under prefix/lib, which, for "portable"
+        # PyPy builds, includes the tk,tcl runtime and a number of shared
+        # objects. In distro-specific builds or on conda this should be empty
+        # (on PyPy3.8+ it will, like on CPython, hold the stdlib).
         host_lib = Path(interpreter.system_prefix) / "lib"
+        stdlib = Path(interpreter.system_stdlib)
         if host_lib.exists() and host_lib.is_dir():
             for path in host_lib.iterdir():
-                if path.is_dir():
+                if stdlib == path:
+                    # For PyPy3.8+ the stdlib lives in lib/pypy3.8
+                    # We need to avoid creating a symlink to it since that
+                    # will defeat the purpose of a virtualenv
                     continue
                 yield PathRefToDest(path, dest=cls.to_lib)
 
