We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
tar_extract is affected by symlinks in current directory
If a current directory has a symlink with the name matching a directory in the installed conan_package.tgz, then the directory is not extracted to the package directory.

Reproduced on 1.15.0-dev. Notice the `include` directory disappears from the package directory.
```
$ mkdir /tmp/testing && cd /tmp/testing
$ conan new Hello/0.1 -t
$ conan create . demo/testing
$ conan upload Hello/0.1@demo/testing --all
$ conan remove Hello/0.1@demo/testing --force
$ conan install Hello/0.1@demo/testing
$ conan get Hello/0.1@demo/testing:57547fe65fffc300f05aa42ee64b3b02eeabb6d7 .
Listing directory '.':
 conaninfo.txt
 conanmanifest.txt
 include
 lib
$ ln -s /tmp include
$ conan remove Hello/0.1@demo/testing --force
$ conan install Hello/0.1@demo/testing
$ conan get Hello/0.1@demo/testing:57547fe65fffc300f05aa42ee64b3b02eeabb6d7 .
Listing directory '.':
 conaninfo.txt
 conanmanifest.txt
 lib
```

The issue is caused by `badpath` check. The `base` is the current directory. Joining current directory and `include`, and resolving the symlink makes it to not to start with the current directory. What is the true intent for the `badpath` method? Is there a good test case to show it's necessary?
```python
  293 def tar_extract(fileobj, destination_dir):
  294     """Extract tar file controlling not absolute paths and fixing the routes
  295     if the tar was zipped in windows"""
  296     def badpath(path, base):
  297         # joinpath will ignore base if path is absolute
  298         return not realpath(abspath(joinpath(base, path))).startswith(base)
  299
  300     def safemembers(members):
  301         base = realpath(abspath("."))
  302
  303         for finfo in members:
  304             if badpath(finfo.name, base) or finfo.islnk():
  305                 continue
  306             else:
  307                 # Fixes unzip a windows zipped file in linux
  308                 finfo.name = finfo.name.replace("\\", "/")
  309                 yield finfo
```




