On the Windows platform, Microsoft documents the rename function as refusing to replace existing destination files. This is allowed by C89 (and C99) but it is not POSIX-compliant, and it can be quite surprising to programs that want to atomically replace files.
Here is a gnulib commit that I am proposing. It (cross)compiles on Mingw, but I am waiting to hear back from my Windows tester to find out whether it actually solves the problem for him. Comments? commit 7243a5ecda0db712da438b1cfcad89c18264fdb3 Author: Ben Pfaff <b...@cs.stanford.edu> Date: Fri Mar 20 22:02:50 2009 -0700 Make rename replace existing destinations on Windows. diff --git a/ChangeLog b/ChangeLog index 4a23101..642a615 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2009-03-20 Ben Pfaff <b...@gnu.org> + + Make rename replace existing destinations on Windows. + * m4/rename.m4: Add test for Mingw. + * lib/rename.c: Add rename replacement that uses MoveFileEx with + MOVEFILE_REPLACE_EXISTING to replace existing destination files. + * doc/posix-functions/rename.texi: Document. + 2009-03-20 Bruno Haible <br...@clisp.org> Make sockets.h self-contained. diff --git a/doc/posix-functions/rename.texi b/doc/posix-functions/rename.texi index 62b1fc8..aa7287a 100644 --- a/doc/posix-functions/rename.texi +++ b/doc/posix-functions/rename.texi @@ -12,8 +12,15 @@ Portability problems fixed by Gnulib: This function does not work when the source file name ends in a slash on some platforms: SunOS 4.1. +...@item +This function will not replace an existing destination on some +platforms: +mingw. @end itemize Portability problems not fixed by Gnulib: @itemize +This function will not replace a destination that is currently opened +by any process: +mingw. @end itemize diff --git a/lib/rename.c b/lib/rename.c index 9667c39..f0486d9 100644 --- a/lib/rename.c +++ b/lib/rename.c @@ -1,8 +1,9 @@ -/* Work around the bug in some systems whereby rename fails when the source - file has a trailing slash. The rename functions of SunOS 4.1.1_U1 and - mips-dec-ultrix4.4 have this bug. +/* 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. - Copyright (C) 2001, 2002, 2003, 2005, 2006 Free Software Foundation, Inc. + Copyright (C) 2001, 2002, 2003, 2005, 2006, 2009 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,6 +23,99 @@ #include <config.h> #undef rename +#if RENAME_DEST_EXISTS_BUG +/* This replacement must come first, otherwise when cross + * compiling to Windows we will guess that it has the trailing + * slash bug and entirely miss this one. */ +#include <errno.h> + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +/* Rename the file SRC to DST. This replacement is necessary on + Windows, on which the system rename function will not replace + an existing DST. */ +int +rpl_rename (char const *src, char const *dst) +{ + if (MoveFileEx (src, dst, MOVEFILE_REPLACE_EXISTING)) + return 0; + else + { + switch (GetLastError()) + { + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + case ERROR_BAD_PATHNAME: + case ERROR_DIRECTORY: + errno = ENOENT; + break; + + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + errno = EACCES; + break; + + case ERROR_OUTOFMEMORY: + errno = ENOMEM; + break; + + case ERROR_CURRENT_DIRECTORY: + errno = EBUSY; + break; + + case ERROR_NOT_SAME_DEVICE: + errno = EXDEV; + break; + + case ERROR_WRITE_PROTECT: + errno = EROFS; + break; + + case ERROR_WRITE_FAULT: + case ERROR_READ_FAULT: + case ERROR_GEN_FAILURE: + errno = EIO; + break; + + case ERROR_HANDLE_DISK_FULL: + case ERROR_DISK_FULL: + case ERROR_DISK_TOO_FRAGMENTED: + errno = ENOSPC; + break; + + case ERROR_FILE_EXISTS: + case ERROR_ALREADY_EXISTS: + errno = EEXIST; + break; + + case ERROR_BUFFER_OVERFLOW: + case ERROR_FILENAME_EXCED_RANGE: + errno = ENAMETOOLONG; + break; + + case ERROR_INVALID_NAME: + case ERROR_DELETE_PENDING: + errno = EPERM; /* ? */ + break; + +#ifndef ERROR_FILE_TOO_LARGE +/* This value is documented but not defined in all versions of windows.h. */ +#define ERROR_FILE_TOO_LARGE 223 +#endif + case ERROR_FILE_TOO_LARGE: + errno = EFBIG; + break; + + default: + errno = EINVAL; + break; + } + + return -1; + } +} +#elif RENAME_TRAILING_SLASH_BUG #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -54,3 +148,4 @@ rpl_rename (char const *src, char const *dst) return ret_val; } +#endif /* RENAME_TRAILING_SLASH_BUG */ diff --git a/m4/rename.m4 b/m4/rename.m4 index fa7ae8d..9829308 100644 --- a/m4/rename.m4 +++ b/m4/rename.m4 @@ -15,7 +15,8 @@ dnl AC_DEFUN([gl_FUNC_RENAME], [ - AC_CACHE_CHECK([whether rename is broken], + AC_REQUIRE([AC_CANONICAL_HOST]) + AC_CACHE_CHECK([whether rename is broken with a trailing slash], gl_cv_func_rename_trailing_slash_bug, [ rm -rf conftest.d1 conftest.d2 @@ -37,13 +38,29 @@ AC_DEFUN([gl_FUNC_RENAME], rm -rf conftest.d1 conftest.d2 ]) - if test $gl_cv_func_rename_trailing_slash_bug = yes; then + 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 AC_LIBOBJ([rename]) AC_DEFINE([rename], [rpl_rename], [Define to rpl_rename if the replacement function should be used.]) - 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.]) + if test $gl_cv_func_rename_trailing_slash_bug; 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; 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 fi ]) -- "If a person keeps faithfully busy each hour of the working day, he can count on waking up some morning to find himself one of the competent ones of his generation." --William James