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