We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
conda update icu (54.1-0 --> 56.1-4 conda-forge)
In a new installation, it appears that going from icu 54 to 56 will fail unless the following is done (at least on linux):

bash Anaconda2-4.2.0-Linux-x86_64.sh
conda remove icu
rm -r $HOME/anaconda2/lib/icu
conda install -c conda-forge icu=56.1

In other words, using the first and fourth lines alone fails with:

CondaOSError: OS error: failed to link (src=u'/home/anaconda2/pkgs/icu-56.1-4/lib/icu/current', dst='/home/anaconda2/lib/icu/current', type=3, error=OSError(17, 'File exists'))


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `conda/common/disk.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 from __future__ import absolute_import, division, print_function, unicode_literals
3 
4 import sys
5 from errno import EACCES, EEXIST, ENOENT, EPERM
6 from itertools import chain
7 from logging import getLogger
8 from os import W_OK, access, chmod, getpid, listdir, lstat, makedirs, rename, unlink, walk
9 from os.path import abspath, basename, dirname, isdir, join, lexists
10 from shutil import rmtree
11 from stat import S_IEXEC, S_IMODE, S_ISDIR, S_ISLNK, S_ISREG, S_IWRITE
12 from time import sleep
13 from uuid import uuid4
14 
15 from ..compat import lchmod, text_type
16 from ..utils import on_win
17 
18 __all__ = ["rm_rf", "exp_backoff_fn", "try_write"]
19 
20 log = getLogger(__name__)
21 
22 
23 def try_write(dir_path, heavy=False):
24     """Test write access to a directory.
25 
26     Args:
27         dir_path (str): directory to test write access
28         heavy (bool): Actually create and delete a file, or do a faster os.access test.
29            https://docs.python.org/dev/library/os.html?highlight=xattr#os.access
30 
31     Returns:
32         bool
33 
34     """
35     if not isdir(dir_path):
36         return False
37     if on_win or heavy:
38         # try to create a file to see if `dir_path` is writable, see #2151
39         temp_filename = join(dir_path, '.conda-try-write-%d' % getpid())
40         try:
41             with open(temp_filename, mode='wb') as fo:
42                 fo.write(b'This is a test file.\n')
43             backoff_unlink(temp_filename)
44             return True
45         except (IOError, OSError):
46             return False
47         finally:
48             backoff_unlink(temp_filename)
49     else:
50         return access(dir_path, W_OK)
51 
52 
53 def backoff_unlink(file_or_symlink_path):
54     def _unlink(path):
55         make_writable(path)
56         unlink(path)
57 
58     try:
59         exp_backoff_fn(lambda f: lexists(f) and _unlink(f), file_or_symlink_path)
60     except (IOError, OSError) as e:
61         if e.errno not in (ENOENT,):
62             # errno.ENOENT File not found error / No such file or directory
63             raise
64 
65 
66 def backoff_rmdir(dirpath):
67     if not isdir(dirpath):
68         return
69 
70     # shutil.rmtree:
71     #   if onerror is set, it is called to handle the error with arguments (func, path, exc_info)
72     #     where func is os.listdir, os.remove, or os.rmdir;
73     #     path is the argument to that function that caused it to fail; and
74     #     exc_info is a tuple returned by sys.exc_info() ==> (type, value, traceback).
75     def retry(func, path, exc_info):
76         if getattr(exc_info[1], 'errno', None) == ENOENT:
77             return
78         recursive_make_writable(dirname(path))
79         func(path)
80 
81     def _rmdir(path):
82         try:
83             recursive_make_writable(path)
84             exp_backoff_fn(rmtree, path, onerror=retry)
85         except (IOError, OSError) as e:
86             if e.errno == ENOENT:
87                 log.debug("no such file or directory: %s", path)
88             else:
89                 raise
90 
91     for root, dirs, files in walk(dirpath, topdown=False):
92         for file in files:
93             backoff_unlink(join(root, file))
94         for dir in dirs:
95             _rmdir(join(root, dir))
96 
97     _rmdir(dirpath)
98 
99 
100 def make_writable(path):
101     try:
102         mode = lstat(path).st_mode
103         if S_ISDIR(mode):
104             chmod(path, S_IMODE(mode) | S_IWRITE | S_IEXEC)
105         elif S_ISREG(mode):
106             chmod(path, S_IMODE(mode) | S_IWRITE)
107         elif S_ISLNK(mode):
108             lchmod(path, S_IMODE(mode) | S_IWRITE)
109         else:
110             log.debug("path cannot be made writable: %s", path)
111     except Exception as e:
112         eno = getattr(e, 'errno', None)
113         if eno in (ENOENT,):
114             log.debug("tried to make writable, but didn't exist: %s", path)
115             raise
116         elif eno in (EACCES, EPERM):
117             log.debug("tried make writable but failed: %s\n%r", path, e)
118         else:
119             log.warn("Error making path writable: %s\n%r", path, e)
120             raise
121 
122 
123 def recursive_make_writable(path):
124     # The need for this function was pointed out at
125     #   https://github.com/conda/conda/issues/3266#issuecomment-239241915
126     # Especially on windows, file removal will often fail because it is marked read-only
127     if isdir(path):
128         for root, dirs, files in walk(path):
129             for path in chain.from_iterable((files, dirs)):
130                 try:
131                     exp_backoff_fn(make_writable, join(root, path))
132                 except (IOError, OSError) as e:
133                     if e.errno == ENOENT:
134                         log.debug("no such file or directory: %s", path)
135                     else:
136                         raise
137     else:
138         exp_backoff_fn(make_writable, path)
139 
140 
141 def exp_backoff_fn(fn, *args, **kwargs):
142     """Mostly for retrying file operations that fail on Windows due to virus scanners"""
143     if not on_win:
144         return fn(*args, **kwargs)
145 
146     import random
147     # with max_tries = 6, max total time ~= 3.2 sec
148     # with max_tries = 7, max total time ~= 6.5 sec
149     max_tries = 7
150     for n in range(max_tries):
151         try:
152             result = fn(*args, **kwargs)
153         except (OSError, IOError) as e:
154             log.debug(repr(e))
155             if e.errno in (EPERM, EACCES):
156                 if n == max_tries-1:
157                     raise
158                 sleep_time = ((2 ** n) + random.random()) * 0.1
159                 caller_frame = sys._getframe(1)
160                 log.debug("retrying %s/%s %s() in %g sec",
161                           basename(caller_frame.f_code.co_filename),
162                           caller_frame.f_lineno, fn.__name__,
163                           sleep_time)
164                 sleep(sleep_time)
165             elif e.errno in (ENOENT,):
166                 # errno.ENOENT File not found error / No such file or directory
167                 raise
168             else:
169                 log.warn("Uncaught backoff with errno %d", e.errno)
170                 raise
171         else:
172             return result
173 
174 
175 def rm_rf(path, max_retries=5, trash=True):
176     """
177     Completely delete path
178     max_retries is the number of times to retry on failure. The default is 5. This only applies
179     to deleting a directory.
180     If removing path fails and trash is True, files will be moved to the trash directory.
181     """
182     try:
183         path = abspath(path)
184         log.debug("rm_rf %s", path)
185         if isdir(path):
186             try:
187                 # On Windows, always move to trash first.
188                 if trash and on_win:
189                     move_result = move_path_to_trash(path, preclean=False)
190                     if move_result:
191                         return True
192                 backoff_rmdir(path)
193             finally:
194                 # If path was removed, ensure it's not in linked_data_
195                 if not isdir(path):
196                     from conda.install import delete_linked_data_any
197                     delete_linked_data_any(path)
198         elif lexists(path):
199             try:
200                 backoff_unlink(path)
201                 return True
202             except (OSError, IOError) as e:
203                 log.debug("%r errno %d\nCannot unlink %s.", e, e.errno, path)
204                 if trash:
205                     move_result = move_path_to_trash(path)
206                     if move_result:
207                         return True
208                 log.info("Failed to remove %s.", path)
209 
210         else:
211             log.debug("rm_rf failed. Not a link, file, or directory: %s", path)
212         return True
213     finally:
214         if lexists(path):
215             log.info("rm_rf failed for %s", path)
216             return False
217 
218 
219 def delete_trash(prefix=None):
220     from ..base.context import context
221     for pkg_dir in context.pkgs_dirs:
222         trash_dir = join(pkg_dir, '.trash')
223         if not lexists(trash_dir):
224             log.debug("Trash directory %s doesn't exist. Moving on.", trash_dir)
225             continue
226         log.debug("removing trash for %s", trash_dir)
227         for p in listdir(trash_dir):
228             path = join(trash_dir, p)
229             try:
230                 if isdir(path):
231                     backoff_rmdir(path)
232                 else:
233                     backoff_unlink(path)
234             except (IOError, OSError) as e:
235                 log.info("Could not delete path in trash dir %s\n%r", path, e)
236         if listdir(trash_dir):
237             log.info("Unable to clean trash directory %s", trash_dir)
238 
239 
240 def move_to_trash(prefix, f, tempdir=None):
241     """
242     Move a file or folder f from prefix to the trash
243 
244     tempdir is a deprecated parameter, and will be ignored.
245 
246     This function is deprecated in favor of `move_path_to_trash`.
247     """
248     return move_path_to_trash(join(prefix, f) if f else prefix)
249 
250 
251 def move_path_to_trash(path, preclean=True):
252     """
253     Move a path to the trash
254     """
255     from ..base.context import context
256     for pkg_dir in context.pkgs_dirs:
257         trash_dir = join(pkg_dir, '.trash')
258 
259         try:
260             makedirs(trash_dir)
261         except (IOError, OSError) as e1:
262             if e1.errno != EEXIST:
263                 continue
264 
265         trash_file = join(trash_dir, text_type(uuid4()))
266 
267         try:
268             rename(path, trash_file)
269         except (IOError, OSError) as e:
270             log.debug("Could not move %s to %s.\n%r", path, trash_file, e)
271         else:
272             log.debug("Moved to trash: %s", path)
273             from ..install import delete_linked_data_any
274             delete_linked_data_any(path)
275             return True
276 
277     return False
278
```
--- 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/conda/common/disk.py b/conda/common/disk.py
--- a/conda/common/disk.py
+++ b/conda/common/disk.py
@@ -6,7 +6,7 @@
 from itertools import chain
 from logging import getLogger
 from os import W_OK, access, chmod, getpid, listdir, lstat, makedirs, rename, unlink, walk
