changeset 12093:b07a0a61b0c1

rename: fix Solaris 9 bug rename("file/","oops") mistakenly succeeded. * lib/rename.c (rpl_rename): Rewrite to recognize trailing slash on non-directory. Avoid calling exit. * modules/rename (Depends-on): Drop xalloc; add lstat, stdbool, strdup. * modules/rename-tests (Depends-on): Drop lstat. * m4/rename.m4 (gl_FUNC_RENAME): Detect Solaris bug. (gl_PREREQ_RENAME): Delete unused macro. Signed-off-by: Eric Blake <ebb9@byu.net>
author Eric Blake <ebb9@byu.net>
date Wed, 30 Sep 2009 16:19:00 -0600
parents e3b5ec5715b7
children 67458384fb3f
files ChangeLog lib/rename.c m4/rename.m4 modules/rename modules/rename-tests
diffstat 5 files changed, 134 insertions(+), 60 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,14 @@
 2009-10-02  Eric Blake  <ebb9@byu.net>
 
+	rename: fix Solaris 9 bug
+	* lib/rename.c (rpl_rename): Rewrite to recognize trailing slash
+	on non-directory.  Avoid calling exit.
+	* modules/rename (Depends-on): Drop xalloc; add lstat, stdbool,
+	strdup.
+	* modules/rename-tests (Depends-on): Drop lstat.
+	* m4/rename.m4 (gl_FUNC_RENAME): Detect Solaris bug.
+	(gl_PREREQ_RENAME): Delete unused macro.
+
 	rename-dest-slash: fix NetBSD bug
 	* lib/rename-dest-slash.c (rpl_rename_dest_slash): Detect hard
 	links.
--- a/lib/rename.c
+++ b/lib/rename.c
@@ -1,7 +1,4 @@
-/* Work around rename bugs in some systems.  On SunOS 4.1.1_U1
-   and mips-dec-ultrix4.4, rename fails when the source file has
-   a trailing slash.  On mingw, rename fails when the destination
-   exists.
+/* Work around rename bugs in some systems.
 
    Copyright (C) 2001, 2002, 2003, 2005, 2006, 2009 Free Software
    Foundation, Inc.
@@ -19,7 +16,7 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
-/* written by Volker Borchert */
+/* Written by Volker Borchert, Eric Blake.  */
 
 #include <config.h>
 
@@ -140,36 +137,111 @@
 # if RENAME_DEST_EXISTS_BUG
 #  error Please report your platform and this message to bug-gnulib@gnu.org.
 # elif RENAME_TRAILING_SLASH_BUG
+
+#  include <errno.h>
 #  include <stdio.h>
 #  include <stdlib.h>
 #  include <string.h>
+#  include <sys/stat.h>
 
 #  include "dirname.h"
-#  include "xalloc.h"
 
-/* Rename the file SRC to DST, removing any trailing
-   slashes from SRC.  Needed for SunOS 4.1.1_U1.  */
+/* Rename the file SRC to DST, fixing any trailing slash bugs.  */
 
 int
 rpl_rename (char const *src, char const *dst)
 {
-  char *src_temp;
-  int ret_val;
-  size_t s_len = strlen (src);
+  size_t src_len = strlen (src);
+  size_t dst_len = strlen (dst);
+  char *src_temp = (char *) src;
+  char *dst_temp = (char *) dst;
+  bool src_slash;
+  bool dst_slash;
+  int ret_val = -1;
+  int rename_errno = ENOTDIR;
+  struct stat src_st;
+  struct stat dst_st;
+
+  if (!src_len || !dst_len)
+    return rename (src, dst); /* Let strace see the ENOENT failure.  */
 
-  if (s_len && src[s_len - 1] == '/')
+  src_slash = src[src_len - 1] == '/';
+  dst_slash = dst[dst_len - 1] == '/';
+  if (!src_slash && !dst_slash)
+    return rename (src, dst);
+
+  /* Presence of a trailing slash requires directory semantics.  If
+     the source does not exist, or if the destination cannot be turned
+     into a directory, give up now.  Otherwise, strip trailing slashes
+     before calling rename.  */
+  if (lstat (src, &src_st))
+    return -1;
+  if (lstat (dst, &dst_st))
+    {
+      if (errno != ENOENT || !S_ISDIR (src_st.st_mode))
+        return -1;
+    }
+  else if (!S_ISDIR (dst_st.st_mode))
+    {
+      errno = ENOTDIR;
+      return -1;
+    }
+  else if (!S_ISDIR (src_st.st_mode))
     {
-      src_temp = xstrdup (src);
-      strip_trailing_slashes (src_temp);
+      errno = EISDIR;
+      return -1;
     }
-  else
-    src_temp = (char *) src;
 
-  ret_val = rename (src_temp, dst);
-
+  /* If stripping the trailing slashes changes from a directory to a
+     symlink, follow the GNU behavior of rejecting the rename.
+     Technically, we could follow the POSIX behavior by chasing a
+     readlink trail, but that is counter-intuitive and harder.  */
+  if (src_slash)
+    {
+      src_temp = strdup (src);
+      if (!src_temp)
+        {
+          /* Rather than rely on strdup-posix, we set errno ourselves.  */
+          rename_errno = ENOMEM;
+          goto out;
+        }
+      strip_trailing_slashes (src_temp);
+      if (lstat (src_temp, &src_st))
+        {
+          rename_errno = errno;
+          goto out;
+        }
+      if (S_ISLNK (src_st.st_mode))
+        goto out;
+    }
+  if (dst_slash)
+    {
+      dst_temp = strdup (dst);
+      if (!dst_temp)
+        {
+          rename_errno = ENOMEM;
+          goto out;
+        }
+      strip_trailing_slashes (dst_temp);
+      if (lstat (dst_temp, &dst_st))
+        {
+          if (errno != ENOENT)
+            {
+              rename_errno = errno;
+              goto out;
+            }
+        }
+      else if (S_ISLNK (dst_st.st_mode))
+        goto out;
+    }
+  ret_val = rename (src_temp, dst_temp);
+  rename_errno = errno;
+ out:
   if (src_temp != src)
     free (src_temp);
-
+  if (dst_temp != dst)
+    free (dst_temp);
+  errno = rename_errno;
   return ret_val;
 }
 # endif /* RENAME_TRAILING_SLASH_BUG */
