(root)/
tar-1.35/
gnu/
link.c
       1  /* Emulate link on platforms that lack it, namely native Windows platforms.
       2  
       3     Copyright (C) 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  #include <config.h>
      19  
      20  #include <unistd.h>
      21  
      22  #include <errno.h>
      23  #include <stdlib.h>
      24  #include <string.h>
      25  #include <sys/stat.h>
      26  
      27  #if !HAVE_LINK
      28  # if defined _WIN32 && ! defined __CYGWIN__
      29  
      30  #  define WIN32_LEAN_AND_MEAN
      31  #  include <windows.h>
      32  
      33  /* Don't assume that UNICODE is not defined.  */
      34  #  undef GetModuleHandle
      35  #  define GetModuleHandle GetModuleHandleA
      36  #  undef CreateHardLink
      37  #  define CreateHardLink CreateHardLinkA
      38  
      39  #  if !(_WIN32_WINNT >= _WIN32_WINNT_WINXP)
      40  
      41  /* Avoid warnings from gcc -Wcast-function-type.  */
      42  #   define GetProcAddress \
      43       (void *) GetProcAddress
      44  
      45  /* CreateHardLink was introduced only in Windows 2000.  */
      46  typedef BOOL (WINAPI * CreateHardLinkFuncType) (LPCSTR lpFileName,
      47                                                  LPCSTR lpExistingFileName,
      48                                                  LPSECURITY_ATTRIBUTES lpSecurityAttributes);
      49  static CreateHardLinkFuncType CreateHardLinkFunc = NULL;
      50  static BOOL initialized = FALSE;
      51  
      52  static void
      53  initialize (void)
      54  {
      55    HMODULE kernel32 = GetModuleHandle ("kernel32.dll");
      56    if (kernel32 != NULL)
      57      {
      58        CreateHardLinkFunc =
      59          (CreateHardLinkFuncType) GetProcAddress (kernel32, "CreateHardLinkA");
      60      }
      61    initialized = TRUE;
      62  }
      63  
      64  #  else
      65  
      66  #   define CreateHardLinkFunc CreateHardLink
      67  
      68  #  endif
      69  
      70  int
      71  link (const char *file1, const char *file2)
      72  {
      73    char *dir;
      74    size_t len1 = strlen (file1);
      75    size_t len2 = strlen (file2);
      76  
      77  #  if !(_WIN32_WINNT >= _WIN32_WINNT_WINXP)
      78    if (!initialized)
      79      initialize ();
      80  #  endif
      81  
      82    if (CreateHardLinkFunc == NULL)
      83      {
      84        /* System does not support hard links.  */
      85        errno = EPERM;
      86        return -1;
      87      }
      88    /* Reject trailing slashes on non-directories; native Windows does not
      89       support hard-linking directories.  */
      90    if ((len1 && (file1[len1 - 1] == '/' || file1[len1 - 1] == '\\'))
      91        || (len2 && (file2[len2 - 1] == '/' || file2[len2 - 1] == '\\')))
      92      {
      93        /* If stat() fails, then link() should fail for the same reason.  */
      94        struct stat st;
      95        if (stat (file1, &st))
      96          {
      97            if (errno == EOVERFLOW)
      98              /* It's surely a file, not a directory (see stat-w32.c).  */
      99              errno = ENOTDIR;
     100            return -1;
     101          }
     102        if (!S_ISDIR (st.st_mode))
     103          errno = ENOTDIR;
     104        else
     105          errno = EPERM;
     106        return -1;
     107      }
     108    /* CreateHardLink("b/.","a",NULL) creates file "b", so we must check
     109       that dirname(file2) exists.  */
     110    dir = strdup (file2);
     111    if (!dir)
     112      return -1;
     113    {
     114      struct stat st;
     115      char *p = strchr (dir, '\0');
     116      while (dir < p && (*--p != '/' && *p != '\\'));
     117      *p = '\0';
     118      if (p != dir && stat (dir, &st) != 0 && errno != EOVERFLOW)
     119        {
     120          free (dir);
     121          return -1;
     122        }
     123      free (dir);
     124    }
     125    /* Now create the link.  */
     126    if (CreateHardLinkFunc (file2, file1, NULL) == 0)
     127      {
     128        /* It is not documented which errors CreateHardLink() can produce.
     129         * The following conversions are based on tests on a Windows XP SP2
     130         * system. */
     131        DWORD err = GetLastError ();
     132        switch (err)
     133          {
     134          case ERROR_ACCESS_DENIED:
     135            errno = EACCES;
     136            break;
     137  
     138          case ERROR_INVALID_FUNCTION:    /* fs does not support hard links */
     139            errno = EPERM;
     140            break;
     141  
     142          case ERROR_NOT_SAME_DEVICE:
     143            errno = EXDEV;
     144            break;
     145  
     146          case ERROR_PATH_NOT_FOUND:
     147          case ERROR_FILE_NOT_FOUND:
     148            errno = ENOENT;
     149            break;
     150  
     151          case ERROR_INVALID_PARAMETER:
     152            errno = ENAMETOOLONG;
     153            break;
     154  
     155          case ERROR_TOO_MANY_LINKS:
     156            errno = EMLINK;
     157            break;
     158  
     159          case ERROR_ALREADY_EXISTS:
     160            errno = EEXIST;
     161            break;
     162  
     163          default:
     164            errno = EIO;
     165          }
     166        return -1;
     167      }
     168  
     169    return 0;
     170  }
     171  
     172  # else /* !Windows */
     173  
     174  #  error "This platform lacks a link function, and Gnulib doesn't provide a replacement. This is a bug in Gnulib."
     175  
     176  # endif /* !Windows */
     177  #else /* HAVE_LINK */
     178  
     179  # undef link
     180  
     181  /* Create a hard link from FILE1 to FILE2, working around platform bugs.  */
     182  int
     183  rpl_link (char const *file1, char const *file2)
     184  {
     185    size_t len1;
     186    size_t len2;
     187    struct stat st;
     188  
     189    /* Don't allow IRIX to dereference dangling file2 symlink.  */
     190    if (lstat (file2, &st) == 0 || errno == EOVERFLOW)
     191      {
     192        errno = EEXIST;
     193        return -1;
     194      }
     195  
     196    /* Reject trailing slashes on non-directories.  */
     197    len1 = strlen (file1);
     198    len2 = strlen (file2);
     199    if ((len1 && file1[len1 - 1] == '/')
     200        || (len2 && file2[len2 - 1] == '/'))
     201      {
     202        /* Let link() decide whether hard-linking directories is legal.
     203           If stat() fails, then link() should fail for the same reason
     204           (although on Solaris 9, link("file/","oops") mistakenly
     205           succeeds); if stat() succeeds, require a directory.  */
     206        if (stat (file1, &st))
     207          return -1;
     208        if (!S_ISDIR (st.st_mode))
     209          {
     210            errno = ENOTDIR;
     211            return -1;
     212          }
     213      }
     214    else
     215      {
     216        /* Fix Cygwin 1.5.x bug where link("a","b/.") creates file "b".  */
     217        char *dir = strdup (file2);
     218        char *p;
     219        if (!dir)
     220          return -1;
     221        /* We already know file2 does not end in slash.  Strip off the
     222           basename, then check that the dirname exists.  */
     223        p = strrchr (dir, '/');
     224        if (p)
     225          {
     226            *p = '\0';
     227            if (stat (dir, &st) != 0 && errno != EOVERFLOW)
     228              {
     229                free (dir);
     230                return -1;
     231              }
     232          }
     233        free (dir);
     234      }
     235    return link (file1, file2);
     236  }
     237  #endif /* HAVE_LINK */