Mercurial > gnulib
annotate lib/linkat.c @ 40057:b06060465f09
maint: Run 'make update-copyright'
author | Paul Eggert <eggert@cs.ucla.edu> |
---|---|
date | Tue, 01 Jan 2019 00:25:11 +0100 |
parents | 10eb9086bea0 |
children |
rev | line source |
---|---|
12068 | 1 /* Create a hard link relative to open directories. |
40057
b06060465f09
maint: Run 'make update-copyright'
Paul Eggert <eggert@cs.ucla.edu>
parents:
19484
diff
changeset
|
2 Copyright (C) 2009-2019 Free Software Foundation, Inc. |
12068 | 3 |
4 This program is free software: you can redistribute it and/or modify | |
5 it under the terms of the GNU General Public License as published by | |
6 the Free Software Foundation; either version 3 of the License, or | |
7 (at your option) any later version. | |
8 | |
9 This program is distributed in the hope that it will be useful, | |
10 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 GNU General Public License for more details. | |
13 | |
14 You should have received a copy of the GNU General Public License | |
19190 | 15 along with this program. If not, see <https://www.gnu.org/licenses/>. */ |
12068 | 16 |
17 /* written by Eric Blake */ | |
18 | |
19 #include <config.h> | |
20 | |
21 #include <unistd.h> | |
22 | |
23 #include <errno.h> | |
24 #include <fcntl.h> | |
25 #include <limits.h> | |
12120 | 26 #include <stdlib.h> |
27 #include <string.h> | |
12068 | 28 #include <sys/stat.h> |
29 | |
30 #include "areadlink.h" | |
31 #include "dirname.h" | |
32 #include "filenamecat.h" | |
33 #include "openat-priv.h" | |
34 | |
35 #if HAVE_SYS_PARAM_H | |
36 # include <sys/param.h> | |
37 #endif | |
38 #ifndef MAXSYMLINKS | |
39 # ifdef SYMLOOP_MAX | |
40 # define MAXSYMLINKS SYMLOOP_MAX | |
41 # else | |
42 # define MAXSYMLINKS 20 | |
43 # endif | |
44 #endif | |
45 | |
17787
461b92d0b2eb
linkat: wrap to handle symlinks on OS X 10.10
Pádraig Brady <P@draigBrady.com>
parents:
17576
diff
changeset
|
46 #if !HAVE_LINKAT || LINKAT_SYMLINK_NOTSUP |
12120 | 47 |
12068 | 48 /* Create a link. If FILE1 is a symlink, either create a hardlink to |
49 that symlink, or fake it by creating an identical symlink. */ | |
12120 | 50 # if LINK_FOLLOWS_SYMLINKS == 0 |
51 # define link_immediate link | |
52 # else | |
12068 | 53 static int |
54 link_immediate (char const *file1, char const *file2) | |
55 { | |
56 char *target = areadlink (file1); | |
57 if (target) | |
58 { | |
59 /* A symlink cannot be modified in-place. Therefore, creating | |
60 an identical symlink behaves like a hard link to a symlink, | |
61 except for incorrect st_ino and st_nlink. However, we must | |
62 be careful of EXDEV. */ | |
63 struct stat st1; | |
64 struct stat st2; | |
65 char *dir = mdir_name (file2); | |
66 if (!dir) | |
67 { | |
68 free (target); | |
69 errno = ENOMEM; | |
70 return -1; | |
71 } | |
72 if (lstat (file1, &st1) == 0 && stat (dir, &st2) == 0) | |
73 { | |
74 if (st1.st_dev == st2.st_dev) | |
75 { | |
76 int result = symlink (target, file2); | |
77 int saved_errno = errno; | |
78 free (target); | |
79 free (dir); | |
80 errno = saved_errno; | |
81 return result; | |
82 } | |
83 free (target); | |
84 free (dir); | |
85 errno = EXDEV; | |
86 return -1; | |
87 } | |
88 free (target); | |
89 free (dir); | |
90 } | |
91 if (errno == ENOMEM) | |
92 return -1; | |
93 return link (file1, file2); | |
94 } | |
12120 | 95 # endif /* LINK_FOLLOWS_SYMLINKS == 0 */ |
12068 | 96 |
97 /* Create a link. If FILE1 is a symlink, create a hardlink to the | |
98 canonicalized file. */ | |
12120 | 99 # if 0 < LINK_FOLLOWS_SYMLINKS |
100 # define link_follow link | |
101 # else | |
12068 | 102 static int |
103 link_follow (char const *file1, char const *file2) | |
104 { | |
105 char *name = (char *) file1; | |
106 char *target; | |
107 int result; | |
108 int i = MAXSYMLINKS; | |
109 | |
110 /* Using realpath or canonicalize_file_name is too heavy-handed: we | |
111 don't need an absolute name, and we don't need to resolve | |
112 intermediate symlinks, just the basename of each iteration. */ | |
113 while (i-- && (target = areadlink (name))) | |
114 { | |
115 if (IS_ABSOLUTE_FILE_NAME (target)) | |
116 { | |
117 if (name != file1) | |
118 free (name); | |
119 name = target; | |
120 } | |
121 else | |
122 { | |
123 char *dir = mdir_name (name); | |
124 if (name != file1) | |
125 free (name); | |
126 if (!dir) | |
127 { | |
128 free (target); | |
129 errno = ENOMEM; | |
130 return -1; | |
131 } | |
132 name = mfile_name_concat (dir, target, NULL); | |
133 free (dir); | |
134 free (target); | |
135 if (!name) | |
136 { | |
137 errno = ENOMEM; | |
138 return -1; | |
139 } | |
140 } | |
141 } | |
142 if (i < 0) | |
143 { | |
144 target = NULL; | |
145 errno = ELOOP; | |
146 } | |
147 if (!target && errno != EINVAL) | |
148 { | |
149 if (name != file1) | |
150 { | |
151 int saved_errno = errno; | |
152 free (name); | |
153 errno = saved_errno; | |
154 } | |
155 return -1; | |
156 } | |
157 result = link (name, file2); | |
158 if (name != file1) | |
159 { | |
160 int saved_errno = errno; | |
161 free (name); | |
162 errno = saved_errno; | |
163 } | |
164 return result; | |
165 } | |
12120 | 166 # endif /* 0 < LINK_FOLLOWS_SYMLINKS */ |
12068 | 167 |
14050
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
168 /* On Solaris, link() doesn't follow symlinks by default, but does so as soon |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
169 as a library or executable takes part in the program that has been compiled |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
170 with "c99" or "cc -xc99=all" or "cc ... /usr/lib/values-xpg4.o ...". */ |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
171 # if LINK_FOLLOWS_SYMLINKS == -1 |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
172 |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
173 /* Reduce the penalty of link_immediate and link_follow by incorporating the |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
174 knowledge that link()'s behaviour depends on the __xpg4 variable. */ |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
175 extern int __xpg4; |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
176 |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
177 static int |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
178 solaris_optimized_link_immediate (char const *file1, char const *file2) |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
179 { |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
180 if (__xpg4 == 0) |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
181 return link (file1, file2); |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
182 return link_immediate (file1, file2); |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
183 } |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
184 |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
185 static int |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
186 solaris_optimized_link_follow (char const *file1, char const *file2) |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
187 { |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
188 if (__xpg4 != 0) |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
189 return link (file1, file2); |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
190 return link_follow (file1, file2); |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
191 } |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
192 |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
193 # define link_immediate solaris_optimized_link_immediate |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
194 # define link_follow solaris_optimized_link_follow |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
195 |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
196 # endif |
6566818713fe
linkat: Make implementation robust against system behaviour variations.
Bruno Haible <bruno@clisp.org>
parents:
13514
diff
changeset
|
197 |
17787
461b92d0b2eb
linkat: wrap to handle symlinks on OS X 10.10
Pádraig Brady <P@draigBrady.com>
parents:
17576
diff
changeset
|
198 #endif /* !HAVE_LINKAT || LINKAT_SYMLINK_NOTSUP */ |
461b92d0b2eb
linkat: wrap to handle symlinks on OS X 10.10
Pádraig Brady <P@draigBrady.com>
parents:
17576
diff
changeset
|
199 |
461b92d0b2eb
linkat: wrap to handle symlinks on OS X 10.10
Pádraig Brady <P@draigBrady.com>
parents:
17576
diff
changeset
|
200 #if !HAVE_LINKAT |
461b92d0b2eb
linkat: wrap to handle symlinks on OS X 10.10
Pádraig Brady <P@draigBrady.com>
parents:
17576
diff
changeset
|
201 |
12068 | 202 /* Create a link to FILE1, in the directory open on descriptor FD1, to FILE2, |
203 in the directory open on descriptor FD2. If FILE1 is a symlink, FLAG | |
204 controls whether to dereference FILE1 first. If possible, do it without | |
205 changing the working directory. Otherwise, resort to using | |
206 save_cwd/fchdir, then rename/restore_cwd. If either the save_cwd or | |
207 the restore_cwd fails, then give a diagnostic and exit nonzero. */ | |
208 | |
209 int | |
210 linkat (int fd1, char const *file1, int fd2, char const *file2, int flag) | |
211 { | |
212 if (flag & ~AT_SYMLINK_FOLLOW) | |
213 { | |
214 errno = EINVAL; | |
215 return -1; | |
216 } | |
217 return at_func2 (fd1, file1, fd2, file2, | |
218 flag ? link_follow : link_immediate); | |
219 } | |
12120 | 220 |
221 #else /* HAVE_LINKAT */ | |
222 | |
223 # undef linkat | |
224 | |
225 /* Create a link. If FILE1 is a symlink, create a hardlink to the | |
226 canonicalized file. */ | |
227 | |
228 static int | |
229 linkat_follow (int fd1, char const *file1, int fd2, char const *file2) | |
230 { | |
231 char *name = (char *) file1; | |
232 char *target; | |
233 int result; | |
234 int i = MAXSYMLINKS; | |
235 | |
236 /* There is no realpathat. */ | |
237 while (i-- && (target = areadlinkat (fd1, name))) | |
238 { | |
239 if (IS_ABSOLUTE_FILE_NAME (target)) | |
240 { | |
241 if (name != file1) | |
242 free (name); | |
243 name = target; | |
244 } | |
245 else | |
246 { | |
247 char *dir = mdir_name (name); | |
248 if (name != file1) | |
249 free (name); | |
250 if (!dir) | |
251 { | |
252 free (target); | |
253 errno = ENOMEM; | |
254 return -1; | |
255 } | |
256 name = mfile_name_concat (dir, target, NULL); | |
257 free (dir); | |
258 free (target); | |
259 if (!name) | |
260 { | |
261 errno = ENOMEM; | |
262 return -1; | |
263 } | |
264 } | |
265 } | |
266 if (i < 0) | |
267 { | |
268 target = NULL; | |
269 errno = ELOOP; | |
270 } | |
271 if (!target && errno != EINVAL) | |
272 { | |
273 if (name != file1) | |
274 { | |
275 int saved_errno = errno; | |
276 free (name); | |
277 errno = saved_errno; | |
278 } | |
279 return -1; | |
280 } | |
281 result = linkat (fd1, name, fd2, file2, 0); | |
282 if (name != file1) | |
283 { | |
284 int saved_errno = errno; | |
285 free (name); | |
286 errno = saved_errno; | |
287 } | |
288 return result; | |
289 } | |
290 | |
291 | |
292 /* Like linkat, but guarantee that AT_SYMLINK_FOLLOW works even on | |
293 older Linux kernels. */ | |
294 | |
295 int | |
296 rpl_linkat (int fd1, char const *file1, int fd2, char const *file2, int flag) | |
297 { | |
298 if (flag & ~AT_SYMLINK_FOLLOW) | |
299 { | |
300 errno = EINVAL; | |
301 return -1; | |
302 } | |
303 | |
14379
2330aac2ae54
maint: adjust cpp indentation to reflect nesting depth
Jim Meyering <meyering@redhat.com>
parents:
14079
diff
changeset
|
304 # if LINKAT_TRAILING_SLASH_BUG |
13514
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
305 /* Reject trailing slashes on non-directories. */ |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
306 { |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
307 size_t len1 = strlen (file1); |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
308 size_t len2 = strlen (file2); |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
309 if ((len1 && file1[len1 - 1] == '/') |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
310 || (len2 && file2[len2 - 1] == '/')) |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
311 { |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
312 /* Let linkat() decide whether hard-linking directories is legal. |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
313 If fstatat() fails, then linkat() should fail for the same reason; |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
314 if fstatat() succeeds, require a directory. */ |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
315 struct stat st; |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
316 if (fstatat (fd1, file1, &st, flag ? 0 : AT_SYMLINK_NOFOLLOW)) |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
317 return -1; |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
318 if (!S_ISDIR (st.st_mode)) |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
319 { |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
320 errno = ENOTDIR; |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
321 return -1; |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
322 } |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
323 } |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
324 } |
14379
2330aac2ae54
maint: adjust cpp indentation to reflect nesting depth
Jim Meyering <meyering@redhat.com>
parents:
14079
diff
changeset
|
325 # endif |
13514
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
326 |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
327 if (!flag) |
17787
461b92d0b2eb
linkat: wrap to handle symlinks on OS X 10.10
Pádraig Brady <P@draigBrady.com>
parents:
17576
diff
changeset
|
328 { |
461b92d0b2eb
linkat: wrap to handle symlinks on OS X 10.10
Pádraig Brady <P@draigBrady.com>
parents:
17576
diff
changeset
|
329 int result = linkat (fd1, file1, fd2, file2, flag); |
461b92d0b2eb
linkat: wrap to handle symlinks on OS X 10.10
Pádraig Brady <P@draigBrady.com>
parents:
17576
diff
changeset
|
330 # if LINKAT_SYMLINK_NOTSUP |
461b92d0b2eb
linkat: wrap to handle symlinks on OS X 10.10
Pádraig Brady <P@draigBrady.com>
parents:
17576
diff
changeset
|
331 /* OS X 10.10 has linkat() but it doesn't support |
461b92d0b2eb
linkat: wrap to handle symlinks on OS X 10.10
Pádraig Brady <P@draigBrady.com>
parents:
17576
diff
changeset
|
332 hardlinks to symlinks. Fallback to our emulation |
461b92d0b2eb
linkat: wrap to handle symlinks on OS X 10.10
Pádraig Brady <P@draigBrady.com>
parents:
17576
diff
changeset
|
333 in that case. */ |
461b92d0b2eb
linkat: wrap to handle symlinks on OS X 10.10
Pádraig Brady <P@draigBrady.com>
parents:
17576
diff
changeset
|
334 if (result == -1 && (errno == ENOTSUP || errno == EOPNOTSUPP)) |
461b92d0b2eb
linkat: wrap to handle symlinks on OS X 10.10
Pádraig Brady <P@draigBrady.com>
parents:
17576
diff
changeset
|
335 return at_func2 (fd1, file1, fd2, file2, link_immediate); |
461b92d0b2eb
linkat: wrap to handle symlinks on OS X 10.10
Pádraig Brady <P@draigBrady.com>
parents:
17576
diff
changeset
|
336 # endif |
461b92d0b2eb
linkat: wrap to handle symlinks on OS X 10.10
Pádraig Brady <P@draigBrady.com>
parents:
17576
diff
changeset
|
337 return result; |
461b92d0b2eb
linkat: wrap to handle symlinks on OS X 10.10
Pádraig Brady <P@draigBrady.com>
parents:
17576
diff
changeset
|
338 } |
13514
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
339 |
12120 | 340 /* Cache the information on whether the system call really works. */ |
341 { | |
342 static int have_follow_really; /* 0 = unknown, 1 = yes, -1 = no */ | |
343 if (0 <= have_follow_really) | |
344 { | |
345 int result = linkat (fd1, file1, fd2, file2, flag); | |
346 if (!(result == -1 && errno == EINVAL)) | |
347 { | |
348 have_follow_really = 1; | |
349 return result; | |
350 } | |
351 have_follow_really = -1; | |
352 } | |
353 } | |
354 return linkat_follow (fd1, file1, fd2, file2); | |
355 } | |
356 | |
357 #endif /* HAVE_LINKAT */ |