--- a/m4/rename.m4
+++ b/m4/rename.m4
@@ -1,4 +1,4 @@
-# serial 15
+# serial 16
 
 # Copyright (C) 2001, 2003, 2005, 2006, 2009 Free Software Foundation, Inc.
 # This file is free software; the Free Software Foundation
@@ -17,53 +17,45 @@
 [
   AC_REQUIRE([AC_CANONICAL_HOST])
   AC_REQUIRE([gl_STDIO_H_DEFAULTS])
-  AC_CACHE_CHECK([whether rename is broken with a trailing slash],
-  gl_cv_func_rename_trailing_slash_bug,
-  [
-    rm -rf conftest.d1 conftest.d2
-    mkdir conftest.d1 ||
-      AC_MSG_ERROR([cannot create temporary directory])
-    AC_RUN_IFELSE([AC_LANG_SOURCE([[
+
+  dnl SunOS 4.1.1_U1 mistakenly forbids rename("dir/","name").
+  dnl Solaris 9 mistakenly allows rename("file/","name").
+  AC_CACHE_CHECK([whether rename honors trailing slash on source],
+    [gl_cv_func_rename_slash_src_works],
+    [rm -rf conftest.f conftest.d1 conftest.d2
+    touch conftest.f && mkdir conftest.d1 ||
+      AC_MSG_ERROR([cannot create temporary files])
+    AC_RUN_IFELSE([AC_LANG_PROGRAM([[
 #       include <stdio.h>
 #       include <stdlib.h>
-        int
-        main ()
-        {
-          exit (rename ("conftest.d1/", "conftest.d2") ? 1 : 0);
-        }
-      ]])],
-      [gl_cv_func_rename_trailing_slash_bug=no],
-      [gl_cv_func_rename_trailing_slash_bug=yes],
+]], [if (rename ("conftest.f/", "conftest.d2") == 0) return 1;
+     if (rename ("conftest.d1/", "conftest.d2") != 0) return 2;])],
+      [gl_cv_func_rename_slash_src_works=yes],
+      [gl_cv_func_rename_slash_src_works=no],
       dnl When crosscompiling, assume rename is broken.
-      [gl_cv_func_rename_trailing_slash_bug=yes])
-
-      rm -rf conftest.d1 conftest.d2
+      [gl_cv_func_rename_slash_src_works="guessing no"])
+    rm -rf conftest.f conftest.d1 conftest.d2
   ])
- AC_CACHE_CHECK([whether rename is broken when the destination exists],
-  gl_cv_func_rename_dest_exists_bug,
-  [
-    case "$host_os" in
+  if test "x$gl_cv_func_rename_slash_src_works" != xyes; then
+    AC_LIBOBJ([rename])
+    REPLACE_RENAME=1
+    AC_DEFINE([RENAME_TRAILING_SLASH_BUG], [1],
+      [Define if rename does not correctly handle slashes on the source
+       argument, such as on Solaris 9 or cygwin 1.5.])
+  fi
+
+  AC_CACHE_CHECK([whether rename is broken when the destination exists],
+    [gl_cv_func_rename_dest_exists_bug],
+    [case "$host_os" in
       mingw*) gl_cv_func_rename_dest_exists_bug=yes ;;
       *) gl_cv_func_rename_dest_exists_bug=no ;;
     esac
   ])
-  if test $gl_cv_func_rename_trailing_slash_bug = yes ||
-     test $gl_cv_func_rename_dest_exists_bug = yes; then
+  if test $gl_cv_func_rename_dest_exists_bug = yes; then
     AC_LIBOBJ([rename])
     REPLACE_RENAME=1
-    if test $gl_cv_func_rename_trailing_slash_bug = yes; then
-      AC_DEFINE([RENAME_TRAILING_SLASH_BUG], [1],
-	[Define if rename does not work for source file names with a trailing
-	 slash, like the one from SunOS 4.1.1_U1.])
-    fi
-    if test $gl_cv_func_rename_dest_exists_bug = yes; then
-      AC_DEFINE([RENAME_DEST_EXISTS_BUG], [1],
-	[Define if rename does not work when the destination file exists,
-	 as on Windows.])
-    fi
-    gl_PREREQ_RENAME
+    AC_DEFINE([RENAME_DEST_EXISTS_BUG], [1],
+      [Define if rename does not work when the destination file exists,
+       as on Windows.])
   fi
 ])
-
-# Prerequisites of lib/rename.c.
-AC_DEFUN([gl_PREREQ_RENAME], [:])
--- a/modules/rename
+++ b/modules/rename
@@ -6,9 +6,11 @@
 m4/rename.m4
 
 Depends-on:
-xalloc
 dirname
+lstat
+stdbool
 stdio
+strdup
 
 configure.ac:
 gl_FUNC_RENAME
--- a/modules/rename-tests
+++ b/modules/rename-tests
@@ -5,7 +5,6 @@
 Depends-on:
 errno
 link
-lstat
 progname
 stdbool
 symlink