mirror of
https://github.com/python/cpython.git
synced 2025-08-03 16:39:00 +00:00
[3.13] GH-89727: Partially fix shutil.rmtree()
recursion error on deep trees (GH-119634) (#119748)
GH-89727: Partially fix `shutil.rmtree()` recursion error on deep trees (GH-119634)
Make `shutil._rmtree_unsafe()` call `os.walk()`, which is implemented
without recursion.
`shutil._rmtree_safe_fd()` is not affected and can still raise a recursion
error.
(cherry picked from commit a150679f90
)
Co-authored-by: Barney Gale <barney.gale@gmail.com>
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
This commit is contained in:
parent
c3cfc04a73
commit
061abf8e4c
4 changed files with 33 additions and 28 deletions
|
@ -606,37 +606,21 @@ else:
|
|||
|
||||
# version vulnerable to race conditions
|
||||
def _rmtree_unsafe(path, onexc):
|
||||
try:
|
||||
with os.scandir(path) as scandir_it:
|
||||
entries = list(scandir_it)
|
||||
except FileNotFoundError:
|
||||
return
|
||||
except OSError as err:
|
||||
onexc(os.scandir, path, err)
|
||||
entries = []
|
||||
for entry in entries:
|
||||
fullname = entry.path
|
||||
try:
|
||||
is_dir = entry.is_dir(follow_symlinks=False)
|
||||
except FileNotFoundError:
|
||||
continue
|
||||
except OSError:
|
||||
is_dir = False
|
||||
|
||||
if is_dir and not entry.is_junction():
|
||||
def onerror(err):
|
||||
if not isinstance(err, FileNotFoundError):
|
||||
onexc(os.scandir, err.filename, err)
|
||||
results = os.walk(path, topdown=False, onerror=onerror, followlinks=os._walk_symlinks_as_files)
|
||||
for dirpath, dirnames, filenames in results:
|
||||
for name in dirnames:
|
||||
fullname = os.path.join(dirpath, name)
|
||||
try:
|
||||
if entry.is_symlink():
|
||||
# This can only happen if someone replaces
|
||||
# a directory with a symlink after the call to
|
||||
# os.scandir or entry.is_dir above.
|
||||
raise OSError("Cannot call rmtree on a symbolic link")
|
||||
os.rmdir(fullname)
|
||||
except FileNotFoundError:
|
||||
continue
|
||||
except OSError as err:
|
||||
onexc(os.path.islink, fullname, err)
|
||||
continue
|
||||
_rmtree_unsafe(fullname, onexc)
|
||||
else:
|
||||
onexc(os.rmdir, fullname, err)
|
||||
for name in filenames:
|
||||
fullname = os.path.join(dirpath, name)
|
||||
try:
|
||||
os.unlink(fullname)
|
||||
except FileNotFoundError:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue