]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
shred: avoid rename race
authorPaul Eggert <eggert@cs.ucla.edu>
Mon, 31 Jul 2017 00:11:24 +0000 (17:11 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Mon, 31 Jul 2017 00:18:31 +0000 (17:18 -0700)
Use renameat2 to avoid a rename race condition, on recent-enough
GNU/Linux.
* bootstrap.conf (gnulib_modules): Add renameat2.
* src/shred.c: Include renameat2.h.
(wipename): Use renameat2 instead of rename.

bootstrap.conf
src/shred.c

index 9064a94bbfdb60582d2872e386fc1bc5d02346d7..188b1ef7861eaabd2d1c16636764a9d26eff6e64 100644 (file)
@@ -206,6 +206,7 @@ gnulib_modules="
   remove
   rename
   renameat
+  renameat2
   rmdir
   root-dev-ino
   rpmatch
index e8fe307e14f8a4641b1f5df12271442c9937531b..47cfc261833bdd69a5585c7479a19a066723a8ff 100644 (file)
@@ -93,6 +93,7 @@
 #include "human.h"
 #include "randint.h"
 #include "randread.h"
+#include "renameat2.h"
 #include "stat-size.h"
 
 /* Default number of times to overwrite.  */
@@ -1077,7 +1078,6 @@ wipename (char *oldname, char const *qoldname, struct Options const *flags)
 {
   char *newname = xstrdup (oldname);
   char *base = last_component (newname);
-  size_t len = base_len (base);
   char *dir = dir_name (newname);
   char *qdir = xstrdup (quotef (dir));
   bool first = true;
@@ -1090,49 +1090,36 @@ wipename (char *oldname, char const *qoldname, struct Options const *flags)
   if (flags->verbose)
     error (0, 0, _("%s: removing"), qoldname);
 
-  while ((flags->remove_file != remove_unlink) && len)
-    {
-      memset (base, nameset[0], len);
-      base[len] = 0;
-      do
-        {
-          struct stat st;
-          if (lstat (newname, &st) < 0)
-            {
-              if (rename (oldname, newname) == 0)
-                {
-                  if (0 <= dir_fd && dosync (dir_fd, qdir) != 0)
-                    ok = false;
-                  if (flags->verbose)
-                    {
-                      /*
-                       * People seem to understand this better than talking
-                       * about renaming oldname.  newname doesn't need
-                       * quoting because we picked it.  oldname needs to
-                       * be quoted only the first time.
-                       */
-                      char const *old = (first ? qoldname : oldname);
-                      error (0, 0, _("%s: renamed to %s"),
-                             old, newname);
-                      first = false;
-                    }
-                  memcpy (oldname + (base - newname), base, len + 1);
-                  break;
-                }
-              else
-                {
-                  /* The rename failed: give up on this length.  */
-                  break;
-                }
-            }
-          else
-            {
-              /* newname exists, so increment BASE so we use another */
-            }
-        }
-      while (incname (base, len));
-      len--;
-    }
+  if (flags->remove_file != remove_unlink)
+    for (size_t len = base_len (base); len != 0; len--)
+      {
+        memset (base, nameset[0], len);
+        base[len] = 0;
+        bool rename_ok;
+        while (! (rename_ok = (renameat2 (AT_FDCWD, oldname, AT_FDCWD, newname,
+                                          RENAME_NOREPLACE)
+                               == 0))
+               && errno == EEXIST && incname (base, len))
+          continue;
+        if (rename_ok)
+          {
+            if (0 <= dir_fd && dosync (dir_fd, qdir) != 0)
+              ok = false;
+            if (flags->verbose)
+              {
+                /* People seem to understand this better than talking
+                   about renaming OLDNAME.  NEWNAME doesn't need
+                   quoting because we picked it.  OLDNAME needs to be
+                   quoted only the first time.  */
+                char const *old = first ? qoldname : oldname;
+                error (0, 0, _("%s: renamed to %s"), old, newname);
+                first = false;
+              }
+            memcpy (oldname + (base - newname), base, len + 1);
+            break;
+          }
+      }
+
   if (unlink (oldname) != 0)
     {
       error (0, errno, _("%s: failed to remove"), qoldname);