tests: tighten link, rmdir, and remove tests
[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 ()
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, false);
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 flag = (i & 0x10 ? AT_SYMLINK_FOLLOW : 0);
138       int fd1 = (i & 8) ? dfd : AT_FDCWD;
139       char *file1 = file_name_concat ((i & 4) ? ".." : cwd, BASE "xx", NULL);
140       int fd2 = (i & 2) ? dfd : AT_FDCWD;
141       char *file2 = file_name_concat ((i & 1) ? ".." : cwd, BASE "xx", NULL);
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       fputs ("skipping test: symlinks not supported on this filesystem\n",
170              stderr);
171       return result;
172     }
173   dfd = open (".", O_RDONLY);
174   ASSERT (0 <= dfd);
175   ASSERT (symlink (BASE "34", BASE "link2") == 0);
176   ASSERT (symlink (BASE "link3", BASE "link3") == 0);
177   ASSERT (symlink (BASE "nowhere", BASE "link4") == 0);
178
179   /* Link cannot overwrite existing files.  */
180   errno = 0;
181   ASSERT (linkat (dfd, BASE "link1", dfd, BASE "sub1", 0) == -1);
182   ASSERT (errno == EEXIST);
183   errno = 0;
184   ASSERT (linkat (dfd, BASE "link1/", dfd, BASE "sub1", 0) == -1);
185   ASSERT (errno == EEXIST || errno == EPERM || errno == EACCES);
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",
191                   AT_SYMLINK_FOLLOW) == -1);
192   ASSERT (errno == EEXIST || errno == EPERM || errno == EACCES);
193   errno = 0;
194   ASSERT (linkat (dfd, BASE "link1/", dfd, BASE "sub1",
195                   AT_SYMLINK_FOLLOW) == -1);
196   ASSERT (errno == EEXIST || errno == EPERM || errno == EACCES);
197   errno = 0;
198   ASSERT (linkat (dfd, BASE "link1", dfd, BASE "sub1/",
199                   AT_SYMLINK_FOLLOW) == -1);
200   ASSERT (errno == EEXIST || errno == EPERM || errno == EACCES);
201   errno = 0;
202   ASSERT (linkat (dfd, BASE "link1", dfd, BASE "link2", 0) == -1);
203   ASSERT (errno == EEXIST);
204   errno = 0;
205   ASSERT (linkat (dfd, BASE "link1", dfd, BASE "link2",
206                   AT_SYMLINK_FOLLOW) == -1);
207   ASSERT (errno == EEXIST || errno == EPERM || errno == EACCES);
208   errno = 0;
209   ASSERT (linkat (dfd, BASE "link1", dfd, BASE "link3", 0) == -1);
210   ASSERT (errno == EEXIST || errno == ELOOP);
211   errno = 0;
212   ASSERT (linkat (dfd, BASE "link1", dfd, BASE "link3",
213                   AT_SYMLINK_FOLLOW) == -1);
214   ASSERT (errno == EEXIST || errno == EPERM || errno == EACCES
215           || errno == ELOOP);
216   errno = 0;
217   ASSERT (linkat (dfd, BASE "link2", dfd, BASE "link3", 0) == -1);
218   ASSERT (errno == EEXIST || errno == ELOOP);
219   errno = 0;
220   ASSERT (linkat (dfd, BASE "link2", dfd, BASE "link3",
221                   AT_SYMLINK_FOLLOW) == -1);
222   ASSERT (errno == EEXIST || errno == ELOOP);
223
224   /* AT_SYMLINK_FOLLOW only follows first argument, not second.  */
225   errno = 0;
226   ASSERT (linkat (dfd, BASE "link1", dfd, BASE "link4", 0) == -1);
227   ASSERT (errno == EEXIST);
228   ASSERT (linkat (dfd, BASE "link1", dfd, BASE "link4",
229                   AT_SYMLINK_FOLLOW) == -1);
230   ASSERT (errno == EEXIST || errno == EPERM || errno == EACCES);
231   errno = 0;
232   ASSERT (linkat (dfd, BASE "34", dfd, BASE "link4", 0) == -1);
233   ASSERT (errno == EEXIST);
234   errno = 0;
235   ASSERT (linkat (dfd, BASE "34", dfd, BASE "link4", AT_SYMLINK_FOLLOW) == -1);
236   ASSERT (errno == EEXIST);
237
238   /* Trailing slash handling.  */
239   errno = 0;
240   ASSERT (linkat (dfd, BASE "link2/", dfd, BASE "link5", 0) == -1);
241   ASSERT (errno == ENOTDIR);
242   errno = 0;
243   ASSERT (linkat (dfd, BASE "link2/", dfd, BASE "link5",
244                   AT_SYMLINK_FOLLOW) == -1);
245   ASSERT (errno == ENOTDIR);
246   errno = 0;
247   ASSERT (linkat (dfd, BASE "link3/", dfd, BASE "link5", 0) == -1);
248   ASSERT (errno == ELOOP);
249   errno = 0;
250   ASSERT (linkat (dfd, BASE "link3/", dfd, BASE "link5",
251                   AT_SYMLINK_FOLLOW) == -1);
252   ASSERT (errno == ELOOP);
253   errno = 0;
254   ASSERT (linkat (dfd, BASE "link4/", dfd, BASE "link5", 0) == -1);
255   ASSERT (errno == ENOENT);
256   errno = 0;
257   ASSERT (linkat (dfd, BASE "link4/", dfd, BASE "link5",
258                   AT_SYMLINK_FOLLOW) == -1);
259   ASSERT (errno == ENOENT);
260
261   /* Check for hard links to symlinks.  */
262   ASSERT (linkat (dfd, BASE "link1", dfd, BASE "link5", 0) == 0);
263   check_same_link (BASE "link1", BASE "link5");
264   ASSERT (unlink (BASE "link5") == 0);
265   errno = 0;
266   ASSERT (linkat (dfd, BASE "link1", dfd, BASE "link5",
267                   AT_SYMLINK_FOLLOW) == -1);
268   ASSERT (errno == EPERM || errno == EACCES);
269   ASSERT (linkat (dfd, BASE "link2", dfd, BASE "link5", 0) == 0);
270   check_same_link (BASE "link2", BASE "link5");
271   ASSERT (unlink (BASE "link5") == 0);
272   ASSERT (linkat (dfd, BASE "link2", dfd, BASE "file", AT_SYMLINK_FOLLOW) == 0);
273   errno = 0;
274   ASSERT (areadlink (BASE "file") == NULL);
275   ASSERT (errno == EINVAL);
276   ASSERT (unlink (BASE "file") == 0);
277   ASSERT (linkat (dfd, BASE "link3", dfd, BASE "link5", 0) == 0);
278   check_same_link (BASE "link3", BASE "link5");
279   ASSERT (unlink (BASE "link5") == 0);
280   errno = 0;
281   ASSERT (linkat (dfd, BASE "link3", dfd, BASE "link5",
282                   AT_SYMLINK_FOLLOW) == -1);
283   ASSERT (errno == ELOOP);
284   ASSERT (linkat (dfd, BASE "link4", dfd, BASE "link5", 0) == 0);
285   check_same_link (BASE "link4", BASE "link5");
286   ASSERT (unlink (BASE "link5") == 0);
287   errno = 0;
288   ASSERT (linkat (dfd, BASE "link4", dfd, BASE "link5",
289                   AT_SYMLINK_FOLLOW) == -1);
290   ASSERT (errno == ENOENT);
291
292   /* Check that symlink to symlink to file is followed all the way.  */
293   ASSERT (symlink (BASE "link2", BASE "link5") == 0);
294   ASSERT (linkat (dfd, BASE "link5", dfd, BASE "link6", 0) == 0);
295   check_same_link (BASE "link5", BASE "link6");
296   ASSERT (unlink (BASE "link6") == 0);
297   ASSERT (linkat (dfd, BASE "link5", dfd, BASE "file", AT_SYMLINK_FOLLOW) == 0);
298   errno = 0;
299   ASSERT (areadlink (BASE "file") == NULL);
300   ASSERT (errno == EINVAL);
301   ASSERT (unlink (BASE "file") == 0);
302   ASSERT (unlink (BASE "link5") == 0);
303   ASSERT (symlink (BASE "link3", BASE "link5") == 0);
304   errno = 0;
305   ASSERT (linkat (dfd, BASE "link5", dfd, BASE "file",
306                   AT_SYMLINK_FOLLOW) == -1);
307   ASSERT (errno == ELOOP);
308   ASSERT (unlink (BASE "link5") == 0);
309   ASSERT (symlink (BASE "link4", BASE "link5") == 0);
310   errno = 0;
311   ASSERT (linkat (dfd, BASE "link5", dfd, BASE "file",
312                   AT_SYMLINK_FOLLOW) == -1);
313   ASSERT (errno == ENOENT);
314
315   /* Now for some real fun with directory crossing.  */
316   ASSERT (symlink (cwd, BASE "sub1/link") == 0);
317   ASSERT (symlink (".././/" BASE "sub1/link/" BASE "link2",
318                    BASE "sub2/link") == 0);
319   ASSERT (close (dfd) == 0);
320   dfd = open (BASE "sub1", O_RDONLY);
321   ASSERT (0 <= dfd);
322   dfd2 = open (BASE "sub2", O_RDONLY);
323   ASSERT (0 < dfd2);
324   ASSERT (linkat (dfd, "../" BASE "sub2/link", dfd2, "./..//" BASE "sub1/file",
325               AT_SYMLINK_FOLLOW) == 0);
326   errno = 0;
327   ASSERT (areadlink (BASE "sub1/file") == NULL);
328   ASSERT (errno == EINVAL);
329
330   /* Cleanup.  */
331   ASSERT (close (dfd) == 0);
332   ASSERT (close (dfd2) == 0);
333   ASSERT (unlink (BASE "sub1/file") == 0);
334   ASSERT (unlink (BASE "sub1/link") == 0);
335   ASSERT (unlink (BASE "sub2/link") == 0);
336   ASSERT (unlink (BASE "32") == 0);
337   ASSERT (unlink (BASE "33") == 0);
338   ASSERT (unlink (BASE "34") == 0);
339   ASSERT (rmdir (BASE "sub1") == 0);
340   ASSERT (rmdir (BASE "sub2") == 0);
341   ASSERT (unlink (BASE "link1") == 0);
342   ASSERT (unlink (BASE "link2") == 0);
343   ASSERT (unlink (BASE "link3") == 0);
344   ASSERT (unlink (BASE "link4") == 0);
345   ASSERT (unlink (BASE "link5") == 0);
346   free (cwd);
347   return result;
348 }