<pre style='margin:0'>
Christopher Nielsen (mascguy) pushed a commit to branch master
in repository macports-legacy-support.

</pre>
<p><a href="https://github.com/macports/macports-legacy-support/commit/8b5a02f8129869dbb715a953551f808fbf13d3b0">https://github.com/macports/macports-legacy-support/commit/8b5a02f8129869dbb715a953551f808fbf13d3b0</a></p>
<pre style="white-space: pre; background: #F8F8F8">The following commit(s) were added to refs/heads/master by this push:
<span style='display:block; white-space:pre;color:#404040;'>     new 8b5a02f  Fix faccessat (setuid, setgid, AT_EACCESS, F_OK)
</span>8b5a02f is described below

<span style='display:block; white-space:pre;color:#808000;'>commit 8b5a02f8129869dbb715a953551f808fbf13d3b0
</span>Author: raf <raf@raf.org>
AuthorDate: Mon Sep 4 19:51:18 2023 +1000

<span style='display:block; white-space:pre;color:#404040;'>    Fix faccessat (setuid, setgid, AT_EACCESS, F_OK)
</span><span style='display:block; white-space:pre;color:#404040;'>    
</span><span style='display:block; white-space:pre;color:#404040;'>    Co-authored-by: Kirill A. Korinsky <kirill@korins.ky>
</span><span style='display:block; white-space:pre;color:#404040;'>    Co-authored-by: Alex Jones <24862823+acjones8@users.noreply.github.com>
</span>---
 Makefile                      |  14 +-
 src/atcalls.c                 |  37 ++--
 test/do_test_faccessat_setuid | 184 ++++++++++++++++++++
 test/test_faccessat.c         | 382 ++++++++++++++++++++++++++++++++++++++++++
 test/test_time.cpp            |  14 --
 5 files changed, 598 insertions(+), 33 deletions(-)

<span style='display:block; white-space:pre;color:#808080;'>diff --git a/Makefile b/Makefile
</span><span style='display:block; white-space:pre;color:#808080;'>index a4cd05b..f7d19c9 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/Makefile
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/Makefile
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -295,6 +295,16 @@ test_cmath: test/test_cmath.cc $(ALLHEADERS)
</span>   $(info 4: $(CXX) -I$(SRCINCDIR) $(CXXFLAGS) -std=c++11 $< -o test/$@_cxx11)
        @-$(CXX) -I$(SRCINCDIR) $(CXXFLAGS) -std=c++11 $< -o test/$@_cxx11 &> /dev/null && echo "4: c++11 legacy cmath build success (test succeeded)!" || echo "4: c++11 legacy cmath build failure (test failed)!"
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+# Special clause for testing faccessat in a setuid program.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Must be run by root.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Assumes there is a _uucp user.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Tests setuid _uucp, setuid root, and setgid tty.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+test_faccessat_setuid: test/test_faccessat
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   @test/do_test_faccessat_setuid "$(BUILDDLIBPATH)"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+test_faccessat_setuid_msg:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   @echo 'Run "sudo make test_faccessat_setuid" to test faccessat properly'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span> $(TESTRUNS): $(TESTRUNPREFIX)%: $(TESTNAMEPREFIX)%
        $<
 
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -325,11 +335,11 @@ install-slib: $(BUILDSLIBPATH)
</span>   $(MKINSTALLDIRS) $(DESTDIR)$(LIBDIR)
        $(INSTALL_DATA) $(BUILDSLIBPATH) $(DESTDIR)$(LIBDIR)
 
