tests: tighten link, rmdir, and remove tests
[gnulib.git] / tests / test-link.h
1 /* Test of link() function.
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 2 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 /* This file is designed to test both link(a,b) and
18    linkat(AT_FDCWD,a,AT_FDCWD,b,0).  FUNC is the function to test.
19    Assumes that BASE and ASSERT are already defined, and that
20    appropriate headers are already included.  If PRINT, warn before
21    skipping tests with status 77.  This test does not exercise link on
22    symlinks.  */
23
24 static int
25 test_link (int (*func) (char const *, char const *), bool print)
26 {
27   int fd;
28   int ret;
29
30   /* Create first file.  */
31   fd = open (BASE "a", O_CREAT | O_EXCL | O_WRONLY, 0600);
32   ASSERT (0 <= fd);
33   ASSERT (write (fd, "hello", 5) == 5);
34   ASSERT (close (fd) == 0);
35
36   /* Not all file systems support link.  Mingw doesn't have reliable
37      st_nlink on hard links, but our implementation does fail with
38      EPERM on poor file systems, and we can detect the inferior stat()
39      via st_ino.  Cygwin 1.5.x copies rather than links files on those
40      file systems, but there, st_nlink and st_ino are reliable.  */
41   ret = func (BASE "a", BASE "b");
42   if (!ret)
43   {
44     struct stat st;
45     ASSERT (stat (BASE "b", &st) == 0);
46     if (st.st_ino && st.st_nlink != 2)
47       {
48         ASSERT (unlink (BASE "b") == 0);
49         errno = EPERM;
50         ret = -1;
51       }
52   }
53   if (ret == -1)
54     {
55       /* If the device does not support hard links, errno is
56          EPERM on Linux, EOPNOTSUPP on FreeBSD.  */
57       switch (errno)
58         {
59         case EPERM:
60         case EOPNOTSUPP:
61           if (print)
62             fputs ("skipping test: "
63                    "hard links not supported on this file system\n",
64                    stderr);
65           ASSERT (unlink (BASE "a") == 0);
66           return 77;
67         default:
68           perror ("link");
69           return 1;
70         }
71     }
72   ASSERT (ret == 0);
73
74   /* Now, for some behavior tests.  Modify the contents of 'b', and
75      ensure that 'a' can see it, both while 'b' exists and after.  */
76   fd = open (BASE "b", O_APPEND | O_WRONLY);
77   ASSERT (0 <= fd);
78   ASSERT (write (fd, "world", 5) == 5);
79   ASSERT (close (fd) == 0);
80   {
81     char buf[11] = { 0 };
82     fd = open (BASE "a", O_RDONLY);
83     ASSERT (0 <= fd);
84     ASSERT (read (fd, buf, 10) == 10);
85     ASSERT (strcmp (buf, "helloworld") == 0);
86     ASSERT (close (fd) == 0);
87     ASSERT (unlink (BASE "b") == 0);
88     fd = open (BASE "a", O_RDONLY);
89     ASSERT (0 <= fd);
90     ASSERT (read (fd, buf, 10) == 10);
91     ASSERT (strcmp (buf, "helloworld") == 0);
92     ASSERT (close (fd) == 0);
93   }
94
95   /* Test for various error conditions.  */
96   ASSERT (mkdir (BASE "d", 0700) == 0);
97   errno = 0;
98   ASSERT (func (BASE "a", ".") == -1);
99   ASSERT (errno == EEXIST || errno == EINVAL);
100   errno = 0;
101   ASSERT (func (BASE "a", BASE "a") == -1);
102   ASSERT (errno == EEXIST);
103   ASSERT (func (BASE "a", BASE "b") == 0);
104   errno = 0;
105   ASSERT (func (BASE "a", BASE "b") == -1);
106   ASSERT (errno == EEXIST);
107   errno = 0;
108   ASSERT (func (BASE "a", BASE "d") == -1);
109   ASSERT (errno == EEXIST);
110   errno = 0;
111   ASSERT (func (BASE "c", BASE "e") == -1);
112   ASSERT (errno == ENOENT);
113   errno = 0;
114   ASSERT (func (BASE "a", BASE "c/.") == -1);
115   ASSERT (errno == ENOENT);
116   errno = 0;
117   ASSERT (func (BASE "a/", BASE "c") == -1);
118   ASSERT (errno == ENOTDIR);
119   errno = 0;
120   ASSERT (func (BASE "a", BASE "c/") == -1);
121   ASSERT (errno == ENOTDIR || errno == ENOENT);
122
123   /* Most platforms reject hard links to directories, and even on
124      those that do permit it, most users can't create them.  We assume
125      that if this test is run as root and we managed to create a hard
126      link, then unlink better be able to clean it up.  */
127   {
128     int result;
129     errno = 0;
130     result = func (BASE "d", BASE "c");
131     if (result == 0)
132       {
133         /* Probably root on Solaris.  */
134         ASSERT (unlink (BASE "c") == 0);
135       }
136     else
137       {
138         /* Most everyone else.  */
139         ASSERT (errno == EPERM || errno == EACCES);
140         errno = 0;
141         ASSERT (func (BASE "d/.", BASE "c") == -1);
142         ASSERT (errno == EPERM || errno == EACCES || errno == EINVAL);
143         errno = 0;
144         ASSERT (func (BASE "d/.//", BASE "c") == -1);
145         ASSERT (errno == EPERM || errno == EACCES || errno == EINVAL);
146       }
147   }
148
149   /* Clean up.  */
150   ASSERT (unlink (BASE "a") == 0);
151   ASSERT (unlink (BASE "b") == 0);
152   errno = 0;
153   ASSERT (unlink (BASE "c") == -1);
154   ASSERT (errno == ENOENT);
155   ASSERT (rmdir (BASE "d") == 0);
156
157   return 0;
158 }