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. */
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. */
40 ASSERT (close (creat (BASE "tmp", 0600)) == 0);
41 ASSERT (stat (BASE "tmp", &st1) == 0);
42 ASSERT (unlink (BASE "tmp") == 0);
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)
50 /* Seconds differ, give it one more shot. */
53 ASSERT (close (creat (BASE "tmp", 0600)) == 0);
54 ASSERT (stat (BASE "tmp", &st2) == 0);
55 ASSERT (unlink (BASE "tmp") == 0);
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;
63 #endif /* !TEST_CHOWN_NAP */
66 # define getegid() ((gid_t) -1)
70 # define HAVE_LCHMOD 0
73 #ifndef CHOWN_CHANGE_TIME_BUG
74 # define CHOWN_CHANGE_TIME_BUG 0
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. */
84 test_lchown (int (*func) (char const *, uid_t, gid_t), bool print)
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);
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)
116 ASSERT (rmdir (BASE "dir") == 0);
118 fputs ("skipping test: no support for ownership\n", stderr);
121 ASSERT (result == 0);
123 ASSERT (close (creat (BASE "dir/file", 0600)) == 0);
124 ASSERT (stat (BASE "dir/file", &st1) == 0);
125 ASSERT (st1.st_uid != (uid_t) -1);
126 ASSERT (st1.st_gid != (gid_t) -1);
127 ASSERT (st1.st_gid == getegid ());
129 /* Sanity check of error cases. */
131 ASSERT (func ("", -1, -1) == -1);
132 ASSERT (errno == ENOENT);
134 ASSERT (func ("no_such", -1, -1) == -1);
135 ASSERT (errno == ENOENT);
137 ASSERT (func ("no_such/", -1, -1) == -1);
138 ASSERT (errno == ENOENT);
140 ASSERT (func (BASE "dir/file/", -1, -1) == -1);
141 ASSERT (errno == ENOTDIR);
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 (func (BASE "dir/file", (uid_t) -1, (gid_t) -1) == 0);
147 ASSERT (stat (BASE "dir/file", &st2) == 0);
148 ASSERT (st1.st_uid == st2.st_uid);
149 ASSERT (st1.st_gid == st2.st_gid);
151 /* Even if the values aren't changing, ctime is required to change
152 if at least one argument is not -1. */
154 ASSERT (func (BASE "dir/file", st1.st_uid, st1.st_gid) == 0);
155 ASSERT (stat (BASE "dir/file", &st2) == 0);
156 ASSERT (st1.st_ctime < st2.st_ctime
157 || (st1.st_ctime == st2.st_ctime
158 && get_stat_ctime_ns (&st1) < get_stat_ctime_ns (&st2)));
160 /* Test symlink behavior. */
161 if (symlink ("link", BASE "dir/link2"))
163 ASSERT (unlink (BASE "dir/file") == 0);
164 ASSERT (rmdir (BASE "dir") == 0);
166 fputs ("skipping test: symlinks not supported on this file system\n",
170 result = func (BASE "dir/link2", -1, -1);
171 if (result == -1 && errno == ENOSYS)
173 ASSERT (unlink (BASE "dir/file") == 0);
174 ASSERT (unlink (BASE "dir/link2") == 0);
175 ASSERT (rmdir (BASE "dir") == 0);
177 fputs ("skipping test: symlink ownership not supported\n", stderr);
180 ASSERT (result == 0);
182 ASSERT (func (BASE "dir/link2/", st1.st_uid, st1.st_gid) == -1);
183 ASSERT (errno == ENOENT);
184 ASSERT (symlink ("file", BASE "dir/link") == 0);
185 ASSERT (mkdir (BASE "dir/sub", 0700) == 0);
186 ASSERT (symlink ("sub", BASE "dir/link3") == 0);
188 /* For non-privileged users, lchown can only portably succeed at
189 changing group ownership of a file we own. If we belong to at
190 least two groups, then verifying the correct change is simple.
191 But if we belong to only one group, then we fall back on the
192 other observable effect of lchown: the ctime must be updated. */
193 gids_count = mgetgroups (NULL, st1.st_gid, &gids);
196 ASSERT (gids[1] != st1.st_gid);
197 ASSERT (gids[1] != (gid_t) -1);
198 ASSERT (lstat (BASE "dir/link", &st2) == 0);
199 ASSERT (st1.st_uid == st2.st_uid);
200 ASSERT (st1.st_gid == st2.st_gid);
201 ASSERT (lstat (BASE "dir/link2", &st2) == 0);
202 ASSERT (st1.st_uid == st2.st_uid);
203 ASSERT (st1.st_gid == st2.st_gid);
206 ASSERT (func (BASE "dir/link2/", -1, gids[1]) == -1);
207 ASSERT (errno == ENOTDIR);
208 ASSERT (stat (BASE "dir/file", &st2) == 0);
209 ASSERT (st1.st_uid == st2.st_uid);
210 ASSERT (st1.st_gid == st2.st_gid);
211 ASSERT (lstat (BASE "dir/link", &st2) == 0);
212 ASSERT (st1.st_uid == st2.st_uid);
213 ASSERT (st1.st_gid == st2.st_gid);
214 ASSERT (lstat (BASE "dir/link2", &st2) == 0);
215 ASSERT (st1.st_uid == st2.st_uid);
216 ASSERT (st1.st_gid == st2.st_gid);
218 ASSERT (func (BASE "dir/link2", -1, gids[1]) == 0);
219 ASSERT (stat (BASE "dir/file", &st2) == 0);
220 ASSERT (st1.st_uid == st2.st_uid);
221 ASSERT (st1.st_gid == st2.st_gid);
222 ASSERT (lstat (BASE "dir/link", &st2) == 0);
223 ASSERT (st1.st_uid == st2.st_uid);
224 ASSERT (st1.st_gid == st2.st_gid);
225 ASSERT (lstat (BASE "dir/link2", &st2) == 0);
226 ASSERT (st1.st_uid == st2.st_uid);
227 ASSERT (gids[1] == st2.st_gid);
229 /* Trailing slash follows through to directory. */
230 ASSERT (lstat (BASE "dir/link3", &st2) == 0);
231 ASSERT (st1.st_uid == st2.st_uid);
232 ASSERT (st1.st_gid == st2.st_gid);
233 ASSERT (lstat (BASE "dir/sub", &st2) == 0);
234 ASSERT (st1.st_uid == st2.st_uid);
235 ASSERT (st1.st_gid == st2.st_gid);
237 ASSERT (func (BASE "dir/link3/", -1, gids[1]) == 0);
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 (gids[1] == st2.st_gid);
245 else if (!CHOWN_CHANGE_TIME_BUG || HAVE_LCHMOD)
247 /* If we don't have lchmod, and lchown fails to change ctime,
248 then we can't test this part of lchown. */
251 ASSERT (stat (BASE "dir/file", &st1) == 0);
252 ASSERT (lstat (BASE "dir/link", &l1) == 0);
253 ASSERT (lstat (BASE "dir/link2", &l2) == 0);
257 ASSERT (func (BASE "dir/link2/", -1, st1.st_gid) == -1);
258 ASSERT (errno == ENOTDIR);
259 ASSERT (stat (BASE "dir/file", &st2) == 0);
260 ASSERT (st1.st_ctime == st2.st_ctime);
261 ASSERT (get_stat_ctime_ns (&st1) == get_stat_ctime_ns (&st2));
262 ASSERT (lstat (BASE "dir/link", &st2) == 0);
263 ASSERT (l1.st_ctime == st2.st_ctime);
264 ASSERT (get_stat_ctime_ns (&l1) == get_stat_ctime_ns (&st2));
265 ASSERT (lstat (BASE "dir/link2", &st2) == 0);
266 ASSERT (l2.st_ctime == st2.st_ctime);
267 ASSERT (get_stat_ctime_ns (&l2) == get_stat_ctime_ns (&st2));
269 ASSERT (func (BASE "dir/link2", -1, st1.st_gid) == 0);
270 ASSERT (stat (BASE "dir/file", &st2) == 0);
271 ASSERT (st1.st_ctime == st2.st_ctime);
272 ASSERT (get_stat_ctime_ns (&st1) == get_stat_ctime_ns (&st2));
273 ASSERT (lstat (BASE "dir/link", &st2) == 0);
274 ASSERT (l1.st_ctime == st2.st_ctime);
275 ASSERT (get_stat_ctime_ns (&l1) == get_stat_ctime_ns (&st2));
276 ASSERT (lstat (BASE "dir/link2", &st2) == 0);
277 ASSERT (l2.st_ctime < st2.st_ctime
278 || (l2.st_ctime == st2.st_ctime
279 && get_stat_ctime_ns (&l2) < get_stat_ctime_ns (&st2)));
281 /* Trailing slash follows through to directory. */
282 ASSERT (lstat (BASE "dir/sub", &st1) == 0);
283 ASSERT (lstat (BASE "dir/link3", &l1) == 0);
285 ASSERT (func (BASE "dir/link3/", -1, st1.st_gid) == 0);
286 ASSERT (lstat (BASE "dir/link3", &st2) == 0);
287 ASSERT (l1.st_ctime == st2.st_ctime);
288 ASSERT (get_stat_ctime_ns (&l1) == get_stat_ctime_ns (&st2));
289 ASSERT (lstat (BASE "dir/sub", &st2) == 0);
290 ASSERT (st1.st_ctime < st2.st_ctime
291 || (st1.st_ctime == st2.st_ctime
292 && get_stat_ctime_ns (&st1) < get_stat_ctime_ns (&st2)));
297 ASSERT (unlink (BASE "dir/file") == 0);
298 ASSERT (unlink (BASE "dir/link") == 0);
299 ASSERT (unlink (BASE "dir/link2") == 0);
300 ASSERT (unlink (BASE "dir/link3") == 0);
301 ASSERT (rmdir (BASE "dir/sub") == 0);
302 ASSERT (rmdir (BASE "dir") == 0);