We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Set encoding for tar file and use unicode path for unpacking
When tarfile.TarFile decodes filenames in Python 2.7 by default it uses
sys.getfilesystemencoding. On Windows this returns "mbcs", which is
lossy when converting from proper utf-8 to bytes (results in '?' for out
of range characters).

We now pass an encoding to tarfile.open which will be used instead.
Since the encoding argument is only ever used for the PAX format, and
since the PAX format guarantees utf-8 encoded information, this should
work in all circumstances.

For filesystem APIs in Python 2, the type of the path object passed
dictates the underlying Windows API that is called. For `str` it is the
`*A` (for ANSI) APIs. For `unicode` it is the `*W` (for Wide character)
APIs. To use the second set of APIs, which properly handles unicode
filenames, we convert the byte path to utf-8.

Fixes #7667.
Filename encoding error in some environments with PAX sdist
**Environment**

* pip version: any
* Python version: 2.7
* OS: Windows, non-Windows in C locale

(pip Windows CI hits this)

**Description**
The PAX format wheel 0.34.1 sdists fail to install on Python 2.7 on Windows with a UnicodeEncodeError, or on non-Windows systems in a non-utf-8 locale: https://github.com/pypa/wheel/issues/331

**Expected behavior**
Unicode filename from the PAX tarball is correctly encoded for the local filesystem.

**How to Reproduce**
Attempt to install a PAX formatted tarball containing a file name that cannot be encoded to the default code page (Windows) or the default locale encoding (non-Windows).

In GNU tar, the affected paths are pre-mangled to something ASCII compatible, but PAX tar preserves them correctly, so the installer needs to handle them itself.

**Output**

See 
https://dev.azure.com/pypa/pip/_build/results?buildId=18040&view=logs&j=404e6841-f5ba-57d9-f2c8-8c5322057572&t=0219f6bf-240d-5b08-c877-377b12af5079&l=309 for a Windows example in the pip test suite.

