From add9fba5e1e248ac77e6abe31749a10cd2672d77 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Wed, 30 Sep 2009 16:19:00 -0600 Subject: [PATCH] rename: fix Solaris 9 bug 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 --- ChangeLog | 9 ++++ lib/rename.c | 110 +++++++++++++++++++++++++++++++++++-------- m4/rename.m4 | 70 ++++++++++++--------------- modules/rename | 4 +- modules/rename-tests | 1 - 5 files changed, 134 insertions(+), 60 deletions(-) diff --git a/ChangeLog b/ChangeLog index ba1cea5d99..4b78fa0d0c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,14 @@ 2009-10-02 Eric Blake + 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. diff --git a/lib/rename.c b/lib/rename.c index 5cd4dee9b0..0709759841 100644 --- a/lib/rename.c +++ b/lib/rename.c @@ -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. @@ -19,7 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -/* written by Volker Borchert */ +/* Written by Volker Borchert, Eric Blake. */ #include @@ -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 + +# include # include # include # include +# include # 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 */ diff --git a/m4/rename.m4 b/m4/rename.m4 index 2e43a87593..2c0d5e86f0 100644 --- a/m4/rename.m4 +++ b/m4/rename.m4 @@ -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 @@ -17,53 +17,45 @@ AC_DEFUN([gl_FUNC_RENAME], [ 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 # include - 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], [:]) diff --git a/modules/rename b/modules/rename index 8d2a968616..649c2fa6c3 100644 --- a/modules/rename +++ b/modules/rename @@ -6,9 +6,11 @@ lib/rename.c m4/rename.m4 Depends-on: -xalloc dirname +lstat +stdbool stdio +strdup configure.ac: gl_FUNC_RENAME diff --git a/modules/rename-tests b/modules/rename-tests index ed586d7e83..9929faf58c 100644 --- a/modules/rename-tests +++ b/modules/rename-tests @@ -5,7 +5,6 @@ tests/test-rename.c Depends-on: errno link -lstat progname stdbool symlink -- 2.30.2