cpython: c910af2e3c98 (original) (raw)

Mercurial > cpython

changeset 77635:c910af2e3c98

#4489: Add a shutil.rmtree that isn't suspectible to symlink attacks It is used automatically on platforms supporting the necessary os.openat() and os.unlinkat() functions. Main code by Martin von Löwis. [#4489]

Hynek Schlawack hs@ox.cx
date Sat, 23 Jun 2012 17:58:42 +0200
parents 46e29122d3f7
children 93263cd0b7d0
files Doc/library/shutil.rst Lib/shutil.py Lib/test/test_shutil.py Misc/NEWS
diffstat 4 files changed, 150 insertions(+), 43 deletions(-)[+] [-] Doc/library/shutil.rst 27 Lib/shutil.py 99 Lib/test/test_shutil.py 63 Misc/NEWS 4

line wrap: on

line diff

--- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -190,14 +190,27 @@ Directory and files operations handled by calling a handler specified by onerror or, if that is omitted, they raise an exception.

+ If onerror is provided, it must be a callable that accepts three

.. function:: move(src, dst)

--- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -337,23 +337,8 @@ def copytree(src, dst, symlinks=False, i raise Error(errors) return dst -def rmtree(path, ignore_errors=False, onerror=None):

-

-

+# version vulnerable to race conditions +def _rmtree_unsafe(path, onerror): try: if os.path.islink(path): # symlinks to directories are forbidden, see bug #1669 @@ -374,7 +359,7 @@ def rmtree(path, ignore_errors=False, on except os.error: mode = 0 if stat.S_ISDIR(mode):

@@ -385,6 +370,84 @@ def rmtree(path, ignore_errors=False, on except os.error: onerror(os.rmdir, path, sys.exc_info()) +# Version using fd-based APIs to protect against races +def _rmtree_safe_fd(topfd, path, onerror):

+ +_use_fd_functions = hasattr(os, 'openat') and hasattr(os, 'unlinkat') +def rmtree(path, ignore_errors=False, onerror=None):

+

+

+ def _basename(path): # A basename() variant which first strips the trailing slash, if present.

--- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -120,29 +120,36 @@ class TestShutil(unittest.TestCase): def test_on_error(self): self.errorState = 0 os.mkdir(TESTFN)

shutil.rmtree(TESTFN, onerror=self.check_args_to_onerror) # Test whether onerror has actually been called.

# Make writable again. os.chmod(TESTFN, old_dir_mode)

# Clean up. shutil.rmtree(TESTFN) def check_args_to_onerror(self, func, arg, exc): # test_rmtree_errors deliberately runs rmtree

@@ -151,20 +158,39 @@ class TestShutil(unittest.TestCase): # FUSE experienced a failure earlier in the process # at os.listdir. The first failure may legally # be either.

+

+

@unittest.skipUnless(hasattr(os, 'chmod'), 'requires os.chmod') @support.skip_unless_symlink @@ -464,7 +490,7 @@ class TestShutil(unittest.TestCase): # When called on a file instead of a directory, don't delete it. handle, path = tempfile.mkstemp() os.close(handle)

def test_copytree_simple(self): @@ -629,6 +655,7 @@ class TestShutil(unittest.TestCase): os.mkdir(src) os.symlink(src, dst) self.assertRaises(OSError, shutil.rmtree, dst)

--- a/Misc/NEWS +++ b/Misc/NEWS @@ -43,6 +43,10 @@ Core and Builtins Library ------- +- Issue #4489: Add a shutil.rmtree that isn't suspectible to symlink attacks.