root/maint/gnulib/tests/test-chown.h

/* [previous][next][first][last][top][bottom][index][help] */

INCLUDED FROM


DEFINITIONS

This source file includes following definitions.
  1. test_chown

   1 /* Tests of chown.
   2    Copyright (C) 2009-2021 Free Software Foundation, Inc.
   3 
   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.
   8 
   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.
  13 
  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/>.  */
  16 
  17 /* Written by Eric Blake <ebb9@byu.net>, 2009.  */
  18 
  19 #include "nap.h"
  20 
  21 #if !HAVE_GETEGID
  22 # define getegid() ((gid_t) -1)
  23 #endif
  24 
  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.  */
  30 
  31 static int
  32 test_chown (int (*func) (char const *, uid_t, gid_t), bool print)
     /* [previous][next][first][last][top][bottom][index][help] */
  33 {
  34   struct stat st1;
  35   struct stat st2;
  36   gid_t *gids = NULL;
  37   int gids_count;
  38   int result;
  39 
  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);
  59 
  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))
  63     {
  64       ASSERT (rmdir (BASE "dir") == 0);
  65       if (print)
  66         fputs ("skipping test: no support for ownership\n", stderr);
  67       return 77;
  68     }
  69   ASSERT (result == 0);
  70 
  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 ());
  76 
  77   /* Sanity check of error cases.  */
  78   errno = 0;
  79   ASSERT (func ("", -1, -1) == -1);
  80   ASSERT (errno == ENOENT);
  81   errno = 0;
  82   ASSERT (func ("no_such", -1, -1) == -1);
  83   ASSERT (errno == ENOENT);
  84   errno = 0;
  85   ASSERT (func ("no_such/", -1, -1) == -1);
  86   ASSERT (errno == ENOENT);
  87   errno = 0;
  88   ASSERT (func (BASE "dir/file/", -1, -1) == -1);
  89   ASSERT (errno == ENOTDIR);
  90 
  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);
  98 
  99   /* Even if the values aren't changing, ctime is required to change
 100      if at least one argument is not -1.  */
 101   nap ();
 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)));
 107 
 108   /* Test symlink behavior.  */
 109   if (symlink ("link", BASE "dir/link2"))
 110     {
 111       ASSERT (unlink (BASE "dir/file") == 0);
 112       ASSERT (rmdir (BASE "dir") == 0);
 113       if (print)
 114         fputs ("skipping test: symlinks not supported on this file system\n",
 115                stderr);
 116       return 77;
 117     }
 118   errno = 0;
 119   ASSERT (func (BASE "dir/link2", -1, -1) == -1);
 120   ASSERT (errno == ENOENT);
 121   errno = 0;
 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);
 125 
 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);
 132   if (1 < gids_count)
 133     {
 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);
 142 
 143       errno = 0;
 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);
 155 
 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);
 166     }
 167   else
 168     {
 169       struct stat l1;
 170       struct stat l2;
 171       ASSERT (stat (BASE "dir/file", &st1) == 0);
 172       ASSERT (lstat (BASE "dir/link", &l1) == 0);
 173       ASSERT (lstat (BASE "dir/link2", &l2) == 0);
 174 
 175       nap ();
 176       errno = 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));
 188 
 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));
 200     }
 201 
 202   /* Cleanup.  */
 203   free (gids);
 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);
 208   return 0;
 209 }

/* [previous][next][first][last][top][bottom][index][help] */