root/maint/gnulib/lib/link.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. initialize
  2. link
  3. rpl_link

   1 /* Emulate link on platforms that lack it, namely native Windows platforms.
   2 
   3    Copyright (C) 2009-2021 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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 */

/* [previous][next][first][last][top][bottom][index][help] */