bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

fix for rename function on Mingw, take 2


From: Ben Pfaff
Subject: fix for rename function on Mingw, take 2
Date: Tue, 07 Apr 2009 20:23:05 -0700
User-agent: Gnus/5.11 (Gnus v5.11) Emacs/22.2 (gnu/linux)

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 the second version of the gnulib commit that I am
proposing.  Compared to the first version of this commit, I have
made the following changes:

    * m4/rename.m4: Increment serial number and remove trailing
      blanks (pointed out by Jim Meyering).

    * lib/rename.c: First try to rename files without
      MOVEFILE_REPLACE_EXISTING, allowing directories to be
      renamed.

Michel also tested that MOVEFILE_REPLACE_EXISTING does the right
thing with hard links, as requested by Bruno.

Since this fixes all the objections that were posted in the first
round, I'm going to push this as soon as I get confirmation from
Michel that the updated commit works properly for him on PSPP
(unless anyone raises a new objection in the meantime).

Michel, can you test that this works for you?  Thanks!

>From ede71861deaf907ba435b6ad9b50b3824add1b7f Mon Sep 17 00:00:00 2001
From: Ben Pfaff <address@hidden>
Date: Tue, 7 Apr 2009 20:15:01 -0700
Subject: [PATCH] Make rename replace existing destinations on Windows.

---
 ChangeLog                       |    8 +++
 doc/posix-functions/rename.texi |    7 ++
 lib/rename.c                    |  117 +++++++++++++++++++++++++++++++++++++-
 m4/rename.m4                    |   29 ++++++++--
 4 files changed, 151 insertions(+), 10 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index e4c9bf6..43c77ea 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2009-03-20  Ben Pfaff  <address@hidden>
+
+       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-04-07  Jim Meyering  <address@hidden>
 
        useless-if-before-free: show how to remove braced useless free, too
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.
address@hidden
+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..d4a8c27 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,113 @@
 #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)
+{
+  int error;
+
+  /* MoveFileEx works if SRC is a directory without any flags,
+     but fails with MOVEFILE_REPLACE_EXISTING, so try without
+     flags first. */
+  if (MoveFileEx (src, dst, 0))
+    return 0;
+
+  /* Then retry with MOVEFILE_REPLACE_EXISTING if the move
+   * failed due */
+  error = GetLastError ();
+  if (error == ERROR_FILE_EXISTS || error == ERROR_ALREADY_EXISTS)
+    {
+      if (MoveFileEx (src, dst, MOVEFILE_REPLACE_EXISTING))
+        return 0;
+
+      error = GetLastError ();
+    }
+
+  switch (error)
+    {
+    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 +162,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..53f9ea5 100644
--- a/m4/rename.m4
+++ b/m4/rename.m4
@@ -1,4 +1,4 @@
-# serial 11
+# serial 12
 
 # Copyright (C) 2001, 2003, 2005, 2006, 2009 Free Software Foundation, Inc.
 # This file is free software; the Free Software Foundation
@@ -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
 ])
-- 
1.5.6.5


-- 
Ben Pfaff 
http://benpfaff.org




reply via email to

[Prev in Thread] Current Thread [Next in Thread]