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