# include <stdlib.h>
# include <string.h>
# include <sys/stat.h>
+# include <unistd.h>
# include "dirname.h"
# include "same-inode.h"
char *dst_temp = (char *) dst;
bool src_slash;
bool dst_slash;
+ bool dst_exists;
int ret_val = -1;
int rename_errno = ENOTDIR;
struct stat src_st;
src_slash = src[src_len - 1] == '/';
dst_slash = dst[dst_len - 1] == '/';
-# if !RENAME_DEST_EXISTS_BUG
+# if !RENAME_HARD_LINK_BUG && !RENAME_DEST_EXISTS_BUG
/* If there are no trailing slashes, then trust the native
implementation unless we also suspect issues with hard link
- detection. */
+ detection or file/directory conflicts. */
if (!src_slash && !dst_slash)
return rename (src, dst);
-# endif /* !RENAME_DEST_EXISTS_BUG */
+# endif /* !RENAME_HARD_LINK_BUG && !RENAME_DEST_EXISTS_BUG */
/* Presence of a trailing slash requires directory semantics. If
the source does not exist, or if the destination cannot be turned
{
if (errno != ENOENT || (!S_ISDIR (src_st.st_mode) && dst_slash))
return -1;
+ dst_exists = false;
}
else
{
errno = S_ISDIR (dst_st.st_mode) ? EISDIR : ENOTDIR;
return -1;
}
-# if RENAME_DEST_EXISTS_BUG
+# if RENAME_HARD_LINK_BUG
if (SAME_INODE (src_st, dst_st))
return 0;
-# endif /* RENAME_DEST_EXISTS_BUG */
+# endif /* RENAME_HARD_LINK_BUG */
+ dst_exists = true;
}
-# if RENAME_TRAILING_SLASH_SOURCE_BUG || RENAME_DEST_EXISTS_BUG
+# if (RENAME_TRAILING_SLASH_SOURCE_BUG || RENAME_DEST_EXISTS_BUG \
+ || RENAME_HARD_LINK_BUG)
/* If the only bug was that a trailing slash was allowed on a
non-existing file destination, as in Solaris 10, then we've
already covered that situation. But if there is any problem with
else if (S_ISLNK (dst_st.st_mode))
goto out;
}
-# endif /* RENAME_TRAILING_SLASH_SOURCE_BUG || RENAME_DEST_EXISTS_BUG */
+# endif /* RENAME_TRAILING_SLASH_SOURCE_BUG || RENAME_DEST_EXISTS_BUG
+ || RENAME_HARD_LINK_BUG */
+
+# if RENAME_DEST_EXISTS_BUG
+ /* Cygwin 1.5 sometimes behaves oddly when moving a non-empty
+ directory on top of an empty one (the old directory name can
+ reappear if the new directory tree is removed). Work around this
+ by removing the target first, but don't remove the target if it
+ is a subdirectory of the source. */
+ if (dst_exists && S_ISDIR (dst_st.st_mode))
+ {
+ if (src_temp != src)
+ free (src_temp);
+ src_temp = canonicalize_file_name (src);
+ if (dst_temp != dst)
+ free (dst_temp);
+ dst_temp = canonicalize_file_name (dst);
+ if (!src_temp || !dst_temp)
+ {
+ rename_errno = ENOMEM;
+ goto out;
+ }
+ src_len = strlen (src_temp);
+ if (strncmp (src_temp, dst_temp, src_len) == 0
+ && dst_temp[src_len] == '/')
+ {
+ rename_errno = EINVAL;
+ goto out;
+ }
+ if (rmdir (dst))
+ {
+ rename_errno = errno;
+ goto out;
+ }
+ }
+# endif /* RENAME_DEST_EXISTS_BUG */
ret_val = rename (src_temp, dst_temp);
rename_errno = errno;
-# serial 19
+# serial 20
# Copyright (C) 2001, 2003, 2005, 2006, 2009 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
argument, such as on Solaris 9 or cygwin 1.5.])
fi
- dnl NetBSD 1.6 mistakenly reduces hard link count on rename("h1","h2").
+ dnl NetBSD 1.6 and cygwin 1.5.x mistakenly reduce hard link count
+ dnl on rename("h1","h2").
+ dnl This bug requires stat'ting targets prior to attempting rename.
+ AC_CACHE_CHECK([whether rename manages hard links correctly],
+ [gl_cv_func_rename_link_works],
+ [rm -rf conftest.f conftest.f1
+ if touch conftest.f && ln conftest.f conftest.f1 &&
+ set x `ls -i conftest.f conftest.f1` && test "$2" = "$4"; then
+ AC_RUN_IFELSE([AC_LANG_PROGRAM([[
+# include <stdio.h>
+# include <stdlib.h>
+# include <unistd.h>
+]], [if (rename ("conftest.f", "conftest.f1")) return 1;
+ if (unlink ("conftest.f1")) return 2;
+ if (rename ("conftest.f", "conftest.f")) return 3;
+ if (rename ("conftest.f1", "conftest.f1") == 0) return 4;])],
+ [gl_cv_func_rename_link_works=yes],
+ [gl_cv_func_rename_link_works=no],
+ dnl When crosscompiling, assume rename is broken.
+ [gl_cv_func_rename_link_works="guessing no"])
+ else
+ gl_cv_func_rename_link_works="guessing no"
+ fi
+ rm -rf conftest.f conftest.f1
+ ])
+ if test "x$gl_cv_func_rename_link_works" != xyes; then
+ AC_LIBOBJ([rename])
+ REPLACE_RENAME=1
+ AC_DEFINE([RENAME_HARD_LINK_BUG], [1],
+ [Define if rename fails to leave hard links alone, as on NetBSD 1.6
+ or Cygwin 1.5.])
+ fi
+
dnl Cygwin 1.5.x mistakenly allows rename("dir","file").
dnl mingw mistakenly forbids rename("dir1","dir2").
dnl These bugs require stripping trailing slash to avoid corrupting
[rm -rf conftest.f conftest.d1 conftest.d2
touch conftest.f && mkdir conftest.d1 conftest.d2 ||
AC_MSG_ERROR([cannot create temporary files])
- if ln conftest.f conftest.f1 && set x `ls -i conftest.f conftest.f1` &&
- test "$2" = "$4"; then
- AC_RUN_IFELSE([AC_LANG_PROGRAM([[
+ AC_RUN_IFELSE([AC_LANG_PROGRAM([[
# include <stdio.h>
# include <stdlib.h>
]], [if (rename ("conftest.d1", "conftest.d2") != 0) return 1;
- if (rename ("conftest.d2", "conftest.f") == 0) return 2;
- if (rename ("conftest.f", "conftest.f1")
- || rename ("conftest.f", "conftest.f")) return 3;])],
- [gl_cv_func_rename_dest_works=yes],
- [gl_cv_func_rename_dest_works=no],
- dnl When crosscompiling, assume rename is broken.
- [gl_cv_func_rename_dest_works="guessing no"])
- else
- gl_cv_func_rename_dest_works="guessing no"
- fi
- rm -rf conftest.f conftest.f1 conftest.d1 conftest.d2
+ if (rename ("conftest.d2", "conftest.f") == 0) return 2;])],
+ [gl_cv_func_rename_dest_works=yes],
+ [gl_cv_func_rename_dest_works=no],
+ dnl When crosscompiling, assume rename is broken.
+ [gl_cv_func_rename_dest_works="guessing no"])
+ rm -rf conftest.f conftest.d1 conftest.d2
])
if test "x$gl_cv_func_rename_dest_works" != xyes; then
AC_LIBOBJ([rename])
REPLACE_RENAME=1
AC_DEFINE([RENAME_DEST_EXISTS_BUG], [1],
[Define if rename does not work when the destination file exists,
- as with NetBSD 1.6 on hard links, or Windows on directories.])
+ as on Cygwin 1.5 or Windows.])
fi
])