Mercurial > hg > octave-kai > gnulib-hg
annotate lib/linkat.c @ 17249:e542fd46ad6f
maint: update all copyright year number ranges
Run "make update-copyright". Compare to commit 1602f0a from last year.
Signed-off-by: Eric Blake <eblake@redhat.com>
author | Eric Blake <eblake@redhat.com> |
---|---|
date | Tue, 01 Jan 2013 00:50:58 +0000 (2013-01-01) |
parents | 8250f2777afc |
children |
rev | line source |
---|---|
12068 | 1 /* Create a hard link relative to open directories. |
17249
e542fd46ad6f
maint: update all copyright year number ranges
Eric Blake <eblake@redhat.com>
parents:
16201
diff
changeset
|
2 Copyright (C) 2009-2013 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 | |
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
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 | |
12120 | 46 #if !HAVE_LINKAT |
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 |
12068 | 198 /* Create a link to FILE1, in the directory open on descriptor FD1, to FILE2, |
199 in the directory open on descriptor FD2. If FILE1 is a symlink, FLAG | |
200 controls whether to dereference FILE1 first. If possible, do it without | |
201 changing the working directory. Otherwise, resort to using | |
202 save_cwd/fchdir, then rename/restore_cwd. If either the save_cwd or | |
203 the restore_cwd fails, then give a diagnostic and exit nonzero. */ | |
204 | |
205 int | |
206 linkat (int fd1, char const *file1, int fd2, char const *file2, int flag) | |
207 { | |
208 if (flag & ~AT_SYMLINK_FOLLOW) | |
209 { | |
210 errno = EINVAL; | |
211 return -1; | |
212 } | |
213 return at_func2 (fd1, file1, fd2, file2, | |
214 flag ? link_follow : link_immediate); | |
215 } | |
12120 | 216 |
217 #else /* HAVE_LINKAT */ | |
218 | |
219 # undef linkat | |
220 | |
221 /* Create a link. If FILE1 is a symlink, create a hardlink to the | |
222 canonicalized file. */ | |
223 | |
224 static int | |
225 linkat_follow (int fd1, char const *file1, int fd2, char const *file2) | |
226 { | |
227 char *name = (char *) file1; | |
228 char *target; | |
229 int result; | |
230 int i = MAXSYMLINKS; | |
231 | |
232 /* There is no realpathat. */ | |
233 while (i-- && (target = areadlinkat (fd1, name))) | |
234 { | |
235 if (IS_ABSOLUTE_FILE_NAME (target)) | |
236 { | |
237 if (name != file1) | |
238 free (name); | |
239 name = target; | |
240 } | |
241 else | |
242 { | |
243 char *dir = mdir_name (name); | |
244 if (name != file1) | |
245 free (name); | |
246 if (!dir) | |
247 { | |
248 free (target); | |
249 errno = ENOMEM; | |
250 return -1; | |
251 } | |
252 name = mfile_name_concat (dir, target, NULL); | |
253 free (dir); | |
254 free (target); | |
255 if (!name) | |
256 { | |
257 errno = ENOMEM; | |
258 return -1; | |
259 } | |
260 } | |
261 } | |
262 if (i < 0) | |
263 { | |
264 target = NULL; | |
265 errno = ELOOP; | |
266 } | |
267 if (!target && errno != EINVAL) | |
268 { | |
269 if (name != file1) | |
270 { | |
271 int saved_errno = errno; | |
272 free (name); | |
273 errno = saved_errno; | |
274 } | |
275 return -1; | |
276 } | |
277 result = linkat (fd1, name, fd2, file2, 0); | |
278 if (name != file1) | |
279 { | |
280 int saved_errno = errno; | |
281 free (name); | |
282 errno = saved_errno; | |
283 } | |
284 return result; | |
285 } | |
286 | |
287 | |
288 /* Like linkat, but guarantee that AT_SYMLINK_FOLLOW works even on | |
289 older Linux kernels. */ | |
290 | |
291 int | |
292 rpl_linkat (int fd1, char const *file1, int fd2, char const *file2, int flag) | |
293 { | |
294 if (flag & ~AT_SYMLINK_FOLLOW) | |
295 { | |
296 errno = EINVAL; | |
297 return -1; | |
298 } | |
299 | |
14379
2330aac2ae54
maint: adjust cpp indentation to reflect nesting depth
Jim Meyering <meyering@redhat.com>
parents:
14079
diff
changeset
|
300 # if LINKAT_TRAILING_SLASH_BUG |
13514
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
301 /* Reject trailing slashes on non-directories. */ |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
302 { |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
303 size_t len1 = strlen (file1); |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
304 size_t len2 = strlen (file2); |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
305 if ((len1 && file1[len1 - 1] == '/') |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
306 || (len2 && file2[len2 - 1] == '/')) |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
307 { |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
308 /* 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
|
309 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
|
310 if fstatat() succeeds, require a directory. */ |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
311 struct stat st; |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
312 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
|
313 return -1; |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
314 if (!S_ISDIR (st.st_mode)) |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
315 { |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
316 errno = ENOTDIR; |
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 } |
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 } |
14379
2330aac2ae54
maint: adjust cpp indentation to reflect nesting depth
Jim Meyering <meyering@redhat.com>
parents:
14079
diff
changeset
|
321 # endif |
13514
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 if (!flag) |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
324 return linkat (fd1, file1, fd2, file2, flag); |
6154fce586b6
linkat: Work around AIX 7.1 bug.
Bruno Haible <bruno@clisp.org>
parents:
12559
diff
changeset
|
325 |
12120 | 326 /* Cache the information on whether the system call really works. */ |
327 { | |
328 static int have_follow_really; /* 0 = unknown, 1 = yes, -1 = no */ | |
329 if (0 <= have_follow_really) | |
330 { | |
331 int result = linkat (fd1, file1, fd2, file2, flag); | |
332 if (!(result == -1 && errno == EINVAL)) | |
333 { | |
334 have_follow_really = 1; | |
335 return result; | |
336 } | |
337 have_follow_really = -1; | |
338 } | |
339 } | |
340 return linkat_follow (fd1, file1, fd2, file2); | |
341 } | |
342 | |
343 #endif /* HAVE_LINKAT */ |