Bump to m4 1.4.19
[platform/upstream/m4.git] / tests / link.c
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
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 */