2 Copyright (C) 2009-2020 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 <https://www.gnu.org/licenses/>. */
17 /* Written by Eric Blake <ebb9@byu.net>, 2009. */
22 # define getegid() ((gid_t) -1)
25 /* This file is designed to test chown(n,o,g) and
26 chownat(AT_FDCWD,n,o,g,0). FUNC is the function to test. Assumes
27 that BASE and ASSERT are already defined, and that appropriate
28 headers are already included. If PRINT, warn before skipping
29 symlink tests with status 77. */
32 test_chown (int (*func
) (char const *, uid_t
, gid_t
), bool print
)
40 /* Solaris 8 is interesting - if the current process belongs to
41 multiple groups, the current directory is owned by a group that
42 the current process belongs to but different than getegid(), and
43 the current directory does not have the S_ISGID bit, then regular
44 files created in the directory belong to the directory's group,
45 but symlinks belong to the current effective group id. If
46 S_ISGID is set, then both files and symlinks belong to the
47 directory's group. However, it is possible to run the testsuite
48 from within a directory owned by a group we don't belong to, in
49 which case all things that we create belong to the current
50 effective gid. So, work around the issues by creating a
51 subdirectory (we are guaranteed that the subdirectory will be
52 owned by one of our current groups), change ownership of that
53 directory to the current effective gid (which will thus succeed),
54 then create all other files within that directory (eliminating
55 questions on whether inheritance or current id triumphs, since
56 the two methods resolve to the same gid). */
57 ASSERT (mkdir (BASE
"dir", 0700) == 0);
58 ASSERT (stat (BASE
"dir", &st1
) == 0);
60 /* Filter out mingw and file systems which have no concept of groups. */
61 result
= func (BASE
"dir", st1
.st_uid
, getegid ());
62 if (result
== -1 && (errno
== ENOSYS
|| errno
== EPERM
))
64 ASSERT (rmdir (BASE
"dir") == 0);
66 fputs ("skipping test: no support for ownership\n", stderr
);
71 ASSERT (close (creat (BASE
"dir/file", 0600)) == 0);
72 ASSERT (stat (BASE
"dir/file", &st1
) == 0);
73 ASSERT (st1
.st_uid
!= (uid_t
) -1);
74 ASSERT (st1
.st_gid
!= (gid_t
) -1);
75 ASSERT (st1
.st_gid
== getegid ());
77 /* Sanity check of error cases. */
79 ASSERT (func ("", -1, -1) == -1);
80 ASSERT (errno
== ENOENT
);
82 ASSERT (func ("no_such", -1, -1) == -1);
83 ASSERT (errno
== ENOENT
);
85 ASSERT (func ("no_such/", -1, -1) == -1);
86 ASSERT (errno
== ENOENT
);
88 ASSERT (func (BASE
"dir/file/", -1, -1) == -1);
89 ASSERT (errno
== ENOTDIR
);
91 /* Check that -1 does not alter ownership. */
92 ASSERT (func (BASE
"dir/file", -1, st1
.st_gid
) == 0);
93 ASSERT (func (BASE
"dir/file", st1
.st_uid
, -1) == 0);
94 ASSERT (func (BASE
"dir/file", (uid_t
) -1, (gid_t
) -1) == 0);
95 ASSERT (stat (BASE
"dir/file", &st2
) == 0);
96 ASSERT (st1
.st_uid
== st2
.st_uid
);
97 ASSERT (st1
.st_gid
== st2
.st_gid
);
99 /* Even if the values aren't changing, ctime is required to change
100 if at least one argument is not -1. */
102 ASSERT (func (BASE
"dir/file", st1
.st_uid
, st1
.st_gid
) == 0);
103 ASSERT (stat (BASE
"dir/file", &st2
) == 0);
104 ASSERT (st1
.st_ctime
< st2
.st_ctime
105 || (st1
.st_ctime
== st2
.st_ctime
106 && get_stat_ctime_ns (&st1
) < get_stat_ctime_ns (&st2
)));
108 /* Test symlink behavior. */
109 if (symlink ("link", BASE
"dir/link2"))
111 ASSERT (unlink (BASE
"dir/file") == 0);
112 ASSERT (rmdir (BASE
"dir") == 0);
114 fputs ("skipping test: symlinks not supported on this file system\n",
119 ASSERT (func (BASE
"dir/link2", -1, -1) == -1);
120 ASSERT (errno
== ENOENT
);
122 ASSERT (func (BASE
"dir/link2/", st1
.st_uid
, st1
.st_gid
) == -1);
123 ASSERT (errno
== ENOENT
);
124 ASSERT (symlink ("file", BASE
"dir/link") == 0);
126 /* For non-privileged users, chown can only portably succeed at
127 changing group ownership of a file we own. If we belong to at
128 least two groups, then verifying the correct change is simple.
129 But if we belong to only one group, then we fall back on the
130 other observable effect of chown: the ctime must be updated. */
131 gids_count
= mgetgroups (NULL
, st1
.st_gid
, &gids
);
134 ASSERT (gids
[1] != st1
.st_gid
);
135 ASSERT (gids
[1] != (gid_t
) -1);
136 ASSERT (lstat (BASE
"dir/link", &st2
) == 0);
137 ASSERT (st1
.st_uid
== st2
.st_uid
);
138 ASSERT (st1
.st_gid
== st2
.st_gid
);
139 ASSERT (lstat (BASE
"dir/link2", &st2
) == 0);
140 ASSERT (st1
.st_uid
== st2
.st_uid
);
141 ASSERT (st1
.st_gid
== st2
.st_gid
);
144 ASSERT (func (BASE
"dir/link2/", -1, gids
[1]) == -1);
145 ASSERT (errno
== ENOTDIR
);
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 ASSERT (lstat (BASE
"dir/link", &st2
) == 0);
150 ASSERT (st1
.st_uid
== st2
.st_uid
);
151 ASSERT (st1
.st_gid
== st2
.st_gid
);
152 ASSERT (lstat (BASE
"dir/link2", &st2
) == 0);
153 ASSERT (st1
.st_uid
== st2
.st_uid
);
154 ASSERT (st1
.st_gid
== st2
.st_gid
);
156 ASSERT (func (BASE
"dir/link2", -1, gids
[1]) == 0);
157 ASSERT (stat (BASE
"dir/file", &st2
) == 0);
158 ASSERT (st1
.st_uid
== st2
.st_uid
);
159 ASSERT (gids
[1] == st2
.st_gid
);
160 ASSERT (lstat (BASE
"dir/link", &st2
) == 0);
161 ASSERT (st1
.st_uid
== st2
.st_uid
);
162 ASSERT (st1
.st_gid
== st2
.st_gid
);
163 ASSERT (lstat (BASE
"dir/link2", &st2
) == 0);
164 ASSERT (st1
.st_uid
== st2
.st_uid
);
165 ASSERT (st1
.st_gid
== st2
.st_gid
);
171 ASSERT (stat (BASE
"dir/file", &st1
) == 0);
172 ASSERT (lstat (BASE
"dir/link", &l1
) == 0);
173 ASSERT (lstat (BASE
"dir/link2", &l2
) == 0);
177 ASSERT (func (BASE
"dir/link2/", -1, st1
.st_gid
) == -1);
178 ASSERT (errno
== ENOTDIR
);
179 ASSERT (stat (BASE
"dir/file", &st2
) == 0);
180 ASSERT (st1
.st_ctime
== st2
.st_ctime
);
181 ASSERT (get_stat_ctime_ns (&st1
) == get_stat_ctime_ns (&st2
));
182 ASSERT (lstat (BASE
"dir/link", &st2
) == 0);
183 ASSERT (l1
.st_ctime
== st2
.st_ctime
);
184 ASSERT (get_stat_ctime_ns (&l1
) == get_stat_ctime_ns (&st2
));
185 ASSERT (lstat (BASE
"dir/link2", &st2
) == 0);
186 ASSERT (l2
.st_ctime
== st2
.st_ctime
);
187 ASSERT (get_stat_ctime_ns (&l2
) == get_stat_ctime_ns (&st2
));
189 ASSERT (func (BASE
"dir/link2", -1, st1
.st_gid
) == 0);
190 ASSERT (stat (BASE
"dir/file", &st2
) == 0);
191 ASSERT (st1
.st_ctime
< st2
.st_ctime
192 || (st1
.st_ctime
== st2
.st_ctime
193 && get_stat_ctime_ns (&st1
) < get_stat_ctime_ns (&st2
)));
194 ASSERT (lstat (BASE
"dir/link", &st2
) == 0);
195 ASSERT (l1
.st_ctime
== st2
.st_ctime
);
196 ASSERT (get_stat_ctime_ns (&l1
) == get_stat_ctime_ns (&st2
));
197 ASSERT (lstat (BASE
"dir/link2", &st2
) == 0);
198 ASSERT (l2
.st_ctime
== st2
.st_ctime
);
199 ASSERT (get_stat_ctime_ns (&l2
) == get_stat_ctime_ns (&st2
));
204 ASSERT (unlink (BASE
"dir/file") == 0);
205 ASSERT (unlink (BASE
"dir/link") == 0);
206 ASSERT (unlink (BASE
"dir/link2") == 0);
207 ASSERT (rmdir (BASE
"dir") == 0);