2 Copyright (C) 2009-2024 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 <ebb9@byu.net>, 2009. */
23 #include "signature.h"
24 SIGNATURE_CHECK (linkat
, int, (int, char const *, int, char const *, int));
33 #include "areadlink.h"
34 #include "filenamecat.h"
35 #include "same-inode.h"
36 #include "ignore-value.h"
39 #define BASE "test-linkat.t"
41 #include "test-link.h"
43 static int dfd1
= AT_FDCWD
;
44 static int dfd2
= AT_FDCWD
;
45 static int flag
= AT_SYMLINK_FOLLOW
;
47 /* Wrapper to test linkat like link. */
49 do_link (char const *name1
, char const *name2
)
51 return linkat (dfd1
, name1
, dfd2
, name2
, flag
);
54 /* Can we expect that link() and linkat(), when called on a symlink,
55 increment the link count of that symlink? */
56 #if LINK_FOLLOWS_SYMLINKS == 0
57 # define EXPECT_LINK_HARDLINKS_SYMLINKS 1
58 #elif LINK_FOLLOWS_SYMLINKS == -1
60 # define EXPECT_LINK_HARDLINKS_SYMLINKS (__xpg4 == 0)
62 # define EXPECT_LINK_HARDLINKS_SYMLINKS 0
65 /* Wrapper to see if two symlinks act the same. */
67 check_same_link (char const *name1
, char const *name2
)
73 ASSERT (lstat (name1
, &st1
) == 0);
74 ASSERT (lstat (name2
, &st2
) == 0);
75 contents1
= areadlink_with_size (name1
, st1
.st_size
);
76 contents2
= areadlink_with_size (name2
, st2
.st_size
);
79 ASSERT (strcmp (contents1
, contents2
) == 0);
80 if (EXPECT_LINK_HARDLINKS_SYMLINKS
)
81 ASSERT (psame_inode (&st1
, &st2
));
94 /* Clean up any trash from prior testsuite runs. */
95 ignore_value (system ("rm -rf " BASE
"*"));
97 /* Test behaviour for invalid file descriptors. */
100 ASSERT (linkat (-1, "foo", AT_FDCWD
, "bar", 0) == -1);
101 ASSERT (errno
== EBADF
);
106 ASSERT (linkat (99, "foo", AT_FDCWD
, "bar", 0) == -1);
107 ASSERT (errno
== EBADF
);
109 ASSERT (close (creat (BASE
"oo", 0600)) == 0);
112 ASSERT (linkat (AT_FDCWD
, BASE
"oo", -1, "bar", 0) == -1);
113 ASSERT (errno
== EBADF
);
117 ASSERT (linkat (AT_FDCWD
, BASE
"oo", 99, "bar", 0) == -1);
118 ASSERT (errno
== EBADF
);
120 ASSERT (unlink (BASE
"oo") == 0);
122 /* Test basic link functionality, without mentioning symlinks. */
123 result
= test_link (do_link
, true);
124 dfd1
= open (".", O_RDONLY
);
126 ASSERT (test_link (do_link
, false) == result
);
128 ASSERT (test_link (do_link
, false) == result
);
130 ASSERT (test_link (do_link
, false) == result
);
132 ASSERT (test_link (do_link
, false) == result
);
134 ASSERT (test_link (do_link
, false) == result
);
136 ASSERT (test_link (do_link
, false) == result
);
137 ASSERT (close (dfd1
) == 0);
139 ASSERT (test_link (do_link
, false) == result
);
141 /* Skip the rest of the test if the file system does not support hard links
144 return test_exit_status
? test_exit_status
: result
;
146 /* Create locations to manipulate. */
147 ASSERT (mkdir (BASE
"sub1", 0700) == 0);
148 ASSERT (mkdir (BASE
"sub2", 0700) == 0);
149 ASSERT (close (creat (BASE
"00", 0600)) == 0);
150 cwd
= getcwd (NULL
, 0);
153 dfd
= open (BASE
"sub1", O_RDONLY
);
155 ASSERT (chdir (BASE
"sub2") == 0);
157 /* There are 16 possible scenarios, based on whether an fd is
158 AT_FDCWD or real, whether a file is absolute or relative, coupled
159 with whether flag is set for 32 iterations.
161 To ensure that we test all of the code paths (rather than
162 triggering early normalization optimizations), we use a loop to
163 repeatedly rename a file in the parent directory, use an fd open
164 on subdirectory 1, all while executing in subdirectory 2; all
165 relative names are thus given with a leading "../". Finally, the
166 last scenario (two relative paths given, neither one AT_FDCWD)
167 has two paths, based on whether the two fds are equivalent, so we
168 do the other variant after the loop. */
169 for (i
= 0; i
< 32; i
++)
171 int fd1
= (i
& 8) ? dfd
: AT_FDCWD
;
172 char *file1
= mfile_name_concat ((i
& 4) ? ".." : cwd
, BASE
"xx", NULL
);
173 int fd2
= (i
& 2) ? dfd
: AT_FDCWD
;
174 char *file2
= mfile_name_concat ((i
& 1) ? ".." : cwd
, BASE
"xx", NULL
);
177 flag
= (i
& 0x10 ? AT_SYMLINK_FOLLOW
: 0);
179 ASSERT (sprintf (strchr (file1
, '\0') - 2, "%02d", i
) == 2);
180 ASSERT (sprintf (strchr (file2
, '\0') - 2, "%02d", i
+ 1) == 2);
181 ASSERT (linkat (fd1
, file1
, fd2
, file2
, flag
) == 0);
182 ASSERT (unlinkat (fd1
, file1
, 0) == 0);
186 dfd2
= open ("..", O_RDONLY
);
188 ASSERT (linkat (dfd
, "../" BASE
"32", dfd2
, BASE
"33", 0) == 0);
189 ASSERT (linkat (dfd
, "../" BASE
"33", dfd2
, BASE
"34",
190 AT_SYMLINK_FOLLOW
) == 0);
191 ASSERT (close (dfd2
) == 0);
193 /* Now we change back to the parent directory, and set dfd to ".",
194 in order to test behavior on symlinks. */
195 ASSERT (chdir ("..") == 0);
196 ASSERT (close (dfd
) == 0);
197 if (symlink (BASE
"sub1", BASE
"link1"))
199 ASSERT (unlink (BASE
"32") == 0);
200 ASSERT (unlink (BASE
"33") == 0);
201 ASSERT (unlink (BASE
"34") == 0);
202 ASSERT (rmdir (BASE
"sub1") == 0);
203 ASSERT (rmdir (BASE
"sub2") == 0);
205 if (test_exit_status
!= EXIT_SUCCESS
)
206 return test_exit_status
;
207 fputs ("skipping test: symlinks not supported on this file system\n",
211 dfd
= open (".", O_RDONLY
);
213 ASSERT (symlink (BASE
"34", BASE
"link2") == 0);
214 ASSERT (symlink (BASE
"link3", BASE
"link3") == 0);
215 ASSERT (symlink (BASE
"nowhere", BASE
"link4") == 0);
217 /* Link cannot overwrite existing files. */
219 ASSERT (linkat (dfd
, BASE
"link1", dfd
, BASE
"sub1", 0) == -1);
220 ASSERT (errno
== EEXIST
);
222 ASSERT (linkat (dfd
, BASE
"link1/", dfd
, BASE
"sub1", 0) == -1);
223 ASSERT (errno
== EEXIST
|| errno
== EPERM
|| errno
== EACCES
);
225 ASSERT (linkat (dfd
, BASE
"link1", dfd
, BASE
"sub1/", 0) == -1);
226 ASSERT (errno
== EEXIST
|| errno
== ENOTDIR
);
228 ASSERT (linkat (dfd
, BASE
"link1", dfd
, BASE
"sub1",
229 AT_SYMLINK_FOLLOW
) == -1);
230 ASSERT (errno
== EEXIST
|| errno
== EPERM
|| errno
== EACCES
);
232 ASSERT (linkat (dfd
, BASE
"link1/", dfd
, BASE
"sub1",
233 AT_SYMLINK_FOLLOW
) == -1);
234 ASSERT (errno
== EEXIST
|| errno
== EPERM
|| errno
== EACCES
237 ASSERT (linkat (dfd
, BASE
"link1", dfd
, BASE
"sub1/",
238 AT_SYMLINK_FOLLOW
) == -1);
239 ASSERT (errno
== EEXIST
|| errno
== EPERM
|| errno
== EACCES
242 ASSERT (linkat (dfd
, BASE
"link1", dfd
, BASE
"link2", 0) == -1);
243 ASSERT (errno
== EEXIST
);
245 ASSERT (linkat (dfd
, BASE
"link1", dfd
, BASE
"link2",
246 AT_SYMLINK_FOLLOW
) == -1);
247 ASSERT (errno
== EEXIST
|| errno
== EPERM
|| errno
== EACCES
);
249 ASSERT (linkat (dfd
, BASE
"link1", dfd
, BASE
"link3", 0) == -1);
250 ASSERT (errno
== EEXIST
|| errno
== ELOOP
);
252 ASSERT (linkat (dfd
, BASE
"link1", dfd
, BASE
"link3",
253 AT_SYMLINK_FOLLOW
) == -1);
254 ASSERT (errno
== EEXIST
|| errno
== EPERM
|| errno
== EACCES
257 ASSERT (linkat (dfd
, BASE
"link2", dfd
, BASE
"link3", 0) == -1);
258 ASSERT (errno
== EEXIST
|| errno
== ELOOP
);
260 ASSERT (linkat (dfd
, BASE
"link2", dfd
, BASE
"link3",
261 AT_SYMLINK_FOLLOW
) == -1);
262 ASSERT (errno
== EEXIST
|| errno
== ELOOP
);
264 /* AT_SYMLINK_FOLLOW only follows first argument, not second. */
266 ASSERT (linkat (dfd
, BASE
"link1", dfd
, BASE
"link4", 0) == -1);
267 ASSERT (errno
== EEXIST
);
268 ASSERT (linkat (dfd
, BASE
"link1", dfd
, BASE
"link4",
269 AT_SYMLINK_FOLLOW
) == -1);
270 ASSERT (errno
== EEXIST
|| errno
== EPERM
|| errno
== EACCES
);
272 ASSERT (linkat (dfd
, BASE
"34", dfd
, BASE
"link4", 0) == -1);
273 ASSERT (errno
== EEXIST
);
275 ASSERT (linkat (dfd
, BASE
"34", dfd
, BASE
"link4", AT_SYMLINK_FOLLOW
) == -1);
276 ASSERT (errno
== EEXIST
);
278 /* Trailing slash handling. */
280 ASSERT (linkat (dfd
, BASE
"link2/", dfd
, BASE
"link5", 0) == -1);
281 ASSERT (errno
== ENOTDIR
);
283 ASSERT (linkat (dfd
, BASE
"link2/", dfd
, BASE
"link5",
284 AT_SYMLINK_FOLLOW
) == -1);
285 ASSERT (errno
== ENOTDIR
|| errno
== EINVAL
);
287 ASSERT (linkat (dfd
, BASE
"link3/", dfd
, BASE
"link5", 0) == -1);
288 ASSERT (errno
== ELOOP
);
290 ASSERT (linkat (dfd
, BASE
"link3/", dfd
, BASE
"link5",
291 AT_SYMLINK_FOLLOW
) == -1);
292 ASSERT (errno
== ELOOP
|| errno
== EINVAL
);
294 ASSERT (linkat (dfd
, BASE
"link4/", dfd
, BASE
"link5", 0) == -1);
295 ASSERT (errno
== ENOENT
);
297 ASSERT (linkat (dfd
, BASE
"link4/", dfd
, BASE
"link5",
298 AT_SYMLINK_FOLLOW
) == -1);
299 ASSERT (errno
== ENOENT
|| errno
== EINVAL
);
301 /* Check for hard links to symlinks. */
302 ASSERT (linkat (dfd
, BASE
"link1", dfd
, BASE
"link5", 0) == 0);
303 check_same_link (BASE
"link1", BASE
"link5");
304 ASSERT (unlink (BASE
"link5") == 0);
306 ASSERT (linkat (dfd
, BASE
"link1", dfd
, BASE
"link5",
307 AT_SYMLINK_FOLLOW
) == -1);
308 ASSERT (errno
== EPERM
|| errno
== EACCES
);
309 ASSERT (linkat (dfd
, BASE
"link2", dfd
, BASE
"link5", 0) == 0);
310 check_same_link (BASE
"link2", BASE
"link5");
311 ASSERT (unlink (BASE
"link5") == 0);
312 ASSERT (linkat (dfd
, BASE
"link2", dfd
, BASE
"file", AT_SYMLINK_FOLLOW
) == 0);
314 ASSERT (areadlink (BASE
"file") == NULL
);
315 ASSERT (errno
== EINVAL
);
316 ASSERT (unlink (BASE
"file") == 0);
317 ASSERT (linkat (dfd
, BASE
"link3", dfd
, BASE
"link5", 0) == 0);
318 check_same_link (BASE
"link3", BASE
"link5");
319 ASSERT (unlink (BASE
"link5") == 0);
321 ASSERT (linkat (dfd
, BASE
"link3", dfd
, BASE
"link5",
322 AT_SYMLINK_FOLLOW
) == -1);
323 ASSERT (errno
== ELOOP
);
324 ASSERT (linkat (dfd
, BASE
"link4", dfd
, BASE
"link5", 0) == 0);
325 check_same_link (BASE
"link4", BASE
"link5");
326 ASSERT (unlink (BASE
"link5") == 0);
328 ASSERT (linkat (dfd
, BASE
"link4", dfd
, BASE
"link5",
329 AT_SYMLINK_FOLLOW
) == -1);
330 ASSERT (errno
== ENOENT
);
332 /* Check that symlink to symlink to file is followed all the way. */
333 ASSERT (symlink (BASE
"link2", BASE
"link5") == 0);
334 ASSERT (linkat (dfd
, BASE
"link5", dfd
, BASE
"link6", 0) == 0);
335 check_same_link (BASE
"link5", BASE
"link6");
336 ASSERT (unlink (BASE
"link6") == 0);
337 ASSERT (linkat (dfd
, BASE
"link5", dfd
, BASE
"file", AT_SYMLINK_FOLLOW
) == 0);
339 ASSERT (areadlink (BASE
"file") == NULL
);
340 ASSERT (errno
== EINVAL
);
341 ASSERT (unlink (BASE
"file") == 0);
342 ASSERT (unlink (BASE
"link5") == 0);
343 ASSERT (symlink (BASE
"link3", BASE
"link5") == 0);
345 ASSERT (linkat (dfd
, BASE
"link5", dfd
, BASE
"file",
346 AT_SYMLINK_FOLLOW
) == -1);
347 ASSERT (errno
== ELOOP
);
348 ASSERT (unlink (BASE
"link5") == 0);
349 ASSERT (symlink (BASE
"link4", BASE
"link5") == 0);
351 ASSERT (linkat (dfd
, BASE
"link5", dfd
, BASE
"file",
352 AT_SYMLINK_FOLLOW
) == -1);
353 ASSERT (errno
== ENOENT
);
355 /* Now for some real fun with directory crossing. */
356 ASSERT (symlink (cwd
, BASE
"sub1/link") == 0);
357 ASSERT (symlink (".././/" BASE
"sub1/link/" BASE
"link2",
358 BASE
"sub2/link") == 0);
359 ASSERT (close (dfd
) == 0);
360 dfd
= open (BASE
"sub1", O_RDONLY
);
362 dfd2
= open (BASE
"sub2", O_RDONLY
);
364 ASSERT (linkat (dfd
, "../" BASE
"sub2/link", dfd2
, "./..//" BASE
"sub1/file",
365 AT_SYMLINK_FOLLOW
) == 0);
367 ASSERT (areadlink (BASE
"sub1/file") == NULL
);
368 ASSERT (errno
== EINVAL
);
371 ASSERT (close (dfd
) == 0);
372 ASSERT (close (dfd2
) == 0);
373 ASSERT (unlink (BASE
"sub1/file") == 0);
374 ASSERT (unlink (BASE
"sub1/link") == 0);
375 ASSERT (unlink (BASE
"sub2/link") == 0);
376 ASSERT (unlink (BASE
"32") == 0);
377 ASSERT (unlink (BASE
"33") == 0);
378 ASSERT (unlink (BASE
"34") == 0);
379 ASSERT (rmdir (BASE
"sub1") == 0);
380 ASSERT (rmdir (BASE
"sub2") == 0);
381 ASSERT (unlink (BASE
"link1") == 0);
382 ASSERT (unlink (BASE
"link2") == 0);
383 ASSERT (unlink (BASE
"link3") == 0);
384 ASSERT (unlink (BASE
"link4") == 0);
385 ASSERT (unlink (BASE
"link5") == 0);
388 return test_exit_status
;