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>
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.
-/* 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.
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>
# 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 */
-# serial 15
+# serial 16
# Copyright (C) 2001, 2003, 2005, 2006, 2009 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
[
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], [:])
m4/rename.m4
Depends-on:
-xalloc
dirname
+lstat
+stdbool
stdio
+strdup
configure.ac:
gl_FUNC_RENAME
Depends-on:
errno
link
-lstat
progname
stdbool
symlink