maint: update copyright
[gnulib.git] / lib / rename.c
index abd1ec5..2116028 100644 (file)
@@ -1,7 +1,6 @@
 /* Work around rename bugs in some systems.
 
-   Copyright (C) 2001, 2002, 2003, 2005, 2006, 2009 Free Software
-   Foundation, Inc.
+   Copyright (C) 2001-2003, 2005-2006, 2009-2014 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
@@ -100,10 +99,10 @@ rpl_rename (char const *src, char const *dst)
   else
     {
       if (S_ISDIR (dst_st.st_mode) != S_ISDIR (src_st.st_mode))
-       {
-         errno = S_ISDIR (dst_st.st_mode) ? EISDIR : ENOTDIR;
-         return -1;
-       }
+        {
+          errno = S_ISDIR (dst_st.st_mode) ? EISDIR : ENOTDIR;
+          return -1;
+        }
       dst_exists = true;
     }
 
@@ -113,13 +112,16 @@ rpl_rename (char const *src, char const *dst)
      replace an existing empty directory, so we have to help it out.
      And canonicalize_file_name is not yet ported to mingw; however,
      for directories, getcwd works as a viable alternative.  Ensure
-     that we can get back to where we started before using it.  */
+     that we can get back to where we started before using it; later
+     attempts to return are fatal.  Note that we can end up losing a
+     directory if rename then fails, but it was empty, so not much
+     damage was done.  */
   if (dst_exists && S_ISDIR (dst_st.st_mode))
     {
       char *cwd = getcwd (NULL, 0);
       char *src_temp;
       char *dst_temp;
-      if (chdir (cwd))
+      if (!cwd || chdir (cwd))
         return -1;
       if (IS_ABSOLUTE_FILE_NAME (src))
         {
@@ -129,11 +131,12 @@ rpl_rename (char const *src, char const *dst)
       else
         {
           src_temp = chdir (src) ? NULL : getcwd (NULL, 0);
-          if (!IS_ABSOLUTE_FILE_NAME (dst))
-            chdir (cwd);
+          if (!IS_ABSOLUTE_FILE_NAME (dst) && chdir (cwd))
+            abort ();
           dst_temp = chdir (dst) ? NULL : getcwd (NULL, 0);
         }
-      chdir (cwd);
+      if (chdir (cwd))
+        abort ();
       free (cwd);
       if (!src_temp || !dst_temp)
         {
@@ -342,13 +345,13 @@ rpl_rename (char const *src, char const *dst)
   else
     {
       if (S_ISDIR (dst_st.st_mode) != S_ISDIR (src_st.st_mode))
-       {
-         errno = S_ISDIR (dst_st.st_mode) ? EISDIR : ENOTDIR;
-         return -1;
-       }
+        {
+          errno = S_ISDIR (dst_st.st_mode) ? EISDIR : ENOTDIR;
+          return -1;
+        }
 # if RENAME_HARD_LINK_BUG
       if (SAME_INODE (src_st, dst_st))
-       return 0;
+        return 0;
 # endif /* RENAME_HARD_LINK_BUG */
       dst_exists = true;
     }
@@ -421,9 +424,16 @@ rpl_rename (char const *src, char const *dst)
      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.  */
+     is a subdirectory of the source.  Note that we can end up losing
+     a directory if rename then fails, but it was empty, so not much
+     damage was done.  */
   if (dst_exists && S_ISDIR (dst_st.st_mode))
     {
+      if (src_st.st_dev != dst_st.st_dev)
+        {
+          rename_errno = EXDEV;
+          goto out;
+        }
       if (src_temp != src)
         free (src_temp);
       src_temp = canonicalize_file_name (src);