Commit 6cd47aed authored by Jérome Perrin's avatar Jérome Perrin

util.rmtree: fix for python 3

On python3, deleting a chmoded folder causes this error:

    Traceback (most recent call last):
      File "/usr/lib/python3.7/shutil.py", line 423, in _rmtree_safe_fd
        dirfd = os.open(entry.name, os.O_RDONLY, dir_fd=topfd)
    PermissionError: [Errno 13] Permission denied: 'directory'

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
      File "slapos.core/slapos/grid/slapgrid.py", line 607, in processSoftwareReleaseList
        software.destroy()
      File "slapos.core/slapos/grid/SlapObject.py", line 360, in destroy
        rmtree(self.software_path)
      File "slapos.core/slapos/util.py", line 267, in rmtree
        shutil.rmtree(path, onerror=chmod_retry)
      File "/usr/lib/python3.7/shutil.py", line 491, in rmtree
        _rmtree_safe_fd(fd, path, onerror)
      File "/usr/lib/python3.7/shutil.py", line 425, in _rmtree_safe_fd
        onerror(os.open, fullname, sys.exc_info())
      File "slapos.core/slapos/util.py", line 264, in chmod_retry
        func(failed_path)
    TypeError: open() missing required argument 'flags' (pos 2)

Our error handler was assuming that on python 3 the EACCESS for the
directory would happen first on an os.lstat call [1], which is true when the
top level folder is not listable, but it did not support the case where
the top level folder is listable but contain another folder that is not listable.
In that case, the first error is an os.open [2]

Tests from erp5.util (where this function came from) only covered the 1 case but
new tests in slapos.core covered the 2.

Anyway, do the "chmod folder and retry from beginning" for both 1 or 2 cases.

[1]: https://github.com/python/cpython/blob/3.6/Lib/shutil.py#L475

[2]: https://github.com/python/cpython/blob/3.6/Lib/shutil.py#L418
parent 7e49ec5e
......@@ -241,10 +241,10 @@ def rmtree(path):
# Depending on the Python version, the following items differ.
if six.PY3:
expected_error_type = PermissionError
expected_func = os.lstat
expected_func_set = {os.lstat, os.open}
else:
expected_error_type = OSError
expected_func = os.listdir
expected_func_set = {os.listdir}
e = exc_info[1]
if isinstance(e, expected_error_type):
if e.errno == errno.ENOENT:
......@@ -252,7 +252,7 @@ def rmtree(path):
# have been already deleted by the recursive call to rmtree.
return
if e.errno == errno.EACCES:
if func is expected_func:
if func in expected_func_set:
os.chmod(failed_path, 0o700)
# corner case to handle errors in listing directories.
# https://bugs.python.org/issue8523
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment