prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Errors untaring source tarballs Docker
  Some source tarballs are created in a way that tries to preserve user and group perms. 
  Yes, it's a bad to create a source tarball for release with these properties -- but I suspect it's easy to go undetected until you run in a security constrained linux.
  
  Cython is one example example I hit when trying to build in a Docker container using ubuntu:latest
  
  ```
  /bin/tar: Cython-0.25.2/.gitrev: Cannot change ownership to uid 96258, gid 5000: Invalid argument
  .... (many many more errors)
  /bin/tar: Cython-0.25.2/USAGE.txt: Cannot change ownership to uid 96258, gid 5000: Invalid argument
  /bin/tar: Cython-0.25.2: Cannot change ownership to uid 96258, gid 5000: Invalid argument
  /bin/tar: Exiting with failure status due to previous errors
  ```
  The tar errors cause spack to stop.  I am not sure if there is anyway to defensively avoid this, but if its possible it would be a good enhancement to spack. 
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `lib/spack/spack/util/compression.py`
  Content:
  ```
  1 # Copyright 2013-2020 Lawrence Livermore National Security, LLC and other
  2 # Spack Project Developers. See the top-level COPYRIGHT file for details.
  3 #
  4 # SPDX-License-Identifier: (Apache-2.0 OR MIT)
  5 
  6 import re
  7 import os
  8 from itertools import product
  9 from spack.util.executable import which
  10 
  11 # Supported archive extensions.
  12 PRE_EXTS   = ["tar", "TAR"]
  13 EXTS       = ["gz", "bz2", "xz", "Z"]
  14 NOTAR_EXTS = ["zip", "tgz", "tbz2", "txz"]
  15 
  16 # Add PRE_EXTS and EXTS last so that .tar.gz is matched *before* .tar or .gz
  17 ALLOWED_ARCHIVE_TYPES = [".".join(l) for l in product(
  18     PRE_EXTS, EXTS)] + PRE_EXTS + EXTS + NOTAR_EXTS
  19 
  20 
  21 def allowed_archive(path):
  22     return any(path.endswith(t) for t in ALLOWED_ARCHIVE_TYPES)
  23 
  24 
  25 def decompressor_for(path, extension=None):
  26     """Get the appropriate decompressor for a path."""
  27     if ((extension and re.match(r'\.?zip$', extension)) or
  28             path.endswith('.zip')):
  29         unzip = which('unzip', required=True)
  30         unzip.add_default_arg('-q')
  31         return unzip
  32     if extension and re.match(r'gz', extension):
  33         gunzip = which('gunzip', required=True)
  34         return gunzip
  35     if extension and re.match(r'bz2', extension):
  36         bunzip2 = which('bunzip2', required=True)
  37         return bunzip2
  38     tar = which('tar', required=True)
  39     tar.add_default_arg('-xf')
  40     return tar
  41 
  42 
  43 def strip_extension(path):
  44     """Get the part of a path that does not include its compressed
  45        type extension."""
  46     for type in ALLOWED_ARCHIVE_TYPES:
  47         suffix = r'\.%s$' % type
  48         if re.search(suffix, path):
  49             return re.sub(suffix, "", path)
  50     return path
  51 
  52 
  53 def extension(path):
  54     """Get the archive extension for a path."""
  55     if path is None:
  56         raise ValueError("Can't call extension() on None")
  57 
  58     # Strip sourceforge suffix.
  59     if re.search(r'((?:sourceforge.net|sf.net)/.*)/download$', path):
  60         path = os.path.dirname(path)
  61 
  62     for t in ALLOWED_ARCHIVE_TYPES:
  63         suffix = r'\.%s$' % t
  64         if re.search(suffix, path):
  65             return t
  66     return None
  67
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/lib/spack/spack/util/compression.py b/lib/spack/spack/util/compression.py
  --- a/lib/spack/spack/util/compression.py
  +++ b/lib/spack/spack/util/compression.py
  @@ -14,7 +14,7 @@
   NOTAR_EXTS = ["zip", "tgz", "tbz2", "txz"]
   
   # Add PRE_EXTS and EXTS last so that .tar.gz is matched *before* .tar or .gz
  -ALLOWED_ARCHIVE_TYPES = [".".join(l) for l in product(
  +ALLOWED_ARCHIVE_TYPES = [".".join(ext) for ext in product(
       PRE_EXTS, EXTS)] + PRE_EXTS + EXTS + NOTAR_EXTS
   
   
  @@ -36,7 +36,7 @@
           bunzip2 = which('bunzip2', required=True)
           return bunzip2
       tar = which('tar', required=True)
  -    tar.add_default_arg('-xf')
  +    tar.add_default_arg('-oxf')
       return tar
  
verification_info: {"golden_diff": "diff --git a/lib/spack/spack/util/compression.py b/lib/spack/spack/util/compression.py\n--- a/lib/spack/spack/util/compression.py\n+++ b/lib/spack/spack/util/compression.py\n@@ -14,7 +14,7 @@\n NOTAR_EXTS = [\"zip\", \"tgz\", \"tbz2\", \"txz\"]\n \n # Add PRE_EXTS and EXTS last so that .tar.gz is matched *before* .tar or .gz\n-ALLOWED_ARCHIVE_TYPES = [\".\".join(l) for l in product(\n+ALLOWED_ARCHIVE_TYPES = [\".\".join(ext) for ext in product(\n     PRE_EXTS, EXTS)] + PRE_EXTS + EXTS + NOTAR_EXTS\n \n \n@@ -36,7 +36,7 @@\n         bunzip2 = which('bunzip2', required=True)\n         return bunzip2\n     tar = which('tar', required=True)\n-    tar.add_default_arg('-xf')\n+    tar.add_default_arg('-oxf')\n     return tar\n", "issue": "Errors untaring source tarballs Docker\nSome source tarballs are created in a way that tries to preserve user and group perms. \r\nYes, it's a bad to create a source tarball for release with these properties -- but I suspect it's easy to go undetected until you run in a security constrained linux.\r\n\r\nCython is one example example I hit when trying to build in a Docker container using ubuntu:latest\r\n\r\n```\r\n/bin/tar: Cython-0.25.2/.gitrev: Cannot change ownership to uid 96258, gid 5000: Invalid argument\r\n.... (many many more errors)\r\n/bin/tar: Cython-0.25.2/USAGE.txt: Cannot change ownership to uid 96258, gid 5000: Invalid argument\r\n/bin/tar: Cython-0.25.2: Cannot change ownership to uid 96258, gid 5000: Invalid argument\r\n/bin/tar: Exiting with failure status due to previous errors\r\n```\r\nThe tar errors cause spack to stop.  I am not sure if there is anyway to defensively avoid this, but if its possible it would be a good enhancement to spack. \r\n\n", "before_files": [{"content": "# Copyright 2013-2020 Lawrence Livermore National Security, LLC and other\n# Spack Project Developers. See the top-level COPYRIGHT file for details.\n#\n# SPDX-License-Identifier: (Apache-2.0 OR MIT)\n\nimport re\nimport os\nfrom itertools import product\nfrom spack.util.executable import which\n\n# Supported archive extensions.\nPRE_EXTS   = [\"tar\", \"TAR\"]\nEXTS       = [\"gz\", \"bz2\", \"xz\", \"Z\"]\nNOTAR_EXTS = [\"zip\", \"tgz\", \"tbz2\", \"txz\"]\n\n# Add PRE_EXTS and EXTS last so that .tar.gz is matched *before* .tar or .gz\nALLOWED_ARCHIVE_TYPES = [\".\".join(l) for l in product(\n    PRE_EXTS, EXTS)] + PRE_EXTS + EXTS + NOTAR_EXTS\n\n\ndef allowed_archive(path):\n    return any(path.endswith(t) for t in ALLOWED_ARCHIVE_TYPES)\n\n\ndef decompressor_for(path, extension=None):\n    \"\"\"Get the appropriate decompressor for a path.\"\"\"\n    if ((extension and re.match(r'\\.?zip$', extension)) or\n            path.endswith('.zip')):\n        unzip = which('unzip', required=True)\n        unzip.add_default_arg('-q')\n        return unzip\n    if extension and re.match(r'gz', extension):\n        gunzip = which('gunzip', required=True)\n        return gunzip\n    if extension and re.match(r'bz2', extension):\n        bunzip2 = which('bunzip2', required=True)\n        return bunzip2\n    tar = which('tar', required=True)\n    tar.add_default_arg('-xf')\n    return tar\n\n\ndef strip_extension(path):\n    \"\"\"Get the part of a path that does not include its compressed\n       type extension.\"\"\"\n    for type in ALLOWED_ARCHIVE_TYPES:\n        suffix = r'\\.%s$' % type\n        if re.search(suffix, path):\n            return re.sub(suffix, \"\", path)\n    return path\n\n\ndef extension(path):\n    \"\"\"Get the archive extension for a path.\"\"\"\n    if path is None:\n        raise ValueError(\"Can't call extension() on None\")\n\n    # Strip sourceforge suffix.\n    if re.search(r'((?:sourceforge.net|sf.net)/.*)/download$', path):\n        path = os.path.dirname(path)\n\n    for t in ALLOWED_ARCHIVE_TYPES:\n        suffix = r'\\.%s$' % t\n        if re.search(suffix, path):\n            return t\n    return None\n", "path": "lib/spack/spack/util/compression.py"}], "after_files": [{"content": "# Copyright 2013-2020 Lawrence Livermore National Security, LLC and other\n# Spack Project Developers. See the top-level COPYRIGHT file for details.\n#\n# SPDX-License-Identifier: (Apache-2.0 OR MIT)\n\nimport re\nimport os\nfrom itertools import product\nfrom spack.util.executable import which\n\n# Supported archive extensions.\nPRE_EXTS   = [\"tar\", \"TAR\"]\nEXTS       = [\"gz\", \"bz2\", \"xz\", \"Z\"]\nNOTAR_EXTS = [\"zip\", \"tgz\", \"tbz2\", \"txz\"]\n\n# Add PRE_EXTS and EXTS last so that .tar.gz is matched *before* .tar or .gz\nALLOWED_ARCHIVE_TYPES = [\".\".join(ext) for ext in product(\n    PRE_EXTS, EXTS)] + PRE_EXTS + EXTS + NOTAR_EXTS\n\n\ndef allowed_archive(path):\n    return any(path.endswith(t) for t in ALLOWED_ARCHIVE_TYPES)\n\n\ndef decompressor_for(path, extension=None):\n    \"\"\"Get the appropriate decompressor for a path.\"\"\"\n    if ((extension and re.match(r'\\.?zip$', extension)) or\n            path.endswith('.zip')):\n        unzip = which('unzip', required=True)\n        unzip.add_default_arg('-q')\n        return unzip\n    if extension and re.match(r'gz', extension):\n        gunzip = which('gunzip', required=True)\n        return gunzip\n    if extension and re.match(r'bz2', extension):\n        bunzip2 = which('bunzip2', required=True)\n        return bunzip2\n    tar = which('tar', required=True)\n    tar.add_default_arg('-oxf')\n    return tar\n\n\ndef strip_extension(path):\n    \"\"\"Get the part of a path that does not include its compressed\n       type extension.\"\"\"\n    for type in ALLOWED_ARCHIVE_TYPES:\n        suffix = r'\\.%s$' % type\n        if re.search(suffix, path):\n            return re.sub(suffix, \"\", path)\n    return path\n\n\ndef extension(path):\n    \"\"\"Get the archive extension for a path.\"\"\"\n    if path is None:\n        raise ValueError(\"Can't call extension() on None\")\n\n    # Strip sourceforge suffix.\n    if re.search(r'((?:sourceforge.net|sf.net)/.*)/download$', path):\n        path = os.path.dirname(path)\n\n    for t in ALLOWED_ARCHIVE_TYPES:\n        suffix = r'\\.%s$' % t\n        if re.search(suffix, path):\n            return t\n    return None\n", "path": "lib/spack/spack/util/compression.py"}]}