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>
 
 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.
        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.
 
    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/>.  */
 
    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>
 
 
 #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
 # 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 <stdio.h>
 #  include <stdlib.h>
 #  include <string.h>
+#  include <sys/stat.h>
 
 #  include "dirname.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)
 {
 
 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);
       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 (src_temp != src)
     free (src_temp);
-
+  if (dst_temp != dst)
+    free (dst_temp);
+  errno = rename_errno;
   return ret_val;
 }
 # endif /* RENAME_TRAILING_SLASH_BUG */
   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
 
 # 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_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>
 #       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.
       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
   ])
       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
     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
 ])
   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:
 m4/rename.m4
 
 Depends-on:
-xalloc
 dirname
 dirname
+lstat
+stdbool
 stdio
 stdio
+strdup
 
 configure.ac:
 gl_FUNC_RENAME
 
 configure.ac:
 gl_FUNC_RENAME
index ed586d7e83dfc6b0c89c61ce3a034f8dbef1e683..9929faf58c748d78b187aff334d4de12733d14d7 100644 (file)
@@ -5,7 +5,6 @@ tests/test-rename.c
 Depends-on:
 errno
 link
 Depends-on:
 errno
 link
-lstat
 progname
 stdbool
 symlink
 progname
 stdbool
 symlink