(root)/
tar-1.35/
gnu/
rename.c
       1  /* Work around rename bugs in some systems.
       2  
       3     Copyright (C) 2001-2003, 2005-2006, 2009-2023 Free Software Foundation, Inc.
       4  
       5     This file is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU Lesser General Public License as
       7     published by the Free Software Foundation; either version 2.1 of the
       8     License, or (at your option) any later version.
       9  
      10     This file is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public License
      16     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  /* Written by Volker Borchert, Eric Blake.  */
      19  
      20  #include <config.h>
      21  
      22  #include <stdio.h>
      23  
      24  #undef rename
      25  
      26  #if defined _WIN32 && ! defined __CYGWIN__
      27  /* The mingw rename has problems with trailing slashes; it also
      28     requires use of native Windows calls to allow atomic renames over
      29     existing files.  */
      30  
      31  # include <errno.h>
      32  # include <stdlib.h>
      33  # include <sys/stat.h>
      34  # include <unistd.h>
      35  
      36  # define WIN32_LEAN_AND_MEAN
      37  # include <windows.h>
      38  
      39  # include "dirname.h"
      40  
      41  /* Don't assume that UNICODE is not defined.  */
      42  # undef MoveFileEx
      43  # define MoveFileEx MoveFileExA
      44  
      45  /* Rename the file SRC to DST.  This replacement is necessary on
      46     Windows, on which the system rename function will not replace
      47     an existing DST.  */
      48  int
      49  rpl_rename (char const *src, char const *dst)
      50  {
      51    int error;
      52    size_t src_len = strlen (src);
      53    size_t dst_len = strlen (dst);
      54    char *src_base = last_component (src);
      55    char *dst_base = last_component (dst);
      56    bool src_slash;
      57    bool dst_slash;
      58    bool dst_exists;
      59    struct stat src_st;
      60    struct stat dst_st;
      61  
      62    /* Filter out dot as last component.  */
      63    if (!src_len || !dst_len)
      64      {
      65        errno = ENOENT;
      66        return -1;
      67      }
      68    if (*src_base == '.')
      69      {
      70        size_t len = base_len (src_base);
      71        if (len == 1 || (len == 2 && src_base[1] == '.'))
      72          {
      73            errno = EINVAL;
      74            return -1;
      75          }
      76      }
      77    if (*dst_base == '.')
      78      {
      79        size_t len = base_len (dst_base);
      80        if (len == 1 || (len == 2 && dst_base[1] == '.'))
      81          {
      82            errno = EINVAL;
      83            return -1;
      84          }
      85      }
      86  
      87    /* Presence of a trailing slash requires directory semantics.  If
      88       the source does not exist, or if the destination cannot be turned
      89       into a directory, give up now.  Otherwise, strip trailing slashes
      90       before calling rename.  There are no symlinks on mingw, so stat
      91       works instead of lstat.  */
      92    src_slash = ISSLASH (src[src_len - 1]);
      93    dst_slash = ISSLASH (dst[dst_len - 1]);
      94    if (stat (src, &src_st))
      95      return -1;
      96    if (stat (dst, &dst_st))
      97      {
      98        if (errno != ENOENT || (!S_ISDIR (src_st.st_mode) && dst_slash))
      99          return -1;
     100        dst_exists = false;
     101      }
     102    else
     103      {
     104        if (S_ISDIR (dst_st.st_mode) != S_ISDIR (src_st.st_mode))
     105          {
     106            errno = S_ISDIR (dst_st.st_mode) ? EISDIR : ENOTDIR;
     107            return -1;
     108          }
     109        dst_exists = true;
     110      }
     111  
     112    /* There are no symlinks, so if a file existed with a trailing
     113       slash, it must be a directory, and we don't have to worry about
     114       stripping strip trailing slash.  However, mingw refuses to
     115       replace an existing empty directory, so we have to help it out.
     116       And canonicalize_file_name is not yet ported to mingw; however,
     117       for directories, getcwd works as a viable alternative.  Ensure
     118       that we can get back to where we started before using it; later
     119       attempts to return are fatal.  Note that we can end up losing a
     120       directory if rename then fails, but it was empty, so not much
     121       damage was done.  */
     122    if (dst_exists && S_ISDIR (dst_st.st_mode))
     123      {
     124        char *cwd = getcwd (NULL, 0);
     125        char *src_temp;
     126        char *dst_temp;
     127        if (!cwd || chdir (cwd))
     128          return -1;
     129        if (IS_ABSOLUTE_FILE_NAME (src))
     130          {
     131            dst_temp = chdir (dst) ? NULL : getcwd (NULL, 0);
     132            src_temp = chdir (src) ? NULL : getcwd (NULL, 0);
     133          }
     134        else
     135          {
     136            src_temp = chdir (src) ? NULL : getcwd (NULL, 0);
     137            if (!IS_ABSOLUTE_FILE_NAME (dst) && chdir (cwd))
     138              abort ();
     139            dst_temp = chdir (dst) ? NULL : getcwd (NULL, 0);
     140          }
     141        if (chdir (cwd))
     142          abort ();
     143        free (cwd);
     144        if (!src_temp || !dst_temp)
     145          {
     146            free (src_temp);
     147            free (dst_temp);
     148            errno = ENOMEM;
     149            return -1;
     150          }
     151        src_len = strlen (src_temp);
     152        if (strncmp (src_temp, dst_temp, src_len) == 0
     153            && (ISSLASH (dst_temp[src_len]) || dst_temp[src_len] == '\0'))
     154          {
     155            error = dst_temp[src_len];
     156            free (src_temp);
     157            free (dst_temp);
     158            if (error)
     159              {
     160                errno = EINVAL;
     161                return -1;
     162              }
     163            return 0;
     164          }
     165        if (rmdir (dst))
     166          {
     167            free (src_temp);
     168            free (dst_temp);
     169            return -1;
     170          }
     171        free (src_temp);
     172        free (dst_temp);
     173      }
     174  
     175    /* MoveFileEx works if SRC is a directory without any flags, but
     176       fails with MOVEFILE_REPLACE_EXISTING, so try without flags first.
     177       Thankfully, MoveFileEx handles hard links correctly, even though
     178       rename() does not.  */
     179    if (MoveFileEx (src, dst, 0))
     180      return 0;
     181  
     182    /* Retry with MOVEFILE_REPLACE_EXISTING if the move failed
     183       due to the destination already existing.  */
     184    error = GetLastError ();
     185    if (error == ERROR_FILE_EXISTS || error == ERROR_ALREADY_EXISTS)
     186      {
     187        if (MoveFileEx (src, dst, MOVEFILE_REPLACE_EXISTING))
     188          return 0;
     189  
     190        error = GetLastError ();
     191      }
     192  
     193    switch (error)
     194      {
     195      case ERROR_FILE_NOT_FOUND:
     196      case ERROR_PATH_NOT_FOUND:
     197      case ERROR_BAD_PATHNAME:
     198      case ERROR_DIRECTORY:
     199        errno = ENOENT;
     200        break;
     201  
     202      case ERROR_ACCESS_DENIED:
     203      case ERROR_SHARING_VIOLATION:
     204        errno = EACCES;
     205        break;
     206  
     207      case ERROR_OUTOFMEMORY:
     208        errno = ENOMEM;
     209        break;
     210  
     211      case ERROR_CURRENT_DIRECTORY:
     212        errno = EBUSY;
     213        break;
     214  
     215      case ERROR_NOT_SAME_DEVICE:
     216        errno = EXDEV;
     217        break;
     218  
     219      case ERROR_WRITE_PROTECT:
     220        errno = EROFS;
     221        break;
     222  
     223      case ERROR_WRITE_FAULT:
     224      case ERROR_READ_FAULT:
     225      case ERROR_GEN_FAILURE:
     226        errno = EIO;
     227        break;
     228  
     229      case ERROR_HANDLE_DISK_FULL:
     230      case ERROR_DISK_FULL:
     231      case ERROR_DISK_TOO_FRAGMENTED:
     232        errno = ENOSPC;
     233        break;
     234  
     235      case ERROR_FILE_EXISTS:
     236      case ERROR_ALREADY_EXISTS:
     237        errno = EEXIST;
     238        break;
     239  
     240      case ERROR_BUFFER_OVERFLOW:
     241      case ERROR_FILENAME_EXCED_RANGE:
     242        errno = ENAMETOOLONG;
     243        break;
     244  
     245      case ERROR_INVALID_NAME:
     246      case ERROR_DELETE_PENDING:
     247        errno = EPERM;        /* ? */
     248        break;
     249  
     250  # ifndef ERROR_FILE_TOO_LARGE
     251  /* This value is documented but not defined in all versions of windows.h.  */
     252  #  define ERROR_FILE_TOO_LARGE 223
     253  # endif
     254      case ERROR_FILE_TOO_LARGE:
     255        errno = EFBIG;
     256        break;
     257  
     258      default:
     259        errno = EINVAL;
     260        break;
     261      }
     262  
     263    return -1;
     264  }
     265  
     266  #else /* ! W32 platform */
     267  
     268  # include <errno.h>
     269  # include <stdio.h>
     270  # include <stdlib.h>
     271  # include <string.h>
     272  # include <sys/stat.h>
     273  # include <unistd.h>
     274  
     275  # include "dirname.h"
     276  # include "same-inode.h"
     277  
     278  /* Rename the file SRC to DST, fixing any trailing slash bugs.  */
     279  
     280  int
     281  rpl_rename (char const *src, char const *dst)
     282  {
     283    size_t src_len = strlen (src);
     284    size_t dst_len = strlen (dst);
     285    char *src_temp = (char *) src;
     286    char *dst_temp = (char *) dst;
     287    bool src_slash;
     288    bool dst_slash;
     289    _GL_UNUSED bool dst_exists;
     290    int ret_val = -1;
     291    int rename_errno = ENOTDIR;
     292    struct stat src_st;
     293    struct stat dst_st;
     294  
     295    if (!src_len || !dst_len)
     296      return rename (src, dst); /* Let strace see the ENOENT failure.  */
     297  
     298  # if RENAME_DEST_EXISTS_BUG
     299    {
     300      char *src_base = last_component (src);
     301      char *dst_base = last_component (dst);
     302      if (*src_base == '.')
     303        {
     304          size_t len = base_len (src_base);
     305          if (len == 1 || (len == 2 && src_base[1] == '.'))
     306            {
     307              errno = EINVAL;
     308              return -1;
     309            }
     310        }
     311      if (*dst_base == '.')
     312        {
     313          size_t len = base_len (dst_base);
     314          if (len == 1 || (len == 2 && dst_base[1] == '.'))
     315            {
     316              errno = EINVAL;
     317              return -1;
     318            }
     319        }
     320    }
     321  # endif /* RENAME_DEST_EXISTS_BUG */
     322  
     323    src_slash = src[src_len - 1] == '/';
     324    dst_slash = dst[dst_len - 1] == '/';
     325  
     326  # if !RENAME_HARD_LINK_BUG && !RENAME_DEST_EXISTS_BUG
     327    /* If there are no trailing slashes, then trust the native
     328       implementation unless we also suspect issues with hard link
     329       detection or file/directory conflicts.  */
     330    if (!src_slash && !dst_slash)
     331      return rename (src, dst);
     332  # endif /* !RENAME_HARD_LINK_BUG && !RENAME_DEST_EXISTS_BUG */
     333  
     334    /* Presence of a trailing slash requires directory semantics.  If
     335       the source does not exist, or if the destination cannot be turned
     336       into a directory, give up now.  Otherwise, strip trailing slashes
     337       before calling rename.  */
     338    if (lstat (src, &src_st))
     339      return -1;
     340    if (lstat (dst, &dst_st))
     341      {
     342        if (errno != ENOENT || (!S_ISDIR (src_st.st_mode) && dst_slash))
     343          return -1;
     344        dst_exists = false;
     345      }
     346    else
     347      {
     348        if (S_ISDIR (dst_st.st_mode) != S_ISDIR (src_st.st_mode))
     349          {
     350            errno = S_ISDIR (dst_st.st_mode) ? EISDIR : ENOTDIR;
     351            return -1;
     352          }
     353  # if RENAME_HARD_LINK_BUG
     354        if (SAME_INODE (src_st, dst_st))
     355          return 0;
     356  # endif /* RENAME_HARD_LINK_BUG */
     357        dst_exists = true;
     358      }
     359  
     360  # if (RENAME_TRAILING_SLASH_SOURCE_BUG || RENAME_DEST_EXISTS_BUG        \
     361        || RENAME_HARD_LINK_BUG)
     362    /* If the only bug was that a trailing slash was allowed on a
     363       nonexistent file destination, as in Solaris 10, then we've
     364       already covered that situation.  But if there is any problem with
     365       a trailing slash on an existing source or destination, as in
     366       Solaris 9, or if a directory can overwrite a symlink, as on
     367       Cygwin 1.5, or if directories cannot be created with trailing
     368       slash, as on NetBSD 1.6, then we must strip the offending slash
     369       and check that we have not encountered a symlink instead of a
     370       directory.
     371  
     372       Stripping a trailing slash interferes with POSIX semantics, where
     373       rename behavior on a symlink with a trailing slash operates on
     374       the corresponding target directory.  We prefer the GNU semantics
     375       of rejecting any use of a symlink with trailing slash, but do not
     376       enforce them, since Solaris 10 is able to obey POSIX semantics
     377       and there might be clients expecting it, as counter-intuitive as
     378       those semantics are.
     379  
     380       Technically, we could also follow the POSIX behavior by chasing a
     381       readlink trail, but that is harder to implement.  */
     382    if (src_slash)
     383      {
     384        src_temp = strdup (src);
     385        if (!src_temp)
     386          {
     387            /* Rather than rely on strdup-posix, we set errno ourselves.  */
     388            rename_errno = ENOMEM;
     389            goto out;
     390          }
     391        strip_trailing_slashes (src_temp);
     392        if (lstat (src_temp, &src_st))
     393          {
     394            rename_errno = errno;
     395            goto out;
     396          }
     397        if (S_ISLNK (src_st.st_mode))
     398          goto out;
     399      }
     400    if (dst_slash)
     401      {
     402        dst_temp = strdup (dst);
     403        if (!dst_temp)
     404          {
     405            rename_errno = ENOMEM;
     406            goto out;
     407          }
     408        strip_trailing_slashes (dst_temp);
     409        if (lstat (dst_temp, &dst_st))
     410          {
     411            if (errno != ENOENT)
     412              {
     413                rename_errno = errno;
     414                goto out;
     415              }
     416          }
     417        else if (S_ISLNK (dst_st.st_mode))
     418          goto out;
     419      }
     420  # endif /* RENAME_TRAILING_SLASH_SOURCE_BUG || RENAME_DEST_EXISTS_BUG
     421             || RENAME_HARD_LINK_BUG */
     422  
     423  # if RENAME_DEST_EXISTS_BUG
     424    /* Cygwin 1.5 sometimes behaves oddly when moving a non-empty
     425       directory on top of an empty one (the old directory name can
     426       reappear if the new directory tree is removed).  Work around this
     427       by removing the target first, but don't remove the target if it
     428       is a subdirectory of the source.  Note that we can end up losing
     429       a directory if rename then fails, but it was empty, so not much
     430       damage was done.  */
     431    if (dst_exists && S_ISDIR (dst_st.st_mode))
     432      {
     433        if (src_st.st_dev != dst_st.st_dev)
     434          {
     435            rename_errno = EXDEV;
     436            goto out;
     437          }
     438        if (src_temp != src)
     439          free (src_temp);
     440        src_temp = canonicalize_file_name (src);
     441        if (dst_temp != dst)
     442          free (dst_temp);
     443        dst_temp = canonicalize_file_name (dst);
     444        if (!src_temp || !dst_temp)
     445          {
     446            rename_errno = ENOMEM;
     447            goto out;
     448          }
     449        src_len = strlen (src_temp);
     450        if (strncmp (src_temp, dst_temp, src_len) == 0
     451            && dst_temp[src_len] == '/')
     452          {
     453            rename_errno = EINVAL;
     454            goto out;
     455          }
     456        if (rmdir (dst))
     457          {
     458            rename_errno = errno;
     459            goto out;
     460          }
     461      }
     462  # endif /* RENAME_DEST_EXISTS_BUG */
     463  
     464    ret_val = rename (src_temp, dst_temp);
     465    rename_errno = errno;
     466  
     467   out: _GL_UNUSED_LABEL;
     468  
     469    if (src_temp != src)
     470      free (src_temp);
     471    if (dst_temp != dst)
     472      free (dst_temp);
     473    errno = rename_errno;
     474    return ret_val;
     475  }
     476  #endif /* ! W32 platform */