2 Copyright (C) 2009 Free Software Foundation, Inc.
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.
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.
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/>. */
17 /* Written by Eric Blake <ebb9@byu.net>, 2009. */
19 #ifndef TEST_CHOWN_NAP
20 /* Sleep long enough to notice a timestamp difference on the file
21 system in the current directory. */
26 /* Assume the worst case file system of FAT, which has a granularity
29 # else /* HAVE_USLEEP */
33 /* Initialize only once, by sleeping for 20 milliseconds (needed
34 since xfs has a quantization of about 10 milliseconds, even
35 though it has a granularity of 1 nanosecond, and since NTFS
36 has a default quantization of 15.25 milliseconds, even though
37 it has a granularity of 100 nanoseconds). If the seconds
38 differ, repeat the test one more time (in case we crossed a
39 quantization boundary on a file system with 1 second
40 resolution). If we can't observe a difference in only the
41 nanoseconds, then fall back to 2 seconds. However, note that
42 usleep (2000000) is allowed to fail with EINVAL. */
45 ASSERT (close (creat (BASE "tmp", 0600)) == 0);
46 ASSERT (stat (BASE "tmp", &st1) == 0);
47 ASSERT (unlink (BASE "tmp") == 0);
50 ASSERT (close (creat (BASE "tmp", 0600)) == 0);
51 ASSERT (stat (BASE "tmp", &st2) == 0);
52 ASSERT (unlink (BASE "tmp") == 0);
53 if (st1.st_mtime != st2.st_mtime)
55 /* Seconds differ, give it one more shot. */
58 ASSERT (close (creat (BASE "tmp", 0600)) == 0);
59 ASSERT (stat (BASE "tmp", &st2) == 0);
60 ASSERT (unlink (BASE "tmp") == 0);
62 if (! (st1.st_mtime == st2.st_mtime
63 && get_stat_mtime_ns (&st1) < get_stat_mtime_ns (&st2)))
70 # endif /* HAVE_USLEEP */
72 #endif /* !TEST_CHOWN_NAP */
75 # define getegid() (-1)
79 # define HAVE_LCHMOD 0
82 #ifndef CHOWN_CHANGE_TIME_BUG
83 # define CHOWN_CHANGE_TIME_BUG 0
86 /* This file is designed to test lchown(n,o,g) and
87 chownat(AT_FDCWD,n,o,g,AT_SYMLINK_NOFOLLOW). FUNC is the function
88 to test. Assumes that BASE and ASSERT are already defined, and
89 that appropriate headers are already included. If PRINT, warn
90 before skipping symlink tests with status 77. */
93 test_lchown (int (*func) (char const *, uid_t, gid_t), bool print)
101 /* Solaris 8 is interesting - if the current process belongs to
102 multiple groups, the current directory is owned by a a group that
103 the current process belongs to but different than getegid(), and
104 the current directory does not have the S_ISGID bit, then regular
105 files created in the directory belong to the directory's group,
106 but symlinks belong to the current effective group id. If
107 S_ISGID is set, then both files and symlinks belong to the
108 directory's group. However, it is possible to run the testsuite
109 from within a directory owned by a group we don't belong to, in
110 which case all things that we create belong to the current
111 effective gid. So, work around the issues by creating a
112 subdirectory (we are guaranteed that the subdirectory will be
113 owned by one of our current groups), change ownership of that
114 directory to the current effective gid (which will thus succeed),
115 then create all other files within that directory (eliminating
116 questions on whether inheritance or current id triumphs, since
117 the two methods resolve to the same gid). */
118 ASSERT (mkdir (BASE "dir", 0700) == 0);
119 ASSERT (stat (BASE "dir", &st1) == 0);
121 /* Filter out mingw, which has no concept of groups. */
122 result = func (BASE "dir", st1.st_uid, getegid ());
123 if (result == -1 && errno == ENOSYS)
125 ASSERT (rmdir (BASE "dir") == 0);
127 fputs ("skipping test: no support for ownership\n", stderr);
130 ASSERT (result == 0);
132 ASSERT (close (creat (BASE "dir/file", 0600)) == 0);
133 ASSERT (stat (BASE "dir/file", &st1) == 0);
134 ASSERT (st1.st_uid != -1);
135 ASSERT (st1.st_gid != -1);
136 ASSERT (st1.st_gid == getegid ());
138 /* Sanity check of error cases. */
140 ASSERT (func ("", -1, -1) == -1);
141 ASSERT (errno == ENOENT);
143 ASSERT (func ("no_such", -1, -1) == -1);
144 ASSERT (errno == ENOENT);
146 ASSERT (func ("no_such/", -1, -1) == -1);
147 ASSERT (errno == ENOENT);
149 ASSERT (func (BASE "dir/file/", -1, -1) == -1);
150 ASSERT (errno == ENOTDIR);
152 /* Check that -1 does not alter ownership. */
153 ASSERT (func (BASE "dir/file", -1, st1.st_gid) == 0);
154 ASSERT (func (BASE "dir/file", st1.st_uid, -1) == 0);
155 ASSERT (stat (BASE "dir/file", &st2) == 0);
156 ASSERT (st1.st_uid == st2.st_uid);
157 ASSERT (st1.st_gid == st2.st_gid);
159 /* Even if the values aren't changing, ctime is required to change
160 if at least one argument is not -1. */
162 ASSERT (func (BASE "dir/file", st1.st_uid, st1.st_gid) == 0);
163 ASSERT (stat (BASE "dir/file", &st2) == 0);
164 ASSERT (st1.st_ctime < st2.st_ctime
165 || (st1.st_ctime == st2.st_ctime
166 && get_stat_ctime_ns (&st1) < get_stat_ctime_ns (&st2)));
168 /* Test symlink behavior. */
169 if (symlink ("link", BASE "dir/link2"))
171 ASSERT (unlink (BASE "dir/file") == 0);
172 ASSERT (rmdir (BASE "dir") == 0);
174 fputs ("skipping test: symlinks not supported on this file system\n",
178 result = func (BASE "dir/link2", -1, -1);
179 if (result == -1 && errno == ENOSYS)
181 ASSERT (unlink (BASE "dir/file") == 0);
182 ASSERT (unlink (BASE "dir/link2") == 0);
183 ASSERT (rmdir (BASE "dir") == 0);
185 fputs ("skipping test: symlink ownership not supported\n", stderr);
188 ASSERT (result == 0);
190 ASSERT (func (BASE "dir/link2/", st1.st_uid, st1.st_gid) == -1);
191 ASSERT (errno == ENOENT);
192 ASSERT (symlink ("file", BASE "dir/link") == 0);
193 ASSERT (mkdir (BASE "dir/sub", 0700) == 0);
194 ASSERT (symlink ("sub", BASE "dir/link3") == 0);
196 /* For non-privileged users, lchown can only portably succeed at
197 changing group ownership of a file we own. If we belong to at
198 least two groups, then verifying the correct change is simple.
199 But if we belong to only one group, then we fall back on the
200 other observable effect of lchown: the ctime must be updated.
201 Be careful of duplicates returned by getgroups. */
202 gids_count = mgetgroups (NULL, -1, &gids);
203 if (2 <= gids_count && gids[0] == gids[1] && 2 < gids_count--)
205 if (1 < gids_count || (gids_count == 1 && gids[0] != st1.st_gid))
207 if (gids[0] == st1.st_gid)
209 ASSERT (1 < gids_count);
210 ASSERT (gids[0] != gids[1]);
213 ASSERT (gids[0] != st1.st_gid);
214 ASSERT (gids[0] != -1);
215 ASSERT (lstat (BASE "dir/link", &st2) == 0);
216 ASSERT (st1.st_uid == st2.st_uid);
217 ASSERT (st1.st_gid == st2.st_gid);
218 ASSERT (lstat (BASE "dir/link2", &st2) == 0);
219 ASSERT (st1.st_uid == st2.st_uid);
220 ASSERT (st1.st_gid == st2.st_gid);
223 ASSERT (func (BASE "dir/link2/", -1, gids[0]) == -1);
224 ASSERT (errno == ENOTDIR);
225 ASSERT (stat (BASE "dir/file", &st2) == 0);
226 ASSERT (st1.st_uid == st2.st_uid);
227 ASSERT (st1.st_gid == st2.st_gid);
228 ASSERT (lstat (BASE "dir/link", &st2) == 0);
229 ASSERT (st1.st_uid == st2.st_uid);
230 ASSERT (st1.st_gid == st2.st_gid);
231 ASSERT (lstat (BASE "dir/link2", &st2) == 0);
232 ASSERT (st1.st_uid == st2.st_uid);
233 ASSERT (st1.st_gid == st2.st_gid);
235 ASSERT (func (BASE "dir/link2", -1, gids[0]) == 0);
236 ASSERT (stat (BASE "dir/file", &st2) == 0);
237 ASSERT (st1.st_uid == st2.st_uid);
238 ASSERT (st1.st_gid == st2.st_gid);
239 ASSERT (lstat (BASE "dir/link", &st2) == 0);
240 ASSERT (st1.st_uid == st2.st_uid);
241 ASSERT (st1.st_gid == st2.st_gid);
242 ASSERT (lstat (BASE "dir/link2", &st2) == 0);
243 ASSERT (st1.st_uid == st2.st_uid);
244 ASSERT (gids[0] == st2.st_gid);
246 /* Trailing slash follows through to directory. */
247 ASSERT (lstat (BASE "dir/link3", &st2) == 0);
248 ASSERT (st1.st_uid == st2.st_uid);
249 ASSERT (st1.st_gid == st2.st_gid);
250 ASSERT (lstat (BASE "dir/sub", &st2) == 0);
251 ASSERT (st1.st_uid == st2.st_uid);
252 ASSERT (st1.st_gid == st2.st_gid);
254 ASSERT (func (BASE "dir/link3/", -1, gids[0]) == 0);
255 ASSERT (lstat (BASE "dir/link3", &st2) == 0);
256 ASSERT (st1.st_uid == st2.st_uid);
257 ASSERT (st1.st_gid == st2.st_gid);
258 ASSERT (lstat (BASE "dir/sub", &st2) == 0);
259 ASSERT (st1.st_uid == st2.st_uid);
260 ASSERT (gids[0] == st2.st_gid);
262 else if (!CHOWN_CHANGE_TIME_BUG || HAVE_LCHMOD)
264 /* If we don't have lchmod, and lchown fails to change ctime,
265 then we can't test this part of lchown. */
268 ASSERT (stat (BASE "dir/file", &st1) == 0);
269 ASSERT (lstat (BASE "dir/link", &l1) == 0);
270 ASSERT (lstat (BASE "dir/link2", &l2) == 0);
274 ASSERT (func (BASE "dir/link2/", -1, st1.st_gid) == -1);
275 ASSERT (errno == ENOTDIR);
276 ASSERT (stat (BASE "dir/file", &st2) == 0);
277 ASSERT (st1.st_ctime == st2.st_ctime);
278 ASSERT (get_stat_ctime_ns (&st1) == get_stat_ctime_ns (&st2));
279 ASSERT (lstat (BASE "dir/link", &st2) == 0);
280 ASSERT (l1.st_ctime == st2.st_ctime);
281 ASSERT (get_stat_ctime_ns (&l1) == get_stat_ctime_ns (&st2));
282 ASSERT (lstat (BASE "dir/link2", &st2) == 0);
283 ASSERT (l2.st_ctime == st2.st_ctime);
284 ASSERT (get_stat_ctime_ns (&l2) == get_stat_ctime_ns (&st2));
286 ASSERT (func (BASE "dir/link2", -1, gids[0]) == 0);
287 ASSERT (stat (BASE "dir/file", &st2) == 0);
288 ASSERT (st1.st_ctime == st2.st_ctime);
289 ASSERT (get_stat_ctime_ns (&st1) == get_stat_ctime_ns (&st2));
290 ASSERT (lstat (BASE "dir/link", &st2) == 0);
291 ASSERT (l1.st_ctime == st2.st_ctime);
292 ASSERT (get_stat_ctime_ns (&l1) == get_stat_ctime_ns (&st2));
293 ASSERT (lstat (BASE "dir/link2", &st2) == 0);
294 ASSERT (l2.st_ctime < st2.st_ctime
295 || (l2.st_ctime == st2.st_ctime
296 && get_stat_ctime_ns (&l2) < get_stat_ctime_ns (&st2)));
298 /* Trailing slash follows through to directory. */
299 ASSERT (lstat (BASE "dir/sub", &st1) == 0);
300 ASSERT (lstat (BASE "dir/link3", &l1) == 0);
302 ASSERT (func (BASE "dir/link3/", -1, gids[0]) == 0);
303 ASSERT (lstat (BASE "dir/link3", &st2) == 0);
304 ASSERT (l1.st_ctime == st2.st_ctime);
305 ASSERT (get_stat_ctime_ns (&l1) == get_stat_ctime_ns (&st2));
306 ASSERT (lstat (BASE "dir/sub", &st2) == 0);
307 ASSERT (st1.st_ctime < st2.st_ctime
308 || (st1.st_ctime == st2.st_ctime
309 && get_stat_ctime_ns (&st1) < get_stat_ctime_ns (&st2)));
314 ASSERT (unlink (BASE "dir/file") == 0);
315 ASSERT (unlink (BASE "dir/link") == 0);
316 ASSERT (unlink (BASE "dir/link2") == 0);
317 ASSERT (unlink (BASE "dir/link3") == 0);
318 ASSERT (rmdir (BASE "dir/sub") == 0);
319 ASSERT (rmdir (BASE "dir") == 0);