- [x] I've read the [CONTRIBUTING guide](https://github.com/conan-io/conan/blob/develop/.github/CONTRIBUTING.md).
- [x] I've specified the Conan version, operating system version and any tool that can be relevant.
- [x] I've explained the steps to reproduce the error or the motivation/use case of the question/suggestion.



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `conans/util/files.py`
Content:
```
1 import errno
2 import hashlib
3 import os
4 import platform
5 import re
6 import shutil
7 import stat
8 import sys
9 import tarfile
10 import tempfile
11 
12 from os.path import abspath, join as joinpath, realpath
13 
14 import six
15 
16 from conans.util.log import logger
17 
18 
19 def walk(top, **kwargs):
20     if six.PY2:
21         # If py2 os.walk receives a unicode object, it will fail if a non-ascii file name is found
22         # during the iteration. More info:
23         # https://stackoverflow.com/questions/21772271/unicodedecodeerror-when-performing-os-walk
24         try:
25             top = str(top)
26         except UnicodeDecodeError:
27             pass
28 
29     return os.walk(top, **kwargs)
30 
31 
32 def make_read_only(path):
33     for root, _, files in walk(path):
34         for f in files:
35             full_path = os.path.join(root, f)
36             mode = os.stat(full_path).st_mode
37             os.chmod(full_path, mode & ~ stat.S_IWRITE)
38 
39 
40 _DIRTY_FOLDER = ".dirty"
41 
42 
43 def set_dirty(folder):
44     dirty_file = os.path.normpath(folder) + _DIRTY_FOLDER
45     save(dirty_file, "")
46 
47 
48 def clean_dirty(folder):
49     dirty_file = os.path.normpath(folder) + _DIRTY_FOLDER
50     os.remove(dirty_file)
51 
52 
53 def is_dirty(folder):
54     dirty_file = os.path.normpath(folder) + _DIRTY_FOLDER
55     return os.path.exists(dirty_file)
56 
57 
58 def decode_text(text):
59     decoders = ["utf-8", "Windows-1252"]
60     for decoder in decoders:
61         try:
62             return text.decode(decoder)
63         except UnicodeDecodeError:
64             continue
65     logger.warning("can't decode %s" % str(text))
66     return text.decode("utf-8", "ignore")  # Ignore not compatible characters
67 
68 
69 def touch(fname, times=None):
70     os.utime(fname, times)
71 
72 
73 def touch_folder(folder):
74     for dirname, _, filenames in walk(folder):
75         for fname in filenames:
76             os.utime(os.path.join(dirname, fname), None)
77 
78 
79 def normalize(text):
80     if platform.system() == "Windows":
81         return re.sub("\r?\n", "\r\n", text)
82     else:
83         return text
84 
85 
86 def md5(content):
87     md5alg = hashlib.md5()
88     if isinstance(content, bytes):
89         tmp = content
90     else:
91         tmp = content.encode("utf-8")
92     md5alg.update(tmp)
93     return md5alg.hexdigest()
94 
95 
96 def md5sum(file_path):
97     return _generic_algorithm_sum(file_path, "md5")
98 
99 
100 def sha1sum(file_path):
101     return _generic_algorithm_sum(file_path, "sha1")
102 
103 
104 def sha256sum(file_path):
105     return _generic_algorithm_sum(file_path, "sha256")
106 
107 
108 def _generic_algorithm_sum(file_path, algorithm_name):
109 
110     with open(file_path, 'rb') as fh:
111         m = hashlib.new(algorithm_name)
112         while True:
113             data = fh.read(8192)
114             if not data:
115                 break
116             m.update(data)
117         return m.hexdigest()
118 
119 
120 def save_append(path, content):
121     try:
122         os.makedirs(os.path.dirname(path))
123     except:
124         pass
125 
126     with open(path, "ab") as handle:
127         handle.write(to_file_bytes(content))
128 
129 
130 def save(path, content, only_if_modified=False):
131     """
132     Saves a file with given content
133     Params:
134         path: path to write file to
135         content: contents to save in the file
136         only_if_modified: file won't be modified if the content hasn't changed
137     """
138     try:
139         os.makedirs(os.path.dirname(path))
140     except:
141         pass
142 
143     new_content = to_file_bytes(content)
144 
145     if only_if_modified and os.path.exists(path):
146         old_content = load(path, binary=True)
147         if old_content == new_content:
148             return
149 
150     with open(path, "wb") as handle:
151         handle.write(new_content)
152 
153 
154 def mkdir_tmp():
155     return tempfile.mkdtemp(suffix='tmp_conan')
156 
157 
158 def to_file_bytes(content):
159     if six.PY3:
160         if not isinstance(content, bytes):
161             content = bytes(content, "utf-8")
162     elif isinstance(content, unicode):
163         content = content.encode("utf-8")
164     return content
165 
166 
167 def save_files(path, files, only_if_modified=False):
168     for name, content in list(files.items()):
169         save(os.path.join(path, name), content, only_if_modified=only_if_modified)
170 
171 
172 def load(path, binary=False):
173     """ Loads a file content """
174     with open(path, 'rb') as handle:
175         tmp = handle.read()
176         return tmp if binary else decode_text(tmp)
177 
178 
179 def relative_dirs(path):
180     """ Walks a dir and return a list with the relative paths """
181     ret = []
182     for dirpath, _, fnames in walk(path):
183         for filename in fnames:
184             tmp = os.path.join(dirpath, filename)
185             tmp = tmp[len(path) + 1:]
186             ret.append(tmp)
187     return ret
188 
189 
190 def get_abs_path(folder, origin):
191     if folder:
192         if os.path.isabs(folder):
193             return folder
194         return os.path.join(origin, folder)
195     return origin
196 
197 
198 def _change_permissions(func, path, exc_info):
199     if not os.access(path, os.W_OK):
200         os.chmod(path, stat.S_IWUSR)
201         func(path)
202     else:
203         raise OSError("Cannot change permissions for {}! Exception info: {}".format(path, exc_info))
204 
205 
206 def rmdir(path):
207     try:
208         shutil.rmtree(path, onerror=_change_permissions)
209     except OSError as err:
210         if err.errno == errno.ENOENT:
211             return
212         raise
213 
214 
215 def remove(path):
216     try:
217         assert os.path.isfile(path)
218         os.remove(path)
219     except (IOError, OSError) as e:  # for py3, handle just PermissionError
220         if e.errno == errno.EPERM or e.errno == errno.EACCES:
221             os.chmod(path, stat.S_IRWXU)
222             os.remove(path)
223             return
224         raise
225 
226 
227 def mkdir(path):
228     """Recursive mkdir, doesnt fail if already existing"""
229     if os.path.exists(path):
230         return
231     os.makedirs(path)
232 
233 
234 def path_exists(path, basedir):
235     """Case sensitive, for windows, optional
236     basedir for skip caps check for tmp folders in testing for example (returned always
237     in lowercase for some strange reason)"""
238     exists = os.path.exists(path)
239     if not exists or sys.platform == "linux2":
240         return exists
241 
242     path = os.path.normpath(path)
243     path = os.path.relpath(path, basedir)
244     chunks = path.split(os.sep)
245     tmp = basedir
246 
247     for chunk in chunks:
248         if chunk and chunk not in os.listdir(tmp):
249             return False
250         tmp = os.path.normpath(tmp + os.sep + chunk)
251     return True
252 
253 
254 def gzopen_without_timestamps(name, mode="r", fileobj=None, compresslevel=None, **kwargs):
255     """ !! Method overrided by laso to pass mtime=0 (!=None) to avoid time.time() was
256         setted in Gzip file causing md5 to change. Not possible using the
257         previous tarfile open because arguments are not passed to GzipFile constructor
258     """
259     from tarfile import CompressionError, ReadError
260 
261     compresslevel = compresslevel or int(os.getenv("CONAN_COMPRESSION_LEVEL", 9))
262 
263     if mode not in ("r", "w"):
264         raise ValueError("mode must be 'r' or 'w'")
265 
266     try:
267         import gzip
268         gzip.GzipFile
269     except (ImportError, AttributeError):
270         raise CompressionError("gzip module is not available")
271 
272     try:
273         fileobj = gzip.GzipFile(name, mode, compresslevel, fileobj, mtime=0)
274     except OSError:
275         if fileobj is not None and mode == 'r':
276             raise ReadError("not a gzip file")
277         raise
278 
279     try:
280         t = tarfile.TarFile.taropen(name, mode, fileobj, **kwargs)
281     except IOError:
282         fileobj.close()
283         if mode == 'r':
284             raise ReadError("not a gzip file")
285         raise
286     except:
287         fileobj.close()
288         raise
289     t._extfileobj = False
290     return t
291 
292 
293 def tar_extract(fileobj, destination_dir):
294     """Extract tar file controlling not absolute paths and fixing the routes
295     if the tar was zipped in windows"""
296     def badpath(path, base):
297         # joinpath will ignore base if path is absolute
298         return not realpath(abspath(joinpath(base, path))).startswith(base)
299 
300     def safemembers(members):
301         base = realpath(abspath("."))
302 
303         for finfo in members:
304             if badpath(finfo.name, base) or finfo.islnk():
305                 continue
306             else:
307                 # Fixes unzip a windows zipped file in linux
308                 finfo.name = finfo.name.replace("\\", "/")
309                 yield finfo
310 
311     the_tar = tarfile.open(fileobj=fileobj)
312     # NOTE: The errorlevel=2 has been removed because it was failing in Win10, it didn't allow to
313     # "could not change modification time", with time=0
314     # the_tar.errorlevel = 2  # raise exception if any error
315     the_tar.extractall(path=destination_dir, members=safemembers(the_tar))
316     the_tar.close()
317 
318 
319 def list_folder_subdirs(basedir, level):
320     ret = []
321     for root, dirs, _ in walk(basedir):
322         rel_path = os.path.relpath(root, basedir)
323         if rel_path == ".":
324             continue
325         dir_split = rel_path.split(os.sep)
326         if len(dir_split) == level:
327             ret.append("/".join(dir_split))
328             dirs[:] = []  # Stop iterate subdirs
329     return ret
330 
331 
332 def exception_message_safe(exc):
333     try:
334         return str(exc)
335     except:
336         return decode_text(repr(exc))
337
```
--- 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/conans/util/files.py b/conans/util/files.py
--- a/conans/util/files.py
+++ b/conans/util/files.py
@@ -298,7 +298,7 @@
         return not realpath(abspath(joinpath(base, path))).startswith(base)
 
     def safemembers(members):
-        base = realpath(abspath("."))
+        base = realpath(abspath(destination_dir))
 
         for finfo in members:
             if badpath(finfo.name, base) or finfo.islnk():

{"golden_diff": "diff --git a/conans/util/files.py b/conans/util/files.py\n--- a/conans/util/files.py\n+++ b/conans/util/files.py\n@@ -298,7 +298,7 @@\n         return not realpath(abspath(joinpath(base, path))).startswith(base)\n \n     def safemembers(members):\n-        base = realpath(abspath(\".\"))\n+        base = realpath(abspath(destination_dir))\n \n         for finfo in members:\n             if badpath(finfo.name, base) or finfo.islnk():\n", "issue": "tar_extract is affected by symlinks in current directory\nIf a current directory has a symlink with the name matching a directory in the installed conan_package.tgz, then the directory is not extracted to the package directory.\r\n\r\nReproduced on 1.15.0-dev. Notice the `include` directory disappears from the package directory.\r\n```\r\n$ mkdir /tmp/testing && cd /tmp/testing\r\n$ conan new Hello/0.1 -t\r\n$ conan create . demo/testing\r\n$ conan upload Hello/0.1@demo/testing --all\r\n$ conan remove Hello/0.1@demo/testing --force\r\n$ conan install Hello/0.1@demo/testing\r\n$ conan get Hello/0.1@demo/testing:57547fe65fffc300f05aa42ee64b3b02eeabb6d7 .\r\nListing directory '.':\r\n conaninfo.txt\r\n conanmanifest.txt\r\n include\r\n lib\r\n$ ln -s /tmp include\r\n$ conan remove Hello/0.1@demo/testing --force\r\n$ conan install Hello/0.1@demo/testing\r\n$ conan get Hello/0.1@demo/testing:57547fe65fffc300f05aa42ee64b3b02eeabb6d7 .\r\nListing directory '.':\r\n conaninfo.txt\r\n conanmanifest.txt\r\n lib\r\n```\r\n\r\nThe issue is caused by `badpath` check. The `base` is the current directory. Joining current directory and `include`, and resolving the symlink makes it to not to start with the current directory. What is the true intent for the `badpath` method? Is there a good test case to show it's necessary?\r\n```python\r\n  293 def tar_extract(fileobj, destination_dir):\r\n  294     \"\"\"Extract tar file controlling not absolute paths and fixing the routes\r\n  295     if the tar was zipped in windows\"\"\"\r\n  296     def badpath(path, base):\r\n  297         # joinpath will ignore base if path is absolute\r\n  298         return not realpath(abspath(joinpath(base, path))).startswith(base)\r\n  299\r\n  300     def safemembers(members):\r\n  301         base = realpath(abspath(\".\"))\r\n  302\r\n  303         for finfo in members:\r\n  304             if badpath(finfo.name, base) or finfo.islnk():\r\n  305                 continue\r\n  306             else:\r\n  307                 # Fixes unzip a windows zipped file in linux\r\n  308                 finfo.name = finfo.name.replace(\"\\\\\", \"/\")\r\n  309                 yield finfo\r\n```\r\n\r\n\r\n\r\n\r\n- [x] I've read the [CONTRIBUTING guide](https://github.com/conan-io/conan/blob/develop/.github/CONTRIBUTING.md).\r\n- [x] I've specified the Conan version, operating system version and any tool that can be relevant.\r\n- [x] I've explained the steps to reproduce the error or the motivation/use case of the question/suggestion.\r\n\r\n\n", "before_files": [{"content": "import errno\nimport hashlib\nimport os\nimport platform\nimport re\nimport shutil\nimport stat\nimport sys\nimport tarfile\nimport tempfile\n\nfrom os.path import abspath, join as joinpath, realpath\n\nimport six\n\nfrom conans.util.log import logger\n\n\ndef walk(top, **kwargs):\n    if six.PY2:\n        # If py2 os.walk receives a unicode object, it will fail if a non-ascii file name is found\n        # during the iteration. More info:\n        # https://stackoverflow.com/questions/21772271/unicodedecodeerror-when-performing-os-walk\n        try:\n            top = str(top)\n        except UnicodeDecodeError:\n            pass\n\n    return os.walk(top, **kwargs)\n\n\ndef make_read_only(path):\n    for root, _, files in walk(path):\n        for f in files:\n            full_path = os.path.join(root, f)\n            mode = os.stat(full_path).st_mode\n            os.chmod(full_path, mode & ~ stat.S_IWRITE)\n\n\n_DIRTY_FOLDER = \".dirty\"\n\n\ndef set_dirty(folder):\n    dirty_file = os.path.normpath(folder) + _DIRTY_FOLDER\n    save(dirty_file, \"\")\n\n\ndef clean_dirty(folder):\n    dirty_file = os.path.normpath(folder) + _DIRTY_FOLDER\n    os.remove(dirty_file)\n\n\ndef is_dirty(folder):\n    dirty_file = os.path.normpath(folder) + _DIRTY_FOLDER\n    return os.path.exists(dirty_file)\n\n\ndef decode_text(text):\n    decoders = [\"utf-8\", \"Windows-1252\"]\n    for decoder in decoders:\n        try:\n            return text.decode(decoder)\n        except UnicodeDecodeError:\n            continue\n    logger.warning(\"can't decode %s\" % str(text))\n    return text.decode(\"utf-8\", \"ignore\")  # Ignore not compatible characters\n\n\ndef touch(fname, times=None):\n    os.utime(fname, times)\n\n\ndef touch_folder(folder):\n    for dirname, _, filenames in walk(folder):\n        for fname in filenames:\n            os.utime(os.path.join(dirname, fname), None)\n\n\ndef normalize(text):\n    if platform.system() == \"Windows\":\n        return re.sub(\"\\r?\\n\", \"\\r\\n\", text)\n    else:\n        return text\n\n\ndef md5(content):\n    md5alg = hashlib.md5()\n    if isinstance(content, bytes):\n        tmp = content\n    else:\n        tmp = content.encode(\"utf-8\")\n    md5alg.update(tmp)\n    return md5alg.hexdigest()\n\n\ndef md5sum(file_path):\n    return _generic_algorithm_sum(file_path, \"md5\")\n\n\ndef sha1sum(file_path):\n    return _generic_algorithm_sum(file_path, \"sha1\")\n\n\ndef sha256sum(file_path):\n    return _generic_algorithm_sum(file_path, \"sha256\")\n\n\ndef _generic_algorithm_sum(file_path, algorithm_name):\n\n    with open(file_path, 'rb') as fh:\n        m = hashlib.new(algorithm_name)\n        while True:\n            data = fh.read(8192)\n            if not data:\n                break\n            m.update(data)\n        return m.hexdigest()\n\n\ndef save_append(path, content):\n    try:\n        os.makedirs(os.path.dirname(path))\n    except:\n        pass\n\n    with open(path, \"ab\") as handle:\n        handle.write(to_file_bytes(content))\n\n\ndef save(path, content, only_if_modified=False):\n    \"\"\"\n    Saves a file with given content\n    Params:\n        path: path to write file to\n        content: contents to save in the file\n        only_if_modified: file won't be modified if the content hasn't changed\n    \"\"\"\n    try:\n        os.makedirs(os.path.dirname(path))\n    except:\n        pass\n\n    new_content = to_file_bytes(content)\n\n    if only_if_modified and os.path.exists(path):\n        old_content = load(path, binary=True)\n        if old_content == new_content:\n            return\n\n    with open(path, \"wb\") as handle:\n        handle.write(new_content)\n\n\ndef mkdir_tmp():\n    return tempfile.mkdtemp(suffix='tmp_conan')\n\n\ndef to_file_bytes(content):\n    if six.PY3:\n        if not isinstance(content, bytes):\n            content = bytes(content, \"utf-8\")\n    elif isinstance(content, unicode):\n        content = content.encode(\"utf-8\")\n    return content\n\n\ndef save_files(path, files, only_if_modified=False):\n    for name, content in list(files.items()):\n        save(os.path.join(path, name), content, only_if_modified=only_if_modified)\n\n\ndef load(path, binary=False):\n    \"\"\" Loads a file content \"\"\"\n    with open(path, 'rb') as handle:\n        tmp = handle.read()\n        return tmp if binary else decode_text(tmp)\n\n\ndef relative_dirs(path):\n    \"\"\" Walks a dir and return a list with the relative paths \"\"\"\n    ret = []\n    for dirpath, _, fnames in walk(path):\n        for filename in fnames:\n            tmp = os.path.join(dirpath, filename)\n            tmp = tmp[len(path) + 1:]\n            ret.append(tmp)\n    return ret\n\n\ndef get_abs_path(folder, origin):\n    if folder:\n        if os.path.isabs(folder):\n            return folder\n        return os.path.join(origin, folder)\n    return origin\n\n\ndef _change_permissions(func, path, exc_info):\n    if not os.access(path, os.W_OK):\n        os.chmod(path, stat.S_IWUSR)\n        func(path)\n    else:\n        raise OSError(\"Cannot change permissions for {}! Exception info: {}\".format(path, exc_info))\n\n\ndef rmdir(path):\n    try:\n        shutil.rmtree(path, onerror=_change_permissions)\n    except OSError as err:\n        if err.errno == errno.ENOENT:\n            return\n        raise\n\n\ndef remove(path):\n    try:\n        assert os.path.isfile(path)\n        os.remove(path)\n    except (IOError, OSError) as e:  # for py3, handle just PermissionError\n        if e.errno == errno.EPERM or e.errno == errno.EACCES:\n            os.chmod(path, stat.S_IRWXU)\n            os.remove(path)\n            return\n        raise\n\n\ndef mkdir(path):\n    \"\"\"Recursive mkdir, doesnt fail if already existing\"\"\"\n    if os.path.exists(path):\n        return\n    os.makedirs(path)\n\n\ndef path_exists(path, basedir):\n    \"\"\"Case sensitive, for windows, optional\n    basedir for skip caps check for tmp folders in testing for example (returned always\n    in lowercase for some strange reason)\"\"\"\n    exists = os.path.exists(path)\n    if not exists or sys.platform == \"linux2\":\n        return exists\n\n    path = os.path.normpath(path)\n    path = os.path.relpath(path, basedir)\n    chunks = path.split(os.sep)\n    tmp = basedir\n\n    for chunk in chunks:\n        if chunk and chunk not in os.listdir(tmp):\n            return False\n        tmp = os.path.normpath(tmp + os.sep + chunk)\n    return True\n\n\ndef gzopen_without_timestamps(name, mode=\"r\", fileobj=None, compresslevel=None, **kwargs):\n    \"\"\" !! Method overrided by laso to pass mtime=0 (!=None) to avoid time.time() was\n        setted in Gzip file causing md5 to change. Not possible using the\n        previous tarfile open because arguments are not passed to GzipFile constructor\n    \"\"\"\n    from tarfile import CompressionError, ReadError\n\n    compresslevel = compresslevel or int(os.getenv(\"CONAN_COMPRESSION_LEVEL\", 9))\n\n    if mode not in (\"r\", \"w\"):\n        raise ValueError(\"mode must be 'r' or 'w'\")\n\n    try:\n        import gzip\n        gzip.GzipFile\n    except (ImportError, AttributeError):\n        raise CompressionError(\"gzip module is not available\")\n\n    try:\n        fileobj = gzip.GzipFile(name, mode, compresslevel, fileobj, mtime=0)\n    except OSError:\n        if fileobj is not None and mode == 'r':\n            raise ReadError(\"not a gzip file\")\n        raise\n\n    try:\n        t = tarfile.TarFile.taropen(name, mode, fileobj, **kwargs)\n    except IOError:\n        fileobj.close()\n        if mode == 'r':\n            raise ReadError(\"not a gzip file\")\n        raise\n    except:\n        fileobj.close()\n        raise\n    t._extfileobj = False\n    return t\n\n\ndef tar_extract(fileobj, destination_dir):\n    \"\"\"Extract tar file controlling not absolute paths and fixing the routes\n    if the tar was zipped in windows\"\"\"\n    def badpath(path, base):\n        # joinpath will ignore base if path is absolute\n        return not realpath(abspath(joinpath(base, path))).startswith(base)\n\n    def safemembers(members):\n        base = realpath(abspath(\".\"))\n\n        for finfo in members:\n            if badpath(finfo.name, base) or finfo.islnk():\n                continue\n            else:\n                # Fixes unzip a windows zipped file in linux\n                finfo.name = finfo.name.replace(\"\\\\\", \"/\")\n                yield finfo\n\n    the_tar = tarfile.open(fileobj=fileobj)\n    # NOTE: The errorlevel=2 has been removed because it was failing in Win10, it didn't allow to\n    # \"could not change modification time\", with time=0\n    # the_tar.errorlevel = 2  # raise exception if any error\n    the_tar.extractall(path=destination_dir, members=safemembers(the_tar))\n    the_tar.close()\n\n\ndef list_folder_subdirs(basedir, level):\n    ret = []\n    for root, dirs, _ in walk(basedir):\n        rel_path = os.path.relpath(root, basedir)\n        if rel_path == \".\":\n            continue\n        dir_split = rel_path.split(os.sep)\n        if len(dir_split) == level:\n            ret.append(\"/\".join(dir_split))\n            dirs[:] = []  # Stop iterate subdirs\n    return ret\n\n\ndef exception_message_safe(exc):\n    try:\n        return str(exc)\n    except:\n        return decode_text(repr(exc))\n", "path": "conans/util/files.py"}], "after_files": [{"content": "import errno\nimport hashlib\nimport os\nimport platform\nimport re\nimport shutil\nimport stat\nimport sys\nimport tarfile\nimport tempfile\n\nfrom os.path import abspath, join as joinpath, realpath\n\nimport six\n\nfrom conans.util.log import logger\n\n\ndef walk(top, **kwargs):\n    if six.PY2:\n        # If py2 os.walk receives a unicode object, it will fail if a non-ascii file name is found\n        # during the iteration. More info:\n        # https://stackoverflow.com/questions/21772271/unicodedecodeerror-when-performing-os-walk\n        try:\n            top = str(top)\n        except UnicodeDecodeError:\n            pass\n\n    return os.walk(top, **kwargs)\n\n\ndef make_read_only(path):\n    for root, _, files in walk(path):\n        for f in files:\n            full_path = os.path.join(root, f)\n            mode = os.stat(full_path).st_mode\n            os.chmod(full_path, mode & ~ stat.S_IWRITE)\n\n\n_DIRTY_FOLDER = \".dirty\"\n\n\ndef set_dirty(folder):\n    dirty_file = os.path.normpath(folder) + _DIRTY_FOLDER\n    save(dirty_file, \"\")\n\n\ndef clean_dirty(folder):\n    dirty_file = os.path.normpath(folder) + _DIRTY_FOLDER\n    os.remove(dirty_file)\n\n\ndef is_dirty(folder):\n    dirty_file = os.path.normpath(folder) + _DIRTY_FOLDER\n    return os.path.exists(dirty_file)\n\n\ndef decode_text(text):\n    decoders = [\"utf-8\", \"Windows-1252\"]\n    for decoder in decoders:\n        try:\n            return text.decode(decoder)\n        except UnicodeDecodeError:\n            continue\n    logger.warning(\"can't decode %s\" % str(text))\n    return text.decode(\"utf-8\", \"ignore\")  # Ignore not compatible characters\n\n\ndef touch(fname, times=None):\n    os.utime(fname, times)\n\n\ndef touch_folder(folder):\n    for dirname, _, filenames in walk(folder):\n        for fname in filenames:\n            os.utime(os.path.join(dirname, fname), None)\n\n\ndef normalize(text):\n    if platform.system() == \"Windows\":\n        return re.sub(\"\\r?\\n\", \"\\r\\n\", text)\n    else:\n        return text\n\n\ndef md5(content):\n    md5alg = hashlib.md5()\n    if isinstance(content, bytes):\n        tmp = content\n    else:\n        tmp = content.encode(\"utf-8\")\n    md5alg.update(tmp)\n    return md5alg.hexdigest()\n\n\ndef md5sum(file_path):\n    return _generic_algorithm_sum(file_path, \"md5\")\n\n\ndef sha1sum(file_path):\n    return _generic_algorithm_sum(file_path, \"sha1\")\n\n\ndef sha256sum(file_path):\n    return _generic_algorithm_sum(file_path, \"sha256\")\n\n\ndef _generic_algorithm_sum(file_path, algorithm_name):\n\n    with open(file_path, 'rb') as fh:\n        m = hashlib.new(algorithm_name)\n        while True:\n            data = fh.read(8192)\n            if not data:\n                break\n            m.update(data)\n        return m.hexdigest()\n\n\ndef save_append(path, content):\n    try:\n        os.makedirs(os.path.dirname(path))\n    except:\n        pass\n\n    with open(path, \"ab\") as handle:\n        handle.write(to_file_bytes(content))\n\n\ndef save(path, content, only_if_modified=False):\n    \"\"\"\n    Saves a file with given content\n    Params:\n        path: path to write file to\n        content: contents to save in the file\n        only_if_modified: file won't be modified if the content hasn't changed\n    \"\"\"\n    try:\n        os.makedirs(os.path.dirname(path))\n    except:\n        pass\n\n    new_content = to_file_bytes(content)\n\n    if only_if_modified and os.path.exists(path):\n        old_content = load(path, binary=True)\n        if old_content == new_content:\n            return\n\n    with open(path, \"wb\") as handle:\n        handle.write(new_content)\n\n\ndef mkdir_tmp():\n    return tempfile.mkdtemp(suffix='tmp_conan')\n\n\ndef to_file_bytes(content):\n    if six.PY3:\n        if not isinstance(content, bytes):\n            content = bytes(content, \"utf-8\")\n    elif isinstance(content, unicode):\n        content = content.encode(\"utf-8\")\n    return content\n\n\ndef save_files(path, files, only_if_modified=False):\n    for name, content in list(files.items()):\n        save(os.path.join(path, name), content, only_if_modified=only_if_modified)\n\n\ndef load(path, binary=False):\n    \"\"\" Loads a file content \"\"\"\n    with open(path, 'rb') as handle:\n        tmp = handle.read()\n        return tmp if binary else decode_text(tmp)\n\n\ndef relative_dirs(path):\n    \"\"\" Walks a dir and return a list with the relative paths \"\"\"\n    ret = []\n    for dirpath, _, fnames in walk(path):\n        for filename in fnames:\n            tmp = os.path.join(dirpath, filename)\n            tmp = tmp[len(path) + 1:]\n            ret.append(tmp)\n    return ret\n\n\ndef get_abs_path(folder, origin):\n    if folder:\n        if os.path.isabs(folder):\n            return folder\n        return os.path.join(origin, folder)\n    return origin\n\n\ndef _change_permissions(func, path, exc_info):\n    if not os.access(path, os.W_OK):\n        os.chmod(path, stat.S_IWUSR)\n        func(path)\n    else:\n        raise OSError(\"Cannot change permissions for {}! Exception info: {}\".format(path, exc_info))\n\n\ndef rmdir(path):\n    try:\n        shutil.rmtree(path, onerror=_change_permissions)\n    except OSError as err:\n        if err.errno == errno.ENOENT:\n            return\n        raise\n\n\ndef remove(path):\n    try:\n        assert os.path.isfile(path)\n        os.remove(path)\n    except (IOError, OSError) as e:  # for py3, handle just PermissionError\n        if e.errno == errno.EPERM or e.errno == errno.EACCES:\n            os.chmod(path, stat.S_IRWXU)\n            os.remove(path)\n            return\n        raise\n\n\ndef mkdir(path):\n    \"\"\"Recursive mkdir, doesnt fail if already existing\"\"\"\n    if os.path.exists(path):\n        return\n    os.makedirs(path)\n\n\ndef path_exists(path, basedir):\n    \"\"\"Case sensitive, for windows, optional\n    basedir for skip caps check for tmp folders in testing for example (returned always\n    in lowercase for some strange reason)\"\"\"\n    exists = os.path.exists(path)\n    if not exists or sys.platform == \"linux2\":\n        return exists\n\n    path = os.path.normpath(path)\n    path = os.path.relpath(path, basedir)\n    chunks = path.split(os.sep)\n    tmp = basedir\n\n    for chunk in chunks:\n        if chunk and chunk not in os.listdir(tmp):\n            return False\n        tmp = os.path.normpath(tmp + os.sep + chunk)\n    return True\n\n\ndef gzopen_without_timestamps(name, mode=\"r\", fileobj=None, compresslevel=None, **kwargs):\n    \"\"\" !! Method overrided by laso to pass mtime=0 (!=None) to avoid time.time() was\n        setted in Gzip file causing md5 to change. Not possible using the\n        previous tarfile open because arguments are not passed to GzipFile constructor\n    \"\"\"\n    from tarfile import CompressionError, ReadError\n\n    compresslevel = compresslevel or int(os.getenv(\"CONAN_COMPRESSION_LEVEL\", 9))\n\n    if mode not in (\"r\", \"w\"):\n        raise ValueError(\"mode must be 'r' or 'w'\")\n\n    try:\n        import gzip\n        gzip.GzipFile\n    except (ImportError, AttributeError):\n        raise CompressionError(\"gzip module is not available\")\n\n    try:\n        fileobj = gzip.GzipFile(name, mode, compresslevel, fileobj, mtime=0)\n    except OSError:\n        if fileobj is not None and mode == 'r':\n            raise ReadError(\"not a gzip file\")\n        raise\n\n    try:\n        t = tarfile.TarFile.taropen(name, mode, fileobj, **kwargs)\n    except IOError:\n        fileobj.close()\n        if mode == 'r':\n            raise ReadError(\"not a gzip file\")\n        raise\n    except:\n        fileobj.close()\n        raise\n    t._extfileobj = False\n    return t\n\n\ndef tar_extract(fileobj, destination_dir):\n    \"\"\"Extract tar file controlling not absolute paths and fixing the routes\n    if the tar was zipped in windows\"\"\"\n    def badpath(path, base):\n        # joinpath will ignore base if path is absolute\n        return not realpath(abspath(joinpath(base, path))).startswith(base)\n\n    def safemembers(members):\n        base = realpath(abspath(destination_dir))\n\n        for finfo in members:\n            if badpath(finfo.name, base) or finfo.islnk():\n                continue\n            else:\n                # Fixes unzip a windows zipped file in linux\n                finfo.name = finfo.name.replace(\"\\\\\", \"/\")\n                yield finfo\n\n    the_tar = tarfile.open(fileobj=fileobj)\n    # NOTE: The errorlevel=2 has been removed because it was failing in Win10, it didn't allow to\n    # \"could not change modification time\", with time=0\n    # the_tar.errorlevel = 2  # raise exception if any error\n    the_tar.extractall(path=destination_dir, members=safemembers(the_tar))\n    the_tar.close()\n\n\ndef list_folder_subdirs(basedir, level):\n    ret = []\n    for root, dirs, _ in walk(basedir):\n        rel_path = os.path.relpath(root, basedir)\n        if rel_path == \".\":\n            continue\n        dir_split = rel_path.split(os.sep)\n        if len(dir_split) == level:\n            ret.append(\"/\".join(dir_split))\n            dirs[:] = []  # Stop iterate subdirs\n    return ret\n\n\ndef exception_message_safe(exc):\n    try:\n        return str(exc)\n    except:\n        return decode_text(repr(exc))\n", "path": "conans/util/files.py"}]}