# HG changeset patch # User Eric Blake # Date 1254432692 21600 # Node ID 8e81b30aa8b7b4bf2093d73ead6bf3892db7b512 # Parent 01c66cf797b5c40d04cab39e07fe66ff82db466d renameat: fix Solaris bugs 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 diff --git a/ChangeLog b/ChangeLog --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,13 @@ 2009-10-02 Eric Blake + 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 --- a/lib/renameat.c +++ b/lib/renameat.c @@ -20,7 +20,127 @@ #include -#include "openat-priv.h" +#if HAVE_RENAMEAT + +# include +# include +# include +# include +# include + +# 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 @@ { return at_func2 (fd1, file1, fd2, file2, rename); } + +#endif /* !HAVE_RENAMEAT */ diff --git a/lib/stdio.in.h b/lib/stdio.in.h --- a/lib/stdio.in.h +++ b/lib/stdio.in.h @@ -445,7 +445,11 @@ #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 --- 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 @@ 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 --- 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 @@ 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 --- a/modules/stdio +++ b/modules/stdio @@ -94,6 +94,7 @@ -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' \