The wheel issue linked above has some Linux examples.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/pip/_internal/utils/unpacking.py`
Content:
```
1 """Utilities related archives.
2 """
3 
4 import logging
5 import os
6 import shutil
7 import stat
8 import tarfile
9 import zipfile
10 from typing import Iterable, List, Optional
11 from zipfile import ZipInfo
12 
13 from pip._internal.exceptions import InstallationError
14 from pip._internal.utils.filetypes import (
15     BZ2_EXTENSIONS,
16     TAR_EXTENSIONS,
17     XZ_EXTENSIONS,
18     ZIP_EXTENSIONS,
19 )
20 from pip._internal.utils.misc import ensure_dir
21 
22 logger = logging.getLogger(__name__)
23 
24 
25 SUPPORTED_EXTENSIONS = ZIP_EXTENSIONS + TAR_EXTENSIONS
26 
27 try:
28     import bz2  # noqa
29 
30     SUPPORTED_EXTENSIONS += BZ2_EXTENSIONS
31 except ImportError:
32     logger.debug("bz2 module is not available")
33 
34 try:
35     # Only for Python 3.3+
36     import lzma  # noqa
37 
38     SUPPORTED_EXTENSIONS += XZ_EXTENSIONS
39 except ImportError:
40     logger.debug("lzma module is not available")
41 
42 
43 def current_umask():
44     # type: () -> int
45     """Get the current umask which involves having to set it temporarily."""
46     mask = os.umask(0)
47     os.umask(mask)
48     return mask
49 
50 
51 def split_leading_dir(path):
52     # type: (str) -> List[str]
53     path = path.lstrip("/").lstrip("\\")
54     if "/" in path and (
55         ("\\" in path and path.find("/") < path.find("\\")) or "\\" not in path
56     ):
57         return path.split("/", 1)
58     elif "\\" in path:
59         return path.split("\\", 1)
60     else:
61         return [path, ""]
62 
63 
64 def has_leading_dir(paths):
65     # type: (Iterable[str]) -> bool
66     """Returns true if all the paths have the same leading path name
67     (i.e., everything is in one subdirectory in an archive)"""
68     common_prefix = None
69     for path in paths:
70         prefix, rest = split_leading_dir(path)
71         if not prefix:
72             return False
73         elif common_prefix is None:
74             common_prefix = prefix
75         elif prefix != common_prefix:
76             return False
77     return True
78 
79 
80 def is_within_directory(directory, target):
81     # type: (str, str) -> bool
82     """
83     Return true if the absolute path of target is within the directory
84     """
85     abs_directory = os.path.abspath(directory)
86     abs_target = os.path.abspath(target)
87 
88     prefix = os.path.commonprefix([abs_directory, abs_target])
89     return prefix == abs_directory
90 
91 
92 def set_extracted_file_to_default_mode_plus_executable(path):
93     # type: (str) -> None
94     """
95     Make file present at path have execute for user/group/world
96     (chmod +x) is no-op on windows per python docs
97     """
98     os.chmod(path, (0o777 & ~current_umask() | 0o111))
99 
100 
101 def zip_item_is_executable(info):
102     # type: (ZipInfo) -> bool
103     mode = info.external_attr >> 16
104     # if mode and regular file and any execute permissions for
105     # user/group/world?
106     return bool(mode and stat.S_ISREG(mode) and mode & 0o111)
107 
108 
109 def unzip_file(filename, location, flatten=True):
110     # type: (str, str, bool) -> None
111     """
112     Unzip the file (with path `filename`) to the destination `location`.  All
113     files are written based on system defaults and umask (i.e. permissions are
114     not preserved), except that regular file members with any execute
115     permissions (user, group, or world) have "chmod +x" applied after being
116     written. Note that for windows, any execute changes using os.chmod are
117     no-ops per the python docs.
118     """
119     ensure_dir(location)
120     zipfp = open(filename, "rb")
121     try:
122         zip = zipfile.ZipFile(zipfp, allowZip64=True)
123         leading = has_leading_dir(zip.namelist()) and flatten
124         for info in zip.infolist():
125             name = info.filename
126             fn = name
127             if leading:
128                 fn = split_leading_dir(name)[1]
129             fn = os.path.join(location, fn)
130             dir = os.path.dirname(fn)
131             if not is_within_directory(location, fn):
132                 message = (
133                     "The zip file ({}) has a file ({}) trying to install "
134                     "outside target directory ({})"
135                 )
136                 raise InstallationError(message.format(filename, fn, location))
137             if fn.endswith("/") or fn.endswith("\\"):
138                 # A directory
139                 ensure_dir(fn)
140             else:
141                 ensure_dir(dir)
142                 # Don't use read() to avoid allocating an arbitrarily large
143                 # chunk of memory for the file's content
144                 fp = zip.open(name)
145                 try:
146                     with open(fn, "wb") as destfp:
147                         shutil.copyfileobj(fp, destfp)
148                 finally:
149                     fp.close()
150                     if zip_item_is_executable(info):
151                         set_extracted_file_to_default_mode_plus_executable(fn)
152     finally:
153         zipfp.close()
154 
155 
156 def untar_file(filename, location):
157     # type: (str, str) -> None
158     """
159     Untar the file (with path `filename`) to the destination `location`.
160     All files are written based on system defaults and umask (i.e. permissions
161     are not preserved), except that regular file members with any execute
162     permissions (user, group, or world) have "chmod +x" applied after being
163     written.  Note that for windows, any execute changes using os.chmod are
164     no-ops per the python docs.
165     """
166     ensure_dir(location)
167     if filename.lower().endswith(".gz") or filename.lower().endswith(".tgz"):
168         mode = "r:gz"
169     elif filename.lower().endswith(BZ2_EXTENSIONS):
170         mode = "r:bz2"
171     elif filename.lower().endswith(XZ_EXTENSIONS):
172         mode = "r:xz"
173     elif filename.lower().endswith(".tar"):
174         mode = "r"
175     else:
176         logger.warning(
177             "Cannot determine compression type for file %s",
178             filename,
179         )
180         mode = "r:*"
181     tar = tarfile.open(filename, mode)
182     try:
183         leading = has_leading_dir([member.name for member in tar.getmembers()])
184         for member in tar.getmembers():
185             fn = member.name
186             if leading:
187                 fn = split_leading_dir(fn)[1]
188             path = os.path.join(location, fn)
189             if not is_within_directory(location, path):
190                 message = (
191                     "The tar file ({}) has a file ({}) trying to install "
192                     "outside target directory ({})"
193                 )
194                 raise InstallationError(message.format(filename, path, location))
195             if member.isdir():
196                 ensure_dir(path)
197             elif member.issym():
198                 try:
199                     # https://github.com/python/typeshed/issues/2673
200                     tar._extract_member(member, path)  # type: ignore
201                 except Exception as exc:
202                     # Some corrupt tar files seem to produce this
203                     # (specifically bad symlinks)
204                     logger.warning(
205                         "In the tar file %s the member %s is invalid: %s",
206                         filename,
207                         member.name,
208                         exc,
209                     )
210                     continue
211             else:
212                 try:
213                     fp = tar.extractfile(member)
214                 except (KeyError, AttributeError) as exc:
215                     # Some corrupt tar files seem to produce this
216                     # (specifically bad symlinks)
217                     logger.warning(
218                         "In the tar file %s the member %s is invalid: %s",
219                         filename,
220                         member.name,
221                         exc,
222                     )
223                     continue
224                 ensure_dir(os.path.dirname(path))
225                 assert fp is not None
226                 with open(path, "wb") as destfp:
227                     shutil.copyfileobj(fp, destfp)
228                 fp.close()
229                 # Update the timestamp (useful for cython compiled files)
230                 tar.utime(member, path)
231                 # member have any execute permissions for user/group/world?
232                 if member.mode & 0o111:
233                     set_extracted_file_to_default_mode_plus_executable(path)
234     finally:
235         tar.close()
236 
237 
238 def unpack_file(
239     filename,  # type: str
240     location,  # type: str
241     content_type=None,  # type: Optional[str]
242 ):
243     # type: (...) -> None
244     filename = os.path.realpath(filename)
245     if (
246         content_type == "application/zip"
247         or filename.lower().endswith(ZIP_EXTENSIONS)
248         or zipfile.is_zipfile(filename)
249     ):
250         unzip_file(filename, location, flatten=not filename.endswith(".whl"))
251     elif (
252         content_type == "application/x-gzip"
253         or tarfile.is_tarfile(filename)
254         or filename.lower().endswith(TAR_EXTENSIONS + BZ2_EXTENSIONS + XZ_EXTENSIONS)
255     ):
256         untar_file(filename, location)
257     else:
258         # FIXME: handle?
259         # FIXME: magic signatures?
260         logger.critical(
261             "Cannot unpack file %s (downloaded from %s, content-type: %s); "
262             "cannot detect archive format",
263             filename,
264             location,
265             content_type,
266         )
267         raise InstallationError(f"Cannot determine archive format of {location}")
268
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/pip/_internal/utils/unpacking.py b/src/pip/_internal/utils/unpacking.py
--- a/src/pip/_internal/utils/unpacking.py
+++ b/src/pip/_internal/utils/unpacking.py
@@ -178,7 +178,7 @@
             filename,
         )
         mode = "r:*"
