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 program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3, or (at your option)
8    any later version.
9 
10    This program 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 General Public License for more details.
14 
15    You should have received a copy of the GNU 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
initialize(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
link(const char * file1,const char * file2)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         int saved_errno = errno;
121         free (dir);
122         errno = saved_errno;
123         return -1;
124       }
125     free (dir);
126   }
127   /* Now create the link.  */
128   if (CreateHardLinkFunc (file2, file1, NULL) == 0)
129     {
130       /* It is not documented which errors CreateHardLink() can produce.
131        * The following conversions are based on tests on a Windows XP SP2
132        * system. */
133       DWORD err = GetLastError ();
134       switch (err)
135         {
136         case ERROR_ACCESS_DENIED:
137           errno = EACCES;
138           break;
139 
140         case ERROR_INVALID_FUNCTION:    /* fs does not support hard links */
141           errno = EPERM;
142           break;
143 
144         case ERROR_NOT_SAME_DEVICE:
145           errno = EXDEV;
146           break;
147 
148         case ERROR_PATH_NOT_FOUND:
149         case ERROR_FILE_NOT_FOUND:
150           errno = ENOENT;
151           break;
152 
153         case ERROR_INVALID_PARAMETER:
154           errno = ENAMETOOLONG;
155           break;
156 
157         case ERROR_TOO_MANY_LINKS:
158           errno = EMLINK;
159           break;
160 
161         case ERROR_ALREADY_EXISTS:
162           errno = EEXIST;
163           break;
164 
165         default:
166           errno = EIO;
167         }
168       return -1;
169     }
170 
171   return 0;
172 }
173 
174 # else /* !Windows */
175 
176 #  error "This platform lacks a link function, and Gnulib doesn't provide a replacement. This is a bug in Gnulib."
177 
178 # endif /* !Windows */
179 #else /* HAVE_LINK */
180 
181 # undef link
182 
183 /* Create a hard link from FILE1 to FILE2, working around platform bugs.  */
184 int
rpl_link(char const * file1,char const * file2)185 rpl_link (char const *file1, char const *file2)
186 {
187   size_t len1;
188   size_t len2;
189   struct stat st;
190 
191   /* Don't allow IRIX to dereference dangling file2 symlink.  */
192   if (lstat (file2, &st) == 0 || errno == EOVERFLOW)
193     {
194       errno = EEXIST;
195       return -1;
196     }
197 
198   /* Reject trailing slashes on non-directories.  */
199   len1 = strlen (file1);
200   len2 = strlen (file2);
201   if ((len1 && file1[len1 - 1] == '/')
202       || (len2 && file2[len2 - 1] == '/'))
203     {
204       /* Let link() decide whether hard-linking directories is legal.
205          If stat() fails, then link() should fail for the same reason
206          (although on Solaris 9, link("file/","oops") mistakenly
207          succeeds); if stat() succeeds, require a directory.  */
208       if (stat (file1, &st))
209         return -1;
210       if (!S_ISDIR (st.st_mode))
211         {
212           errno = ENOTDIR;
213           return -1;
214         }
215     }
216   else
217     {
218       /* Fix Cygwin 1.5.x bug where link("a","b/.") creates file "b".  */
219       char *dir = strdup (file2);
220       char *p;
221       if (!dir)
222         return -1;
223       /* We already know file2 does not end in slash.  Strip off the
224          basename, then check that the dirname exists.  */
225       p = strrchr (dir, '/');
226       if (p)
227         {
228           *p = '\0';
229           if (stat (dir, &st) != 0 && errno != EOVERFLOW)
230             {
231               int saved_errno = errno;
232               free (dir);
233               errno = saved_errno;
234               return -1;
235             }
236         }
237       free (dir);
238     }
239   return link (file1, file2);
240 }
241 #endif /* HAVE_LINK */
242