[Python-checkins] cpython: #4489: Don't follow ever symlinks in rmtree

hynek.schlawack python-checkins at python.org
Thu Jun 28 12:08:08 CEST 2012


http://hg.python.org/cpython/rev/f9f798f1421e
changeset:   77829:f9f798f1421e
user:        Hynek Schlawack <hs at ox.cx>
date:        Thu Jun 28 12:07:29 2012 +0200
summary:
  #4489: Don't follow ever symlinks in rmtree

Also added several regression tests.

files:
  Lib/shutil.py           |   4 +-
  Lib/test/test_shutil.py |  34 ++++++++++++++++++++++++++++-
  2 files changed, 35 insertions(+), 3 deletions(-)


diff --git a/Lib/shutil.py b/Lib/shutil.py
--- a/Lib/shutil.py
+++ b/Lib/shutil.py
@@ -380,7 +380,7 @@
     for name in names:
         fullname = os.path.join(path, name)
         try:
-            orig_st = os.stat(name, dir_fd=topfd)
+            orig_st = os.stat(name, dir_fd=topfd, follow_symlinks=False)
             mode = orig_st.st_mode
         except os.error:
             mode = 0
@@ -445,7 +445,7 @@
             if (stat.S_ISDIR(orig_st.st_mode) and
                 os.path.samestat(orig_st, os.fstat(fd))):
                 _rmtree_safe_fd(fd, path, onerror)
-            elif (stat.S_ISREG(orig_st.st_mode)):
+            else:
                 raise NotADirectoryError(20,
                                          "Not a directory: '{}'".format(path))
         finally:
diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py
--- a/Lib/test/test_shutil.py
+++ b/Lib/test/test_shutil.py
@@ -117,6 +117,38 @@
         self.assertIsInstance(victim, bytes)
         shutil.rmtree(victim)
 
+    @support.skip_unless_symlink
+    def test_rmtree_fails_on_symlink(self):
+        tmp = self.mkdtemp()
+        dir_ = os.path.join(tmp, 'dir')
+        os.mkdir(dir_)
+        link = os.path.join(tmp, 'link')
+        os.symlink(dir_, link)
+        self.assertRaises(OSError, shutil.rmtree, link)
+        self.assertTrue(os.path.exists(dir_))
+
+    @support.skip_unless_symlink
+    def test_rmtree_works_on_symlinks(self):
+        tmp = self.mkdtemp()
+        dir1 = os.path.join(tmp, 'dir1')
+        dir2 = os.path.join(dir1, 'dir2')
+        dir3 = os.path.join(tmp, 'dir3')
+        for d in dir1, dir2, dir3:
+            os.mkdir(d)
+        file1 = os.path.join(tmp, 'file1')
+        write_file(file1, 'foo')
+        link1 = os.path.join(dir1, 'link1')
+        os.symlink(dir2, link1)
+        link2 = os.path.join(dir1, 'link2')
+        os.symlink(dir3, link2)
+        link3 = os.path.join(dir1, 'link3')
+        os.symlink(file1, link3)
+        # make sure symlinks are removed but not followed
+        shutil.rmtree(dir1)
+        self.assertFalse(os.path.exists(dir1))
+        self.assertTrue(os.path.exists(dir3))
+        self.assertTrue(os.path.exists(file1))
+
     def test_rmtree_errors(self):
         # filename is guaranteed not to exist
         filename = tempfile.mktemp()
@@ -184,7 +216,7 @@
     def test_rmtree_does_not_choke_on_failing_lstat(self):
         try:
             orig_lstat = os.lstat
-            def raiser(fn):
+            def raiser(fn, *args, **kwargs):
                 if fn != TESTFN:
                     raise OSError()
                 else:

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list