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 #define 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;
65 # define getegid() ((gid_t) -1)
68 /* This file is designed to test chown(n,o,g) and
69 chownat(AT_FDCWD,n,o,g,0). FUNC is the function to test. Assumes
70 that BASE and ASSERT are already defined, and that appropriate
71 headers are already included. If PRINT, warn before skipping
72 symlink tests with status 77. */
75 test_chown (int (*func) (char const *, uid_t, gid_t), bool print)
83 /* Solaris 8 is interesting - if the current process belongs to
84 multiple groups, the current directory is owned by a a group that
85 the current process belongs to but different than getegid(), and
86 the current directory does not have the S_ISGID bit, then regular
87 files created in the directory belong to the directory's group,
88 but symlinks belong to the current effective group id. If
89 S_ISGID is set, then both files and symlinks belong to the
90 directory's group. However, it is possible to run the testsuite
91 from within a directory owned by a group we don't belong to, in
92 which case all things that we create belong to the current
93 effective gid. So, work around the issues by creating a
94 subdirectory (we are guaranteed that the subdirectory will be
95 owned by one of our current groups), change ownership of that
96 directory to the current effective gid (which will thus succeed),
97 then create all other files within that directory (eliminating
98 questions on whether inheritance or current id triumphs, since
99 the two methods resolve to the same gid). */
100 ASSERT (mkdir (BASE "dir", 0700) == 0);
101 ASSERT (stat (BASE "dir", &st1) == 0);
103 /* Filter out mingw, which has no concept of groups. */
104 result = func (BASE "dir", st1.st_uid, getegid ());
105 if (result == -1 && errno == ENOSYS)
107 ASSERT (rmdir (BASE "dir") == 0);
109 fputs ("skipping test: no support for ownership\n", stderr);
112 ASSERT (result == 0);
114 ASSERT (close (creat (BASE "dir/file", 0600)) == 0);
115 ASSERT (stat (BASE "dir/file", &st1) == 0);
116 ASSERT (st1.st_uid != (uid_t) -1);
117 ASSERT (st1.st_gid != (uid_t) -1);
118 ASSERT (st1.st_gid == getegid ());
120 /* Sanity check of error cases. */
122 ASSERT (func ("", -1, -1) == -1);
123 ASSERT (errno == ENOENT);
125 ASSERT (func ("no_such", -1, -1) == -1);
126 ASSERT (errno == ENOENT);
128 ASSERT (func ("no_such/", -1, -1) == -1);
129 ASSERT (errno == ENOENT);
131 ASSERT (func (BASE "dir/file/", -1, -1) == -1);
132 ASSERT (errno == ENOTDIR);
134 /* Check that -1 does not alter ownership. */
135 ASSERT (func (BASE "dir/file", -1, st1.st_gid) == 0);
136 ASSERT (func (BASE "dir/file", st1.st_uid, -1) == 0);
137 ASSERT (func (BASE "dir/file", (uid_t) -1, (gid_t) -1) == 0);
138 ASSERT (stat (BASE "dir/file", &st2) == 0);
139 ASSERT (st1.st_uid == st2.st_uid);
140 ASSERT (st1.st_gid == st2.st_gid);
142 /* Even if the values aren't changing, ctime is required to change
143 if at least one argument is not -1. */
145 ASSERT (func (BASE "dir/file", st1.st_uid, st1.st_gid) == 0);
146 ASSERT (stat (BASE "dir/file", &st2) == 0);
147 ASSERT (st1.st_ctime < st2.st_ctime
148 || (st1.st_ctime == st2.st_ctime
149 && get_stat_ctime_ns (&st1) < get_stat_ctime_ns (&st2)));
151 /* Test symlink behavior. */
152 if (symlink ("link", BASE "dir/link2"))
154 ASSERT (unlink (BASE "dir/file") == 0);
155 ASSERT (rmdir (BASE "dir") == 0);
157 fputs ("skipping test: symlinks not supported on this file system\n",
162 ASSERT (func (BASE "dir/link2", -1, -1) == -1);
163 ASSERT (errno == ENOENT);
165 ASSERT (func (BASE "dir/link2/", st1.st_uid, st1.st_gid) == -1);
166 ASSERT (errno == ENOENT);
167 ASSERT (symlink ("file", BASE "dir/link") == 0);
169 /* For non-privileged users, chown can only portably succeed at
170 changing group ownership of a file we own. If we belong to at
171 least two groups, then verifying the correct change is simple.
172 But if we belong to only one group, then we fall back on the
173 other observable effect of chown: the ctime must be updated. */
174 gids_count = mgetgroups (NULL, st1.st_gid, &gids);
177 ASSERT (gids[1] != st1.st_gid);
178 ASSERT (gids[1] != (gid_t) -1);
179 ASSERT (lstat (BASE "dir/link", &st2) == 0);
180 ASSERT (st1.st_uid == st2.st_uid);
181 ASSERT (st1.st_gid == st2.st_gid);
182 ASSERT (lstat (BASE "dir/link2", &st2) == 0);
183 ASSERT (st1.st_uid == st2.st_uid);
184 ASSERT (st1.st_gid == st2.st_gid);
187 ASSERT (func (BASE "dir/link2/", -1, gids[1]) == -1);
188 ASSERT (errno == ENOTDIR);
189 ASSERT (stat (BASE "dir/file", &st2) == 0);
190 ASSERT (st1.st_uid == st2.st_uid);
191 ASSERT (st1.st_gid == st2.st_gid);
192 ASSERT (lstat (BASE "dir/link", &st2) == 0);
193 ASSERT (st1.st_uid == st2.st_uid);
194 ASSERT (st1.st_gid == st2.st_gid);
195 ASSERT (lstat (BASE "dir/link2", &st2) == 0);
196 ASSERT (st1.st_uid == st2.st_uid);
197 ASSERT (st1.st_gid == st2.st_gid);
199 ASSERT (func (BASE "dir/link2", -1, gids[1]) == 0);
200 ASSERT (stat (BASE "dir/file", &st2) == 0);
201 ASSERT (st1.st_uid == st2.st_uid);
202 ASSERT (gids[1] == st2.st_gid);
203 ASSERT (lstat (BASE "dir/link", &st2) == 0);
204 ASSERT (st1.st_uid == st2.st_uid);
205 ASSERT (st1.st_gid == st2.st_gid);
206 ASSERT (lstat (BASE "dir/link2", &st2) == 0);
207 ASSERT (st1.st_uid == st2.st_uid);
208 ASSERT (st1.st_gid == st2.st_gid);
214 ASSERT (stat (BASE "dir/file", &st1) == 0);
215 ASSERT (lstat (BASE "dir/link", &l1) == 0);
216 ASSERT (lstat (BASE "dir/link2", &l2) == 0);
220 ASSERT (func (BASE "dir/link2/", -1, st1.st_gid) == -1);
221 ASSERT (errno == ENOTDIR);
222 ASSERT (stat (BASE "dir/file", &st2) == 0);
223 ASSERT (st1.st_ctime == st2.st_ctime);
224 ASSERT (get_stat_ctime_ns (&st1) == get_stat_ctime_ns (&st2));
225 ASSERT (lstat (BASE "dir/link", &st2) == 0);
226 ASSERT (l1.st_ctime == st2.st_ctime);
227 ASSERT (get_stat_ctime_ns (&l1) == get_stat_ctime_ns (&st2));
228 ASSERT (lstat (BASE "dir/link2", &st2) == 0);
229 ASSERT (l2.st_ctime == st2.st_ctime);
230 ASSERT (get_stat_ctime_ns (&l2) == get_stat_ctime_ns (&st2));
232 ASSERT (func (BASE "dir/link2", -1, st1.st_gid) == 0);
233 ASSERT (stat (BASE "dir/file", &st2) == 0);
234 ASSERT (st1.st_ctime < st2.st_ctime
235 || (st1.st_ctime == st2.st_ctime
236 && get_stat_ctime_ns (&st1) < get_stat_ctime_ns (&st2)));
237 ASSERT (lstat (BASE "dir/link", &st2) == 0);
238 ASSERT (l1.st_ctime == st2.st_ctime);
239 ASSERT (get_stat_ctime_ns (&l1) == get_stat_ctime_ns (&st2));
240 ASSERT (lstat (BASE "dir/link2", &st2) == 0);
241 ASSERT (l2.st_ctime == st2.st_ctime);
242 ASSERT (get_stat_ctime_ns (&l2) == get_stat_ctime_ns (&st2));
247 ASSERT (unlink (BASE "dir/file") == 0);
248 ASSERT (unlink (BASE "dir/link") == 0);
249 ASSERT (unlink (BASE "dir/link2") == 0);
250 ASSERT (rmdir (BASE "dir") == 0);