Blame gnulib-tests/test-chown.h

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
}