link: detect FreeBSD bug
[gnulib.git] / tests / test-linkat.c
1 /* Tests of linkat.
2    Copyright (C) 2009 Free Software Foundation, Inc.
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 <ebb9@byu.net>, 2009.  */
18
19 #include <config.h>
20
21 #include <unistd.h>
22
23 #include <fcntl.h>
24 #include <errno.h>
25 #include <stdbool.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include "areadlink.h"
31 #include "filenamecat.h"
32 #include "same-inode.h"
33 #include "xgetcwd.h"
34
35 #define ASSERT(expr) \
36   do                                                                         \
37     {                                                                        \
38       if (!(expr))                                                           \
39         {                                                                    \
40           fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__);  \
41           fflush (stderr);                                                   \
42           abort ();                                                          \
43         }                                                                    \
44     }                                                                        \
45   while (0)
46
47 #define BASE "test-linkat.t"
48
49 #include "test-link.h"
50
51 static int dfd1 = AT_FDCWD;
52 static int dfd2 = AT_FDCWD;
53 static int flag = AT_SYMLINK_FOLLOW;
54
55 /* Wrapper to test linkat like link.  */
56 static int
57 do_link (char const *name1, char const *name2)
58 {
59   return linkat (dfd1, name1, dfd2, name2, flag);
60 }
61
62 /* Wrapper to see if two symlinks act the same.  */
63 static void
64 check_same_link (char const *name1, char const *name2)
65 {
66   struct stat st1;
67   struct stat st2;
68   char *contents1;
69   char *contents2;
70   ASSERT (lstat (name1, &st1) == 0);
71   ASSERT (lstat (name2, &st2) == 0);
72   contents1 = areadlink_with_size (name1, st1.st_size);
73   contents2 = areadlink_with_size (name2, st2.st_size);
74   ASSERT (contents1);
75   ASSERT (contents2);
76   ASSERT (strcmp (contents1, contents2) == 0);
77   if (!LINK_FOLLOWS_SYMLINKS)
78     ASSERT (SAME_INODE (st1, st2));
79   free (contents1);
80   free (contents2);
81 }
82
83 int
84 main (void)
85 {
86   int i;
87   int dfd;
88   char *cwd;
89   int result;
90
91   /* Clean up any trash from prior testsuite runs.  */
92   ASSERT (system ("rm -rf " BASE "*") == 0);
93
94   /* Test basic link functionality, without mentioning symlinks.  */
95   result = test_link (do_link, true);
96   dfd1 = open (".", O_RDONLY);
97   ASSERT (0 <= dfd1);
98   ASSERT (test_link (do_link, false) == result);
99   dfd2 = dfd1;
100   ASSERT (test_link (do_link, false) == result);
101   dfd1 = AT_FDCWD;
102   ASSERT (test_link (do_link, false) == result);
103   flag = 0;
104   ASSERT (test_link (do_link, false) == result);
105   dfd1 = dfd2;
106   ASSERT (test_link (do_link, false) == result);
107   dfd2 = AT_FDCWD;
108   ASSERT (test_link (do_link, false) == result);
109   ASSERT (close (dfd1) == 0);
110   dfd1 = AT_FDCWD;
111   ASSERT (test_link (do_link, false) == result);
112
113   /* Create locations to manipulate.  */
114   ASSERT (mkdir (BASE "sub1", 0700) == 0);
115   ASSERT (mkdir (BASE "sub2", 0700) == 0);
116   ASSERT (close (creat (BASE "00", 0600)) == 0);
117   cwd = xgetcwd ();
118
119   dfd = open (BASE "sub1", O_RDONLY);
120   ASSERT (0 <= dfd);
121   ASSERT (chdir (BASE "sub2") == 0);
122
123   /* There are 16 possible scenarios, based on whether an fd is
124      AT_FDCWD or real, whether a file is absolute or relative, coupled
125      with whether flag is set for 32 iterations.
126
127      To ensure that we test all of the code paths (rather than
128      triggering early normalization optimizations), we use a loop to
129      repeatedly rename a file in the parent directory, use an fd open
130      on subdirectory 1, all while executing in subdirectory 2; all
131      relative names are thus given with a leading "../".  Finally, the
132      last scenario (two relative paths given, neither one AT_FDCWD)
133      has two paths, based on whether the two fds are equivalent, so we
134      do the other variant after the loop.  */
135   for (i = 0; i < 32; i++)
136     {
137       int fd1 = (i & 8) ? dfd : AT_FDCWD;
138       char *file1 = file_name_concat ((i & 4) ? ".." : cwd, BASE "xx", NULL);
139       int fd2 = (i & 2) ? dfd : AT_FDCWD;
140       char *file2 = file_name_concat ((i & 1) ? ".." : cwd, BASE "xx", NULL);
141       flag = (i & 0x10 ? AT_SYMLINK_FOLLOW : 0);
142
143       ASSERT (sprintf (strchr (file1, '\0') - 2, "%02d", i) == 2);
144       ASSERT (sprintf (strchr (file2, '\0') - 2, "%02d", i + 1) == 2);
145       ASSERT (linkat (fd1, file1, fd2, file2, flag) == 0);
146       ASSERT (unlinkat (fd1, file1, 0) == 0);
147       free (file1);
148       free (file2);
149     }
150   dfd2 = open ("..", O_RDONLY);
151   ASSERT (0 <= dfd2);
152   ASSERT (linkat (dfd, "../" BASE "32", dfd2, BASE "33", 0) == 0);
153   ASSERT (linkat (dfd, "../" BASE "33", dfd2, BASE "34",
154                   AT_SYMLINK_FOLLOW) == 0);
155   ASSERT (close (dfd2) == 0);
156
157   /* Now we change back to the parent directory, and set dfd to ".",
158      in order to test behavior on symlinks.  */
159   ASSERT (chdir ("..") == 0);
160   ASSERT (close (dfd) == 0);
161   if (symlink (BASE "sub1", BASE "link1"))
162     {
163       ASSERT (unlink (BASE "32") == 0);
164       ASSERT (unlink (BASE "33") == 0);
165       ASSERT (unlink (BASE "34") == 0);
166       ASSERT (rmdir (BASE "sub1") == 0);
167       ASSERT (rmdir (BASE "sub2") == 0);
168       free (cwd);
169       if (!result)
170         fputs ("skipping test: symlinks not supported on this file system\n",
171                stderr);
172       return result;
173     }
174   dfd = open (".", O_RDONLY);
175   ASSERT (0 <= dfd);
176   ASSERT (symlink (BASE "34", BASE "link2") == 0);
177   ASSERT (symlink (BASE "link3", BASE "link3") == 0);
178   ASSERT (symlink (BASE "nowhere", BASE "link4") == 0);
179
180   /* Link cannot overwrite existing files.  */
181   errno = 0;
182   ASSERT (linkat (dfd, BASE "link1", dfd, BASE "sub1", 0) == -1);
183   ASSERT (errno == EEXIST);
184   errno = 0;
185   ASSERT (linkat (dfd, BASE "link1/", dfd, BASE "sub1", 0) == -1);
186   ASSERT (errno == EEXIST || errno == EPERM || errno == EACCES);
187   errno = 0;
188   ASSERT (linkat (dfd, BASE "link1", dfd, BASE "sub1/", 0) == -1);
189   ASSERT (errno == EEXIST);
190   errno = 0;
191   ASSERT (linkat (dfd, BASE "link1", dfd, BASE "sub1",
192                   AT_SYMLINK_FOLLOW) == -1);
193   ASSERT (errno == EEXIST || errno == EPERM || errno == EACCES);
194   errno = 0;
195   ASSERT (linkat (dfd, BASE "link1/", dfd, BASE "sub1",
196                   AT_SYMLINK_FOLLOW) == -1);
197   ASSERT (errno == EEXIST || errno == EPERM || errno == EACCES);
198   errno = 0;
199   ASSERT (linkat (dfd, BASE "link1", dfd, BASE "sub1/",
200                   AT_SYMLINK_FOLLOW) == -1);
201   ASSERT (errno == EEXIST || errno == EPERM || errno == EACCES);
202   errno = 0;
203   ASSERT (linkat (dfd, BASE "link1", dfd, BASE "link2", 0) == -1);
204   ASSERT (errno == EEXIST);
205   errno = 0;
206   ASSERT (linkat (dfd, BASE "link1", dfd, BASE "link2",
207                   AT_SYMLINK_FOLLOW) == -1);
208   ASSERT (errno == EEXIST || errno == EPERM || errno == EACCES);
209   errno = 0;
210   ASSERT (linkat (dfd, BASE "link1", dfd, BASE "link3", 0) == -1);
211   ASSERT (errno == EEXIST || errno == ELOOP);
212   errno = 0;
213   ASSERT (linkat (dfd, BASE "link1", dfd, BASE "link3",
214                   AT_SYMLINK_FOLLOW) == -1);
215   ASSERT (errno == EEXIST || errno == EPERM || errno == EACCES
216           || errno == ELOOP);
217   errno = 0;
218   ASSERT (linkat (dfd, BASE "link2", dfd, BASE "link3", 0) == -1);
219   ASSERT (errno == EEXIST || errno == ELOOP);
220   errno = 0;
221   ASSERT (linkat (dfd, BASE "link2", dfd, BASE "link3",
222                   AT_SYMLINK_FOLLOW) == -1);
223   ASSERT (errno == EEXIST || errno == ELOOP);
224
225   /* AT_SYMLINK_FOLLOW only follows first argument, not second.  */
226   errno = 0;
227   ASSERT (linkat (dfd, BASE "link1", dfd, BASE "link4", 0) == -1);
228   ASSERT (errno == EEXIST);
229   ASSERT (linkat (dfd, BASE "link1", dfd, BASE "link4",
230                   AT_SYMLINK_FOLLOW) == -1);
231   ASSERT (errno == EEXIST || errno == EPERM || errno == EACCES);
232   errno = 0;
233   ASSERT (linkat (dfd, BASE "34", dfd, BASE "link4", 0) == -1);
234   ASSERT (errno == EEXIST);
235   errno = 0;
236   ASSERT (linkat (dfd, BASE "34", dfd, BASE "link4", AT_SYMLINK_FOLLOW) == -1);
237   ASSERT (errno == EEXIST);
238
239   /* Trailing slash handling.  */
240   errno = 0;
241   ASSERT (linkat (dfd, BASE "link2/", dfd, BASE "link5", 0) == -1);
242   ASSERT (errno == ENOTDIR);
243   errno = 0;
244   ASSERT (linkat (dfd, BASE "link2/", dfd, BASE "link5",
245                   AT_SYMLINK_FOLLOW) == -1);
246   ASSERT (errno == ENOTDIR);
247   errno = 0;
248   ASSERT (linkat (dfd, BASE "link3/", dfd, BASE "link5", 0) == -1);
249   ASSERT (errno == ELOOP);
250   errno = 0;
251   ASSERT (linkat (dfd, BASE "link3/", dfd, BASE "link5",
252                   AT_SYMLINK_FOLLOW) == -1);
253   ASSERT (errno == ELOOP);
254   errno = 0;
255   ASSERT (linkat (dfd, BASE "link4/", dfd, BASE "link5", 0) == -1);
256   ASSERT (errno == ENOENT);
257   errno = 0;
258   ASSERT (linkat (dfd, BASE "link4/", dfd, BASE "link5",
259                   AT_SYMLINK_FOLLOW) == -1);
260   ASSERT (errno == ENOENT);
261
262   /* Check for hard links to symlinks.  */
263   ASSERT (linkat (dfd, BASE "link1", dfd, BASE "link5", 0) == 0);
264   check_same_link (BASE "link1", BASE "link5");
265   ASSERT (unlink (BASE "link5") == 0);
266   errno = 0;
267   ASSERT (linkat (dfd, BASE "link1", dfd, BASE "link5",
268                   AT_SYMLINK_FOLLOW) == -1);
269   ASSERT (errno == EPERM || errno == EACCES);
270   ASSERT (linkat (dfd, BASE "link2", dfd, BASE "link5", 0) == 0);
271   check_same_link (BASE "link2", BASE "link5");
272   ASSERT (unlink (BASE "link5") == 0);
273   ASSERT (linkat (dfd, BASE "link2", dfd, BASE "file", AT_SYMLINK_FOLLOW) == 0);
274   errno = 0;
275   ASSERT (areadlink (BASE "file") == NULL);
276   ASSERT (errno == EINVAL);
277   ASSERT (unlink (BASE "file") == 0);
278   ASSERT (linkat (dfd, BASE "link3", dfd, BASE "link5", 0) == 0);
279   check_same_link (BASE "link3", BASE "link5");
280   ASSERT (unlink (BASE "link5") == 0);
281   errno = 0;
282   ASSERT (linkat (dfd, BASE "link3", dfd, BASE "link5",
283                   AT_SYMLINK_FOLLOW) == -1);
284   ASSERT (errno == ELOOP);
285   ASSERT (linkat (dfd, BASE "link4", dfd, BASE "link5", 0) == 0);
286   check_same_link (BASE "link4", BASE "link5");
287   ASSERT (unlink (BASE "link5") == 0);
288   errno = 0;
289   ASSERT (linkat (dfd, BASE "link4", dfd, BASE "link5",
290                   AT_SYMLINK_FOLLOW) == -1);
291   ASSERT (errno == ENOENT);
292
293   /* Check that symlink to symlink to file is followed all the way.  */
294   ASSERT (symlink (BASE "link2", BASE "link5") == 0);
295   ASSERT (linkat (dfd, BASE "link5", dfd, BASE "link6", 0) == 0);
296   check_same_link (BASE "link5", BASE "link6");
297   ASSERT (unlink (BASE "link6") == 0);
298   ASSERT (linkat (dfd, BASE "link5", dfd, BASE "file", AT_SYMLINK_FOLLOW) == 0);
299   errno = 0;
300   ASSERT (areadlink (BASE "file") == NULL);
301   ASSERT (errno == EINVAL);
302   ASSERT (unlink (BASE "file") == 0);
303   ASSERT (unlink (BASE "link5") == 0);
304   ASSERT (symlink (BASE "link3", BASE "link5") == 0);
305   errno = 0;
306   ASSERT (linkat (dfd, BASE "link5", dfd, BASE "file",
307                   AT_SYMLINK_FOLLOW) == -1);
308   ASSERT (errno == ELOOP);
309   ASSERT (unlink (BASE "link5") == 0);
310   ASSERT (symlink (BASE "link4", BASE "link5") == 0);
311   errno = 0;
312   ASSERT (linkat (dfd, BASE "link5", dfd, BASE "file",
313                   AT_SYMLINK_FOLLOW) == -1);
314   ASSERT (errno == ENOENT);
315
316   /* Now for some real fun with directory crossing.  */
317   ASSERT (symlink (cwd, BASE "sub1/link") == 0);
318   ASSERT (symlink (".././/" BASE "sub1/link/" BASE "link2",
319                    BASE "sub2/link") == 0);
320   ASSERT (close (dfd) == 0);
321   dfd = open (BASE "sub1", O_RDONLY);
322   ASSERT (0 <= dfd);
323   dfd2 = open (BASE "sub2", O_RDONLY);
324   ASSERT (0 < dfd2);
325   ASSERT (linkat (dfd, "../" BASE "sub2/link", dfd2, "./..//" BASE "sub1/file",
326               AT_SYMLINK_FOLLOW) == 0);
327   errno = 0;
328   ASSERT (areadlink (BASE "sub1/file") == NULL);
329   ASSERT (errno == EINVAL);
330
331   /* Cleanup.  */
332   ASSERT (close (dfd) == 0);
333   ASSERT (close (dfd2) == 0);
334   ASSERT (unlink (BASE "sub1/file") == 0);
335   ASSERT (unlink (BASE "sub1/link") == 0);
336   ASSERT (unlink (BASE "sub2/link") == 0);
337   ASSERT (unlink (BASE "32") == 0);
338   ASSERT (unlink (BASE "33") == 0);
339   ASSERT (unlink (BASE "34") == 0);
340   ASSERT (rmdir (BASE "sub1") == 0);
341   ASSERT (rmdir (BASE "sub2") == 0);
342   ASSERT (unlink (BASE "link1") == 0);
343   ASSERT (unlink (BASE "link2") == 0);
344   ASSERT (unlink (BASE "link3") == 0);
345   ASSERT (unlink (BASE "link4") == 0);
346   ASSERT (unlink (BASE "link5") == 0);
347   free (cwd);
348   return result;
349 }