rename: fix Solaris 9 bug
authorEric Blake <ebb9@byu.net>
Wed, 30 Sep 2009 22:19:00 +0000 (16:19 -0600)
committerEric Blake <ebb9@byu.net>
Fri, 2 Oct 2009 12:06:57 +0000 (06:06 -0600)
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>
ChangeLog
lib/rename.c
m4/rename.m4
modules/rename
modules/rename-tests

index ba1cea5d99fef959e54804926513df7d0706ab1e..4b78fa0d0c086cf5229d6bc76a682e01e5b57764 100644 (file)
--- 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.
index 5cd4dee9b024c38bd63376c9cf69e4432c6fc9b2..0709759841d557c360db2ee111da033297e51309 100644 (file)
@@ -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 @@ rpl_rename (char const *src, char const *dst)
 # 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.  */
+
+  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))
+    {
+      errno = EISDIR;
+      return -1;
+    }
 
-  if (s_len && src[s_len - 1] == '/')
+  /* 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 = xstrdup (src);
+      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;
     }
-  else
-    src_temp = (char *) src;
-
-  ret_val = rename (src_temp, dst);
-
+  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 */
index 2e43a87593bb184e9fc8ef5b273661bbb7b42b83..2c0d5e86f05b64e85696c4f20be2d5e411a6f8f1 100644 (file)
@@ -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_DEFUN([gl_FUNC_RENAME],
 [
   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], [:])
index 8d2a968616370f2397cd71b55fb25fec4266ab9d..649c2fa6c316e823c6ae32a9e191032f3b4455fe 100644 (file)
@@ -6,9 +6,11 @@ lib/rename.c
 m4/rename.m4
 
 Depends-on:
-xalloc
 dirname
+lstat
+stdbool
 stdio
+strdup
 
 configure.ac:
 gl_FUNC_RENAME
index ed586d7e83dfc6b0c89c61ce3a034f8dbef1e683..9929faf58c748d78b187aff334d4de12733d14d7 100644 (file)
@@ -5,7 +5,6 @@ tests/test-rename.c
 Depends-on:
 errno
 link
-lstat
 progname
 stdbool
 symlink