<span style='display:block; white-space:pre;background:#ffe0e0;'>-test check: $(TESTRUNS) test_cmath
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+test check: $(TESTRUNS) test_cmath test_faccessat_setuid_msg
</span> 
 clean:
        $(RM) $(foreach D,$(SRCDIR) $(TESTDIR),$D/*.o $D/*.o.* $D/*.d)
<span style='display:block; white-space:pre;background:#ffe0e0;'>-        $(RM) $(BUILDDLIBPATH) $(BUILDSLIBPATH) $(BUILDSYSLIBPATH) $(TESTPRGS) test/test_cmath_*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   $(RM) $(BUILDDLIBPATH) $(BUILDSLIBPATH) $(BUILDSYSLIBPATH) $(TESTPRGS) test/test_cmath_* test/test_faccessat_setuid
</span>   @$(RMDIR) $(BUILDDLIBDIR) $(BUILDSLIBDIR)
 
 .PHONY: all dlib slib clean check test $(TESTRUNS) test_cmath
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/atcalls.c b/src/atcalls.c
</span><span style='display:block; white-space:pre;color:#808080;'>index 8b9bbe8..1b7f07f 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/atcalls.c
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/atcalls.c
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -2,6 +2,7 @@
</span>  * atcalls.c
  *
  * Copyright (c) 2013-2015 Apple Inc. All rights reserved.
<span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Copyright (C) 2023 raf <raf@raf.org>
</span>  *
  * @APPLE_LICENSE_HEADER_START@
  *
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -94,23 +95,23 @@ fail:
</span> 
 int faccessat(int dirfd, const char *pathname, int mode, int flags)
 {
<span style='display:block; white-space:pre;background:#ffe0e0;'>-    struct attrlist al = {
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-        .bitmapcount = ATTR_BIT_MAP_COUNT,
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-        .commonattr  = ATTR_CMN_USERACCESS,
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-    };
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-    struct {
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-        uint32_t length;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-        uint32_t access;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-    } __attribute__((aligned(4), packed)) ab;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-    unsigned long opts = 0;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-    ERR_ON(EINVAL, flags & ~AT_SYMLINK_NOFOLLOW);
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-    if (flags & AT_SYMLINK_NOFOLLOW)
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-        opts |= FSOPT_NOFOLLOW;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-    if (getattrlistat(dirfd, pathname, &al, &ab, sizeof(ab), opts) < 0)
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-        return -1;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-    ERR_ON(EACCES, (ab.access & mode) != (uint32_t)mode);
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-    return 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    ERR_ON(EINVAL, flags & ~AT_EACCESS);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    uid_t ruid = getuid(), euid = geteuid();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    gid_t rgid = getgid(), egid = getegid();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    int check_euid = ruid != euid && (flags & AT_EACCESS);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    int check_egid = rgid != egid && (flags & AT_EACCESS);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (check_euid)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        setreuid(euid, ruid);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (check_egid)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        setregid(egid, rgid);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    int access_rc = ATCALL(dirfd, pathname, access(pathname, mode));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    int access_errno = errno;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (check_euid)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        setreuid(ruid, euid);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (check_egid)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        setregid(rgid, egid);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    errno = access_errno;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return access_rc;
</span> }
 
 #if 0
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -299,3 +300,5 @@ int unlinkat(int dirfd, const char *pathname, int flags)
</span> }
 
 #endif  /* __MP_LEGACY_SUPPORT_ATCALLS__ */
<span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/* vi:set et ts=4 sw=4: */
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/test/do_test_faccessat_setuid b/test/do_test_faccessat_setuid
</span>new file mode 100755
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..e978ce8
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/test/do_test_faccessat_setuid
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,184 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#!/bin/sh
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Copyright (C) 2023 raf <raf@raf.org>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Permission to use, copy, modify, and distribute this software for any
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# purpose with or without fee is hereby granted, provided that the above
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# copyright notice and this permission notice appear in all copies.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# When run as root, test faccessat() properly
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+if [ "$(whoami)" != root ]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+then
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   echo 'Run "sudo make test_faccessat_setuid" to test faccessat properly'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   exit 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+fi
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Define some names and functions
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+lib="$1"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+s=test/test_faccessat_setuid
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+t=test/tmp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+fail=0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+die() { echo "$0: $@" >&2; rm -rf $s $t; exit 1; }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+setid() # usage: setid owner group mode fname [createcmd]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   if [ -n "$5" ]; then $5 $4 || die "$5 $4 failed"; fi
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   chown $1 $4 || die "chown $1 $4 failed"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   chgrp $2 $4 || die "chgrp $2 $4 failed"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   chmod $3 $4 || die "chmod $3 $4 failed"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+setup() # usage: setup owner group mode
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   cp test/test_faccessat $s || die "cp $s failed"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   # The following replaces the @executable_path-based library reference
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   # with an absolute path reference to the library-being-tested.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   # This is needed for a setuid/setgid program to be allowed to load a library.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   install_name_tool -change "@executable_path/../$lib" "$(pwd)/$lib" $s || die "install_name_tool $s failed"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   setid $1 $2 $3 $s
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   setid $1 $2 $3 $t mkdir
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+check()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   "$@" || fail=1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+clean()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   rm -rf $s $t
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+get_group()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   echo $(id $1 | sed -E 's/^.*gid=-?[0-9]+\(//; s/\).*$//')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+get_supp_group()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   echo $(id $1 | sed -E 's/^.*groups=-?[0-9]+\([^)]+\),-?[0-9]+\(//; s/\).*$//')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+uucp_group=$(get_group _uucp)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+nobody_group=$(get_group nobody)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+nobody_supp_group=$(get_supp_group nobody)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Run normal test as setuid _uucp (to test AT_EACCESS)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+echo setuid _uucp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+setup _uucp $uucp_group 4755
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+check sudo -u nobody $s
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+clean
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Run normal test as setuid root (to test AT_EACCESS)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+echo setuid root
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+setup root wheel 4755
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+check sudo -u nobody $s
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+clean
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Test different numbers of leading dirs and leading dirs with different
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# permissions (to test leading executable check)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+setup _uucp $uucp_group 4755
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+setid _uucp $uucp_group 644 $t/f touch
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+setid _uucp $uucp_group 755 $t/d1 mkdir
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+setid _uucp $uucp_group 000 $t/d2 mkdir
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+setid _uucp $uucp_group 644 $t/d1/f touch
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+setid _uucp $uucp_group 644 $t/d2/f touch
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+echo leading dirs ruid=nobody euid=_uucp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+check sudo -u nobody $s test test/ \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   $t $t/ $t/f \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   $t/d1 $t/d1/ $t/d1/f \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   $t/d2 $t/d2/ $t/d2/f
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+echo leading dirs ruid=root euid=_uucp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+check $s test test/ \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   $t $t/ $t/f \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   $t/d1 $t/d1/ $t/d1/f \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   $t/d2 $t/d2/ $t/d2/f
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+chmod 755 $s
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+echo leading dirs nobody
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+check sudo -u nobody $s test test/ \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   $t $t/ $t/f \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   $t/d1 $t/d1/ $t/d1/f \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   $t/d2 $t/d2/ $t/d2/f
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+echo leading dirs root
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+check $s test test/ \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   $t $t/ $t/f \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   $t/d1 $t/d1/ $t/d1/f \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   $t/d2 $t/d2/ $t/d2/f
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+clean
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Test lots of permissions without setuid/setgid with the same user's files
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# (to test when uid matches)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+modes="444 400 040 004 222 200 020 002 111 100 010 001 000 755 644 777 4755 2755 1777"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+checkperms() # usage: checkperms fuser fgroup mode
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   setup nobody $nobody_group $3
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   cases=
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   for m in $modes
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   do
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           setid $1 $2 $m $t/$m touch
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           setid $1 $2 $m $t/$m.d mkdir
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           cases="$cases $t/$m $t/$m.d"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   done
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   sudo -u nobody $s $cases
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   clean
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+echo perm same user
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+checkperms nobody $nobody_group 755
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Test lots of permissions without setuid/setgid with different user's files
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# (to test when uid/gid don't match)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+echo perm diff user
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+checkperms _uucp $uucp_group 755
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Test lots of permissions without setuid/setgid with different user's files
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# but the same group (to test when uid doesn't match but gid does match)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+echo perm same group
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+checkperms _uucp $nobody_group 755
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Test lots of permissions without setuid/setgid with different user's files
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# but same supplementary group (to test when uid/gid don't match but a
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# supplementary group matches)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+echo perm same supp group
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+checkperms _uucp $nobody_supp_group 755
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Test lots of permissions with setuid with different user's files
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# (to test setuid)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+echo perm setuid _uucp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+checkperms _uucp $uucp_group 4755
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Test lots of permissions with setgid with different user's files
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# (to test setgid)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+echo perm setgid _uucp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+checkperms _uucp $uucp_group 2755
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+exit $fail
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# vi:set ts=4 sw=4:
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/test/test_faccessat.c b/test/test_faccessat.c
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..a923163
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/test/test_faccessat.c
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,382 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Copyright (C) 2023 raf <raf@raf.org>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Permission to use, copy, modify, and distribute this software for any
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * purpose with or without fee is hereby granted, provided that the above
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * copyright notice and this permission notice appear in all copies.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <stdlib.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <stdio.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <unistd.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <errno.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <fcntl.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <string.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <sys/stat.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <pwd.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <grp.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+int main(int ac, char **av)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int failures = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   uid_t real_uid = getuid();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   uid_t effective_uid = geteuid();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   uid_t real_gid = getgid();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   uid_t effective_gid = getegid();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   // When supplied with arguments, compare faccessat() against access().
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   // We can't test AT_EACCESS here because access() can't do that.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   // But some tests will be setuid, some setgid, and some neither.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   // See test/do_test_faccessat_setuid for details.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   if (ac > 1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           // For each file, test all modes in isolation and in all combinations
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           int modes[8] = { F_OK, R_OK, W_OK, X_OK, R_OK|W_OK, W_OK|X_OK, X_OK|R_OK, R_OK|W_OK|X_OK };
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           int a, m;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           for (a = 1; a < ac; ++a)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   for (m = 0; m < 8; ++m)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                           int faccessat_rc = faccessat(AT_FDCWD, av[a], modes[m], 0);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                           int faccessat_errno = errno;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                           int access_rc = access(av[a], modes[m]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                           int access_errno = errno;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                           if (faccessat_rc != access_rc || (faccessat_rc < 0 && faccessat_errno != access_errno))
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                           {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                   if (!failures)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                           fprintf(stderr, "faccessat_setuid: uid=%s euid=%s gid=%s egid=%s\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                                   getpwuid(real_uid) ? getpwuid(real_uid)->pw_name : "?",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                                   getpwuid(effective_uid) ? getpwuid(effective_uid)->pw_name : "?",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                                   getgrgid(real_gid) ? getgrgid(real_gid)->gr_name : "?",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                                   getgrgid(effective_gid) ? getgrgid(effective_gid)->gr_name : "?"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                           );
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                   ++failures;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                   fprintf(stderr, "faccessat(%s, %s%s%s%s) fail: %d %s != %d %s\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                           av[a],
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                           (modes[m] == F_OK) ? "F" : "",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                           (modes[m] & R_OK) ? "R" : "",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                           (modes[m] & W_OK) ? "W" : "",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                           (modes[m] & X_OK) ? "X" : "",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                           faccessat_rc,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                           (faccessat_rc == -1) ? strerror(faccessat_errno) : "",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                           access_rc,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                           (access_rc == -1) ? strerror(access_errno) : ""
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                   );
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                   char buf[1024];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                   snprintf(buf, 1024, "/bin/ls -l %s", av[a]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                   system(buf);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                           }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           unlink(av[0]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           return (failures) ? 1 : 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   // Without arguments, just create readable/writable/executable/inaccessible
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   // files and test faccessat() with and without AT_EACCESS. This is part of
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   // the automatic tests, but as part of manual testing might be setuid.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   // Instead of comparing against access(), these tests have explicit expected
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   // results.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   #define TMP "test/tmp"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   const char *readable_path = TMP "/readable";
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   const char *writable_path = TMP "/writable";
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   const char *executable_path = TMP "/executable";
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   const char *inaccessible_path = TMP "/inaccessible";
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   const char *nonexistent_path = TMP "/nonexistent";
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   /* Create test/tmp directory if not already there (see make test_faccessat_setuid) */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int mkdir_rc = mkdir(TMP, 0700);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int mkdir_errno = errno;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   if (mkdir_rc == -1 && mkdir_errno != EEXIST)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           fprintf(stderr, "failed to create tmp directory %s: %s\n", TMP, strerror(errno));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           return 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   /* Create readable/writable/executable/inaccessible files (owned by effective user) */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int readable_fd = creat(readable_path, 0400);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   if (readable_fd == -1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           fprintf(stderr, "failed to create readable file %s: %s\n", readable_path, strerror(errno));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           if (mkdir_rc == 0)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   rmdir(TMP);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           return 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   close(readable_fd);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int writable_fd = creat(writable_path, 0200);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   if (writable_fd == -1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           fprintf(stderr, "failed to create writable file %s: %s\n", writable_path, strerror(errno));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           unlink(readable_path);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           if (mkdir_rc == 0)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   rmdir(TMP);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           return 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   close(writable_fd);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int executable_fd = creat(executable_path, 0100);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   if (executable_fd == -1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           fprintf(stderr, "failed to create executable file %s: %s\n", executable_path, strerror(errno));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           unlink(readable_path);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           unlink(writable_path);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           if (mkdir_rc == 0)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   rmdir(TMP);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           return 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   close(executable_fd);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int inaccessible_fd = creat(inaccessible_path, 0000);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   if (inaccessible_fd == -1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           fprintf(stderr, "failed to create inaccessible file %s: %s\n", inaccessible_path, strerror(errno));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           unlink(readable_path);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           unlink(writable_path);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           unlink(executable_path);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           if (mkdir_rc == 0)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   rmdir(TMP);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           return 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   close(inaccessible_fd);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   /* Test faccessat() with no flags and with AT_EACCESS */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int dfd = open(".", O_RDONLY); errno = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int readable_rok = faccessat(dfd, readable_path, R_OK, 0);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int readable_rok_errno = errno; errno = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int writable_wok = faccessat(dfd, writable_path, W_OK, 0);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int writable_wok_errno = errno; errno = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int executable_xok = faccessat(dfd, executable_path, X_OK, 0);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int executable_xok_errno = errno; errno = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int inaccessible_rok = faccessat(dfd, inaccessible_path, R_OK, 0);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int inaccessible_rok_errno = errno; errno = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int inaccessible_wok = faccessat(dfd, inaccessible_path, W_OK, 0);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int inaccessible_wok_errno = errno; errno = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int inaccessible_xok = faccessat(dfd, inaccessible_path, X_OK, 0);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int inaccessible_xok_errno = errno; errno = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int readable_erok = faccessat(dfd, readable_path, R_OK, AT_EACCESS);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int readable_erok_errno = errno; errno = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int writable_ewok = faccessat(dfd, writable_path, W_OK, AT_EACCESS);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int writable_ewok_errno = errno; errno = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int executable_exok = faccessat(dfd, executable_path, X_OK, AT_EACCESS);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int executable_exok_errno = errno; errno = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int inaccessible_erok = faccessat(dfd, inaccessible_path, R_OK, AT_EACCESS);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int inaccessible_erok_errno = errno; errno = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int inaccessible_ewok = faccessat(dfd, inaccessible_path, W_OK, AT_EACCESS);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int inaccessible_ewok_errno = errno; errno = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int inaccessible_exok = faccessat(dfd, inaccessible_path, X_OK, AT_EACCESS);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int inaccessible_exok_errno = errno; errno = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int readable_fok = faccessat(dfd, readable_path, F_OK, 0);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int readable_fok_errno = errno; errno = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int writable_fok = faccessat(dfd, writable_path, F_OK, 0);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int writable_fok_errno = errno; errno = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int executable_fok = faccessat(dfd, executable_path, F_OK, 0);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int executable_fok_errno = errno; errno = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int inaccessible_fok = faccessat(dfd, inaccessible_path, F_OK, 0);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int inaccessible_fok_errno = errno; errno = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int readable_efok = faccessat(dfd, readable_path, F_OK, AT_EACCESS);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int readable_efok_errno = errno; errno = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int writable_efok = faccessat(dfd, writable_path, F_OK, AT_EACCESS);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int writable_efok_errno = errno; errno = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int executable_efok = faccessat(dfd, executable_path, F_OK, AT_EACCESS);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int executable_efok_errno = errno; errno = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int inaccessible_efok = faccessat(dfd, inaccessible_path, F_OK, AT_EACCESS);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int inaccessible_efok_errno = errno; errno = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int nonexistent_fok = faccessat(dfd, nonexistent_path, F_OK, 0);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int nonexistent_fok_errno = errno; errno = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int nonexistent_efok = faccessat(dfd, nonexistent_path, F_OK, AT_EACCESS);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int nonexistent_efok_errno = errno; errno = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   close (dfd);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   #define TEST(cond, message, e) if (!(cond)) { errno = 0; fprintf(stderr, "%s %s\n", message, (e) ? strerror(e) : ""); ++failures; }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   /* Test F_OK */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   TEST(readable_fok == 0, "root: faccessat(readable F_OK) does not exist = FAIL", readable_fok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   TEST(writable_fok == 0, "root: faccessat(writable F_OK) does not exist = FAIL", writable_fok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   TEST(executable_fok == 0, "root: faccessat(executable F_OK) does not exist = FAIL", executable_fok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   TEST(inaccessible_fok == 0, "root: faccessat(inaccessible F_OK) does not exist = FAIL", inaccessible_fok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   TEST(readable_efok == 0, "root: faccessat(readable F_OK AT_EACCESS) does not exist = FAIL", readable_efok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   TEST(writable_efok == 0, "root: faccessat(writable F_OK AT_EACCESS) does not exist = FAIL", writable_efok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   TEST(executable_efok == 0, "root: faccessat(executable F_OK AT_EACCESS) does not exist = FAIL", executable_efok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   TEST(inaccessible_efok == 0, "root: faccessat(inaccessible F_OK AT_EACCESS) does not exist = FAIL", inaccessible_efok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   TEST(nonexistent_fok == -1, "root: faccessat(nonexistent F_OK) does exist = FAIL", nonexistent_fok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   TEST(nonexistent_efok == -1, "root: faccessat(nonexistent F_OK AT_EACCESS) does exist = FAIL", nonexistent_efok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   /* Test non-setuid program */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   if (real_uid == effective_uid)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           /* Test as non-root user */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           if (real_uid != 0)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   TEST(readable_rok == 0, "not root: faccessat(readable R_OK) is unreadable = FAIL", readable_rok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   TEST(writable_wok == 0, "not root: faccessat(writable W_OK) is unwritable = FAIL", writable_wok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   TEST(executable_xok == 0, "not root: faccessat(executable X_OK) is unexecutable = FAIL", executable_xok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   TEST(inaccessible_rok == -1, "not root: faccessat(inaccessible R_OK) is readable = FAIL", inaccessible_rok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   TEST(inaccessible_wok == -1, "not root: faccessat(inaccessible W_OK) is writable = FAIL", inaccessible_wok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   TEST(inaccessible_xok == -1, "not root: faccessat(inaccessible X_OK) is executable = FAIL", inaccessible_xok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   /* This isn't meaningful because uid == euid */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   TEST(readable_erok == 0, "not root: faccessat(readable R_OK AT_EACCESS) is unreadable = FAIL", readable_erok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   TEST(writable_ewok == 0, "not root: faccessat(writable W_OK AT_EACCESS) is unwritable = FAIL", writable_ewok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   TEST(executable_exok == 0, "not root: faccessat(executable X_OK AT_EACCESS) is unexecutable = FAIL", executable_exok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   TEST(inaccessible_erok == -1, "not root: faccessat(inaccessible R_OK AT_EACCESS) is readable = FAIL", inaccessible_erok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   TEST(inaccessible_ewok == -1, "not root: faccessat(inaccessible W_OK AT_EACCESS) is writable = FAIL", inaccessible_ewok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   TEST(inaccessible_exok == -1, "not root: faccessat(inaccessible X_OK AT_EACCESS) is executable = FAIL", inaccessible_exok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           /* Test as root user */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           else
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   TEST(readable_rok == 0, "root: faccessat(readable R_OK) is unreadable = FAIL", readable_rok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   TEST(writable_wok == 0, "root: faccessat(writable W_OK) is unwritable = FAIL", writable_wok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   TEST(executable_xok == 0, "root: faccessat(executable X_OK) is unexecutable = FAIL", executable_xok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   TEST(inaccessible_rok == 0, "root: faccessat(inaccessible R_OK) is unreadable = FAIL", inaccessible_rok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   TEST(inaccessible_wok == 0, "root: faccessat(inaccessible W_OK) is unwritable = FAIL", inaccessible_wok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   TEST(inaccessible_xok == -1, "root: faccessat(inaccessible X_OK) is executable = FAIL", inaccessible_xok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   /* This isn't meaningful because uid == euid */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   TEST(readable_erok == 0, "root: faccessat(readable R_OK AT_EACCESS) is unreadable = FAIL", readable_erok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   TEST(writable_ewok == 0, "root: faccessat(writable W_OK AT_EACCESS) is unwritable = FAIL", writable_ewok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   TEST(executable_exok == 0, "root: faccessat(executable X_OK AT_EACCESS) is unexecutable = FAIL", executable_exok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   TEST(inaccessible_erok == 0, "root: faccessat(inaccessible R_OK AT_EACCESS) is unreadable = FAIL", inaccessible_erok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   TEST(inaccessible_ewok == 0, "root: faccessat(inaccessible W_OK AT_EACCESS) is unwritable = FAIL", inaccessible_ewok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   TEST(inaccessible_exok == -1, "root: faccessat(inaccessible X_OK AT_EACCESS) is executable = FAIL", inaccessible_exok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   /* Test setuid root program */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   /* Note: Must be setuid root, run as non-root, linked using absolute path */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   else if (real_uid != 0 && effective_uid == 0)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(readable_rok == -1, "setuid root: non-root user: faccessat(readable R_OK) is readable = FAIL", readable_rok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(writable_wok == -1, "setuid root: non-root user: faccessat(writable W_OK) is writable = FAIL", writable_wok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(executable_xok == -1, "setuid root: non-root user: faccessat(executable X_OK) is executable = FAIL", executable_xok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(inaccessible_rok == -1, "setuid root: non-root user: faccessat(inaccessible R_OK) is readable = FAIL", inaccessible_rok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(inaccessible_wok == -1, "setuid root: non-root user: faccessat(inaccessible W_OK) is writable = FAIL", inaccessible_wok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(inaccessible_xok == -1, "setuid root: non-root user: faccessat(inaccessible X_OK) is executable = FAIL", inaccessible_xok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           /* This is meaningful because uid != euid */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(readable_erok == 0, "setuid root: non-root user: faccessat(readable R_OK AT_EACCESS) is unreadable = FAIL", readable_erok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(writable_ewok == 0, "setuid root: non-root user: faccessat(writable W_OK AT_EACCESS) is unwritable = FAIL", writable_ewok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(executable_exok == 0, "setuid root: non-root user: faccessat(executable X_OK AT_EACCESS) is unexecutable = FAIL", executable_exok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(inaccessible_erok == 0, "setuid root: non-root user: faccessat(inaccessible R_OK AT_EACCESS) is unreadable = FAIL", inaccessible_erok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(inaccessible_ewok == 0, "setuid root: non-root user: faccessat(inaccessible W_OK AT_EACCESS) is unwritable = FAIL", inaccessible_ewok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(inaccessible_exok == -1, "setuid root: non-root user: faccessat(inaccessible X_OK AT_EACCESS) is executable = FAIL", inaccessible_exok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   /* Test setuid non-root program */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   /* Note: Must be setuid, run as non-root other user, linked using absolute path */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   else if (real_uid != 0 && effective_uid != real_uid)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(readable_rok == -1, "setuid non-root: non-root other user: faccessat(readable R_OK) is readable = FAIL", readable_rok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(writable_wok == -1, "setuid non-root: non-root other user: faccessat(writable W_OK) is writable = FAIL", writable_wok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(executable_xok == -1, "setuid non-root: non-root other user: faccessat(executable X_OK) is executable = FAIL", executable_xok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(inaccessible_rok == -1, "setuid non-root: non-root other user: faccessat(inaccessible R_OK) is readable = FAIL", inaccessible_rok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(inaccessible_wok == -1, "setuid non-root: non-root other user: faccessat(inaccessible W_OK) is writable = FAIL", inaccessible_wok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(inaccessible_xok == -1, "setuid non-root: non-root other user: faccessat(inaccessible X_OK) is executable = FAIL", inaccessible_xok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           /* This is meaningful because uid != euid */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(readable_erok == 0, "setuid non-root: non-root other user: faccessat(readable R_OK AT_EACCESS) is unreadable = FAIL", readable_erok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(writable_ewok == 0, "setuid non-root: non-root other user: faccessat(writable W_OK AT_EACCESS) is unwritable = FAIL", writable_ewok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(executable_exok == 0, "setuid non-root: non-root other user: faccessat(executable X_OK AT_EACCESS) is unexecutable = FAIL", executable_exok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(inaccessible_erok == -1, "setuid non-root: non-root other user: faccessat(inaccessible R_OK AT_EACCESS) is readable = FAIL", inaccessible_erok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(inaccessible_ewok == -1, "setuid non-root: non-root other user: faccessat(inaccessible W_OK AT_EACCESS) is writable = FAIL", inaccessible_ewok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(inaccessible_exok == -1, "setuid non-root: non-root other user: faccessat(inaccessible X_OK AT_EACCESS) is executable = FAIL", inaccessible_exok_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   /* Report any unexpected situation (won't happen) */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   else
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           fprintf(stderr, "This uid/euid combination isn't currently tested.\n");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           ++failures;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   /* Only show the test files and process permissions if there were failures */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   if (failures)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           fprintf(stderr, "faccessat_setuid: uid=%s euid=%s gid=%s egid=%s\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   getpwuid(real_uid) ? getpwuid(real_uid)->pw_name : "?",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   getpwuid(effective_uid) ? getpwuid(effective_uid)->pw_name : "?",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   getgrgid(real_gid) ? getgrgid(real_gid)->gr_name : "?",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   getgrgid(effective_gid) ? getgrgid(effective_gid)->gr_name : "?"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           );
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           system("/bin/ls -l test/tmp/*ble");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   /* Test argument validation */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int check_pathname_rc = faccessat(AT_FDCWD, NULL, R_OK, 0);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int check_pathname_errno = errno;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int check_dirfd_rc = faccessat(-1, "pathname", R_OK, 0);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int check_dirfd_errno = errno;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int check_mode_rc = faccessat(AT_FDCWD, "pathname", -1, 0); // Apple doesn't check this
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int check_mode_errno = errno;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int check_flag_rc = faccessat(AT_FDCWD, "pathname", R_OK, -1);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int check_flag_errno = errno;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   TEST(check_pathname_rc == -1, "check pathname failed", 0)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   if (check_pathname_rc == -1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(check_pathname_errno == EFAULT, "check pathname errno wrong (should be EFAULT)", check_pathname_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   TEST(check_dirfd_rc == -1, "check dirfd failed", 0)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   if (check_dirfd_rc == -1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(check_dirfd_errno == EBADF, "check dirfd errno wrong (should be EBADF)", check_dirfd_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   TEST(check_mode_rc == -1, "check mode failed", 0) // Apple doesn't check this argument - failure is ENOENT/EPERM
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   //if (check_mode_rc == -1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   //      TEST(check_mode_errno == EINVAL, "check mode errno wrong (should be EINVAL)", check_mode_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   if (check_mode_rc == -1) // On 10.14 this is ENOENT. On 10.6 it's EPERM
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(check_mode_errno == ENOENT || check_mode_errno == EPERM, "check mode errno wrong (should be ENOENT or EPERM)", check_mode_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   TEST(check_flag_rc == -1, "check flag failed", 0)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   if (check_flag_rc == -1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           TEST(check_flag_errno == EINVAL, "check flag errno wrong (should be EINVAL)", check_flag_errno)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   /* Cleanup */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   unlink(readable_path);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   unlink(writable_path);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   unlink(executable_path);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   unlink(inaccessible_path);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   if (mkdir_rc == 0)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           rmdir(TMP);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   /* Delete test/test_faccessat_setuid. Don't wait for make clean. */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   if (real_uid != effective_uid || real_gid != effective_gid)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           unlink(av[0]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   return (failures) ? 1 : 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/* vi:set noet ts=4 sw=4: */
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/test/test_time.cpp b/test/test_time.cpp
</span><span style='display:block; white-space:pre;color:#808080;'>index 41b2a03..236ea18 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/test/test_time.cpp
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/test/test_time.cpp
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -72,20 +72,6 @@ static void* test_blackhole_thread(void *arg)
</span>   return NULL;
 }
 
<span style='display:block; white-space:pre;background:#ffe0e0;'>-static void* test_wait_thread(void *arg)
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-{
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  int c = 0;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  while ( ++c < 10 )
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  {
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-    usleep(100);
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-    ::pthread_mutex_lock(&lock);
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-    std::cout << "[t2] CLOCK_THREAD_CPUTIME_ID ("<< CLOCK_THREAD_CPUTIME_ID << ") " << time(CLOCK_THREAD_CPUTIME_ID) << std::endl;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-    ::pthread_mutex_unlock(&lock);
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  }
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  return NULL;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-}
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-
</span> int main()
 {
   {
</pre><pre style='margin:0'>

</pre>