regex: Fix fastmap for multibyte character ranges.
[gnulib.git] / tests / test-lchown.h
1 /* Tests of lchown.
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 #ifndef TEST_CHOWN_NAP
20 /* Sleep long enough to notice a timestamp difference on the file
21    system in the current directory.  */
22 static void
23 nap (void)
24 {
25   static long delay;
26   if (!delay)
27     {
28       /* Initialize only once, by sleeping for 20 milliseconds (needed
29          since xfs has a quantization of about 10 milliseconds, even
30          though it has a granularity of 1 nanosecond, and since NTFS
31          has a default quantization of 15.25 milliseconds, even though
32          it has a granularity of 100 nanoseconds).  If the seconds
33          differ, repeat the test one more time (in case we crossed a
34          quantization boundary on a file system with 1 second
35          resolution).  If we can't observe a difference in only the
36          nanoseconds, then fall back to 1 second if the time is odd,
37          and 2 seconds (needed for FAT) if time is even.  */
38       struct stat st1;
39       struct stat st2;
40       ASSERT (close (creat (BASE "tmp", 0600)) == 0);
41       ASSERT (stat (BASE "tmp", &st1) == 0);
42       ASSERT (unlink (BASE "tmp") == 0);
43       delay = 20000;
44       usleep (delay);
45       ASSERT (close (creat (BASE "tmp", 0600)) == 0);
46       ASSERT (stat (BASE "tmp", &st2) == 0);
47       ASSERT (unlink (BASE "tmp") == 0);
48       if (st1.st_mtime != st2.st_mtime)
49         {
50           /* Seconds differ, give it one more shot.  */
51           st1 = st2;
52           usleep (delay);
53           ASSERT (close (creat (BASE "tmp", 0600)) == 0);
54           ASSERT (stat (BASE "tmp", &st2) == 0);
55           ASSERT (unlink (BASE "tmp") == 0);
56         }
57       if (! (st1.st_mtime == st2.st_mtime
58              && get_stat_mtime_ns (&st1) < get_stat_mtime_ns (&st2)))
59         delay = (st1.st_mtime & 1) ? 1000000 : 2000000;
60     }
61   usleep (delay);
62 }
63 #endif /* !TEST_CHOWN_NAP */
64
65 #if !HAVE_GETEGID
66 # define getegid() (-1)
67 #endif
68
69 #ifndef HAVE_LCHMOD
70 # define HAVE_LCHMOD 0
71 #endif
72
73 #ifndef CHOWN_CHANGE_TIME_BUG
74 # define CHOWN_CHANGE_TIME_BUG 0
75 #endif
76
77 /* This file is designed to test lchown(n,o,g) and
78    chownat(AT_FDCWD,n,o,g,AT_SYMLINK_NOFOLLOW).  FUNC is the function
79    to test.  Assumes that BASE and ASSERT are already defined, and
80    that appropriate headers are already included.  If PRINT, warn
81    before skipping symlink tests with status 77.  */
82
83 static int
84 test_lchown (int (*func) (char const *, uid_t, gid_t), bool print)
85 {
86   struct stat st1;
87   struct stat st2;
88   gid_t *gids = NULL;
89   int gids_count;
90   int result;
91
92   /* Solaris 8 is interesting - if the current process belongs to
93      multiple groups, the current directory is owned by a a group that
94      the current process belongs to but different than getegid(), and
95      the current directory does not have the S_ISGID bit, then regular
96      files created in the directory belong to the directory's group,
97      but symlinks belong to the current effective group id.  If
98      S_ISGID is set, then both files and symlinks belong to the
99      directory's group.  However, it is possible to run the testsuite
100      from within a directory owned by a group we don't belong to, in
101      which case all things that we create belong to the current
102      effective gid.  So, work around the issues by creating a
103      subdirectory (we are guaranteed that the subdirectory will be
104      owned by one of our current groups), change ownership of that
105      directory to the current effective gid (which will thus succeed),
106      then create all other files within that directory (eliminating
107      questions on whether inheritance or current id triumphs, since
108      the two methods resolve to the same gid).  */
109   ASSERT (mkdir (BASE "dir", 0700) == 0);
110   ASSERT (stat (BASE "dir", &st1) == 0);
111
112   /* Filter out mingw, which has no concept of groups.  */
113   result = func (BASE "dir", st1.st_uid, getegid ());
114   if (result == -1 && errno == ENOSYS)
115     {
116       ASSERT (rmdir (BASE "dir") == 0);
117       if (print)
118         fputs ("skipping test: no support for ownership\n", stderr);
119       return 77;
120     }
121   ASSERT (result == 0);
122
123   ASSERT (close (creat (BASE "dir/file", 0600)) == 0);
124   ASSERT (stat (BASE "dir/file", &st1) == 0);
125   ASSERT (st1.st_uid != -1);
126   ASSERT (st1.st_gid != -1);
127   ASSERT (st1.st_gid == getegid ());
128
129   /* Sanity check of error cases.  */
130   errno = 0;
131   ASSERT (func ("", -1, -1) == -1);
132   ASSERT (errno == ENOENT);
133   errno = 0;
134   ASSERT (func ("no_such", -1, -1) == -1);
135   ASSERT (errno == ENOENT);
136   errno = 0;
137   ASSERT (func ("no_such/", -1, -1) == -1);
138   ASSERT (errno == ENOENT);
139   errno = 0;
140   ASSERT (func (BASE "dir/file/", -1, -1) == -1);
141   ASSERT (errno == ENOTDIR);
142
143   /* Check that -1 does not alter ownership.  */
144   ASSERT (func (BASE "dir/file", -1, st1.st_gid) == 0);
145   ASSERT (func (BASE "dir/file", st1.st_uid, -1) == 0);
146   ASSERT (stat (BASE "dir/file", &st2) == 0);
147   ASSERT (st1.st_uid == st2.st_uid);
148   ASSERT (st1.st_gid == st2.st_gid);
149
150   /* Even if the values aren't changing, ctime is required to change
151      if at least one argument is not -1.  */
152   nap ();
153   ASSERT (func (BASE "dir/file", st1.st_uid, st1.st_gid) == 0);
154   ASSERT (stat (BASE "dir/file", &st2) == 0);
155   ASSERT (st1.st_ctime < st2.st_ctime
156           || (st1.st_ctime == st2.st_ctime
157               && get_stat_ctime_ns (&st1) < get_stat_ctime_ns (&st2)));
158
159   /* Test symlink behavior.  */
160   if (symlink ("link", BASE "dir/link2"))
161     {
162       ASSERT (unlink (BASE "dir/file") == 0);
163       ASSERT (rmdir (BASE "dir") == 0);
164       if (print)
165         fputs ("skipping test: symlinks not supported on this file system\n",
166                stderr);
167       return 77;
168     }
169   result = func (BASE "dir/link2", -1, -1);
170   if (result == -1 && errno == ENOSYS)
171     {
172       ASSERT (unlink (BASE "dir/file") == 0);
173       ASSERT (unlink (BASE "dir/link2") == 0);
174       ASSERT (rmdir (BASE "dir") == 0);
175       if (print)
176         fputs ("skipping test: symlink ownership not supported\n", stderr);
177       return 77;
178     }
179   ASSERT (result == 0);
180   errno = 0;
181   ASSERT (func (BASE "dir/link2/", st1.st_uid, st1.st_gid) == -1);
182   ASSERT (errno == ENOENT);
183   ASSERT (symlink ("file", BASE "dir/link") == 0);
184   ASSERT (mkdir (BASE "dir/sub", 0700) == 0);
185   ASSERT (symlink ("sub", BASE "dir/link3") == 0);
186
187   /* For non-privileged users, lchown can only portably succeed at
188      changing group ownership of a file we own.  If we belong to at
189      least two groups, then verifying the correct change is simple.
190      But if we belong to only one group, then we fall back on the
191      other observable effect of lchown: the ctime must be updated.
192      Be careful of duplicates returned by getgroups.  */
193   gids_count = mgetgroups (NULL, -1, &gids);
194   if (2 <= gids_count && gids[0] == gids[1] && 2 < gids_count--)
195     gids[1] = gids[2];
196   if (1 < gids_count || (gids_count == 1 && gids[0] != st1.st_gid))
197     {
198       if (gids[0] == st1.st_gid)
199         {
200           ASSERT (1 < gids_count);
201           ASSERT (gids[0] != gids[1]);
202           gids[0] = gids[1];
203         }
204       ASSERT (gids[0] != st1.st_gid);
205       ASSERT (gids[0] != -1);
206       ASSERT (lstat (BASE "dir/link", &st2) == 0);
207       ASSERT (st1.st_uid == st2.st_uid);
208       ASSERT (st1.st_gid == st2.st_gid);
209       ASSERT (lstat (BASE "dir/link2", &st2) == 0);
210       ASSERT (st1.st_uid == st2.st_uid);
211       ASSERT (st1.st_gid == st2.st_gid);
212
213       errno = 0;
214       ASSERT (func (BASE "dir/link2/", -1, gids[0]) == -1);
215       ASSERT (errno == ENOTDIR);
216       ASSERT (stat (BASE "dir/file", &st2) == 0);
217       ASSERT (st1.st_uid == st2.st_uid);
218       ASSERT (st1.st_gid == st2.st_gid);
219       ASSERT (lstat (BASE "dir/link", &st2) == 0);
220       ASSERT (st1.st_uid == st2.st_uid);
221       ASSERT (st1.st_gid == st2.st_gid);
222       ASSERT (lstat (BASE "dir/link2", &st2) == 0);
223       ASSERT (st1.st_uid == st2.st_uid);
224       ASSERT (st1.st_gid == st2.st_gid);
225
226       ASSERT (func (BASE "dir/link2", -1, gids[0]) == 0);
227       ASSERT (stat (BASE "dir/file", &st2) == 0);
228       ASSERT (st1.st_uid == st2.st_uid);
229       ASSERT (st1.st_gid == st2.st_gid);
230       ASSERT (lstat (BASE "dir/link", &st2) == 0);
231       ASSERT (st1.st_uid == st2.st_uid);
232       ASSERT (st1.st_gid == st2.st_gid);
233       ASSERT (lstat (BASE "dir/link2", &st2) == 0);
234       ASSERT (st1.st_uid == st2.st_uid);
235       ASSERT (gids[0] == st2.st_gid);
236
237       /* Trailing slash follows through to directory.  */
238       ASSERT (lstat (BASE "dir/link3", &st2) == 0);
239       ASSERT (st1.st_uid == st2.st_uid);
240       ASSERT (st1.st_gid == st2.st_gid);
241       ASSERT (lstat (BASE "dir/sub", &st2) == 0);
242       ASSERT (st1.st_uid == st2.st_uid);
243       ASSERT (st1.st_gid == st2.st_gid);
244
245       ASSERT (func (BASE "dir/link3/", -1, gids[0]) == 0);
246       ASSERT (lstat (BASE "dir/link3", &st2) == 0);
247       ASSERT (st1.st_uid == st2.st_uid);
248       ASSERT (st1.st_gid == st2.st_gid);
249       ASSERT (lstat (BASE "dir/sub", &st2) == 0);
250       ASSERT (st1.st_uid == st2.st_uid);
251       ASSERT (gids[0] == st2.st_gid);
252     }
253   else if (!CHOWN_CHANGE_TIME_BUG || HAVE_LCHMOD)
254     {
255       /* If we don't have lchmod, and lchown fails to change ctime,
256          then we can't test this part of lchown.  */
257       struct stat l1;
258       struct stat l2;
259       ASSERT (stat (BASE "dir/file", &st1) == 0);
260       ASSERT (lstat (BASE "dir/link", &l1) == 0);
261       ASSERT (lstat (BASE "dir/link2", &l2) == 0);
262
263       nap ();
264       errno = 0;
265       ASSERT (func (BASE "dir/link2/", -1, st1.st_gid) == -1);
266       ASSERT (errno == ENOTDIR);
267       ASSERT (stat (BASE "dir/file", &st2) == 0);
268       ASSERT (st1.st_ctime == st2.st_ctime);
269       ASSERT (get_stat_ctime_ns (&st1) == get_stat_ctime_ns (&st2));
270       ASSERT (lstat (BASE "dir/link", &st2) == 0);
271       ASSERT (l1.st_ctime == st2.st_ctime);
272       ASSERT (get_stat_ctime_ns (&l1) == get_stat_ctime_ns (&st2));
273       ASSERT (lstat (BASE "dir/link2", &st2) == 0);
274       ASSERT (l2.st_ctime == st2.st_ctime);
275       ASSERT (get_stat_ctime_ns (&l2) == get_stat_ctime_ns (&st2));
276
277       ASSERT (func (BASE "dir/link2", -1, gids[0]) == 0);
278       ASSERT (stat (BASE "dir/file", &st2) == 0);
279       ASSERT (st1.st_ctime == st2.st_ctime);
280       ASSERT (get_stat_ctime_ns (&st1) == get_stat_ctime_ns (&st2));
281       ASSERT (lstat (BASE "dir/link", &st2) == 0);
282       ASSERT (l1.st_ctime == st2.st_ctime);
283       ASSERT (get_stat_ctime_ns (&l1) == get_stat_ctime_ns (&st2));
284       ASSERT (lstat (BASE "dir/link2", &st2) == 0);
285       ASSERT (l2.st_ctime < st2.st_ctime
286               || (l2.st_ctime == st2.st_ctime
287                   && get_stat_ctime_ns (&l2) < get_stat_ctime_ns (&st2)));
288
289       /* Trailing slash follows through to directory.  */
290       ASSERT (lstat (BASE "dir/sub", &st1) == 0);
291       ASSERT (lstat (BASE "dir/link3", &l1) == 0);
292       nap ();
293       ASSERT (func (BASE "dir/link3/", -1, gids[0]) == 0);
294       ASSERT (lstat (BASE "dir/link3", &st2) == 0);
295       ASSERT (l1.st_ctime == st2.st_ctime);
296       ASSERT (get_stat_ctime_ns (&l1) == get_stat_ctime_ns (&st2));
297       ASSERT (lstat (BASE "dir/sub", &st2) == 0);
298       ASSERT (st1.st_ctime < st2.st_ctime
299               || (st1.st_ctime == st2.st_ctime
300                   && get_stat_ctime_ns (&st1) < get_stat_ctime_ns (&st2)));
301     }
302
303   /* Cleanup.  */
304   free (gids);
305   ASSERT (unlink (BASE "dir/file") == 0);
306   ASSERT (unlink (BASE "dir/link") == 0);
307   ASSERT (unlink (BASE "dir/link2") == 0);
308   ASSERT (unlink (BASE "dir/link3") == 0);
309   ASSERT (rmdir (BASE "dir/sub") == 0);
310   ASSERT (rmdir (BASE "dir") == 0);
311   return 0;
312 }