Commit 508c6a3f authored by Thomas Gambier's avatar Thomas Gambier 🚴🏼

rmtree: import more code from slapos.core to support case of parent directory not writable

See merge request nexedi/slapos.buildout!22
parents 44b3c076 6dcf6b6e
Pipeline #12934 passed with stage
in 0 seconds
......@@ -16,6 +16,8 @@
import shutil
import os
import doctest
import errno
import sys
def rmtree (path):
"""
......@@ -26,6 +28,8 @@ def rmtree (path):
Also it tries to remove symlink itself if a symlink as passed as
path argument.
Finally, it tries to make parent directory writable.
>>> from tempfile import mkdtemp
Let's make a directory ...
......@@ -46,7 +50,11 @@ def rmtree (path):
and make it unwriteable
>>> os.chmod (foo, 256) # 0400
>>> os.chmod (foo, 0o400)
and make parent dir unwritable
>>> os.chmod (d, 0o400)
rmtree should be able to remove it:
......@@ -89,7 +97,9 @@ def rmtree (path):
>>> os.path.isdir (d)
0
"""
def retry_writeable (func, path, exc):
def chmod_retry(func, failed_path, exc_info):
"""Make sure the directories are executable and writable.
"""
if func is os.path.islink:
os.unlink(path)
elif func is os.lstat:
......@@ -97,10 +107,33 @@ def rmtree (path):
raise
os.unlink(path)
else:
os.chmod(path, 0o600)
func(path)
# Depending on the Python version, the following items differ.
if sys.version_info >= (3, ):
expected_error_type = PermissionError
expected_func_tuple = (os.lstat, os.open)
else:
expected_error_type = OSError
expected_func_tuple = (os.listdir, )
e = exc_info[1]
if isinstance(e, expected_error_type):
if e.errno == errno.ENOENT:
# because we are calling again rmtree on listdir errors, this path might
# have been already deleted by the recursive call to rmtree.
return
if e.errno == errno.EACCES:
if func in expected_func_tuple:
os.chmod(failed_path, 0o700)
# corner case to handle errors in listing directories.
# https://bugs.python.org/issue8523
return shutil.rmtree(failed_path, onerror=chmod_retry)
# If parent directory is not writable, we still cannot delete the file.
# But make sure not to change the parent of the folder we are deleting.
if failed_path != path:
os.chmod(os.path.dirname(failed_path), 0o700)
return func(failed_path)
raise
shutil.rmtree (path, onerror = retry_writeable)
shutil.rmtree(path, onerror=chmod_retry)
def test_suite():
return doctest.DocTestSuite()
......
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