-from os.path import abspath, basename, dirname, isdir, join, lexists
+from os.path import abspath, basename, dirname, isdir, isfile, islink, join, lexists
 from shutil import rmtree
 from stat import S_IEXEC, S_IMODE, S_ISDIR, S_ISLNK, S_ISREG, S_IWRITE
 from time import sleep
@@ -192,10 +192,10 @@
                 backoff_rmdir(path)
             finally:
                 # If path was removed, ensure it's not in linked_data_
-                if not isdir(path):
+                if islink(path) or isfile(path):
                     from conda.install import delete_linked_data_any
                     delete_linked_data_any(path)
-        elif lexists(path):
+        if lexists(path):
             try:
                 backoff_unlink(path)
                 return True

{"golden_diff": "diff --git a/conda/common/disk.py b/conda/common/disk.py\n--- a/conda/common/disk.py\n+++ b/conda/common/disk.py\n@@ -6,7 +6,7 @@\n from itertools import chain\n from logging import getLogger\n from os import W_OK, access, chmod, getpid, listdir, lstat, makedirs, rename, unlink, walk\n-from os.path import abspath, basename, dirname, isdir, join, lexists\n+from os.path import abspath, basename, dirname, isdir, isfile, islink, join, lexists\n from shutil import rmtree\n from stat import S_IEXEC, S_IMODE, S_ISDIR, S_ISLNK, S_ISREG, S_IWRITE\n from time import sleep\n@@ -192,10 +192,10 @@\n                 backoff_rmdir(path)\n             finally:\n                 # If path was removed, ensure it's not in linked_data_\n-                if not isdir(path):\n+                if islink(path) or isfile(path):\n                     from conda.install import delete_linked_data_any\n                     delete_linked_data_any(path)\n-        elif lexists(path):\n+        if lexists(path):\n             try:\n                 backoff_unlink(path)\n                 return True\n", "issue": "conda update icu (54.1-0 --> 56.1-4 conda-forge)\nIn a new installation, it appears that going from icu 54 to 56 will fail unless the following is done (at least on linux):\n\nbash Anaconda2-4.2.0-Linux-x86_64.sh\nconda remove icu\nrm -r $HOME/anaconda2/lib/icu\nconda install -c conda-forge icu=56.1\n\nIn other words, using the first and fourth lines alone fails with:\n\nCondaOSError: OS error: failed to link (src=u'/home/anaconda2/pkgs/icu-56.1-4/lib/icu/current', dst='/home/anaconda2/lib/icu/current', type=3, error=OSError(17, 'File exists'))\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nimport sys\nfrom errno import EACCES, EEXIST, ENOENT, EPERM\nfrom itertools import chain\nfrom logging import getLogger\nfrom os import W_OK, access, chmod, getpid, listdir, lstat, makedirs, rename, unlink, walk\nfrom os.path import abspath, basename, dirname, isdir, join, lexists\nfrom shutil import rmtree\nfrom stat import S_IEXEC, S_IMODE, S_ISDIR, S_ISLNK, S_ISREG, S_IWRITE\nfrom time import sleep\nfrom uuid import uuid4\n\nfrom ..compat import lchmod, text_type\nfrom ..utils import on_win\n\n__all__ = [\"rm_rf\", \"exp_backoff_fn\", \"try_write\"]\n\nlog = getLogger(__name__)\n\n\ndef try_write(dir_path, heavy=False):\n    \"\"\"Test write access to a directory.\n\n    Args:\n        dir_path (str): directory to test write access\n        heavy (bool): Actually create and delete a file, or do a faster os.access test.\n           https://docs.python.org/dev/library/os.html?highlight=xattr#os.access\n\n    Returns:\n        bool\n\n    \"\"\"\n    if not isdir(dir_path):\n        return False\n    if on_win or heavy:\n        # try to create a file to see if `dir_path` is writable, see #2151\n        temp_filename = join(dir_path, '.conda-try-write-%d' % getpid())\n        try:\n            with open(temp_filename, mode='wb') as fo:\n                fo.write(b'This is a test file.\\n')\n            backoff_unlink(temp_filename)\n            return True\n        except (IOError, OSError):\n            return False\n        finally:\n            backoff_unlink(temp_filename)\n    else:\n        return access(dir_path, W_OK)\n\n\ndef backoff_unlink(file_or_symlink_path):\n    def _unlink(path):\n        make_writable(path)\n        unlink(path)\n\n    try:\n        exp_backoff_fn(lambda f: lexists(f) and _unlink(f), file_or_symlink_path)\n    except (IOError, OSError) as e:\n        if e.errno not in (ENOENT,):\n            # errno.ENOENT File not found error / No such file or directory\n            raise\n\n\ndef backoff_rmdir(dirpath):\n    if not isdir(dirpath):\n        return\n\n    # shutil.rmtree:\n    #   if onerror is set, it is called to handle the error with arguments (func, path, exc_info)\n    #     where func is os.listdir, os.remove, or os.rmdir;\n    #     path is the argument to that function that caused it to fail; and\n    #     exc_info is a tuple returned by sys.exc_info() ==> (type, value, traceback).\n    def retry(func, path, exc_info):\n        if getattr(exc_info[1], 'errno', None) == ENOENT:\n            return\n        recursive_make_writable(dirname(path))\n        func(path)\n\n    def _rmdir(path):\n        try:\n            recursive_make_writable(path)\n            exp_backoff_fn(rmtree, path, onerror=retry)\n        except (IOError, OSError) as e:\n            if e.errno == ENOENT:\n                log.debug(\"no such file or directory: %s\", path)\n            else:\n                raise\n\n    for root, dirs, files in walk(dirpath, topdown=False):\n        for file in files:\n            backoff_unlink(join(root, file))\n        for dir in dirs:\n            _rmdir(join(root, dir))\n\n    _rmdir(dirpath)\n\n\ndef make_writable(path):\n    try:\n        mode = lstat(path).st_mode\n        if S_ISDIR(mode):\n            chmod(path, S_IMODE(mode) | S_IWRITE | S_IEXEC)\n        elif S_ISREG(mode):\n            chmod(path, S_IMODE(mode) | S_IWRITE)\n        elif S_ISLNK(mode):\n            lchmod(path, S_IMODE(mode) | S_IWRITE)\n        else:\n            log.debug(\"path cannot be made writable: %s\", path)\n    except Exception as e:\n        eno = getattr(e, 'errno', None)\n        if eno in (ENOENT,):\n            log.debug(\"tried to make writable, but didn't exist: %s\", path)\n            raise\n        elif eno in (EACCES, EPERM):\n            log.debug(\"tried make writable but failed: %s\\n%r\", path, e)\n        else:\n            log.warn(\"Error making path writable: %s\\n%r\", path, e)\n            raise\n\n\ndef recursive_make_writable(path):\n    # The need for this function was pointed out at\n    #   https://github.com/conda/conda/issues/3266#issuecomment-239241915\n    # Especially on windows, file removal will often fail because it is marked read-only\n    if isdir(path):\n        for root, dirs, files in walk(path):\n            for path in chain.from_iterable((files, dirs)):\n                try:\n                    exp_backoff_fn(make_writable, join(root, path))\n                except (IOError, OSError) as e:\n                    if e.errno == ENOENT:\n                        log.debug(\"no such file or directory: %s\", path)\n                    else:\n                        raise\n    else:\n        exp_backoff_fn(make_writable, path)\n\n\ndef exp_backoff_fn(fn, *args, **kwargs):\n    \"\"\"Mostly for retrying file operations that fail on Windows due to virus scanners\"\"\"\n    if not on_win:\n        return fn(*args, **kwargs)\n\n    import random\n    # with max_tries = 6, max total time ~= 3.2 sec\n    # with max_tries = 7, max total time ~= 6.5 sec\n    max_tries = 7\n    for n in range(max_tries):\n        try:\n            result = fn(*args, **kwargs)\n        except (OSError, IOError) as e:\n            log.debug(repr(e))\n            if e.errno in (EPERM, EACCES):\n                if n == max_tries-1:\n                    raise\n                sleep_time = ((2 ** n) + random.random()) * 0.1\n                caller_frame = sys._getframe(1)\n                log.debug(\"retrying %s/%s %s() in %g sec\",\n                          basename(caller_frame.f_code.co_filename),\n                          caller_frame.f_lineno, fn.__name__,\n                          sleep_time)\n                sleep(sleep_time)\n            elif e.errno in (ENOENT,):\n                # errno.ENOENT File not found error / No such file or directory\n                raise\n            else:\n                log.warn(\"Uncaught backoff with errno %d\", e.errno)\n                raise\n        else:\n            return result\n\n\ndef rm_rf(path, max_retries=5, trash=True):\n    \"\"\"\n    Completely delete path\n    max_retries is the number of times to retry on failure. The default is 5. This only applies\n    to deleting a directory.\n    If removing path fails and trash is True, files will be moved to the trash directory.\n    \"\"\"\n    try:\n        path = abspath(path)\n        log.debug(\"rm_rf %s\", path)\n        if isdir(path):\n            try:\n                # On Windows, always move to trash first.\n                if trash and on_win:\n                    move_result = move_path_to_trash(path, preclean=False)\n                    if move_result:\n                        return True\n                backoff_rmdir(path)\n            finally:\n                # If path was removed, ensure it's not in linked_data_\n                if not isdir(path):\n                    from conda.install import delete_linked_data_any\n                    delete_linked_data_any(path)\n        elif lexists(path):\n            try:\n                backoff_unlink(path)\n                return True\n            except (OSError, IOError) as e:\n                log.debug(\"%r errno %d\\nCannot unlink %s.\", e, e.errno, path)\n                if trash:\n                    move_result = move_path_to_trash(path)\n                    if move_result:\n                        return True\n                log.info(\"Failed to remove %s.\", path)\n\n        else:\n            log.debug(\"rm_rf failed. Not a link, file, or directory: %s\", path)\n        return True\n    finally:\n        if lexists(path):\n            log.info(\"rm_rf failed for %s\", path)\n            return False\n\n\ndef delete_trash(prefix=None):\n    from ..base.context import context\n    for pkg_dir in context.pkgs_dirs:\n        trash_dir = join(pkg_dir, '.trash')\n        if not lexists(trash_dir):\n            log.debug(\"Trash directory %s doesn't exist. Moving on.\", trash_dir)\n            continue\n        log.debug(\"removing trash for %s\", trash_dir)\n        for p in listdir(trash_dir):\n            path = join(trash_dir, p)\n            try:\n                if isdir(path):\n                    backoff_rmdir(path)\n                else:\n                    backoff_unlink(path)\n            except (IOError, OSError) as e:\n                log.info(\"Could not delete path in trash dir %s\\n%r\", path, e)\n        if listdir(trash_dir):\n            log.info(\"Unable to clean trash directory %s\", trash_dir)\n\n\ndef move_to_trash(prefix, f, tempdir=None):\n    \"\"\"\n    Move a file or folder f from prefix to the trash\n\n    tempdir is a deprecated parameter, and will be ignored.\n\n    This function is deprecated in favor of `move_path_to_trash`.\n    \"\"\"\n    return move_path_to_trash(join(prefix, f) if f else prefix)\n\n\ndef move_path_to_trash(path, preclean=True):\n    \"\"\"\n    Move a path to the trash\n    \"\"\"\n    from ..base.context import context\n    for pkg_dir in context.pkgs_dirs:\n        trash_dir = join(pkg_dir, '.trash')\n\n        try:\n            makedirs(trash_dir)\n        except (IOError, OSError) as e1:\n            if e1.errno != EEXIST:\n                continue\n\n        trash_file = join(trash_dir, text_type(uuid4()))\n\n        try:\n            rename(path, trash_file)\n        except (IOError, OSError) as e:\n            log.debug(\"Could not move %s to %s.\\n%r\", path, trash_file, e)\n        else:\n            log.debug(\"Moved to trash: %s\", path)\n            from ..install import delete_linked_data_any\n            delete_linked_data_any(path)\n            return True\n\n    return False\n", "path": "conda/common/disk.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nimport sys\nfrom errno import EACCES, EEXIST, ENOENT, EPERM\nfrom itertools import chain\nfrom logging import getLogger\nfrom os import W_OK, access, chmod, getpid, listdir, lstat, makedirs, rename, unlink, walk\nfrom os.path import abspath, basename, dirname, isdir, isfile, islink, join, lexists\nfrom shutil import rmtree\nfrom stat import S_IEXEC, S_IMODE, S_ISDIR, S_ISLNK, S_ISREG, S_IWRITE\nfrom time import sleep\nfrom uuid import uuid4\n\nfrom ..compat import lchmod, text_type\nfrom ..utils import on_win\n\n__all__ = [\"rm_rf\", \"exp_backoff_fn\", \"try_write\"]\n\nlog = getLogger(__name__)\n\n\ndef try_write(dir_path, heavy=False):\n    \"\"\"Test write access to a directory.\n\n    Args:\n        dir_path (str): directory to test write access\n        heavy (bool): Actually create and delete a file, or do a faster os.access test.\n           https://docs.python.org/dev/library/os.html?highlight=xattr#os.access\n\n    Returns:\n        bool\n\n    \"\"\"\n    if not isdir(dir_path):\n        return False\n    if on_win or heavy:\n        # try to create a file to see if `dir_path` is writable, see #2151\n        temp_filename = join(dir_path, '.conda-try-write-%d' % getpid())\n        try:\n            with open(temp_filename, mode='wb') as fo:\n                fo.write(b'This is a test file.\\n')\n            backoff_unlink(temp_filename)\n            return True\n        except (IOError, OSError):\n            return False\n        finally:\n            backoff_unlink(temp_filename)\n    else:\n        return access(dir_path, W_OK)\n\n\ndef backoff_unlink(file_or_symlink_path):\n    def _unlink(path):\n        make_writable(path)\n        unlink(path)\n\n    try:\n        exp_backoff_fn(lambda f: lexists(f) and _unlink(f), file_or_symlink_path)\n    except (IOError, OSError) as e:\n        if e.errno not in (ENOENT,):\n            # errno.ENOENT File not found error / No such file or directory\n            raise\n\n\ndef backoff_rmdir(dirpath):\n    if not isdir(dirpath):\n        return\n\n    # shutil.rmtree:\n    #   if onerror is set, it is called to handle the error with arguments (func, path, exc_info)\n    #     where func is os.listdir, os.remove, or os.rmdir;\n    #     path is the argument to that function that caused it to fail; and\n    #     exc_info is a tuple returned by sys.exc_info() ==> (type, value, traceback).\n    def retry(func, path, exc_info):\n        if getattr(exc_info[1], 'errno', None) == ENOENT:\n            return\n        recursive_make_writable(dirname(path))\n        func(path)\n\n    def _rmdir(path):\n        try:\n            recursive_make_writable(path)\n            exp_backoff_fn(rmtree, path, onerror=retry)\n        except (IOError, OSError) as e:\n            if e.errno == ENOENT:\n                log.debug(\"no such file or directory: %s\", path)\n            else:\n                raise\n\n    for root, dirs, files in walk(dirpath, topdown=False):\n        for file in files:\n            backoff_unlink(join(root, file))\n        for dir in dirs:\n            _rmdir(join(root, dir))\n\n    _rmdir(dirpath)\n\n\ndef make_writable(path):\n    try:\n        mode = lstat(path).st_mode\n        if S_ISDIR(mode):\n            chmod(path, S_IMODE(mode) | S_IWRITE | S_IEXEC)\n        elif S_ISREG(mode):\n            chmod(path, S_IMODE(mode) | S_IWRITE)\n        elif S_ISLNK(mode):\n            lchmod(path, S_IMODE(mode) | S_IWRITE)\n        else:\n            log.debug(\"path cannot be made writable: %s\", path)\n    except Exception as e:\n        eno = getattr(e, 'errno', None)\n        if eno in (ENOENT,):\n            log.debug(\"tried to make writable, but didn't exist: %s\", path)\n            raise\n        elif eno in (EACCES, EPERM):\n            log.debug(\"tried make writable but failed: %s\\n%r\", path, e)\n        else:\n            log.warn(\"Error making path writable: %s\\n%r\", path, e)\n            raise\n\n\ndef recursive_make_writable(path):\n    # The need for this function was pointed out at\n    #   https://github.com/conda/conda/issues/3266#issuecomment-239241915\n    # Especially on windows, file removal will often fail because it is marked read-only\n    if isdir(path):\n        for root, dirs, files in walk(path):\n            for path in chain.from_iterable((files, dirs)):\n                try:\n                    exp_backoff_fn(make_writable, join(root, path))\n                except (IOError, OSError) as e:\n                    if e.errno == ENOENT:\n                        log.debug(\"no such file or directory: %s\", path)\n                    else:\n                        raise\n    else:\n        exp_backoff_fn(make_writable, path)\n\n\ndef exp_backoff_fn(fn, *args, **kwargs):\n    \"\"\"Mostly for retrying file operations that fail on Windows due to virus scanners\"\"\"\n    if not on_win:\n        return fn(*args, **kwargs)\n\n    import random\n    # with max_tries = 6, max total time ~= 3.2 sec\n    # with max_tries = 7, max total time ~= 6.5 sec\n    max_tries = 7\n    for n in range(max_tries):\n        try:\n            result = fn(*args, **kwargs)\n        except (OSError, IOError) as e:\n            log.debug(repr(e))\n            if e.errno in (EPERM, EACCES):\n                if n == max_tries-1:\n                    raise\n                sleep_time = ((2 ** n) + random.random()) * 0.1\n                caller_frame = sys._getframe(1)\n                log.debug(\"retrying %s/%s %s() in %g sec\",\n                          basename(caller_frame.f_code.co_filename),\n                          caller_frame.f_lineno, fn.__name__,\n                          sleep_time)\n                sleep(sleep_time)\n            elif e.errno in (ENOENT,):\n                # errno.ENOENT File not found error / No such file or directory\n                raise\n            else:\n                log.warn(\"Uncaught backoff with errno %d\", e.errno)\n                raise\n        else:\n            return result\n\n\ndef rm_rf(path, max_retries=5, trash=True):\n    \"\"\"\n    Completely delete path\n    max_retries is the number of times to retry on failure. The default is 5. This only applies\n    to deleting a directory.\n    If removing path fails and trash is True, files will be moved to the trash directory.\n    \"\"\"\n    try:\n        path = abspath(path)\n        log.debug(\"rm_rf %s\", path)\n        if isdir(path):\n            try:\n                # On Windows, always move to trash first.\n                if trash and on_win:\n                    move_result = move_path_to_trash(path, preclean=False)\n                    if move_result:\n                        return True\n                backoff_rmdir(path)\n            finally:\n                # If path was removed, ensure it's not in linked_data_\n                if islink(path) or isfile(path):\n                    from conda.install import delete_linked_data_any\n                    delete_linked_data_any(path)\n        if lexists(path):\n            try:\n                backoff_unlink(path)\n                return True\n            except (OSError, IOError) as e:\n                log.debug(\"%r errno %d\\nCannot unlink %s.\", e, e.errno, path)\n                if trash:\n                    move_result = move_path_to_trash(path)\n                    if move_result:\n                        return True\n                log.info(\"Failed to remove %s.\", path)\n\n        else:\n            log.debug(\"rm_rf failed. Not a link, file, or directory: %s\", path)\n        return True\n    finally:\n        if lexists(path):\n            log.info(\"rm_rf failed for %s\", path)\n            return False\n\n\ndef delete_trash(prefix=None):\n    from ..base.context import context\n    for pkg_dir in context.pkgs_dirs:\n        trash_dir = join(pkg_dir, '.trash')\n        if not lexists(trash_dir):\n            log.debug(\"Trash directory %s doesn't exist. Moving on.\", trash_dir)\n            continue\n        log.debug(\"removing trash for %s\", trash_dir)\n        for p in listdir(trash_dir):\n            path = join(trash_dir, p)\n            try:\n                if isdir(path):\n                    backoff_rmdir(path)\n                else:\n                    backoff_unlink(path)\n            except (IOError, OSError) as e:\n                log.info(\"Could not delete path in trash dir %s\\n%r\", path, e)\n        if listdir(trash_dir):\n            log.info(\"Unable to clean trash directory %s\", trash_dir)\n\n\ndef move_to_trash(prefix, f, tempdir=None):\n    \"\"\"\n    Move a file or folder f from prefix to the trash\n\n    tempdir is a deprecated parameter, and will be ignored.\n\n    This function is deprecated in favor of `move_path_to_trash`.\n    \"\"\"\n    return move_path_to_trash(join(prefix, f) if f else prefix)\n\n\ndef move_path_to_trash(path, preclean=True):\n    \"\"\"\n    Move a path to the trash\n    \"\"\"\n    from ..base.context import context\n    for pkg_dir in context.pkgs_dirs:\n        trash_dir = join(pkg_dir, '.trash')\n\n        try:\n            makedirs(trash_dir)\n        except (IOError, OSError) as e1:\n            if e1.errno != EEXIST:\n                continue\n\n        trash_file = join(trash_dir, text_type(uuid4()))\n\n        try:\n            rename(path, trash_file)\n        except (IOError, OSError) as e:\n            log.debug(\"Could not move %s to %s.\\n%r\", path, trash_file, e)\n        else:\n            log.debug(\"Moved to trash: %s\", path)\n            from ..install import delete_linked_data_any\n            delete_linked_data_any(path)\n            return True\n\n    return False\n", "path": "conda/common/disk.py"}]}