|
Packit Service |
2723c6 |
/* Tests of chown.
|
|
Packit Service |
2723c6 |
Copyright (C) 2009-2018 Free Software Foundation, Inc.
|
|
Packit Service |
2723c6 |
|
|
Packit Service |
2723c6 |
This program is free software: you can redistribute it and/or modify
|
|
Packit Service |
2723c6 |
it under the terms of the GNU General Public License as published by
|
|
Packit Service |
2723c6 |
the Free Software Foundation; either version 3 of the License, or
|
|
Packit Service |
2723c6 |
(at your option) any later version.
|
|
Packit Service |
2723c6 |
|
|
Packit Service |
2723c6 |
This program is distributed in the hope that it will be useful,
|
|
Packit Service |
2723c6 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
2723c6 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit Service |
2723c6 |
GNU General Public License for more details.
|
|
Packit Service |
2723c6 |
|
|
Packit Service |
2723c6 |
You should have received a copy of the GNU General Public License
|
|
Packit Service |
2723c6 |
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|
Packit Service |
2723c6 |
|
|
Packit Service |
2723c6 |
/* Written by Eric Blake <ebb9@byu.net>, 2009. */
|
|
Packit Service |
2723c6 |
|
|
Packit Service |
2723c6 |
#include "nap.h"
|
|
Packit Service |
2723c6 |
|
|
Packit Service |
2723c6 |
#if !HAVE_GETEGID
|
|
Packit Service |
2723c6 |
# define getegid() ((gid_t) -1)
|
|
Packit Service |
2723c6 |
#endif
|
|
Packit Service |
2723c6 |
|
|
Packit Service |
2723c6 |
/* This file is designed to test chown(n,o,g) and
|
|
Packit Service |
2723c6 |
chownat(AT_FDCWD,n,o,g,0). FUNC is the function to test. Assumes
|
|
Packit Service |
2723c6 |
that BASE and ASSERT are already defined, and that appropriate
|
|
Packit Service |
2723c6 |
headers are already included. If PRINT, warn before skipping
|
|
Packit Service |
2723c6 |
symlink tests with status 77. */
|
|
Packit Service |
2723c6 |
|
|
Packit Service |
2723c6 |
static int
|
|
Packit Service |
2723c6 |
test_chown (int (*func) (char const *, uid_t, gid_t), bool print)
|
|
Packit Service |
2723c6 |
{
|
|
Packit Service |
2723c6 |
struct stat st1;
|
|
Packit Service |
2723c6 |
struct stat st2;
|
|
Packit Service |
2723c6 |
gid_t *gids = NULL;
|
|
Packit Service |
2723c6 |
int gids_count;
|
|
Packit Service |
2723c6 |
int result;
|
|
Packit Service |
2723c6 |
|
|
Packit Service |
2723c6 |
/* Solaris 8 is interesting - if the current process belongs to
|
|
Packit Service |
2723c6 |
multiple groups, the current directory is owned by a group that
|
|
Packit Service |
2723c6 |
the current process belongs to but different than getegid(), and
|
|
Packit Service |
2723c6 |
the current directory does not have the S_ISGID bit, then regular
|
|
Packit Service |
2723c6 |
files created in the directory belong to the directory's group,
|
|
Packit Service |
2723c6 |
but symlinks belong to the current effective group id. If
|
|
Packit Service |
2723c6 |
S_ISGID is set, then both files and symlinks belong to the
|
|
Packit Service |
2723c6 |
directory's group. However, it is possible to run the testsuite
|
|
Packit Service |
2723c6 |
from within a directory owned by a group we don't belong to, in
|
|
Packit Service |
2723c6 |
which case all things that we create belong to the current
|
|
Packit Service |
2723c6 |
effective gid. So, work around the issues by creating a
|
|
Packit Service |
2723c6 |
subdirectory (we are guaranteed that the subdirectory will be
|
|
Packit Service |
2723c6 |
owned by one of our current groups), change ownership of that
|
|
Packit Service |
2723c6 |
directory to the current effective gid (which will thus succeed),
|
|
Packit Service |
2723c6 |
then create all other files within that directory (eliminating
|
|
Packit Service |
2723c6 |
questions on whether inheritance or current id triumphs, since
|
|
Packit Service |
2723c6 |
the two methods resolve to the same gid). */
|
|
Packit Service |
2723c6 |
ASSERT (mkdir (BASE "dir", 0700) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (stat (BASE "dir", &st1) == 0);
|
|
Packit Service |
2723c6 |
|
|
Packit Service |
2723c6 |
/* Filter out mingw and file systems which have no concept of groups. */
|
|
Packit Service |
2723c6 |
result = func (BASE "dir", st1.st_uid, getegid ());
|
|
Packit Service |
2723c6 |
if (result == -1 && (errno == ENOSYS || errno == EPERM))
|
|
Packit Service |
2723c6 |
{
|
|
Packit Service |
2723c6 |
ASSERT (rmdir (BASE "dir") == 0);
|
|
Packit Service |
2723c6 |
if (print)
|
|
Packit Service |
2723c6 |
fputs ("skipping test: no support for ownership\n", stderr);
|
|
Packit Service |
2723c6 |
return 77;
|
|
Packit Service |
2723c6 |
}
|
|
Packit Service |
2723c6 |
ASSERT (result == 0);
|
|
Packit Service |
2723c6 |
|
|
Packit Service |
2723c6 |
ASSERT (close (creat (BASE "dir/file", 0600)) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (stat (BASE "dir/file", &st1) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (st1.st_uid != (uid_t) -1);
|
|
Packit Service |
2723c6 |
ASSERT (st1.st_gid != (gid_t) -1);
|
|
Packit Service |
2723c6 |
ASSERT (st1.st_gid == getegid ());
|
|
Packit Service |
2723c6 |
|
|
Packit Service |
2723c6 |
/* Sanity check of error cases. */
|
|
Packit Service |
2723c6 |
errno = 0;
|
|
Packit Service |
2723c6 |
ASSERT (func ("", -1, -1) == -1);
|
|
Packit Service |
2723c6 |
ASSERT (errno == ENOENT);
|
|
Packit Service |
2723c6 |
errno = 0;
|
|
Packit Service |
2723c6 |
ASSERT (func ("no_such", -1, -1) == -1);
|
|
Packit Service |
2723c6 |
ASSERT (errno == ENOENT);
|
|
Packit Service |
2723c6 |
errno = 0;
|
|
Packit Service |
2723c6 |
ASSERT (func ("no_such/", -1, -1) == -1);
|
|
Packit Service |
2723c6 |
ASSERT (errno == ENOENT);
|
|
Packit Service |
2723c6 |
errno = 0;
|
|
Packit Service |
2723c6 |
ASSERT (func (BASE "dir/file/", -1, -1) == -1);
|
|
Packit Service |
2723c6 |
ASSERT (errno == ENOTDIR);
|
|
Packit Service |
2723c6 |
|
|
Packit Service |
2723c6 |
/* Check that -1 does not alter ownership. */
|
|
Packit Service |
2723c6 |
ASSERT (func (BASE "dir/file", -1, st1.st_gid) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (func (BASE "dir/file", st1.st_uid, -1) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (func (BASE "dir/file", (uid_t) -1, (gid_t) -1) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (stat (BASE "dir/file", &st2) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (st1.st_uid == st2.st_uid);
|
|
Packit Service |
2723c6 |
ASSERT (st1.st_gid == st2.st_gid);
|
|
Packit Service |
2723c6 |
|
|
Packit Service |
2723c6 |
/* Even if the values aren't changing, ctime is required to change
|
|
Packit Service |
2723c6 |
if at least one argument is not -1. */
|
|
Packit Service |
2723c6 |
nap ();
|
|
Packit Service |
2723c6 |
ASSERT (func (BASE "dir/file", st1.st_uid, st1.st_gid) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (stat (BASE "dir/file", &st2) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (st1.st_ctime < st2.st_ctime
|
|
Packit Service |
2723c6 |
|| (st1.st_ctime == st2.st_ctime
|
|
Packit Service |
2723c6 |
&& get_stat_ctime_ns (&st1) < get_stat_ctime_ns (&st2)));
|
|
Packit Service |
2723c6 |
|
|
Packit Service |
2723c6 |
/* Test symlink behavior. */
|
|
Packit Service |
2723c6 |
if (symlink ("link", BASE "dir/link2"))
|
|
Packit Service |
2723c6 |
{
|
|
Packit Service |
2723c6 |
ASSERT (unlink (BASE "dir/file") == 0);
|
|
Packit Service |
2723c6 |
ASSERT (rmdir (BASE "dir") == 0);
|
|
Packit Service |
2723c6 |
if (print)
|
|
Packit Service |
2723c6 |
fputs ("skipping test: symlinks not supported on this file system\n",
|
|
Packit Service |
2723c6 |
stderr);
|
|
Packit Service |
2723c6 |
return 77;
|
|
Packit Service |
2723c6 |
}
|
|
Packit Service |
2723c6 |
errno = 0;
|
|
Packit Service |
2723c6 |
ASSERT (func (BASE "dir/link2", -1, -1) == -1);
|
|
Packit Service |
2723c6 |
ASSERT (errno == ENOENT);
|
|
Packit Service |
2723c6 |
errno = 0;
|
|
Packit Service |
2723c6 |
ASSERT (func (BASE "dir/link2/", st1.st_uid, st1.st_gid) == -1);
|
|
Packit Service |
2723c6 |
ASSERT (errno == ENOENT);
|
|
Packit Service |
2723c6 |
ASSERT (symlink ("file", BASE "dir/link") == 0);
|
|
Packit Service |
2723c6 |
|
|
Packit Service |
2723c6 |
/* For non-privileged users, chown can only portably succeed at
|
|
Packit Service |
2723c6 |
changing group ownership of a file we own. If we belong to at
|
|
Packit Service |
2723c6 |
least two groups, then verifying the correct change is simple.
|
|
Packit Service |
2723c6 |
But if we belong to only one group, then we fall back on the
|
|
Packit Service |
2723c6 |
other observable effect of chown: the ctime must be updated. */
|
|
Packit Service |
2723c6 |
gids_count = mgetgroups (NULL, st1.st_gid, &gids);
|
|
Packit Service |
2723c6 |
if (1 < gids_count)
|
|
Packit Service |
2723c6 |
{
|
|
Packit Service |
2723c6 |
ASSERT (gids[1] != st1.st_gid);
|
|
Packit Service |
2723c6 |
ASSERT (gids[1] != (gid_t) -1);
|
|
Packit Service |
2723c6 |
ASSERT (lstat (BASE "dir/link", &st2) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (st1.st_uid == st2.st_uid);
|
|
Packit Service |
2723c6 |
ASSERT (st1.st_gid == st2.st_gid);
|
|
Packit Service |
2723c6 |
ASSERT (lstat (BASE "dir/link2", &st2) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (st1.st_uid == st2.st_uid);
|
|
Packit Service |
2723c6 |
ASSERT (st1.st_gid == st2.st_gid);
|
|
Packit Service |
2723c6 |
|
|
Packit Service |
2723c6 |
errno = 0;
|
|
Packit Service |
2723c6 |
ASSERT (func (BASE "dir/link2/", -1, gids[1]) == -1);
|
|
Packit Service |
2723c6 |
ASSERT (errno == ENOTDIR);
|
|
Packit Service |
2723c6 |
ASSERT (stat (BASE "dir/file", &st2) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (st1.st_uid == st2.st_uid);
|
|
Packit Service |
2723c6 |
ASSERT (st1.st_gid == st2.st_gid);
|
|
Packit Service |
2723c6 |
ASSERT (lstat (BASE "dir/link", &st2) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (st1.st_uid == st2.st_uid);
|
|
Packit Service |
2723c6 |
ASSERT (st1.st_gid == st2.st_gid);
|
|
Packit Service |
2723c6 |
ASSERT (lstat (BASE "dir/link2", &st2) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (st1.st_uid == st2.st_uid);
|
|
Packit Service |
2723c6 |
ASSERT (st1.st_gid == st2.st_gid);
|
|
Packit Service |
2723c6 |
|
|
Packit Service |
2723c6 |
ASSERT (func (BASE "dir/link2", -1, gids[1]) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (stat (BASE "dir/file", &st2) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (st1.st_uid == st2.st_uid);
|
|
Packit Service |
2723c6 |
ASSERT (gids[1] == st2.st_gid);
|
|
Packit Service |
2723c6 |
ASSERT (lstat (BASE "dir/link", &st2) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (st1.st_uid == st2.st_uid);
|
|
Packit Service |
2723c6 |
ASSERT (st1.st_gid == st2.st_gid);
|
|
Packit Service |
2723c6 |
ASSERT (lstat (BASE "dir/link2", &st2) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (st1.st_uid == st2.st_uid);
|
|
Packit Service |
2723c6 |
ASSERT (st1.st_gid == st2.st_gid);
|
|
Packit Service |
2723c6 |
}
|
|
Packit Service |
2723c6 |
else
|
|
Packit Service |
2723c6 |
{
|
|
Packit Service |
2723c6 |
struct stat l1;
|
|
Packit Service |
2723c6 |
struct stat l2;
|
|
Packit Service |
2723c6 |
ASSERT (stat (BASE "dir/file", &st1) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (lstat (BASE "dir/link", &l1) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (lstat (BASE "dir/link2", &l2) == 0);
|
|
Packit Service |
2723c6 |
|
|
Packit Service |
2723c6 |
nap ();
|
|
Packit Service |
2723c6 |
errno = 0;
|
|
Packit Service |
2723c6 |
ASSERT (func (BASE "dir/link2/", -1, st1.st_gid) == -1);
|
|
Packit Service |
2723c6 |
ASSERT (errno == ENOTDIR);
|
|
Packit Service |
2723c6 |
ASSERT (stat (BASE "dir/file", &st2) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (st1.st_ctime == st2.st_ctime);
|
|
Packit Service |
2723c6 |
ASSERT (get_stat_ctime_ns (&st1) == get_stat_ctime_ns (&st2));
|
|
Packit Service |
2723c6 |
ASSERT (lstat (BASE "dir/link", &st2) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (l1.st_ctime == st2.st_ctime);
|
|
Packit Service |
2723c6 |
ASSERT (get_stat_ctime_ns (&l1) == get_stat_ctime_ns (&st2));
|
|
Packit Service |
2723c6 |
ASSERT (lstat (BASE "dir/link2", &st2) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (l2.st_ctime == st2.st_ctime);
|
|
Packit Service |
2723c6 |
ASSERT (get_stat_ctime_ns (&l2) == get_stat_ctime_ns (&st2));
|
|
Packit Service |
2723c6 |
|
|
Packit Service |
2723c6 |
ASSERT (func (BASE "dir/link2", -1, st1.st_gid) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (stat (BASE "dir/file", &st2) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (st1.st_ctime < st2.st_ctime
|
|
Packit Service |
2723c6 |
|| (st1.st_ctime == st2.st_ctime
|
|
Packit Service |
2723c6 |
&& get_stat_ctime_ns (&st1) < get_stat_ctime_ns (&st2)));
|
|
Packit Service |
2723c6 |
ASSERT (lstat (BASE "dir/link", &st2) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (l1.st_ctime == st2.st_ctime);
|
|
Packit Service |
2723c6 |
ASSERT (get_stat_ctime_ns (&l1) == get_stat_ctime_ns (&st2));
|
|
Packit Service |
2723c6 |
ASSERT (lstat (BASE "dir/link2", &st2) == 0);
|
|
Packit Service |
2723c6 |
ASSERT (l2.st_ctime == st2.st_ctime);
|
|
Packit Service |
2723c6 |
ASSERT (get_stat_ctime_ns (&l2) == get_stat_ctime_ns (&st2));
|
|
Packit Service |
2723c6 |
}
|
|
Packit Service |
2723c6 |
|
|
Packit Service |
2723c6 |
/* Cleanup. */
|
|
Packit Service |
2723c6 |
free (gids);
|
|
Packit Service |
2723c6 |
ASSERT (unlink (BASE "dir/file") == 0);
|
|
Packit Service |
2723c6 |
ASSERT (unlink (BASE "dir/link") == 0);
|
|
Packit Service |
2723c6 |
ASSERT (unlink (BASE "dir/link2") == 0);
|
|
Packit Service |
2723c6 |
ASSERT (rmdir (BASE "dir") == 0);
|
|
Packit Service |
2723c6 |
return 0;
|
|
Packit Service |
2723c6 |
}
|