summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--lib/renameat.c124
-rw-r--r--lib/stdio.in.h6
-rw-r--r--m4/renameat.m47
-rw-r--r--m4/stdio_h.m43
-rw-r--r--modules/stdio1
6 files changed, 145 insertions, 4 deletions
diff --git a/ChangeLog b/ChangeLog
index 370f6644d4..3eba651219 100644
--- 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.
diff --git a/lib/renameat.c b/lib/renameat.c
index 244ee38edd..375dcf81e4 100644
--- a/lib/renameat.c
+++ b/lib/renameat.c
@@ -20,7 +20,127 @@
#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 */
diff --git a/lib/stdio.in.h b/lib/stdio.in.h
index 3777e85d32..7a0bc12d39 100644
--- a/lib/stdio.in.h
+++ b/lib/stdio.in.h
@@ -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
diff --git a/m4/renameat.m4 b/m4/renameat.m4
index bda7660c4c..6dc7ff31ab 100644
--- a/m4/renameat.m4
+++ b/m4/renameat.m4
@@ -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
])
diff --git a/m4/stdio_h.m4 b/m4/stdio_h.m4
index 6f8c035a52..ed828eac89 100644
--- a/m4/stdio_h.m4
+++ b/m4/stdio_h.m4
@@ -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])
diff --git a/modules/stdio b/modules/stdio
index 8f75e19a6b..8b6e050e37 100644
--- a/modules/stdio
+++ b/modules/stdio
@@ -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' \