mirror of
https://github.com/python/cpython.git
synced 2025-12-04 16:43:27 +00:00
bpo-46245: Add optional parameter dir_fd in shutil.rmtree() (GH-30365)
This commit is contained in:
parent
5eb03b1b51
commit
02fbaf4887
5 changed files with 49 additions and 7 deletions
|
|
@ -286,7 +286,7 @@ Directory and files operations
|
||||||
.. versionadded:: 3.8
|
.. versionadded:: 3.8
|
||||||
The *dirs_exist_ok* parameter.
|
The *dirs_exist_ok* parameter.
|
||||||
|
|
||||||
.. function:: rmtree(path, ignore_errors=False, onerror=None)
|
.. function:: rmtree(path, ignore_errors=False, onerror=None, *, dir_fd=None)
|
||||||
|
|
||||||
.. index:: single: directory; deleting
|
.. index:: single: directory; deleting
|
||||||
|
|
||||||
|
|
@ -296,6 +296,9 @@ Directory and files operations
|
||||||
handled by calling a handler specified by *onerror* or, if that is omitted,
|
handled by calling a handler specified by *onerror* or, if that is omitted,
|
||||||
they raise an exception.
|
they raise an exception.
|
||||||
|
|
||||||
|
This function can support :ref:`paths relative to directory descriptors
|
||||||
|
<dir_fd>`.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
On platforms that support the necessary fd-based functions a symlink
|
On platforms that support the necessary fd-based functions a symlink
|
||||||
|
|
@ -315,7 +318,7 @@ Directory and files operations
|
||||||
*excinfo*, will be the exception information returned by
|
*excinfo*, will be the exception information returned by
|
||||||
:func:`sys.exc_info`. Exceptions raised by *onerror* will not be caught.
|
:func:`sys.exc_info`. Exceptions raised by *onerror* will not be caught.
|
||||||
|
|
||||||
.. audit-event:: shutil.rmtree path shutil.rmtree
|
.. audit-event:: shutil.rmtree path,dir_fd shutil.rmtree
|
||||||
|
|
||||||
.. versionchanged:: 3.3
|
.. versionchanged:: 3.3
|
||||||
Added a symlink attack resistant version that is used automatically
|
Added a symlink attack resistant version that is used automatically
|
||||||
|
|
@ -325,6 +328,9 @@ Directory and files operations
|
||||||
On Windows, will no longer delete the contents of a directory junction
|
On Windows, will no longer delete the contents of a directory junction
|
||||||
before removing the junction.
|
before removing the junction.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.11
|
||||||
|
The *dir_fd* parameter.
|
||||||
|
|
||||||
.. attribute:: rmtree.avoids_symlink_attacks
|
.. attribute:: rmtree.avoids_symlink_attacks
|
||||||
|
|
||||||
Indicates whether the current platform and implementation provides a
|
Indicates whether the current platform and implementation provides a
|
||||||
|
|
|
||||||
|
|
@ -283,6 +283,13 @@ os
|
||||||
(Contributed by Dong-hee Na in :issue:`44611`.)
|
(Contributed by Dong-hee Na in :issue:`44611`.)
|
||||||
|
|
||||||
|
|
||||||
|
shutil
|
||||||
|
------
|
||||||
|
|
||||||
|
* Add optional parameter *dir_fd* in :func:`shutil.rmtree`.
|
||||||
|
(Contributed by Serhiy Storchaka in :issue:`46245`.)
|
||||||
|
|
||||||
|
|
||||||
socket
|
socket
|
||||||
------
|
------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -684,9 +684,14 @@ _use_fd_functions = ({os.open, os.stat, os.unlink, os.rmdir} <=
|
||||||
os.scandir in os.supports_fd and
|
os.scandir in os.supports_fd and
|
||||||
os.stat in os.supports_follow_symlinks)
|
os.stat in os.supports_follow_symlinks)
|
||||||
|
|
||||||
def rmtree(path, ignore_errors=False, onerror=None):
|
def rmtree(path, ignore_errors=False, onerror=None, *, dir_fd=None):
|
||||||
"""Recursively delete a directory tree.
|
"""Recursively delete a directory tree.
|
||||||
|
|
||||||
|
If dir_fd is not None, it should be a file descriptor open to a directory;
|
||||||
|
path will then be relative to that directory.
|
||||||
|
dir_fd may not be implemented on your platform.
|
||||||
|
If it is unavailable, using it will raise a NotImplementedError.
|
||||||
|
|
||||||
If ignore_errors is set, errors are ignored; otherwise, if onerror
|
If ignore_errors is set, errors are ignored; otherwise, if onerror
|
||||||
is set, it is called to handle the error with arguments (func,
|
is set, it is called to handle the error with arguments (func,
|
||||||
path, exc_info) where func is platform and implementation dependent;
|
path, exc_info) where func is platform and implementation dependent;
|
||||||
|
|
@ -695,7 +700,7 @@ def rmtree(path, ignore_errors=False, onerror=None):
|
||||||
is false and onerror is None, an exception is raised.
|
is false and onerror is None, an exception is raised.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
sys.audit("shutil.rmtree", path)
|
sys.audit("shutil.rmtree", path, dir_fd)
|
||||||
if ignore_errors:
|
if ignore_errors:
|
||||||
def onerror(*args):
|
def onerror(*args):
|
||||||
pass
|
pass
|
||||||
|
|
@ -709,12 +714,12 @@ def rmtree(path, ignore_errors=False, onerror=None):
|
||||||
# Note: To guard against symlink races, we use the standard
|
# Note: To guard against symlink races, we use the standard
|
||||||
# lstat()/open()/fstat() trick.
|
# lstat()/open()/fstat() trick.
|
||||||
try:
|
try:
|
||||||
orig_st = os.lstat(path)
|
orig_st = os.lstat(path, dir_fd=dir_fd)
|
||||||
except Exception:
|
except Exception:
|
||||||
onerror(os.lstat, path, sys.exc_info())
|
onerror(os.lstat, path, sys.exc_info())
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
fd = os.open(path, os.O_RDONLY)
|
fd = os.open(path, os.O_RDONLY, dir_fd=dir_fd)
|
||||||
fd_closed = False
|
fd_closed = False
|
||||||
except Exception:
|
except Exception:
|
||||||
onerror(os.open, path, sys.exc_info())
|
onerror(os.open, path, sys.exc_info())
|
||||||
|
|
@ -725,7 +730,7 @@ def rmtree(path, ignore_errors=False, onerror=None):
|
||||||
try:
|
try:
|
||||||
os.close(fd)
|
os.close(fd)
|
||||||
fd_closed = True
|
fd_closed = True
|
||||||
os.rmdir(path)
|
os.rmdir(path, dir_fd=dir_fd)
|
||||||
except OSError:
|
except OSError:
|
||||||
onerror(os.rmdir, path, sys.exc_info())
|
onerror(os.rmdir, path, sys.exc_info())
|
||||||
else:
|
else:
|
||||||
|
|
@ -738,6 +743,8 @@ def rmtree(path, ignore_errors=False, onerror=None):
|
||||||
if not fd_closed:
|
if not fd_closed:
|
||||||
os.close(fd)
|
os.close(fd)
|
||||||
else:
|
else:
|
||||||
|
if dir_fd is not None:
|
||||||
|
raise NotImplementedError("dir_fd unavailable on this platform")
|
||||||
try:
|
try:
|
||||||
if _rmtree_islink(path):
|
if _rmtree_islink(path):
|
||||||
# symlinks to directories are forbidden, see bug #1669
|
# symlinks to directories are forbidden, see bug #1669
|
||||||
|
|
|
||||||
|
|
@ -405,6 +405,27 @@ class TestRmTree(BaseTest, unittest.TestCase):
|
||||||
self.assertFalse(shutil._use_fd_functions)
|
self.assertFalse(shutil._use_fd_functions)
|
||||||
self.assertFalse(shutil.rmtree.avoids_symlink_attacks)
|
self.assertFalse(shutil.rmtree.avoids_symlink_attacks)
|
||||||
|
|
||||||
|
@unittest.skipUnless(shutil._use_fd_functions, "dir_fd is not supported")
|
||||||
|
def test_rmtree_with_dir_fd(self):
|
||||||
|
tmp_dir = self.mkdtemp()
|
||||||
|
victim = 'killme'
|
||||||
|
fullname = os.path.join(tmp_dir, victim)
|
||||||
|
dir_fd = os.open(tmp_dir, os.O_RDONLY)
|
||||||
|
self.addCleanup(os.close, dir_fd)
|
||||||
|
os.mkdir(fullname)
|
||||||
|
os.mkdir(os.path.join(fullname, 'subdir'))
|
||||||
|
write_file(os.path.join(fullname, 'subdir', 'somefile'), 'foo')
|
||||||
|
self.assertTrue(os.path.exists(fullname))
|
||||||
|
shutil.rmtree(victim, dir_fd=dir_fd)
|
||||||
|
self.assertFalse(os.path.exists(fullname))
|
||||||
|
|
||||||
|
@unittest.skipIf(shutil._use_fd_functions, "dir_fd is supported")
|
||||||
|
def test_rmtree_with_dir_fd_unsupported(self):
|
||||||
|
tmp_dir = self.mkdtemp()
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
shutil.rmtree(tmp_dir, dir_fd=0)
|
||||||
|
self.assertTrue(os.path.exists(tmp_dir))
|
||||||
|
|
||||||
def test_rmtree_dont_delete_file(self):
|
def test_rmtree_dont_delete_file(self):
|
||||||
# When called on a file instead of a directory, don't delete it.
|
# When called on a file instead of a directory, don't delete it.
|
||||||
handle, path = tempfile.mkstemp(dir=self.mkdtemp())
|
handle, path = tempfile.mkstemp(dir=self.mkdtemp())
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Add optional parameter *dir_fd* in :func:`shutil.rmtree`.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue