renameat: fix Solaris bugs
authorEric Blake <ebb9@byu.net>
Thu, 1 Oct 2009 21:31:32 +0000 (15:31 -0600)
committerEric Blake <ebb9@byu.net>
Fri, 2 Oct 2009 12:30:26 +0000 (06:30 -0600)
renameat(fd,"file",fd,"name/") failed, just like rename.

* m4/renameat.m4 (gl_FUNC_RENAMEAT): Replace renameat if rename
needed fixing.
* m4/stdio_h.m4 (gl_STDIO_H_DEFAULTS): New witness.
* modules/stdio (Makefile.am): Substitute it.
* lib/stdio.in.h (renameat): Declare replacement.
* lib/renameat.c (rpl_renameat): Implement fix.

Signed-off-by: Eric Blake <ebb9@byu.net>
ChangeLog
lib/renameat.c
lib/stdio.in.h
m4/renameat.m4
m4/stdio_h.m4
modules/stdio

index 370f664..3eba651 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
 2009-10-02  Eric Blake  <ebb9@byu.net>
 
+       renameat: fix Solaris bugs
+       * m4/renameat.m4 (gl_FUNC_RENAMEAT): Replace renameat if rename
+       needed fixing.
+       * m4/stdio_h.m4 (gl_STDIO_H_DEFAULTS): New witness.
+       * modules/stdio (Makefile.am): Substitute it.
+       * lib/stdio.in.h (renameat): Declare replacement.
+       * lib/renameat.c (rpl_renameat): Implement fix.
+
        renameat: new module
        * modules/renameat: New file.
        * lib/renameat.c (renameat): Likewise.
index 244ee38..375dcf8 100644 (file)
 
 #include <stdio.h>
 
-#include "openat-priv.h"
+#if HAVE_RENAMEAT
+
+# include <errno.h>
+# include <stdbool.h>
+# include <stdlib.h>
+# include <string.h>
+# include <sys/stat.h>
+
+# include "dirname.h"
+# include "openat.h"
+
+# undef renameat
+
+/* renameat does not honor trailing / on Solaris 10.  Solve it in a
+   similar manner to rename.  No need to worry about bugs not present
+   on Solaris, since all other systems either lack renameat or honor
+   trailing slash correctly.  */
+
+int
+rpl_renameat (int fd1, char const *src, int fd2, char const *dst)
+{
+  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;
+
+  /* Let strace see any ENOENT failure.  */
+  if (!src_len || !dst_len)
+    return renameat (fd1, src, fd2, dst);
+
+  src_slash = src[src_len - 1] == '/';
+  dst_slash = dst[dst_len - 1] == '/';
+  if (!src_slash && !dst_slash)
+    return renameat (fd1, src, fd2, 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 (lstatat (fd1, src, &src_st))
+    return -1;
+  if (lstatat (fd2, 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 RENAME_TRAILING_SLASH_SOURCE_BUG
+  /* See the lengthy comment in rename.c why Solaris 9 is forced to
+     GNU behavior, while Solaris 10 is left with POSIX behavior,
+     regarding symlinks with trailing slash.  */
+  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 (lstatat (fd1, 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 (lstatat (fd2, dst_temp, &dst_st))
+        {
+          if (errno != ENOENT)
+            {
+              rename_errno = errno;
+              goto out;
+            }
+        }
+      else if (S_ISLNK (dst_st.st_mode))
+        goto out;
+    }
+# endif /* RENAME_TRAILING_SLASH_SOURCE_BUG */
+
+  ret_val = renameat (fd1, src_temp, fd2, 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;
+}
+
+#else /* !HAVE_RENAMEAT */
+
+# include "openat-priv.h"
 
 /* Rename FILE1, in the directory open on descriptor FD1, to FILE2, in
    the directory open on descriptor FD2.  If possible, do it without
@@ -33,3 +153,5 @@ renameat (int fd1, char const *file1, int fd2, char const *file2)
 {
   return at_func2 (fd1, file1, fd2, file2, rename);
 }
+
+#endif /* !HAVE_RENAMEAT */
index 3777e85..7a0bc12 100644 (file)
@@ -445,7 +445,11 @@ extern int rename (const char *old, const char *new);
 #endif
 
 #if @GNULIB_RENAMEAT@
-# if !@HAVE_RENAMEAT@
+# if @REPLACE_RENAMEAT@
+#  undef renameat
+#  define renameat rpl_renameat
+# endif
+# if !@HAVE_RENAMEAT@ || @REPLACE_RENAMEAT@
 extern int renameat (int fd1, char const *file1, int fd2, char const *file2);
 # endif
 #elif defined GNULIB_POSIXCHECK
index bda7660..6dc7ff3 100644 (file)
@@ -1,4 +1,4 @@
-# serial 1
+# serial 2
 # See if we need to provide renameat replacement.
 
 dnl Copyright (C) 2009 Free Software Foundation, Inc.
@@ -19,5 +19,10 @@ AC_DEFUN([gl_FUNC_RENAMEAT],
     HAVE_RENAMEAT=0
     AC_LIBOBJ([renameat])
     AC_LIBOBJ([at-func2])
+  elif test $REPLACE_RENAME = 1; then
+    dnl Solaris 9 and 10 have the same bugs in renameat as in rename.
+    REPLACE_RENAMEAT=1
+    AC_LIBOBJ([renameat])
+    AC_LIBOBJ([at-func2])
   fi
 ])
index 6f8c035..ed828ea 100644 (file)
@@ -1,4 +1,4 @@
-# stdio_h.m4 serial 20
+# stdio_h.m4 serial 21
 dnl Copyright (C) 2007-2009 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -112,6 +112,7 @@ AC_DEFUN([gl_STDIO_H_DEFAULTS],
   REPLACE_PRINTF=0;              AC_SUBST([REPLACE_PRINTF])
   REPLACE_REMOVE=0;              AC_SUBST([REPLACE_REMOVE])
   REPLACE_RENAME=0;              AC_SUBST([REPLACE_RENAME])
+  REPLACE_RENAMEAT=0;            AC_SUBST([REPLACE_RENAMEAT])
   REPLACE_SNPRINTF=0;            AC_SUBST([REPLACE_SNPRINTF])
   REPLACE_SPRINTF=0;             AC_SUBST([REPLACE_SPRINTF])
   REPLACE_STDIO_WRITE_FUNCS=0;   AC_SUBST([REPLACE_STDIO_WRITE_FUNCS])
index 8f75e19..8b6e050 100644 (file)
@@ -94,6 +94,7 @@ stdio.h: stdio.in.h
              -e 's|@''REPLACE_PRINTF''@|$(REPLACE_PRINTF)|g' \
              -e 's|@''REPLACE_REMOVE''@|$(REPLACE_REMOVE)|g' \
              -e 's|@''REPLACE_RENAME''@|$(REPLACE_RENAME)|g' \
+             -e 's|@''REPLACE_RENAMEAT''@|$(REPLACE_RENAMEAT)|g' \
              -e 's|@''REPLACE_SNPRINTF''@|$(REPLACE_SNPRINTF)|g' \
              -e 's|@''REPLACE_SPRINTF''@|$(REPLACE_SPRINTF)|g' \
              -e 's|@''REPLACE_STDIO_WRITE_FUNCS''@|$(REPLACE_STDIO_WRITE_FUNCS)|g' \