rm: improve efficiency of rm -r (without -f) from O(N^2) to O(N)
authorJim Meyering <meyering@redhat.com>
Thu, 3 Sep 2009 13:15:09 +0000 (15:15 +0200)
committerJim Meyering <meyering@redhat.com>
Fri, 11 Sep 2009 12:08:58 +0000 (14:08 +0200)
where N is the depth of the deepest hierarchy rm is processing.
* src/remove.c (write_protected_non_symlink): Use faccessat to
avoid O(N)-per-entry cost of calling euidaccess.
* m4/jm-macros.m4 (coreutils_MACROS): Check for faccessat.
* NEWS (Improvements): Mention it.

NEWS
m4/jm-macros.m4
src/remove.c

diff --git a/NEWS b/NEWS
index 1825bec..6cfe8bb 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,16 @@ GNU coreutils NEWS                                    -*- outline -*-
   This makes rm -rf significantly faster (400-500%) in some pathological
   cases, and slightly slower (20%) in at least one pathological case.
 
+  rm -r deletes deep hierarchies more efficiently.  Before, it took O(N^2)
+  time, now it takes O(N).  However, this improvement is not as pronounced
+  as might be expected for very deep trees, because prior to this change, for
+  any relative name length longer than 8KiB, rm -r would sacrifice official
+  conformance to avoid the disproportionate O(N^2) performance penalty.
+  Leading to another improvement:
+
+  rm -r is now slightly more standards-conformant when operating on
+  write-protected relative file names longer than 8KiB.
+
 
 * Noteworthy changes in release 7.6 (2009-09-11) [stable]
 
index f4d43f1..75ee75e 100644 (file)
@@ -92,6 +92,9 @@ AC_DEFUN([coreutils_MACROS],
   # for cp.c
   AC_CHECK_FUNCS_ONCE([utimensat])
 
+  # for remove.c
+  AC_CHECK_FUNCS_ONCE([faccessat])
+
   dnl This can't use AC_REQUIRE; I'm not quite sure why.
   cu_PREREQ_STAT_PROG
 
index 32f67a1..2db3859 100644 (file)
@@ -172,6 +172,13 @@ write_protected_non_symlink (int fd_cwd,
         mess up with long file names). */
 
   {
+    /* Use faccessat if possible, so as to avoid the expense
+       of processing an N-component name.  */
+#if HAVE_FACCESSAT && AT_EACCESS
+    if (faccessat (fd_cwd, file, W_OK, AT_EACCESS) == 0)
+      return 0;
+#endif
+
     /* This implements #5: */
     size_t file_name_len = strlen (full_name);