fchmod-tests, fchmodat tests, lchmod tests: Add more tests.
[gnulib.git] / lib / linkat.c
blob6c1c54f327d88128f31422187059bdaf628a6245
1 /* Create a hard link relative to open directories.
2 Copyright (C) 2009-2021 Free Software Foundation, Inc.
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.
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.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* written by Eric Blake */
19 #include <config.h>
21 #include <unistd.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <limits.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/stat.h>
30 #include "areadlink.h"
31 #include "dirname.h"
32 #include "eloop-threshold.h"
33 #include "filenamecat.h"
34 #include "openat-priv.h"
36 #if !HAVE_LINKAT || LINKAT_SYMLINK_NOTSUP
38 /* Create a link. If FILE1 is a symlink, either create a hardlink to
39 that symlink, or fake it by creating an identical symlink. */
40 # if LINK_FOLLOWS_SYMLINKS == 0
41 # define link_immediate link
42 # else
43 static int
44 link_immediate (char const *file1, char const *file2)
46 char *target = areadlink (file1);
47 if (target)
49 /* A symlink cannot be modified in-place. Therefore, creating
50 an identical symlink behaves like a hard link to a symlink,
51 except for incorrect st_ino and st_nlink. However, we must
52 be careful of EXDEV. */
53 struct stat st1;
54 struct stat st2;
55 char *dir = mdir_name (file2);
56 if (!dir)
58 free (target);
59 errno = ENOMEM;
60 return -1;
62 if (lstat (file1, &st1) == 0 && stat (dir, &st2) == 0)
64 if (st1.st_dev == st2.st_dev)
66 int result = symlink (target, file2);
67 int saved_errno = errno;
68 free (target);
69 free (dir);
70 errno = saved_errno;
71 return result;
73 free (target);
74 free (dir);
75 errno = EXDEV;
76 return -1;
78 free (target);
79 free (dir);
81 if (errno == ENOMEM)
82 return -1;
83 return link (file1, file2);
85 # endif /* LINK_FOLLOWS_SYMLINKS == 0 */
87 /* Create a link. If FILE1 is a symlink, create a hardlink to the
88 canonicalized file. */
89 # if 0 < LINK_FOLLOWS_SYMLINKS
90 # define link_follow link
91 # else
92 static int
93 link_follow (char const *file1, char const *file2)
95 char *name = (char *) file1;
96 char *target;
97 int result;
98 int i = __eloop_threshold ();
100 /* Using realpath or canonicalize_file_name is too heavy-handed: we
101 don't need an absolute name, and we don't need to resolve
102 intermediate symlinks, just the basename of each iteration. */
103 while (i-- && (target = areadlink (name)))
105 if (IS_ABSOLUTE_FILE_NAME (target))
107 if (name != file1)
108 free (name);
109 name = target;
111 else
113 char *dir = mdir_name (name);
114 if (name != file1)
115 free (name);
116 if (!dir)
118 free (target);
119 errno = ENOMEM;
120 return -1;
122 name = mfile_name_concat (dir, target, NULL);
123 free (dir);
124 free (target);
125 if (!name)
127 errno = ENOMEM;
128 return -1;
132 if (i < 0)
134 target = NULL;
135 errno = ELOOP;
137 if (!target && errno != EINVAL)
139 if (name != file1)
141 int saved_errno = errno;
142 free (name);
143 errno = saved_errno;
145 return -1;
147 result = link (name, file2);
148 if (name != file1)
150 int saved_errno = errno;
151 free (name);
152 errno = saved_errno;
154 return result;
156 # endif /* 0 < LINK_FOLLOWS_SYMLINKS */
158 /* On Solaris, link() doesn't follow symlinks by default, but does so as soon
159 as a library or executable takes part in the program that has been compiled
160 with "c99" or "cc -xc99=all" or "cc ... /usr/lib/values-xpg4.o ...". */
161 # if LINK_FOLLOWS_SYMLINKS == -1
163 /* Reduce the penalty of link_immediate and link_follow by incorporating the
164 knowledge that link()'s behaviour depends on the __xpg4 variable. */
165 extern int __xpg4;
167 static int
168 solaris_optimized_link_immediate (char const *file1, char const *file2)
170 if (__xpg4 == 0)
171 return link (file1, file2);
172 return link_immediate (file1, file2);
175 static int
176 solaris_optimized_link_follow (char const *file1, char const *file2)
178 if (__xpg4 != 0)
179 return link (file1, file2);
180 return link_follow (file1, file2);
183 # define link_immediate solaris_optimized_link_immediate
184 # define link_follow solaris_optimized_link_follow
186 # endif
188 #endif /* !HAVE_LINKAT || LINKAT_SYMLINK_NOTSUP */
190 #if !HAVE_LINKAT
192 /* Create a link to FILE1, in the directory open on descriptor FD1, to FILE2,
193 in the directory open on descriptor FD2. If FILE1 is a symlink, FLAG
194 controls whether to dereference FILE1 first. If possible, do it without
195 changing the working directory. Otherwise, resort to using
196 save_cwd/fchdir, then rename/restore_cwd. If either the save_cwd or
197 the restore_cwd fails, then give a diagnostic and exit nonzero. */
200 linkat (int fd1, char const *file1, int fd2, char const *file2, int flag)
202 if (flag & ~AT_SYMLINK_FOLLOW)
204 errno = EINVAL;
205 return -1;
207 return at_func2 (fd1, file1, fd2, file2,
208 flag ? link_follow : link_immediate);
211 #else /* HAVE_LINKAT */
213 # undef linkat
215 /* Create a link. If FILE1 is a symlink, create a hardlink to the
216 canonicalized file. */
218 static int
219 linkat_follow (int fd1, char const *file1, int fd2, char const *file2)
221 char *name = (char *) file1;
222 char *target;
223 int result;
224 int i = __eloop_threshold ();
226 /* There is no realpathat. */
227 while (i-- && (target = areadlinkat (fd1, name)))
229 if (IS_ABSOLUTE_FILE_NAME (target))
231 if (name != file1)
232 free (name);
233 name = target;
235 else
237 char *dir = mdir_name (name);
238 if (name != file1)
239 free (name);
240 if (!dir)
242 free (target);
243 errno = ENOMEM;
244 return -1;
246 name = mfile_name_concat (dir, target, NULL);
247 free (dir);
248 free (target);
249 if (!name)
251 errno = ENOMEM;
252 return -1;
256 if (i < 0)
258 target = NULL;
259 errno = ELOOP;
261 if (!target && errno != EINVAL)
263 if (name != file1)
265 int saved_errno = errno;
266 free (name);
267 errno = saved_errno;
269 return -1;
271 result = linkat (fd1, name, fd2, file2, 0);
272 if (name != file1)
274 int saved_errno = errno;
275 free (name);
276 errno = saved_errno;
278 return result;
282 /* Like linkat, but guarantee that AT_SYMLINK_FOLLOW works even on
283 older Linux kernels. */
286 rpl_linkat (int fd1, char const *file1, int fd2, char const *file2, int flag)
288 if (flag & ~AT_SYMLINK_FOLLOW)
290 errno = EINVAL;
291 return -1;
294 # if LINKAT_TRAILING_SLASH_BUG
295 /* Reject trailing slashes on non-directories. */
297 size_t len1 = strlen (file1);
298 size_t len2 = strlen (file2);
299 if ((len1 && file1[len1 - 1] == '/')
300 || (len2 && file2[len2 - 1] == '/'))
302 /* Let linkat() decide whether hard-linking directories is legal.
303 If fstatat() fails, then linkat() should fail for the same reason;
304 if fstatat() succeeds, require a directory. */
305 struct stat st;
306 if (fstatat (fd1, file1, &st, flag ? 0 : AT_SYMLINK_NOFOLLOW))
307 return -1;
308 if (!S_ISDIR (st.st_mode))
310 errno = ENOTDIR;
311 return -1;
315 # endif
317 if (!flag)
319 int result = linkat (fd1, file1, fd2, file2, flag);
320 # if LINKAT_SYMLINK_NOTSUP
321 /* OS X 10.10 has linkat() but it doesn't support
322 hardlinks to symlinks. Fallback to our emulation
323 in that case. */
324 if (result == -1 && (errno == ENOTSUP || errno == EOPNOTSUPP))
325 return at_func2 (fd1, file1, fd2, file2, link_immediate);
326 # endif
327 return result;
330 /* Cache the information on whether the system call really works. */
332 static int have_follow_really; /* 0 = unknown, 1 = yes, -1 = no */
333 if (0 <= have_follow_really)
335 int result = linkat (fd1, file1, fd2, file2, flag);
336 if (!(result == -1 && errno == EINVAL))
338 have_follow_really = 1;
339 return result;
341 have_follow_really = -1;
344 return linkat_follow (fd1, file1, fd2, file2);
347 #endif /* HAVE_LINKAT */