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

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

INCLUDED FROM


DEFINITIONS

This source file includes following definitions.
  1. test_lchown

   1 /* Tests of lchown.
   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 #ifndef HAVE_LCHMOD
  26 # define HAVE_LCHMOD 0
  27 #endif
  28 
  29 #ifndef CHOWN_CHANGE_TIME_BUG
  30 # define CHOWN_CHANGE_TIME_BUG 0
  31 #endif
  32 
  33 /* This file is designed to test lchown(n,o,g) and
  34    chownat(AT_FDCWD,n,o,g,AT_SYMLINK_NOFOLLOW).  FUNC is the function
  35    to test.  Assumes that BASE and ASSERT are already defined, and
  36    that appropriate headers are already included.  If PRINT, warn
  37    before skipping symlink tests with status 77.  */
  38 
  39 static int
  40 test_lchown (int (*func) (char const *, uid_t, gid_t), bool print)
     /* [previous][next][first][last][top][bottom][index][help] */
  41 {
  42   struct stat st1;
  43   struct stat st2;
  44   gid_t *gids = NULL;
  45   int gids_count;
  46   int result;
  47 
  48   /* Solaris 8 is interesting - if the current process belongs to
  49      multiple groups, the current directory is owned by a group that
  50      the current process belongs to but different than getegid(), and
  51      the current directory does not have the S_ISGID bit, then regular
  52      files created in the directory belong to the directory's group,
  53      but symlinks belong to the current effective group id.  If
  54      S_ISGID is set, then both files and symlinks belong to the
  55      directory's group.  However, it is possible to run the testsuite
  56      from within a directory owned by a group we don't belong to, in
  57      which case all things that we create belong to the current
  58      effective gid.  So, work around the issues by creating a
  59      subdirectory (we are guaranteed that the subdirectory will be
  60      owned by one of our current groups), change ownership of that
  61      directory to the current effective gid (which will thus succeed),
  62      then create all other files within that directory (eliminating
  63      questions on whether inheritance or current id triumphs, since
  64      the two methods resolve to the same gid).  */
  65   ASSERT (mkdir (BASE "dir", 0700) == 0);
  66   ASSERT (stat (BASE "dir", &st1) == 0);
  67 
  68   /* Filter out mingw and file systems which have no concept of groups.  */
  69   result = func (BASE "dir", st1.st_uid, getegid ());
  70   if (result == -1 && (errno == ENOSYS || errno == EPERM))
  71     {
  72       ASSERT (rmdir (BASE "dir") == 0);
  73       if (print)
  74         fputs ("skipping test: no support for ownership\n", stderr);
  75       return 77;
  76     }
  77   ASSERT (result == 0);
  78 
  79   ASSERT (close (creat (BASE "dir/file", 0600)) == 0);
  80   ASSERT (stat (BASE "dir/file", &st1) == 0);
  81   ASSERT (st1.st_uid != (uid_t) -1);
  82   ASSERT (st1.st_gid != (gid_t) -1);
  83   ASSERT (st1.st_gid == getegid ());
  84 
  85   /* Sanity check of error cases.  */
  86   errno = 0;
  87   ASSERT (func ("", -1, -1) == -1);
  88   ASSERT (errno == ENOENT);
  89   errno = 0;
  90   ASSERT (func ("no_such", -1, -1) == -1);
  91   ASSERT (errno == ENOENT);
  92   errno = 0;
  93   ASSERT (func ("no_such/", -1, -1) == -1);
  94   ASSERT (errno == ENOENT);
  95   errno = 0;
  96   ASSERT (func (BASE "dir/file/", -1, -1) == -1);
  97   ASSERT (errno == ENOTDIR);
  98 
  99   /* Check that -1 does not alter ownership.  */
 100   ASSERT (func (BASE "dir/file", -1, st1.st_gid) == 0);
 101   ASSERT (func (BASE "dir/file", st1.st_uid, -1) == 0);
 102   ASSERT (func (BASE "dir/file", (uid_t) -1, (gid_t) -1) == 0);
 103   ASSERT (stat (BASE "dir/file", &st2) == 0);
 104   ASSERT (st1.st_uid == st2.st_uid);
 105   ASSERT (st1.st_gid == st2.st_gid);
 106 
 107   /* Even if the values aren't changing, ctime is required to change
 108      if at least one argument is not -1.  */
 109   nap ();
 110   ASSERT (func (BASE "dir/file", st1.st_uid, st1.st_gid) == 0);
 111   ASSERT (stat (BASE "dir/file", &st2) == 0);
 112   ASSERT (st1.st_ctime < st2.st_ctime
 113           || (st1.st_ctime == st2.st_ctime
 114               && get_stat_ctime_ns (&st1) < get_stat_ctime_ns (&st2)));
 115 
 116   /* Test symlink behavior.  */
 117   if (symlink ("link", BASE "dir/link2"))
 118     {
 119       ASSERT (unlink (BASE "dir/file") == 0);
 120       ASSERT (rmdir (BASE "dir") == 0);
 121       if (print)
 122         fputs ("skipping test: symlinks not supported on this file system\n",
 123                stderr);
 124       return 77;
 125     }
 126   result = func (BASE "dir/link2", -1, -1);
 127   if (result == -1 && (errno == ENOSYS || errno == EOPNOTSUPP))
 128     {
 129       ASSERT (unlink (BASE "dir/file") == 0);
 130       ASSERT (unlink (BASE "dir/link2") == 0);
 131       ASSERT (rmdir (BASE "dir") == 0);
 132       if (print)
 133         fputs ("skipping test: symlink ownership not supported\n", stderr);
 134       return 77;
 135     }
 136   ASSERT (result == 0);
 137   errno = 0;
 138   ASSERT (func (BASE "dir/link2/", st1.st_uid, st1.st_gid) == -1);
 139   ASSERT (errno == ENOENT);
 140   ASSERT (symlink ("file", BASE "dir/link") == 0);
 141   ASSERT (mkdir (BASE "dir/sub", 0700) == 0);
 142   ASSERT (symlink ("sub", BASE "dir/link3") == 0);
 143 
 144   /* For non-privileged users, lchown can only portably succeed at
 145      changing group ownership of a file we own.  If we belong to at
 146      least two groups, then verifying the correct change is simple.
 147      But if we belong to only one group, then we fall back on the
 148      other observable effect of lchown: the ctime must be updated.  */
 149   gids_count = mgetgroups (NULL, st1.st_gid, &gids);
 150   if (1 < gids_count)
 151     {
 152       ASSERT (gids[1] != st1.st_gid);
 153       ASSERT (gids[1] != (gid_t) -1);
 154       ASSERT (lstat (BASE "dir/link", &st2) == 0);
 155       ASSERT (st1.st_uid == st2.st_uid);
 156       ASSERT (st1.st_gid == st2.st_gid);
 157       ASSERT (lstat (BASE "dir/link2", &st2) == 0);
 158       ASSERT (st1.st_uid == st2.st_uid);
 159       ASSERT (st1.st_gid == st2.st_gid);
 160 
 161       errno = 0;
 162       ASSERT (func (BASE "dir/link2/", -1, gids[1]) == -1);
 163       ASSERT (errno == ENOTDIR);
 164       ASSERT (stat (BASE "dir/file", &st2) == 0);
 165       ASSERT (st1.st_uid == st2.st_uid);
 166       ASSERT (st1.st_gid == st2.st_gid);
 167       ASSERT (lstat (BASE "dir/link", &st2) == 0);
 168       ASSERT (st1.st_uid == st2.st_uid);
 169       ASSERT (st1.st_gid == st2.st_gid);
 170       ASSERT (lstat (BASE "dir/link2", &st2) == 0);
 171       ASSERT (st1.st_uid == st2.st_uid);
 172       ASSERT (st1.st_gid == st2.st_gid);
 173 
 174       ASSERT (func (BASE "dir/link2", -1, gids[1]) == 0);
 175       ASSERT (stat (BASE "dir/file", &st2) == 0);
 176       ASSERT (st1.st_uid == st2.st_uid);
 177       ASSERT (st1.st_gid == st2.st_gid);
 178       ASSERT (lstat (BASE "dir/link", &st2) == 0);
 179       ASSERT (st1.st_uid == st2.st_uid);
 180       ASSERT (st1.st_gid == st2.st_gid);
 181       ASSERT (lstat (BASE "dir/link2", &st2) == 0);
 182       ASSERT (st1.st_uid == st2.st_uid);
 183       ASSERT (gids[1] == st2.st_gid);
 184 
 185       /* Trailing slash follows through to directory.  */
 186       ASSERT (lstat (BASE "dir/link3", &st2) == 0);
 187       ASSERT (st1.st_uid == st2.st_uid);
 188       ASSERT (st1.st_gid == st2.st_gid);
 189       ASSERT (lstat (BASE "dir/sub", &st2) == 0);
 190       ASSERT (st1.st_uid == st2.st_uid);
 191       ASSERT (st1.st_gid == st2.st_gid);
 192 
 193       ASSERT (func (BASE "dir/link3/", -1, gids[1]) == 0);
 194       ASSERT (lstat (BASE "dir/link3", &st2) == 0);
 195       ASSERT (st1.st_uid == st2.st_uid);
 196       ASSERT (st1.st_gid == st2.st_gid);
 197       ASSERT (lstat (BASE "dir/sub", &st2) == 0);
 198       ASSERT (st1.st_uid == st2.st_uid);
 199       ASSERT (gids[1] == st2.st_gid);
 200     }
 201   else if (!CHOWN_CHANGE_TIME_BUG || HAVE_LCHMOD)
 202     {
 203       /* If we don't have lchmod, and lchown fails to change ctime,
 204          then we can't test this part of lchown.  */
 205       struct stat l1;
 206       struct stat l2;
 207       ASSERT (stat (BASE "dir/file", &st1) == 0);
 208       ASSERT (lstat (BASE "dir/link", &l1) == 0);
 209       ASSERT (lstat (BASE "dir/link2", &l2) == 0);
 210 
 211       nap ();
 212       errno = 0;
 213       ASSERT (func (BASE "dir/link2/", -1, st1.st_gid) == -1);
 214       ASSERT (errno == ENOTDIR);
 215       ASSERT (stat (BASE "dir/file", &st2) == 0);
 216       ASSERT (st1.st_ctime == st2.st_ctime);
 217       ASSERT (get_stat_ctime_ns (&st1) == get_stat_ctime_ns (&st2));
 218       ASSERT (lstat (BASE "dir/link", &st2) == 0);
 219       ASSERT (l1.st_ctime == st2.st_ctime);
 220       ASSERT (get_stat_ctime_ns (&l1) == get_stat_ctime_ns (&st2));
 221       ASSERT (lstat (BASE "dir/link2", &st2) == 0);
 222       ASSERT (l2.st_ctime == st2.st_ctime);
 223       ASSERT (get_stat_ctime_ns (&l2) == get_stat_ctime_ns (&st2));
 224 
 225       ASSERT (func (BASE "dir/link2", -1, st1.st_gid) == 0);
 226       ASSERT (stat (BASE "dir/file", &st2) == 0);
 227       ASSERT (st1.st_ctime == st2.st_ctime);
 228       ASSERT (get_stat_ctime_ns (&st1) == get_stat_ctime_ns (&st2));
 229       ASSERT (lstat (BASE "dir/link", &st2) == 0);
 230       ASSERT (l1.st_ctime == st2.st_ctime);
 231       ASSERT (get_stat_ctime_ns (&l1) == get_stat_ctime_ns (&st2));
 232       ASSERT (lstat (BASE "dir/link2", &st2) == 0);
 233       ASSERT (l2.st_ctime < st2.st_ctime
 234               || (l2.st_ctime == st2.st_ctime
 235                   && get_stat_ctime_ns (&l2) < get_stat_ctime_ns (&st2)));
 236 
 237       /* Trailing slash follows through to directory.  */
 238       ASSERT (lstat (BASE "dir/sub", &st1) == 0);
 239       ASSERT (lstat (BASE "dir/link3", &l1) == 0);
 240       nap ();
 241       ASSERT (func (BASE "dir/link3/", -1, st1.st_gid) == 0);
 242       ASSERT (lstat (BASE "dir/link3", &st2) == 0);
 243       ASSERT (l1.st_ctime == st2.st_ctime);
 244       ASSERT (get_stat_ctime_ns (&l1) == get_stat_ctime_ns (&st2));
 245       ASSERT (lstat (BASE "dir/sub", &st2) == 0);
 246       ASSERT (st1.st_ctime < st2.st_ctime
 247               || (st1.st_ctime == st2.st_ctime
 248                   && get_stat_ctime_ns (&st1) < get_stat_ctime_ns (&st2)));
 249     }
 250 
 251   /* Cleanup.  */
 252   free (gids);
 253   ASSERT (unlink (BASE "dir/file") == 0);
 254   ASSERT (unlink (BASE "dir/link") == 0);
 255   ASSERT (unlink (BASE "dir/link2") == 0);
 256   ASSERT (unlink (BASE "dir/link3") == 0);
 257   ASSERT (rmdir (BASE "dir/sub") == 0);
 258   ASSERT (rmdir (BASE "dir") == 0);
 259   return 0;
 260 }

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