-    tar = tarfile.open(filename, mode)
+    tar = tarfile.open(filename, mode, encoding="utf-8")
     try:
         leading = has_leading_dir([member.name for member in tar.getmembers()])
         for member in tar.getmembers():

{"golden_diff": "diff --git a/src/pip/_internal/utils/unpacking.py b/src/pip/_internal/utils/unpacking.py\n--- a/src/pip/_internal/utils/unpacking.py\n+++ b/src/pip/_internal/utils/unpacking.py\n@@ -178,7 +178,7 @@\n             filename,\n         )\n         mode = \"r:*\"\n-    tar = tarfile.open(filename, mode)\n+    tar = tarfile.open(filename, mode, encoding=\"utf-8\")\n     try:\n         leading = has_leading_dir([member.name for member in tar.getmembers()])\n         for member in tar.getmembers():\n", "issue": "Set encoding for tar file and use unicode path for unpacking\nWhen tarfile.TarFile decodes filenames in Python 2.7 by default it uses\r\nsys.getfilesystemencoding. On Windows this returns \"mbcs\", which is\r\nlossy when converting from proper utf-8 to bytes (results in '?' for out\r\nof range characters).\r\n\r\nWe now pass an encoding to tarfile.open which will be used instead.\r\nSince the encoding argument is only ever used for the PAX format, and\r\nsince the PAX format guarantees utf-8 encoded information, this should\r\nwork in all circumstances.\r\n\r\nFor filesystem APIs in Python 2, the type of the path object passed\r\ndictates the underlying Windows API that is called. For `str` it is the\r\n`*A` (for ANSI) APIs. For `unicode` it is the `*W` (for Wide character)\r\nAPIs. To use the second set of APIs, which properly handles unicode\r\nfilenames, we convert the byte path to utf-8.\r\n\r\nFixes #7667.\nFilename encoding error in some environments with PAX sdist\n**Environment**\r\n\r\n* pip version: any\r\n* Python version: 2.7\r\n* OS: Windows, non-Windows in C locale\r\n\r\n(pip Windows CI hits this)\r\n\r\n**Description**\r\nThe PAX format wheel 0.34.1 sdists fail to install on Python 2.7 on Windows with a UnicodeEncodeError, or on non-Windows systems in a non-utf-8 locale: https://github.com/pypa/wheel/issues/331\r\n\r\n**Expected behavior**\r\nUnicode filename from the PAX tarball is correctly encoded for the local filesystem.\r\n\r\n**How to Reproduce**\r\nAttempt to install a PAX formatted tarball containing a file name that cannot be encoded to the default code page (Windows) or the default locale encoding (non-Windows).\r\n\r\nIn GNU tar, the affected paths are pre-mangled to something ASCII compatible, but PAX tar preserves them correctly, so the installer needs to handle them itself.\r\n\r\n**Output**\r\n\r\nSee \r\nhttps://dev.azure.com/pypa/pip/_build/results?buildId=18040&view=logs&j=404e6841-f5ba-57d9-f2c8-8c5322057572&t=0219f6bf-240d-5b08-c877-377b12af5079&l=309 for a Windows example in the pip test suite.\r\n\r\nThe wheel issue linked above has some Linux examples.\n", "before_files": [{"content": "\"\"\"Utilities related archives.\n\"\"\"\n\nimport logging\nimport os\nimport shutil\nimport stat\nimport tarfile\nimport zipfile\nfrom typing import Iterable, List, Optional\nfrom zipfile import ZipInfo\n\nfrom pip._internal.exceptions import InstallationError\nfrom pip._internal.utils.filetypes import (\n    BZ2_EXTENSIONS,\n    TAR_EXTENSIONS,\n    XZ_EXTENSIONS,\n    ZIP_EXTENSIONS,\n)\nfrom pip._internal.utils.misc import ensure_dir\n\nlogger = logging.getLogger(__name__)\n\n\nSUPPORTED_EXTENSIONS = ZIP_EXTENSIONS + TAR_EXTENSIONS\n\ntry:\n    import bz2  # noqa\n\n    SUPPORTED_EXTENSIONS += BZ2_EXTENSIONS\nexcept ImportError:\n    logger.debug(\"bz2 module is not available\")\n\ntry:\n    # Only for Python 3.3+\n    import lzma  # noqa\n\n    SUPPORTED_EXTENSIONS += XZ_EXTENSIONS\nexcept ImportError:\n    logger.debug(\"lzma module is not available\")\n\n\ndef current_umask():\n    # type: () -> int\n    \"\"\"Get the current umask which involves having to set it temporarily.\"\"\"\n    mask = os.umask(0)\n    os.umask(mask)\n    return mask\n\n\ndef split_leading_dir(path):\n    # type: (str) -> List[str]\n    path = path.lstrip(\"/\").lstrip(\"\\\\\")\n    if \"/\" in path and (\n        (\"\\\\\" in path and path.find(\"/\") < path.find(\"\\\\\")) or \"\\\\\" not in path\n    ):\n        return path.split(\"/\", 1)\n    elif \"\\\\\" in path:\n        return path.split(\"\\\\\", 1)\n    else:\n        return [path, \"\"]\n\n\ndef has_leading_dir(paths):\n    # type: (Iterable[str]) -> bool\n    \"\"\"Returns true if all the paths have the same leading path name\n    (i.e., everything is in one subdirectory in an archive)\"\"\"\n    common_prefix = None\n    for path in paths:\n        prefix, rest = split_leading_dir(path)\n        if not prefix:\n            return False\n        elif common_prefix is None:\n            common_prefix = prefix\n        elif prefix != common_prefix:\n            return False\n    return True\n\n\ndef is_within_directory(directory, target):\n    # type: (str, str) -> bool\n    \"\"\"\n    Return true if the absolute path of target is within the directory\n    \"\"\"\n    abs_directory = os.path.abspath(directory)\n    abs_target = os.path.abspath(target)\n\n    prefix = os.path.commonprefix([abs_directory, abs_target])\n    return prefix == abs_directory\n\n\ndef set_extracted_file_to_default_mode_plus_executable(path):\n    # type: (str) -> None\n    \"\"\"\n    Make file present at path have execute for user/group/world\n    (chmod +x) is no-op on windows per python docs\n    \"\"\"\n    os.chmod(path, (0o777 & ~current_umask() | 0o111))\n\n\ndef zip_item_is_executable(info):\n    # type: (ZipInfo) -> bool\n    mode = info.external_attr >> 16\n    # if mode and regular file and any execute permissions for\n    # user/group/world?\n    return bool(mode and stat.S_ISREG(mode) and mode & 0o111)\n\n\ndef unzip_file(filename, location, flatten=True):\n    # type: (str, str, bool) -> None\n    \"\"\"\n    Unzip the file (with path `filename`) to the destination `location`.  All\n    files are written based on system defaults and umask (i.e. permissions are\n    not preserved), except that regular file members with any execute\n    permissions (user, group, or world) have \"chmod +x\" applied after being\n    written. Note that for windows, any execute changes using os.chmod are\n    no-ops per the python docs.\n    \"\"\"\n    ensure_dir(location)\n    zipfp = open(filename, \"rb\")\n    try:\n        zip = zipfile.ZipFile(zipfp, allowZip64=True)\n        leading = has_leading_dir(zip.namelist()) and flatten\n        for info in zip.infolist():\n            name = info.filename\n            fn = name\n            if leading:\n                fn = split_leading_dir(name)[1]\n            fn = os.path.join(location, fn)\n            dir = os.path.dirname(fn)\n            if not is_within_directory(location, fn):\n                message = (\n                    \"The zip file ({}) has a file ({}) trying to install \"\n                    \"outside target directory ({})\"\n                )\n                raise InstallationError(message.format(filename, fn, location))\n            if fn.endswith(\"/\") or fn.endswith(\"\\\\\"):\n                # A directory\n                ensure_dir(fn)\n            else:\n                ensure_dir(dir)\n                # Don't use read() to avoid allocating an arbitrarily large\n                # chunk of memory for the file's content\n                fp = zip.open(name)\n                try:\n                    with open(fn, \"wb\") as destfp:\n                        shutil.copyfileobj(fp, destfp)\n                finally:\n                    fp.close()\n                    if zip_item_is_executable(info):\n                        set_extracted_file_to_default_mode_plus_executable(fn)\n    finally:\n        zipfp.close()\n\n\ndef untar_file(filename, location):\n    # type: (str, str) -> None\n    \"\"\"\n    Untar the file (with path `filename`) to the destination `location`.\n    All files are written based on system defaults and umask (i.e. permissions\n    are not preserved), except that regular file members with any execute\n    permissions (user, group, or world) have \"chmod +x\" applied after being\n    written.  Note that for windows, any execute changes using os.chmod are\n    no-ops per the python docs.\n    \"\"\"\n    ensure_dir(location)\n    if filename.lower().endswith(\".gz\") or filename.lower().endswith(\".tgz\"):\n        mode = \"r:gz\"\n    elif filename.lower().endswith(BZ2_EXTENSIONS):\n        mode = \"r:bz2\"\n    elif filename.lower().endswith(XZ_EXTENSIONS):\n        mode = \"r:xz\"\n    elif filename.lower().endswith(\".tar\"):\n        mode = \"r\"\n    else:\n        logger.warning(\n            \"Cannot determine compression type for file %s\",\n            filename,\n        )\n        mode = \"r:*\"\n    tar = tarfile.open(filename, mode)\n    try:\n        leading = has_leading_dir([member.name for member in tar.getmembers()])\n        for member in tar.getmembers():\n            fn = member.name\n            if leading:\n                fn = split_leading_dir(fn)[1]\n            path = os.path.join(location, fn)\n            if not is_within_directory(location, path):\n                message = (\n                    \"The tar file ({}) has a file ({}) trying to install \"\n                    \"outside target directory ({})\"\n                )\n                raise InstallationError(message.format(filename, path, location))\n            if member.isdir():\n                ensure_dir(path)\n            elif member.issym():\n                try:\n                    # https://github.com/python/typeshed/issues/2673\n                    tar._extract_member(member, path)  # type: ignore\n                except Exception as exc:\n                    # Some corrupt tar files seem to produce this\n                    # (specifically bad symlinks)\n                    logger.warning(\n                        \"In the tar file %s the member %s is invalid: %s\",\n                        filename,\n                        member.name,\n                        exc,\n                    )\n                    continue\n            else:\n                try:\n                    fp = tar.extractfile(member)\n                except (KeyError, AttributeError) as exc:\n                    # Some corrupt tar files seem to produce this\n                    # (specifically bad symlinks)\n                    logger.warning(\n                        \"In the tar file %s the member %s is invalid: %s\",\n                        filename,\n                        member.name,\n                        exc,\n                    )\n                    continue\n                ensure_dir(os.path.dirname(path))\n                assert fp is not None\n                with open(path, \"wb\") as destfp:\n                    shutil.copyfileobj(fp, destfp)\n                fp.close()\n                # Update the timestamp (useful for cython compiled files)\n                tar.utime(member, path)\n                # member have any execute permissions for user/group/world?\n                if member.mode & 0o111:\n                    set_extracted_file_to_default_mode_plus_executable(path)\n    finally:\n        tar.close()\n\n\ndef unpack_file(\n    filename,  # type: str\n    location,  # type: str\n    content_type=None,  # type: Optional[str]\n):\n    # type: (...) -> None\n    filename = os.path.realpath(filename)\n    if (\n        content_type == \"application/zip\"\n        or filename.lower().endswith(ZIP_EXTENSIONS)\n        or zipfile.is_zipfile(filename)\n    ):\n        unzip_file(filename, location, flatten=not filename.endswith(\".whl\"))\n    elif (\n        content_type == \"application/x-gzip\"\n        or tarfile.is_tarfile(filename)\n        or filename.lower().endswith(TAR_EXTENSIONS + BZ2_EXTENSIONS + XZ_EXTENSIONS)\n    ):\n        untar_file(filename, location)\n    else:\n        # FIXME: handle?\n        # FIXME: magic signatures?\n        logger.critical(\n            \"Cannot unpack file %s (downloaded from %s, content-type: %s); \"\n            \"cannot detect archive format\",\n            filename,\n            location,\n            content_type,\n        )\n        raise InstallationError(f\"Cannot determine archive format of {location}\")\n", "path": "src/pip/_internal/utils/unpacking.py"}], "after_files": [{"content": "\"\"\"Utilities related archives.\n\"\"\"\n\nimport logging\nimport os\nimport shutil\nimport stat\nimport tarfile\nimport zipfile\nfrom typing import Iterable, List, Optional\nfrom zipfile import ZipInfo\n\nfrom pip._internal.exceptions import InstallationError\nfrom pip._internal.utils.filetypes import (\n    BZ2_EXTENSIONS,\n    TAR_EXTENSIONS,\n    XZ_EXTENSIONS,\n    ZIP_EXTENSIONS,\n)\nfrom pip._internal.utils.misc import ensure_dir\n\nlogger = logging.getLogger(__name__)\n\n\nSUPPORTED_EXTENSIONS = ZIP_EXTENSIONS + TAR_EXTENSIONS\n\ntry:\n    import bz2  # noqa\n\n    SUPPORTED_EXTENSIONS += BZ2_EXTENSIONS\nexcept ImportError:\n    logger.debug(\"bz2 module is not available\")\n\ntry:\n    # Only for Python 3.3+\n    import lzma  # noqa\n\n    SUPPORTED_EXTENSIONS += XZ_EXTENSIONS\nexcept ImportError:\n    logger.debug(\"lzma module is not available\")\n\n\ndef current_umask():\n    # type: () -> int\n    \"\"\"Get the current umask which involves having to set it temporarily.\"\"\"\n    mask = os.umask(0)\n    os.umask(mask)\n    return mask\n\n\ndef split_leading_dir(path):\n    # type: (str) -> List[str]\n    path = path.lstrip(\"/\").lstrip(\"\\\\\")\n    if \"/\" in path and (\n        (\"\\\\\" in path and path.find(\"/\") < path.find(\"\\\\\")) or \"\\\\\" not in path\n    ):\n        return path.split(\"/\", 1)\n    elif \"\\\\\" in path:\n        return path.split(\"\\\\\", 1)\n    else:\n        return [path, \"\"]\n\n\ndef has_leading_dir(paths):\n    # type: (Iterable[str]) -> bool\n    \"\"\"Returns true if all the paths have the same leading path name\n    (i.e., everything is in one subdirectory in an archive)\"\"\"\n    common_prefix = None\n    for path in paths:\n        prefix, rest = split_leading_dir(path)\n        if not prefix:\n            return False\n        elif common_prefix is None:\n            common_prefix = prefix\n        elif prefix != common_prefix:\n            return False\n    return True\n\n\ndef is_within_directory(directory, target):\n    # type: (str, str) -> bool\n    \"\"\"\n    Return true if the absolute path of target is within the directory\n    \"\"\"\n    abs_directory = os.path.abspath(directory)\n    abs_target = os.path.abspath(target)\n\n    prefix = os.path.commonprefix([abs_directory, abs_target])\n    return prefix == abs_directory\n\n\ndef set_extracted_file_to_default_mode_plus_executable(path):\n    # type: (str) -> None\n    \"\"\"\n    Make file present at path have execute for user/group/world\n    (chmod +x) is no-op on windows per python docs\n    \"\"\"\n    os.chmod(path, (0o777 & ~current_umask() | 0o111))\n\n\ndef zip_item_is_executable(info):\n    # type: (ZipInfo) -> bool\n    mode = info.external_attr >> 16\n    # if mode and regular file and any execute permissions for\n    # user/group/world?\n    return bool(mode and stat.S_ISREG(mode) and mode & 0o111)\n\n\ndef unzip_file(filename, location, flatten=True):\n    # type: (str, str, bool) -> None\n    \"\"\"\n    Unzip the file (with path `filename`) to the destination `location`.  All\n    files are written based on system defaults and umask (i.e. permissions are\n    not preserved), except that regular file members with any execute\n    permissions (user, group, or world) have \"chmod +x\" applied after being\n    written. Note that for windows, any execute changes using os.chmod are\n    no-ops per the python docs.\n    \"\"\"\n    ensure_dir(location)\n    zipfp = open(filename, \"rb\")\n    try:\n        zip = zipfile.ZipFile(zipfp, allowZip64=True)\n        leading = has_leading_dir(zip.namelist()) and flatten\n        for info in zip.infolist():\n            name = info.filename\n            fn = name\n            if leading:\n                fn = split_leading_dir(name)[1]\n            fn = os.path.join(location, fn)\n            dir = os.path.dirname(fn)\n            if not is_within_directory(location, fn):\n                message = (\n                    \"The zip file ({}) has a file ({}) trying to install \"\n                    \"outside target directory ({})\"\n                )\n                raise InstallationError(message.format(filename, fn, location))\n            if fn.endswith(\"/\") or fn.endswith(\"\\\\\"):\n                # A directory\n                ensure_dir(fn)\n            else:\n                ensure_dir(dir)\n                # Don't use read() to avoid allocating an arbitrarily large\n                # chunk of memory for the file's content\n                fp = zip.open(name)\n                try:\n                    with open(fn, \"wb\") as destfp:\n                        shutil.copyfileobj(fp, destfp)\n                finally:\n                    fp.close()\n                    if zip_item_is_executable(info):\n                        set_extracted_file_to_default_mode_plus_executable(fn)\n    finally:\n        zipfp.close()\n\n\ndef untar_file(filename, location):\n    # type: (str, str) -> None\n    \"\"\"\n    Untar the file (with path `filename`) to the destination `location`.\n    All files are written based on system defaults and umask (i.e. permissions\n    are not preserved), except that regular file members with any execute\n    permissions (user, group, or world) have \"chmod +x\" applied after being\n    written.  Note that for windows, any execute changes using os.chmod are\n    no-ops per the python docs.\n    \"\"\"\n    ensure_dir(location)\n    if filename.lower().endswith(\".gz\") or filename.lower().endswith(\".tgz\"):\n        mode = \"r:gz\"\n    elif filename.lower().endswith(BZ2_EXTENSIONS):\n        mode = \"r:bz2\"\n    elif filename.lower().endswith(XZ_EXTENSIONS):\n        mode = \"r:xz\"\n    elif filename.lower().endswith(\".tar\"):\n        mode = \"r\"\n    else:\n        logger.warning(\n            \"Cannot determine compression type for file %s\",\n            filename,\n        )\n        mode = \"r:*\"\n    tar = tarfile.open(filename, mode, encoding=\"utf-8\")\n    try:\n        leading = has_leading_dir([member.name for member in tar.getmembers()])\n        for member in tar.getmembers():\n            fn = member.name\n            if leading:\n                fn = split_leading_dir(fn)[1]\n            path = os.path.join(location, fn)\n            if not is_within_directory(location, path):\n                message = (\n                    \"The tar file ({}) has a file ({}) trying to install \"\n                    \"outside target directory ({})\"\n                )\n                raise InstallationError(message.format(filename, path, location))\n            if member.isdir():\n                ensure_dir(path)\n            elif member.issym():\n                try:\n                    # https://github.com/python/typeshed/issues/2673\n                    tar._extract_member(member, path)  # type: ignore\n                except Exception as exc:\n                    # Some corrupt tar files seem to produce this\n                    # (specifically bad symlinks)\n                    logger.warning(\n                        \"In the tar file %s the member %s is invalid: %s\",\n                        filename,\n                        member.name,\n                        exc,\n                    )\n                    continue\n            else:\n                try:\n                    fp = tar.extractfile(member)\n                except (KeyError, AttributeError) as exc:\n                    # Some corrupt tar files seem to produce this\n                    # (specifically bad symlinks)\n                    logger.warning(\n                        \"In the tar file %s the member %s is invalid: %s\",\n                        filename,\n                        member.name,\n                        exc,\n                    )\n                    continue\n                ensure_dir(os.path.dirname(path))\n                assert fp is not None\n                with open(path, \"wb\") as destfp:\n                    shutil.copyfileobj(fp, destfp)\n                fp.close()\n                # Update the timestamp (useful for cython compiled files)\n                tar.utime(member, path)\n                # member have any execute permissions for user/group/world?\n                if member.mode & 0o111:\n                    set_extracted_file_to_default_mode_plus_executable(path)\n    finally:\n        tar.close()\n\n\ndef unpack_file(\n    filename,  # type: str\n    location,  # type: str\n    content_type=None,  # type: Optional[str]\n):\n    # type: (...) -> None\n    filename = os.path.realpath(filename)\n    if (\n        content_type == \"application/zip\"\n        or filename.lower().endswith(ZIP_EXTENSIONS)\n        or zipfile.is_zipfile(filename)\n    ):\n        unzip_file(filename, location, flatten=not filename.endswith(\".whl\"))\n    elif (\n        content_type == \"application/x-gzip\"\n        or tarfile.is_tarfile(filename)\n        or filename.lower().endswith(TAR_EXTENSIONS + BZ2_EXTENSIONS + XZ_EXTENSIONS)\n    ):\n        untar_file(filename, location)\n    else:\n        # FIXME: handle?\n        # FIXME: magic signatures?\n        logger.critical(\n            \"Cannot unpack file %s (downloaded from %s, content-type: %s); \"\n            \"cannot detect archive format\",\n            filename,\n            location,\n            content_type,\n        )\n        raise InstallationError(f\"Cannot determine archive format of {location}\")\n", "path": "src/pip/_internal/utils/unpacking.py"}]}