Commit | Line | Data |
---|---|---|
3243fffc MW |
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 Lesser 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 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 <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 */ |