Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(14)

Unified Diff: client/utils/file_path.py

Issue 2934983003: Improve error handling for file_path.rmtree() (Closed)
Patch Set: fs.exits() call Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: client/utils/file_path.py
diff --git a/client/utils/file_path.py b/client/utils/file_path.py
index 6cb52d1b6eb46e3ccc7d66870519e3ab2c9713df..68e03b39c645ac2e28fbc2abf073782770493a2e 100644
--- a/client/utils/file_path.py
+++ b/client/utils/file_path.py
@@ -1092,21 +1092,18 @@ def rmtree(root):
# errors is a list of tuple(function, path, excinfo).
errors = []
fs.rmtree(root, onerror=lambda *args: errors.append(args))
- if not errors:
+ if not errors or not fs.exists(root):
+ if i:
+ sys.stderr.write('Succeeded.\n')
return True
if not i and sys.platform == 'win32':
- for _, path, _ in errors:
+ for path in sorted(set(path for _, path, _ in errors)):
try:
change_acl_for_delete(path)
except Exception as e:
sys.stderr.write('- %s (failed to update ACL: %s)\n' % (path, e))
- if i == max_tries - 1:
- sys.stderr.write(
- 'Failed to delete %s. The following files remain:\n' % root)
- for _, path, _ in errors:
- sys.stderr.write('- %s\n' % path)
- else:
+ if i != max_tries - 1:
delay = (i+1)*2
sys.stderr.write(
'Failed to delete %s (%d files remaining).\n'
@@ -1115,50 +1112,24 @@ def rmtree(root):
(root, len(errors), delay))
time.sleep(delay)
+ sys.stderr.write(
+ 'Failed to delete %s. The following files remain:\n' % root)
+ # The same path may be listed multiple times.
+ for path in sorted(set(path for _, path, _ in errors)):
+ sys.stderr.write('- %s\n' % path)
+
# If soft retries fail on Linux, there's nothing better we can do.
if sys.platform != 'win32':
raise errors[0][2][0], errors[0][2][1], errors[0][2][2]
- # The soft way was not good enough. Try the hard way. Enumerates both:
- # - all child processes from this process.
- # - processes where the main executable in inside 'root'. The reason is that
- # the ancestry may be broken so stray grand-children processes could be
- # undetected by the first technique.
- # This technique is not fool-proof but gets mostly there.
- def get_processes():
- processes = enum_processes_win()
- tree_processes = filter_processes_tree_win(processes)
- dir_processes = filter_processes_dir_win(processes, root)
- # Convert to dict to remove duplicates.
- processes = dict((p.ProcessId, p) for p in tree_processes)
- processes.update((p.ProcessId, p) for p in dir_processes)
- processes.pop(os.getpid())
- return processes
-
- for i in xrange(3):
- sys.stderr.write('Enumerating processes:\n')
- processes = get_processes()
- if not processes:
+ # The soft way was not good enough. Try the hard way.
+ for i in xrange(max_tries):
+ if not _kill_children_processes_win(root):
break
- for _, proc in sorted(processes.iteritems()):
- sys.stderr.write(
- '- pid %d; Handles: %d; Exe: %s; Cmd: %s\n' % (
- proc.ProcessId,
- proc.HandleCount,
- proc.ExecutablePath,
- proc.CommandLine))
- sys.stderr.write('Terminating %d processes.\n' % len(processes))
- for pid in sorted(processes):
- try:
- # Killing is asynchronous.
- os.kill(pid, 9)
- sys.stderr.write('- %d killed\n' % pid)
- except OSError:
- sys.stderr.write('- failed to kill %s\n' % pid)
- if i < 2:
+ if i != max_tries - 1:
time.sleep((i+1)*2)
else:
- processes = get_processes()
+ processes = _get_children_processes_win(root)
if processes:
sys.stderr.write('Failed to terminate processes.\n')
raise errors[0][2][0], errors[0][2][1], errors[0][2][2]
@@ -1166,11 +1137,66 @@ def rmtree(root):
# Now that annoying processes in root are evicted, try again.
errors = []
fs.rmtree(root, onerror=lambda *args: errors.append(args))
- if errors:
- # There's no hope.
+ if errors and fs.exists(root):
+ # There's no hope: the directory was tried to be removed 4 times. Give up
+ # and raise an exception.
sys.stderr.write(
'Failed to delete %s. The following files remain:\n' % root)
- for _, path, _ in errors:
+ # The same path may be listed multiple times.
+ for path in sorted(set(path for _, path, _ in errors)):
sys.stderr.write('- %s\n' % path)
raise errors[0][2][0], errors[0][2][1], errors[0][2][2]
return False
+
+
+## Private code.
+
+
+def _kill_children_processes_win(root):
+ """Try to kill all children processes indistriminately and prints updates to
+ stderr.
+
+ Returns:
+ True if at least one child process was found.
+ """
+ processes = _get_children_processes_win(root)
+ if not processes:
+ return False
+ sys.stderr.write('Enumerating processes:\n')
+ for _, proc in sorted(processes.iteritems()):
+ sys.stderr.write(
+ '- pid %d; Handles: %d; Exe: %s; Cmd: %s\n' % (
+ proc.ProcessId,
+ proc.HandleCount,
+ proc.ExecutablePath,
+ proc.CommandLine))
+ sys.stderr.write('Terminating %d processes:\n' % len(processes))
+ for pid in sorted(processes):
+ try:
+ # Killing is asynchronous.
+ os.kill(pid, 9)
+ sys.stderr.write('- %d killed\n' % pid)
+ except OSError:
+ sys.stderr.write('- failed to kill %s\n' % pid)
+ return True
+
+
+def _get_children_processes_win(root):
+ """Returns a list of processes.
+
+ Enumerates both:
+ - all child processes from this process.
+ - processes where the main executable in inside 'root'. The reason is that
+ the ancestry may be broken so stray grand-children processes could be
+ undetected by the first technique.
+
+ This technique is not fool-proof but gets mostly there.
+ """
+ processes = enum_processes_win()
+ tree_processes = filter_processes_tree_win(processes)
+ dir_processes = filter_processes_dir_win(processes, root)
+ # Convert to dict to remove duplicates.
+ processes = dict((p.ProcessId, p) for p in tree_processes)
+ processes.update((p.ProcessId, p) for p in dir_processes)
+ processes.pop(os.getpid())
+ return processes
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698