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