unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#12632: file permissions checking mishandled when setuid
@ 2012-10-13  1:58 Paul Eggert
  2012-10-13  7:23 ` Eli Zaretskii
  2012-11-13  2:19 ` bug#12632: updated version of the patch Paul Eggert
  0 siblings, 2 replies; 47+ messages in thread
From: Paul Eggert @ 2012-10-13  1:58 UTC (permalink / raw)
  To: 12632

Tags: patch

In two places Emacs uses 'euidaccess' if available, falling
back on 'access' otherwise; on GNUish platforms
this properly checks file permissions on when Emacs is
running setuid on platforms that have 'euidaccess'.

Unfortunately, the 'access' fallback means setuid Emacs is
mishandling file permissions on POSIXish platforms that lack
'euidaccess'.  Also, in seven other places Emacs invokes
'access' directly, even though 'euidaccess' is what's
wanted, which means setuid Emacs is mishandling these file
permissions even on GNUish platforms.

Here's a patch.

This patch has a four FIXMEs indicating where the code could
most likely be cleaned up on DOS_NT platforms.  The patch
should work fine as-is on DOS_NT, but it'd be nicer if the
DOS_NT support could supply an euidaccess function to allow
simplifying the mainline code.  I'll CC: this bug report to
Eli to give him a heads-up.

=== modified file 'ChangeLog'
--- ChangeLog	2012-10-11 11:29:47 +0000
+++ ChangeLog	2012-10-13 01:49:41 +0000
@@ -1,3 +1,12 @@
+2012-10-13  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use euidaccess, not access, when checking file permissions.
+	* configure.ac (euidaccess): Remove check; gnulib does this for us now.
+	* lib/euidaccess.c, lib/getgroups.c, lib/group-member.c:
+	* lib/root-uid.h, lib/xalloc-oversized.h, m4/euidaccess.m4:
+	* m4/getgroups.m4, m4/group-member.m4: New files, from gnulib.
+	* lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate.
+
 2012-10-11  Kenichi Handa  <handa@gnu.org>
 
 	* .bzrignore: Add several files under admin/charsets.

=== modified file 'admin/ChangeLog'
--- admin/ChangeLog	2012-10-11 11:29:47 +0000
+++ admin/ChangeLog	2012-10-13 01:49:41 +0000
@@ -1,3 +1,9 @@
+2012-10-13  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use euidaccess, not access, when checking file permissions.
+	* merge-gnulib (GNULIB_MODULES): Add euidaccess.
+	(GNULIB_TOOL_FLAGS): Avoid malloc-posix.
+
 2012-10-11  Kenichi Handa  <handa@gnu.org>
 
 	* charsets/mapconv: Adjusted for the change of mapfiles/*.gz to

=== modified file 'admin/merge-gnulib'
--- admin/merge-gnulib	2012-09-27 01:06:23 +0000
+++ admin/merge-gnulib	2012-10-12 23:28:31 +0000
@@ -28,7 +28,7 @@
 GNULIB_MODULES='
   alloca-opt c-ctype c-strcase
   careadlinkat crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512
-  dtoastr dtotimespec dup2 environ execinfo
+  dtoastr dtotimespec dup2 environ euidaccess execinfo
   filemode getloadavg getopt-gnu gettime gettimeofday
   ignore-value intprops largefile lstat
   manywarnings mktime pselect pthread_sigmask readlink
@@ -40,7 +40,7 @@
 
 GNULIB_TOOL_FLAGS='
   --avoid=errno --avoid=fcntl --avoid=fcntl-h --avoid=fstat
-  --avoid=msvc-inval --avoid=msvc-nothrow
+  --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow
   --avoid=raise --avoid=select --avoid=sigprocmask --avoid=sys_types
   --avoid=threadlib
   --conditional-dependencies --import --no-changelog --no-vc-files

=== modified file 'configure.ac'
--- configure.ac	2012-10-08 16:09:00 +0000
+++ configure.ac	2012-10-12 23:28:31 +0000
@@ -2872,7 +2872,7 @@
 AC_CHECK_FUNCS(gethostname \
 closedir getrusage get_current_dir_name \
 lrand48 setsid \
-fpathconf select euidaccess getpagesize setlocale \
+fpathconf select getpagesize setlocale \
 utimes getrlimit setrlimit setpgid getcwd shutdown getaddrinfo \
 __fpending strsignal setitimer \
 sendto recvfrom getsockname getpeername getifaddrs freeifaddrs \

=== added file 'lib/euidaccess.c'
--- lib/euidaccess.c	1970-01-01 00:00:00 +0000
+++ lib/euidaccess.c	2012-10-12 23:28:31 +0000
@@ -0,0 +1,221 @@
+/* euidaccess -- check if effective user id can access file
+
+   Copyright (C) 1990-1991, 1995, 1998, 2000, 2003-2006, 2008-2012 Free
+   Software Foundation, Inc.
+
+   This file is part of the GNU C Library.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by David MacKenzie and Torbjorn Granlund.
+   Adapted for GNU C library by Roland McGrath.  */
+
+#ifndef _LIBC
+# include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "root-uid.h"
+
+#if HAVE_LIBGEN_H
+# include <libgen.h>
+#endif
+
+#include <errno.h>
+#ifndef __set_errno
+# define __set_errno(val) errno = (val)
+#endif
+
+#if defined EACCES && !defined EACCESS
+# define EACCESS EACCES
+#endif
+
+#ifndef F_OK
+# define F_OK 0
+# define X_OK 1
+# define W_OK 2
+# define R_OK 4
+#endif
+
+
+#ifdef _LIBC
+
+# define access __access
+# define getuid __getuid
+# define getgid __getgid
+# define geteuid __geteuid
+# define getegid __getegid
+# define group_member __group_member
+# define euidaccess __euidaccess
+# undef stat
+# define stat stat64
+
+#endif
+
+/* Return 0 if the user has permission of type MODE on FILE;
+   otherwise, return -1 and set 'errno'.
+   Like access, except that it uses the effective user and group
+   id's instead of the real ones, and it does not always check for read-only
+   file system, text busy, etc.  */
+
+int
+euidaccess (const char *file, int mode)
+{
+#if HAVE_FACCESSAT                   /* glibc, AIX 7, Solaris 11, Cygwin 1.7 */
+  return faccessat (AT_FDCWD, file, mode, AT_EACCESS);
+#elif defined EFF_ONLY_OK               /* IRIX, OSF/1, Interix */
+  return access (file, mode | EFF_ONLY_OK);
+#elif defined ACC_SELF                  /* AIX */
+  return accessx (file, mode, ACC_SELF);
+#elif HAVE_EACCESS                      /* FreeBSD */
+  return eaccess (file, mode);
+#else       /* Mac OS X, NetBSD, OpenBSD, HP-UX, Solaris, Cygwin, mingw, BeOS */
+
+  uid_t uid = getuid ();
+  gid_t gid = getgid ();
+  uid_t euid = geteuid ();
+  gid_t egid = getegid ();
+  struct stat stats;
+
+# if HAVE_DECL_SETREGID && PREFER_NONREENTRANT_EUIDACCESS
+
+  /* Define PREFER_NONREENTRANT_EUIDACCESS if you prefer euidaccess to
+     return the correct result even if this would make it
+     nonreentrant.  Define this only if your entire application is
+     safe even if the uid or gid might temporarily change.  If your
+     application uses signal handlers or threads it is probably not
+     safe.  */
+
+  if (mode == F_OK)
+    return stat (file, &stats);
+  else
+    {
+      int result;
+      int saved_errno;
+
+      if (uid != euid)
+        setreuid (euid, uid);
+      if (gid != egid)
+        setregid (egid, gid);
+
+      result = access (file, mode);
+      saved_errno = errno;
+
+      /* Restore them.  */
+      if (uid != euid)
+        setreuid (uid, euid);
+      if (gid != egid)
+        setregid (gid, egid);
+
+      errno = saved_errno;
+      return result;
+    }
+
+# else
+
+  /* The following code assumes the traditional Unix model, and is not
+     correct on systems that have ACLs or the like.  However, it's
+     better than nothing, and it is reentrant.  */
+
+  unsigned int granted;
+  if (uid == euid && gid == egid)
+    /* If we are not set-uid or set-gid, access does the same.  */
+    return access (file, mode);
+
+  if (stat (file, &stats) != 0)
+    return -1;
+
+  /* The super-user can read and write any file, and execute any file
+     that anyone can execute.  */
+  if (euid == ROOT_UID
+      && ((mode & X_OK) == 0
+          || (stats.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))))
+    return 0;
+
+  /* Convert the mode to traditional form, clearing any bogus bits.  */
+  if (R_OK == 4 && W_OK == 2 && X_OK == 1 && F_OK == 0)
+    mode &= 7;
+  else
+    mode = ((mode & R_OK ? 4 : 0)
+            + (mode & W_OK ? 2 : 0)
+            + (mode & X_OK ? 1 : 0));
+
+  if (mode == 0)
+    return 0;                   /* The file exists.  */
+
+  /* Convert the file's permission bits to traditional form.  */
+  if (S_IRUSR == (4 << 6) && S_IWUSR == (2 << 6) && S_IXUSR == (1 << 6)
+      && S_IRGRP == (4 << 3) && S_IWGRP == (2 << 3) && S_IXGRP == (1 << 3)
+      && S_IROTH == (4 << 0) && S_IWOTH == (2 << 0) && S_IXOTH == (1 << 0))
+    granted = stats.st_mode;
+  else
+    granted = ((stats.st_mode & S_IRUSR ? 4 << 6 : 0)
+               + (stats.st_mode & S_IWUSR ? 2 << 6 : 0)
+               + (stats.st_mode & S_IXUSR ? 1 << 6 : 0)
+               + (stats.st_mode & S_IRGRP ? 4 << 3 : 0)
+               + (stats.st_mode & S_IWGRP ? 2 << 3 : 0)
+               + (stats.st_mode & S_IXGRP ? 1 << 3 : 0)
+               + (stats.st_mode & S_IROTH ? 4 << 0 : 0)
+               + (stats.st_mode & S_IWOTH ? 2 << 0 : 0)
+               + (stats.st_mode & S_IXOTH ? 1 << 0 : 0));
+
+  if (euid == stats.st_uid)
+    granted >>= 6;
+  else if (egid == stats.st_gid || group_member (stats.st_gid))
+    granted >>= 3;
+
+  if ((mode & ~granted) == 0)
+    return 0;
+  __set_errno (EACCESS);
+  return -1;
+
+# endif
+#endif
+}
+#undef euidaccess
+#ifdef weak_alias
+weak_alias (__euidaccess, euidaccess)
+#endif
+\f
+#ifdef TEST
+# include <error.h>
+# include <stdio.h>
+# include <stdlib.h>
+
+char *program_name;
+
+int
+main (int argc, char **argv)
+{
+  char *file;
+  int mode;
+  int err;
+
+  program_name = argv[0];
+  if (argc < 3)
+    abort ();
+  file = argv[1];
+  mode = atoi (argv[2]);
+
+  err = euidaccess (file, mode);
+  printf ("%d\n", err);
+  if (err != 0)
+    error (0, errno, "%s", file);
+  exit (0);
+}
+#endif

=== added file 'lib/getgroups.c'
--- lib/getgroups.c	1970-01-01 00:00:00 +0000
+++ lib/getgroups.c	2012-10-12 23:28:31 +0000
@@ -0,0 +1,116 @@
+/* provide consistent interface to getgroups for systems that don't allow N==0
+
+   Copyright (C) 1996, 1999, 2003, 2006-2012 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* written by Jim Meyering */
+
+#include <config.h>
+
+#include <unistd.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#if !HAVE_GETGROUPS
+
+/* Provide a stub that fails with ENOSYS, since there is no group
+   information available on mingw.  */
+int
+getgroups (int n _GL_UNUSED, GETGROUPS_T *groups _GL_UNUSED)
+{
+  errno = ENOSYS;
+  return -1;
+}
+
+#else /* HAVE_GETGROUPS */
+
+# undef getgroups
+# ifndef GETGROUPS_ZERO_BUG
+#  define GETGROUPS_ZERO_BUG 0
+# endif
+
+/* On at least Ultrix 4.3 and NextStep 3.2, getgroups (0, NULL) always
+   fails.  On other systems, it returns the number of supplemental
+   groups for the process.  This function handles that special case
+   and lets the system-provided function handle all others.  However,
+   it can fail with ENOMEM if memory is tight.  It is unspecified
+   whether the effective group id is included in the list.  */
+
+int
+rpl_getgroups (int n, gid_t *group)
+{
+  int n_groups;
+  GETGROUPS_T *gbuf;
+  int saved_errno;
+
+  if (n < 0)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  if (n != 0 || !GETGROUPS_ZERO_BUG)
+    {
+      int result;
+      if (sizeof *group == sizeof *gbuf)
+        return getgroups (n, (GETGROUPS_T *) group);
+
+      if (SIZE_MAX / sizeof *gbuf <= n)
+        {
+          errno = ENOMEM;
+          return -1;
+        }
+      gbuf = malloc (n * sizeof *gbuf);
+      if (!gbuf)
+        return -1;
+      result = getgroups (n, gbuf);
+      if (0 <= result)
+        {
+          n = result;
+          while (n--)
+            group[n] = gbuf[n];
+        }
+      saved_errno = errno;
+      free (gbuf);
+      errno == saved_errno;
+      return result;
+    }
+
+  n = 20;
+  while (1)
+    {
+      /* No need to worry about address arithmetic overflow here,
+         since the ancient systems that we're running on have low
+         limits on the number of secondary groups.  */
+      gbuf = malloc (n * sizeof *gbuf);
+      if (!gbuf)
+        return -1;
+      n_groups = getgroups (n, gbuf);
+      if (n_groups == -1 ? errno != EINVAL : n_groups < n)
+        break;
+      free (gbuf);
+      n *= 2;
+    }
+
+  saved_errno = errno;
+  free (gbuf);
+  errno = saved_errno;
+
+  return n_groups;
+}
+
+#endif /* HAVE_GETGROUPS */

=== modified file 'lib/gnulib.mk'
--- lib/gnulib.mk	2012-10-04 07:15:42 +0000
+++ lib/gnulib.mk	2012-10-12 23:28:31 +0000
@@ -21,7 +21,7 @@
 # the same distribution terms as the rest of that program.
 #
 # Generated by gnulib-tool.
-# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=errno --avoid=fcntl --avoid=fcntl-h --avoid=fstat --avoid=msvc-inval --avoid=msvc-nothrow --avoid=raise --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo filemode getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings mktime pselect pthread_sigmask readlink socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys
 _time time timer-time timespec-add timespec-sub utimens warnings
+# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=errno --avoid=fcntl --avoid=fcntl-h --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=raise --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ euidaccess execinfo filemode getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings mktime pselect pthread_sigmask readlink socklen stat-time stdalign stdarg stdbool stdio strftime strtoima
 x strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub utimens warnings
 
 
 MOSTLYCLEANFILES += core *.stackdump
@@ -150,6 +150,15 @@
 
 ## end   gnulib module dup2
 
+## begin gnulib module euidaccess
+
+
+EXTRA_DIST += euidaccess.c
+
+EXTRA_libgnu_a_SOURCES += euidaccess.c
+
+## end   gnulib module euidaccess
+
 ## begin gnulib module execinfo
 
 BUILT_SOURCES += $(EXECINFO_H)
@@ -183,6 +192,17 @@
 
 ## end   gnulib module filemode
 
+## begin gnulib module getgroups
+
+if gl_GNULIB_ENABLED_getgroups
+
+endif
+EXTRA_DIST += getgroups.c
+
+EXTRA_libgnu_a_SOURCES += getgroups.c
+
+## end   gnulib module getgroups
+
 ## begin gnulib module getloadavg
 
 
@@ -242,6 +262,17 @@
 
 ## end   gnulib module gettimeofday
 
+## begin gnulib module group-member
+
+if gl_GNULIB_ENABLED_a9786850e999ae65a836a6041e8e5ed1
+
+endif
+EXTRA_DIST += group-member.c
+
+EXTRA_libgnu_a_SOURCES += group-member.c
+
+## end   gnulib module group-member
+
 ## begin gnulib module ignore-value
 
 
@@ -354,6 +385,13 @@
 
 ## end   gnulib module readlink
 
+## begin gnulib module root-uid
+
+
+EXTRA_DIST += root-uid.h
+
+## end   gnulib module root-uid
+
 ## begin gnulib module signal-h
 
 BUILT_SOURCES += signal.h
@@ -1312,6 +1350,15 @@
 
 ## end   gnulib module verify
 
+## begin gnulib module xalloc-oversized
+
+if gl_GNULIB_ENABLED_682e609604ccaac6be382e4ee3a4eaec
+
+endif
+EXTRA_DIST += xalloc-oversized.h
+
+## end   gnulib module xalloc-oversized
+
 
 mostlyclean-local: mostlyclean-generic
 	@for dir in '' $(MOSTLYCLEANDIRS); do \

=== added file 'lib/group-member.c'
--- lib/group-member.c	1970-01-01 00:00:00 +0000
+++ lib/group-member.c	2012-10-12 23:28:31 +0000
@@ -0,0 +1,119 @@
+/* group-member.c -- determine whether group id is in calling user's group list
+
+   Copyright (C) 1994, 1997-1998, 2003, 2005-2006, 2009-2012 Free Software
+   Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <unistd.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdlib.h>
+
+#include "xalloc-oversized.h"
+
+/* Most processes have no more than this many groups, and for these
+   processes we can avoid using malloc.  */
+enum { GROUPBUF_SIZE = 100 };
+
+struct group_info
+  {
+    gid_t *group;
+    gid_t groupbuf[GROUPBUF_SIZE];
+  };
+
+static void
+free_group_info (struct group_info const *g)
+{
+  if (g->group != g->groupbuf)
+    free (g->group);
+}
+
+static int
+get_group_info (struct group_info *gi)
+{
+  int n_groups = getgroups (GROUPBUF_SIZE, gi->groupbuf);
+  gi->group = gi->groupbuf;
+
+  if (n_groups < 0)
+    {
+      int n_group_slots = getgroups (0, NULL);
+      if (0 <= n_group_slots
+          && ! xalloc_oversized (n_group_slots, sizeof *gi->group))
+        {
+          gi->group = malloc (n_group_slots * sizeof *gi->group);
+          if (gi->group)
+            n_groups = getgroups (n_group_slots, gi->group);
+        }
+    }
+
+  /* In case of error, the user loses.  */
+  return n_groups;
+}
+
+/* Return non-zero if GID is one that we have in our groups list.
+   Note that the groups list is not guaranteed to contain the current
+   or effective group ID, so they should generally be checked
+   separately.  */
+
+int
+group_member (gid_t gid)
+{
+  int i;
+  int found;
+  struct group_info gi;
+  int n_groups = get_group_info (&gi);
+
+  /* Search through the list looking for GID. */
+  found = 0;
+  for (i = 0; i < n_groups; i++)
+    {
+      if (gid == gi.group[i])
+        {
+          found = 1;
+          break;
+        }
+    }
+
+  free_group_info (&gi);
+
+  return found;
+}
+
+#ifdef TEST
+
+char *program_name;
+
+int
+main (int argc, char **argv)
+{
+  int i;
+
+  program_name = argv[0];
+
+  for (i = 1; i < argc; i++)
+    {
+      gid_t gid;
+
+      gid = atoi (argv[i]);
+      printf ("%d: %s\n", gid, group_member (gid) ? "yes" : "no");
+    }
+  exit (0);
+}
+
+#endif /* TEST */

=== added file 'lib/root-uid.h'
--- lib/root-uid.h	1970-01-01 00:00:00 +0000
+++ lib/root-uid.h	2012-10-12 23:28:31 +0000
@@ -0,0 +1,30 @@
+/* The user ID that always has appropriate privileges in the POSIX sense.
+
+   Copyright 2012 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   Written by Paul Eggert.  */
+
+#ifndef ROOT_UID_H_
+#define ROOT_UID_H_
+
+/* The user ID that always has appropriate privileges in the POSIX sense.  */
+#ifdef __TANDEM
+# define ROOT_UID 65535
+#else
+# define ROOT_UID 0
+#endif
+
+#endif

=== added file 'lib/xalloc-oversized.h'
--- lib/xalloc-oversized.h	1970-01-01 00:00:00 +0000
+++ lib/xalloc-oversized.h	2012-10-12 23:28:31 +0000
@@ -0,0 +1,38 @@
+/* xalloc-oversized.h -- memory allocation size checking
+
+   Copyright (C) 1990-2000, 2003-2004, 2006-2012 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef XALLOC_OVERSIZED_H_
+# define XALLOC_OVERSIZED_H_
+
+# include <stddef.h>
+
+/* Return 1 if an array of N objects, each of size S, cannot exist due
+   to size arithmetic overflow.  S must be positive and N must be
+   nonnegative.  This is a macro, not a function, so that it
+   works correctly even when SIZE_MAX < N.
+
+   By gnulib convention, SIZE_MAX represents overflow in size
+   calculations, so the conservative dividend to use here is
+   SIZE_MAX - 1, since SIZE_MAX might represent an overflowed value.
+   However, malloc (SIZE_MAX) fails on all known hosts where
+   sizeof (ptrdiff_t) <= sizeof (size_t), so do not bother to test for
+   exactly-SIZE_MAX allocations on such hosts; this avoids a test and
+   branch when S is known to be 1.  */
+# define xalloc_oversized(n, s) \
+    ((size_t) (sizeof (ptrdiff_t) <= sizeof (size_t) ? -1 : -2) / (s) < (n))
+
+#endif /* !XALLOC_OVERSIZED_H_ */

=== added file 'm4/euidaccess.m4'
--- m4/euidaccess.m4	1970-01-01 00:00:00 +0000
+++ m4/euidaccess.m4	2012-10-12 23:28:31 +0000
@@ -0,0 +1,52 @@
+# euidaccess.m4 serial 14
+dnl Copyright (C) 2002-2012 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_NONREENTRANT_EUIDACCESS],
+[
+  AC_REQUIRE([gl_FUNC_EUIDACCESS])
+  AC_DEFINE([PREFER_NONREENTRANT_EUIDACCESS], [1],
+    [Define this if you prefer euidaccess to return the correct result
+     even if this would make it nonreentrant.  Define this only if your
+     entire application is safe even if the uid or gid might temporarily
+     change.  If your application uses signal handlers or threads it
+     is probably not safe.])
+])
+
+AC_DEFUN([gl_FUNC_EUIDACCESS],
+[
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+
+  dnl Persuade glibc <unistd.h> to declare euidaccess().
+  AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
+
+  AC_CHECK_FUNCS([euidaccess])
+  if test $ac_cv_func_euidaccess = no; then
+    HAVE_EUIDACCESS=0
+  fi
+])
+
+# Prerequisites of lib/euidaccess.c.
+AC_DEFUN([gl_PREREQ_EUIDACCESS], [
+  dnl Prefer POSIX faccessat over non-standard euidaccess.
+  AC_CHECK_FUNCS_ONCE([faccessat])
+  dnl Try various other non-standard fallbacks.
+  AC_CHECK_HEADERS_ONCE([libgen.h])
+  AC_CHECK_DECLS_ONCE([setregid])
+  AC_REQUIRE([AC_FUNC_GETGROUPS])
+
+  # Solaris 9 and 10 need -lgen to get the eaccess function.
+  # Save and restore LIBS so -lgen isn't added to it.  Otherwise, *all*
+  # programs in the package would end up linked with that potentially-shared
+  # library, inducing unnecessary run-time overhead.
+  LIB_EACCESS=
+  AC_SUBST([LIB_EACCESS])
+  gl_saved_libs=$LIBS
+    AC_SEARCH_LIBS([eaccess], [gen],
+                   [test "$ac_cv_search_eaccess" = "none required" ||
+                    LIB_EACCESS=$ac_cv_search_eaccess])
+    AC_CHECK_FUNCS([eaccess])
+  LIBS=$gl_saved_libs
+])

=== added file 'm4/getgroups.m4'
--- m4/getgroups.m4	1970-01-01 00:00:00 +0000
+++ m4/getgroups.m4	2012-10-12 23:28:31 +0000
@@ -0,0 +1,107 @@
+# serial 18
+
+dnl From Jim Meyering.
+dnl A wrapper around AC_FUNC_GETGROUPS.
+
+# Copyright (C) 1996-1997, 1999-2004, 2008-2012 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+m4_version_prereq([2.70], [] ,[
+
+# This is taken from the following Autoconf patch:
+# http://git.savannah.gnu.org/gitweb/?p=autoconf.git;a=commitdiff;h=7fbb553727ed7e0e689a17594b58559ecf3ea6e9
+AC_DEFUN([AC_FUNC_GETGROUPS],
+[
+  AC_REQUIRE([AC_TYPE_GETGROUPS])dnl
+  AC_REQUIRE([AC_TYPE_SIZE_T])dnl
+  AC_REQUIRE([AC_CANONICAL_HOST])dnl for cross-compiles
+  AC_CHECK_FUNC([getgroups])
+
+  # If we don't yet have getgroups, see if it's in -lbsd.
+  # This is reported to be necessary on an ITOS 3000WS running SEIUX 3.1.
+  ac_save_LIBS=$LIBS
+  if test $ac_cv_func_getgroups = no; then
+    AC_CHECK_LIB(bsd, getgroups, [GETGROUPS_LIB=-lbsd])
+  fi
+
+  # Run the program to test the functionality of the system-supplied
+  # getgroups function only if there is such a function.
+  if test $ac_cv_func_getgroups = yes; then
+    AC_CACHE_CHECK([for working getgroups], [ac_cv_func_getgroups_works],
+      [AC_RUN_IFELSE(
+         [AC_LANG_PROGRAM(
+            [AC_INCLUDES_DEFAULT],
+            [[/* On Ultrix 4.3, getgroups (0, 0) always fails.  */
+              return getgroups (0, 0) == -1;]])
+         ],
+         [ac_cv_func_getgroups_works=yes],
+         [ac_cv_func_getgroups_works=no],
+         [case "$host_os" in # ((
+                    # Guess yes on glibc systems.
+            *-gnu*) ac_cv_func_getgroups_works="guessing yes" ;;
+                    # If we don't know, assume the worst.
+            *)      ac_cv_func_getgroups_works="guessing no" ;;
+          esac
+         ])
+      ])
+  else
+    ac_cv_func_getgroups_works=no
+  fi
+  case "$ac_cv_func_getgroups_works" in
+    *yes)
+      AC_DEFINE([HAVE_GETGROUPS], [1],
+        [Define to 1 if your system has a working `getgroups' function.])
+      ;;
+  esac
+  LIBS=$ac_save_LIBS
+])# AC_FUNC_GETGROUPS
+
+])
+
+AC_DEFUN([gl_FUNC_GETGROUPS],
+[
+  AC_REQUIRE([AC_TYPE_GETGROUPS])
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+
+  AC_FUNC_GETGROUPS
+  if test $ac_cv_func_getgroups != yes; then
+    HAVE_GETGROUPS=0
+  else
+    if test "$ac_cv_type_getgroups" != gid_t \
+       || { case "$ac_cv_func_getgroups_works" in
+              *yes) false;;
+              *) true;;
+            esac
+          }; then
+      REPLACE_GETGROUPS=1
+      AC_DEFINE([GETGROUPS_ZERO_BUG], [1], [Define this to 1 if
+        getgroups(0,NULL) does not return the number of groups.])
+    else
+      dnl Detect FreeBSD bug; POSIX requires getgroups(-1,ptr) to fail.
+      AC_CACHE_CHECK([whether getgroups handles negative values],
+        [gl_cv_func_getgroups_works],
+        [AC_RUN_IFELSE([AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT],
+          [[int size = getgroups (0, 0);
+            gid_t *list = malloc (size * sizeof *list);
+            return getgroups (-1, list) != -1;]])],
+          [gl_cv_func_getgroups_works=yes],
+          [gl_cv_func_getgroups_works=no],
+          [case "$host_os" in
+                     # Guess yes on glibc systems.
+             *-gnu*) gl_cv_func_getgroups_works="guessing yes" ;;
+                     # If we don't know, assume the worst.
+             *)      gl_cv_func_getgroups_works="guessing no" ;;
+           esac
+          ])])
+      case "$gl_cv_func_getgroups_works" in
+        *yes) ;;
+        *) REPLACE_GETGROUPS=1 ;;
+      esac
+    fi
+  fi
+  test -n "$GETGROUPS_LIB" && LIBS="$GETGROUPS_LIB $LIBS"
+])

=== modified file 'm4/gnulib-comp.m4'
--- m4/gnulib-comp.m4	2012-09-27 01:06:23 +0000
+++ m4/gnulib-comp.m4	2012-10-12 23:28:31 +0000
@@ -53,17 +53,20 @@
   # Code from module dtotimespec:
   # Code from module dup2:
   # Code from module environ:
+  # Code from module euidaccess:
   # Code from module execinfo:
   # Code from module extensions:
   AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
   # Code from module extern-inline:
   # Code from module filemode:
+  # Code from module getgroups:
   # Code from module getloadavg:
   # Code from module getopt-gnu:
   # Code from module getopt-posix:
   # Code from module gettext-h:
   # Code from module gettime:
   # Code from module gettimeofday:
+  # Code from module group-member:
   # Code from module ignore-value:
   # Code from module include_next:
   # Code from module intprops:
@@ -79,6 +82,7 @@
   # Code from module pselect:
   # Code from module pthread_sigmask:
   # Code from module readlink:
+  # Code from module root-uid:
   # Code from module signal-h:
   # Code from module snippet/_Noreturn:
   # Code from module snippet/arg-nonnull:
@@ -120,6 +124,7 @@
   # Code from module utimens:
   # Code from module verify:
   # Code from module warnings:
+  # Code from module xalloc-oversized:
 ])
 
 # This macro should be invoked from ./configure.ac, in the section
@@ -154,6 +159,12 @@
   gl_UNISTD_MODULE_INDICATOR([dup2])
   gl_ENVIRON
   gl_UNISTD_MODULE_INDICATOR([environ])
+  gl_FUNC_EUIDACCESS
+  if test $HAVE_EUIDACCESS = 0; then
+    AC_LIBOBJ([euidaccess])
+    gl_PREREQ_EUIDACCESS
+  fi
+  gl_UNISTD_MODULE_INDICATOR([euidaccess])
   gl_EXECINFO_H
   AC_REQUIRE([gl_EXTERN_INLINE])
   gl_FILEMODE
@@ -269,18 +280,32 @@
   gl_UNISTD_H
   gl_UTIMENS
   gl_gnulib_enabled_dosname=false
+  gl_gnulib_enabled_getgroups=false
   gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=false
+  gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1=false
   gl_gnulib_enabled_pathmax=false
   gl_gnulib_enabled_stat=false
   gl_gnulib_enabled_strtoll=false
   gl_gnulib_enabled_strtoull=false
   gl_gnulib_enabled_verify=false
+  gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=false
   func_gl_gnulib_m4code_dosname ()
   {
     if ! $gl_gnulib_enabled_dosname; then
       gl_gnulib_enabled_dosname=true
     fi
   }
+  func_gl_gnulib_m4code_getgroups ()
+  {
+    if ! $gl_gnulib_enabled_getgroups; then
+      gl_FUNC_GETGROUPS
+      if test $HAVE_GETGROUPS = 0 || test $REPLACE_GETGROUPS = 1; then
+        AC_LIBOBJ([getgroups])
+      fi
+      gl_UNISTD_MODULE_INDICATOR([getgroups])
+      gl_gnulib_enabled_getgroups=true
+    fi
+  }
   func_gl_gnulib_m4code_be453cec5eecf5731a274f2de7f2db36 ()
   {
     if ! $gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36; then
@@ -289,6 +314,24 @@
       gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=true
     fi
   }
+  func_gl_gnulib_m4code_a9786850e999ae65a836a6041e8e5ed1 ()
+  {
+    if ! $gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1; then
+      gl_FUNC_GROUP_MEMBER
+      if test $HAVE_GROUP_MEMBER = 0; then
+        AC_LIBOBJ([group-member])
+        gl_PREREQ_GROUP_MEMBER
+      fi
+      gl_UNISTD_MODULE_INDICATOR([group-member])
+      gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1=true
+      if test $HAVE_GROUP_MEMBER = 0; then
+        func_gl_gnulib_m4code_getgroups
+      fi
+      if test $HAVE_GROUP_MEMBER = 0; then
+        func_gl_gnulib_m4code_682e609604ccaac6be382e4ee3a4eaec
+      fi
+    fi
+  }
   func_gl_gnulib_m4code_pathmax ()
   {
     if ! $gl_gnulib_enabled_pathmax; then
@@ -347,6 +390,18 @@
       gl_gnulib_enabled_verify=true
     fi
   }
+  func_gl_gnulib_m4code_682e609604ccaac6be382e4ee3a4eaec ()
+  {
+    if ! $gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec; then
+      gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=true
+    fi
+  }
+  if test $HAVE_EUIDACCESS = 0; then
+    func_gl_gnulib_m4code_a9786850e999ae65a836a6041e8e5ed1
+  fi
+  if test $HAVE_EUIDACCESS = 0; then
+    func_gl_gnulib_m4code_stat
+  fi
   if test $REPLACE_GETOPT = 1; then
     func_gl_gnulib_m4code_be453cec5eecf5731a274f2de7f2db36
   fi
@@ -373,12 +428,15 @@
   fi
   m4_pattern_allow([^gl_GNULIB_ENABLED_])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_dosname], [$gl_gnulib_enabled_dosname])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_getgroups], [$gl_gnulib_enabled_getgroups])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_be453cec5eecf5731a274f2de7f2db36], [$gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_a9786850e999ae65a836a6041e8e5ed1], [$gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_pathmax], [$gl_gnulib_enabled_pathmax])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_stat], [$gl_gnulib_enabled_stat])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_strtoll], [$gl_gnulib_enabled_strtoll])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_strtoull], [$gl_gnulib_enabled_strtoull])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_verify], [$gl_gnulib_enabled_verify])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_682e609604ccaac6be382e4ee3a4eaec], [$gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec])
   # End of code from modules
   m4_ifval(gl_LIBSOURCES_LIST, [
     m4_syscmd([test ! -d ]m4_defn([gl_LIBSOURCES_DIR])[ ||
@@ -538,12 +596,14 @@
   lib/dtoastr.c
   lib/dtotimespec.c
   lib/dup2.c
+  lib/euidaccess.c
   lib/execinfo.c
   lib/execinfo.in.h
   lib/filemode.c
   lib/filemode.h
   lib/ftoastr.c
   lib/ftoastr.h
+  lib/getgroups.c
   lib/getloadavg.c
   lib/getopt.c
   lib/getopt.in.h
@@ -552,6 +612,7 @@
   lib/gettext.h
   lib/gettime.c
   lib/gettimeofday.c
+  lib/group-member.c
   lib/ignore-value.h
   lib/intprops.h
   lib/inttypes.in.h
@@ -564,6 +625,7 @@
   lib/pselect.c
   lib/pthread_sigmask.c
   lib/readlink.c
+  lib/root-uid.h
   lib/sha1.c
   lib/sha1.h
   lib/sha256.c
@@ -605,21 +667,25 @@
   lib/utimens.c
   lib/utimens.h
   lib/verify.h
+  lib/xalloc-oversized.h
   m4/00gnulib.m4
   m4/alloca.m4
   m4/c-strtod.m4
   m4/clock_time.m4
   m4/dup2.m4
   m4/environ.m4
+  m4/euidaccess.m4
   m4/execinfo.m4
   m4/extensions.m4
   m4/extern-inline.m4
   m4/filemode.m4
+  m4/getgroups.m4
   m4/getloadavg.m4
   m4/getopt.m4
   m4/gettime.m4
   m4/gettimeofday.m4
   m4/gnulib-common.m4
+  m4/group-member.m4
   m4/include_next.m4
   m4/inttypes.m4
   m4/largefile.m4

=== added file 'm4/group-member.m4'
--- m4/group-member.m4	1970-01-01 00:00:00 +0000
+++ m4/group-member.m4	2012-10-12 23:28:31 +0000
@@ -0,0 +1,29 @@
+# serial 14
+
+# Copyright (C) 1999-2001, 2003-2007, 2009-2012 Free Software Foundation, Inc.
+
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+dnl Written by Jim Meyering
+
+AC_DEFUN([gl_FUNC_GROUP_MEMBER],
+[
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+
+  dnl Persuade glibc <unistd.h> to declare group_member().
+  AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
+
+  dnl Do this replacement check manually because I want the hyphen
+  dnl (not the underscore) in the filename.
+  AC_CHECK_FUNC([group_member], , [
+    HAVE_GROUP_MEMBER=0
+  ])
+])
+
+# Prerequisites of lib/group-member.c.
+AC_DEFUN([gl_PREREQ_GROUP_MEMBER],
+[
+  AC_REQUIRE([AC_FUNC_GETGROUPS])
+])

=== modified file 'src/ChangeLog'
--- src/ChangeLog	2012-10-13 00:52:01 +0000
+++ src/ChangeLog	2012-10-13 01:49:41 +0000
@@ -1,5 +1,22 @@
 2012-10-13  Paul Eggert  <eggert@cs.ucla.edu>
 
+	Use euidaccess, not access, when checking file permissions.
+	* Makefile.in (LIB_EACCESS): New macro.
+	(LIBES): Use it.
+	* callproc.c (init_callproc):
+	* charset.c (init_charset):
+	* fileio.c (check_existing) [!DOS_NT]:
+	(check_executable) [!DOS_NT && !HAVE_EUIDACCESS]:
+	* lread.c (openp, load_path_check):
+	* process.c (allocate_pty):
+	* xrdb.c (file_p):
+	Use euidaccess, not access.  Use symbolic names instead of integers
+	for the flags, as they're portable now.
+	* fileio.c (Ffile_readable_p) [!DOS_NT && !macintosh]:
+	Use euidaccess, not stat + open + close.
+	(Ffile_writable_p): Remove NILP check before calling euidaccess,
+	as nil isn't possible here.
+
 	Fix some stat-related races.
 	* fileio.c (Fwrite_region): Avoid race condition if a file is
 	removed or renamed by some other process immediately after Emacs

=== modified file 'src/Makefile.in'
--- src/Makefile.in	2012-10-08 12:53:18 +0000
+++ src/Makefile.in	2012-10-13 01:49:41 +0000
@@ -150,6 +150,7 @@
 M17N_FLT_LIBS = @M17N_FLT_LIBS@
 
 LIB_CLOCK_GETTIME=@LIB_CLOCK_GETTIME@
+LIB_EACCESS=@LIB_EACCESS@
 LIB_TIMER_TIME=@LIB_TIMER_TIME@
 
 DBUS_CFLAGS = @DBUS_CFLAGS@
@@ -392,7 +393,7 @@
 LIBES = $(LIBS) $(W32_LIBS) $(LIBX_BASE) $(LIBIMAGE) \
    $(LIBX_OTHER) $(LIBSOUND) \
    $(RSVG_LIBS) $(IMAGEMAGICK_LIBS) $(LIB_CLOCK_GETTIME) \
-   $(LIB_TIMER_TIME) $(DBUS_LIBS) \
+   $(LIB_EACCESS) $(LIB_TIMER_TIME) $(DBUS_LIBS) \
    $(LIB_EXECINFO) \
    $(LIBXML2_LIBS) $(LIBGPM) $(LIBRESOLV) $(LIBS_SYSTEM) \
    $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \

=== modified file 'src/callproc.c'
--- src/callproc.c	2012-09-23 22:25:22 +0000
+++ src/callproc.c	2012-10-12 23:28:31 +0000
@@ -1597,13 +1597,13 @@
 #endif
     {
       tempdir = Fdirectory_file_name (Vexec_directory);
-      if (access (SSDATA (tempdir), 0) < 0)
+      if (euidaccess (SSDATA (tempdir), F_OK) < 0)
 	dir_warning ("Warning: arch-dependent data dir (%s) does not exist.\n",
 		     Vexec_directory);
     }
 
   tempdir = Fdirectory_file_name (Vdata_directory);
-  if (access (SSDATA (tempdir), 0) < 0)
+  if (euidaccess (SSDATA (tempdir), F_OK) < 0)
     dir_warning ("Warning: arch-independent data dir (%s) does not exist.\n",
 		 Vdata_directory);
 

=== modified file 'src/charset.c'
--- src/charset.c	2012-10-01 06:36:54 +0000
+++ src/charset.c	2012-10-12 23:28:31 +0000
@@ -2293,7 +2293,7 @@
 {
   Lisp_Object tempdir;
   tempdir = Fexpand_file_name (build_string ("charsets"), Vdata_directory);
-  if (access (SSDATA (tempdir), 0) < 0)
+  if (euidaccess (SSDATA (tempdir), F_OK) != 0)
     {
       /* This used to be non-fatal (dir_warning), but it should not
          happen, and if it does sooner or later it will cause some

=== modified file 'src/fileio.c'
--- src/fileio.c	2012-10-13 00:52:01 +0000
+++ src/fileio.c	2012-10-13 00:55:20 +0000
@@ -2428,13 +2428,11 @@
 check_existing (const char *filename)
 {
 #ifdef DOS_NT
-  /* The full emulation of Posix 'stat' is too expensive on
-     DOS/Windows, when all we want to know is whether the file exists.
-     So we use 'access' instead, which is much more lightweight.  */
+  /* FIXME: an euidaccess implementation should be added to the
+     DOS/Windows ports and this #ifdef branch should be removed.  */
   return (access (filename, F_OK) >= 0);
 #else
-  struct stat st;
-  return (stat (filename, &st) >= 0);
+  return euidaccess (filename, F_OK) == 0;
 #endif
 }
 
@@ -2444,19 +2442,14 @@
 check_executable (char *filename)
 {
 #ifdef DOS_NT
+  /* FIXME: an euidaccess implementation should be added to the
+     DOS/Windows ports and this #ifdef branch should be removed.  */
   struct stat st;
   if (stat (filename, &st) < 0)
     return 0;
   return ((st.st_mode & S_IEXEC) != 0);
 #else /* not DOS_NT */
-#ifdef HAVE_EUIDACCESS
-  return (euidaccess (filename, 1) >= 0);
-#else
-  /* Access isn't quite right because it uses the real uid
-     and we really want to test with the effective uid.
-     But Unix doesn't give us a right way to do it.  */
-  return (access (filename, 1) >= 0);
-#endif
+  return euidaccess (filename, X_OK) == 0;
 #endif /* not DOS_NT */
 }
 
@@ -2466,13 +2459,14 @@
 check_writable (const char *filename)
 {
 #ifdef MSDOS
+  /* FIXME: an euidaccess implementation should be added to the
+     DOS/Windows ports and this #ifdef branch should be removed.  */
   struct stat st;
   if (stat (filename, &st) < 0)
     return 0;
   return (st.st_mode & S_IWRITE || S_ISDIR (st.st_mode));
 #else /* not MSDOS */
-#ifdef HAVE_EUIDACCESS
-  bool res = (euidaccess (filename, 2) >= 0);
+  bool res = euidaccess (filename, W_OK) == 0;
 #ifdef CYGWIN
   /* euidaccess may have returned failure because Cygwin couldn't
      determine the file's UID or GID; if so, we return success. */
@@ -2485,14 +2479,6 @@
     }
 #endif /* CYGWIN */
   return res;
-#else /* not HAVE_EUIDACCESS */
-  /* Access isn't quite right because it uses the real uid
-     and we really want to test with the effective uid.
-     But Unix doesn't give us a right way to do it.
-     Opening with O_WRONLY could work for an ordinary file,
-     but would lose for directories.  */
-  return (access (filename, 2) >= 0);
-#endif /* not HAVE_EUIDACCESS */
 #endif /* not MSDOS */
 }
 
@@ -2549,9 +2535,6 @@
 {
   Lisp_Object absname;
   Lisp_Object handler;
-  int desc;
-  int flags;
-  struct stat statbuf;
 
   CHECK_STRING (filename);
   absname = Fexpand_file_name (filename, Qnil);
@@ -2565,28 +2548,13 @@
   absname = ENCODE_FILE (absname);
 
 #if defined (DOS_NT) || defined (macintosh)
-  /* Under MS-DOS, Windows, and Macintosh, open does not work for
-     directories.  */
+  /* FIXME: an euidaccess implementation should be added to the
+     DOS/Windows ports and this #ifdef branch should be removed.  */
   if (access (SDATA (absname), 0) == 0)
     return Qt;
   return Qnil;
 #else /* not DOS_NT and not macintosh */
-  flags = O_RDONLY;
-#ifdef O_NONBLOCK
-  /* Opening a fifo without O_NONBLOCK can wait.
-     We don't want to wait.  But we don't want to mess wth O_NONBLOCK
-     except in the case of a fifo, on a system which handles it.  */
-  desc = stat (SSDATA (absname), &statbuf);
-  if (desc < 0)
-    return Qnil;
-  if (S_ISFIFO (statbuf.st_mode))
-    flags |= O_NONBLOCK;
-#endif
-  desc = emacs_open (SSDATA (absname), flags, 0);
-  if (desc < 0)
-    return Qnil;
-  emacs_close (desc);
-  return Qt;
+  return euidaccess (SSDATA (absname), R_OK) == 0 ? Qt : Qnil;
 #endif /* not DOS_NT and not macintosh */
 }
 
@@ -2626,8 +2594,7 @@
      should check ACLs though, which do affect this.  */
   return (access (SDATA (dir), D_OK) < 0) ? Qnil : Qt;
 #else
-  return (check_writable (!NILP (dir) ? SSDATA (dir) : "")
-	  ? Qt : Qnil);
+  return check_writable (SSDATA (dir)) ? Qt : Qnil;
 #endif
 }
 \f

=== modified file 'src/lread.c'
--- src/lread.c	2012-10-12 15:19:54 +0000
+++ src/lread.c	2012-10-12 23:28:31 +0000
@@ -1404,7 +1404,7 @@
 If SUFFIXES is non-nil, it should be a list of suffixes to append to
 file name when searching.
 If non-nil, PREDICATE is used instead of `file-readable-p'.
-PREDICATE can also be an integer to pass to the access(2) function,
+PREDICATE can also be an integer to pass to the euidaccess(2) function,
 in which case file-name-handlers are ignored.
 This function will normally skip directories, so if you want it to find
 directories, make sure the PREDICATE function returns `dir-ok' for them.  */)
@@ -1558,8 +1558,8 @@
 		{
 		  /* Check that we can access or open it.  */
 		  if (NATNUMP (predicate))
-		    fd = (((XFASTINT (predicate) & ~INT_MAX) == 0
-			   && access (pfn, XFASTINT (predicate)) == 0)
+		    fd = ((XFASTINT (predicate) <= INT_MAX
+			   && euidaccess (pfn, XFASTINT (predicate)) == 0)
 			  ? 1 : -1);
 		  else
 		    fd = emacs_open (pfn, O_RDONLY, 0);
@@ -4088,7 +4088,7 @@
       if (STRINGP (dirfile))
         {
           dirfile = Fdirectory_file_name (dirfile);
-          if (access (SSDATA (dirfile), 0) < 0)
+          if (euidaccess (SSDATA (dirfile), F_OK) != 0)
             dir_warning ("Warning: Lisp directory `%s' does not exist.\n",
                          XCAR (path_tail));
         }

=== modified file 'src/process.c'
--- src/process.c	2012-10-13 00:52:01 +0000
+++ src/process.c	2012-10-13 00:55:20 +0000
@@ -663,7 +663,7 @@
 #else
 	    sprintf (pty_name, "/dev/tty%c%x", c, i);
 #endif /* no PTY_TTY_NAME_SPRINTF */
-	    if (access (pty_name, 6) != 0)
+	    if (euidaccess (pty_name, R_OK | W_OK) != 0)
 	      {
 		emacs_close (fd);
 # ifndef __sgi

=== modified file 'src/xrdb.c'
--- src/xrdb.c	2012-09-15 07:06:56 +0000
+++ src/xrdb.c	2012-10-12 23:28:31 +0000
@@ -268,7 +268,7 @@
 {
   struct stat status;
 
-  return (access (filename, 4) == 0             /* exists and is readable */
+  return (euidaccess (filename, R_OK) == 0      /* exists and is readable */
 	  && stat (filename, &status) == 0	/* get the status */
 	  && (S_ISDIR (status.st_mode)) == 0);	/* not a directory */
 }







^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-13  1:58 bug#12632: file permissions checking mishandled when setuid Paul Eggert
@ 2012-10-13  7:23 ` Eli Zaretskii
  2012-10-13  8:36   ` Eli Zaretskii
  2012-11-13  2:19 ` bug#12632: updated version of the patch Paul Eggert
  1 sibling, 1 reply; 47+ messages in thread
From: Eli Zaretskii @ 2012-10-13  7:23 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 12632

> Date: Fri, 12 Oct 2012 18:58:14 -0700
> From: Paul Eggert <eggert@cs.ucla.edu>
> CC: Eli Zaretskii <eliz@gnu.org>
> 
> This patch has a four FIXMEs indicating where the code could
> most likely be cleaned up on DOS_NT platforms.  The patch
> should work fine as-is on DOS_NT, but it'd be nicer if the
> DOS_NT support could supply an euidaccess function to allow
> simplifying the mainline code.  I'll CC: this bug report to
> Eli to give him a heads-up.

The FIXMEs are OK, but I see no reason for them to come _instead_ of
comments which explain why 'access' is used instead of 'stat'.  Those
comments should be removed only when 'euidaccess' is implemented for
those platforms.

Regarding this hunk:

> @@ -2626,8 +2594,7 @@
>       should check ACLs though, which do affect this.  */
>    return (access (SDATA (dir), D_OK) < 0) ? Qnil : Qt;
>  #else
> -  return (check_writable (!NILP (dir) ? SSDATA (dir) : "")
> -	  ? Qt : Qnil);
> +  return check_writable (SSDATA (dir)) ? Qt : Qnil;
>  #endif

How will the new code work if 'dir' is nil?

Also, what about lread.c:openp, around line 1555: doesn't it want
'euidaccess' as well, rather than 'stat'?  I think using 'euidaccess'
there will allow a further optimization, in that the call to 'stat' is
not needed at all, because right below it we call either 'access' or
'emacs_open'.





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-13  7:23 ` Eli Zaretskii
@ 2012-10-13  8:36   ` Eli Zaretskii
  2012-10-14  6:16     ` Paul Eggert
  0 siblings, 1 reply; 47+ messages in thread
From: Eli Zaretskii @ 2012-10-13  8:36 UTC (permalink / raw)
  To: eggert; +Cc: 12632

In addition, I don't understand why is it a good idea to use
'euidaccess' in check_existing.  Isn't the fact of the mere existence
of a file independent of user's access rights?

If 'access' is as good here as 'euidaccess', I think we should use the
former, as it is less expensive than the latter (AFAIU).





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-13  8:36   ` Eli Zaretskii
@ 2012-10-14  6:16     ` Paul Eggert
  2012-10-14  6:56       ` Eli Zaretskii
  0 siblings, 1 reply; 47+ messages in thread
From: Paul Eggert @ 2012-10-14  6:16 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 12632

[-- Attachment #1: Type: text/plain, Size: 971 bytes --]

On 10/13/2012 12:23 AM, Eli Zaretskii wrote:

> The FIXMEs are OK, but I see no reason for them to come _instead_ of
> comments which explain why 'access' is used instead of 'stat'.

OK, I left those comments alone in the attached revision
of the patch.

> How will the new code work if 'dir' is nil?

'dir' can't be nil there.  But this is a separate issue, so I've
omitted that change from the revised patch.

> Also, what about lread.c:openp, around line 1555: doesn't it want
> 'euidaccess' as well, rather than 'stat'?

Sure, we can do that.  Done in the revised patch.

> I don't understand why is it a good idea to use 'euidaccess' in
> check_existing.  Isn't the fact of the mere existence of a file
> independent of user's access rights?

No, because you cannot even stat a file that's in a directory that you
can't search.  Using 'access' rather than 'euidaccess' might
let a setuid Emacs search directories that it shouldn't be able
to search, or vice versa.


[-- Attachment #2: euidaccess.txt --]
[-- Type: text/plain, Size: 47247 bytes --]

=== modified file 'ChangeLog'
--- ChangeLog	2012-10-11 11:29:47 +0000
+++ ChangeLog	2012-10-14 06:03:50 +0000
@@ -1,3 +1,12 @@
+2012-10-14  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use euidaccess, not access, when checking file permissions (Bug#12632).
+	* configure.ac (euidaccess): Remove check; gnulib does this for us now.
+	* lib/euidaccess.c, lib/getgroups.c, lib/group-member.c:
+	* lib/root-uid.h, lib/xalloc-oversized.h, m4/euidaccess.m4:
+	* m4/getgroups.m4, m4/group-member.m4: New files, from gnulib.
+	* lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate.
+
 2012-10-11  Kenichi Handa  <handa@gnu.org>
 
 	* .bzrignore: Add several files under admin/charsets.

=== modified file 'admin/ChangeLog'
--- admin/ChangeLog	2012-10-11 11:29:47 +0000
+++ admin/ChangeLog	2012-10-14 06:03:50 +0000
@@ -1,3 +1,9 @@
+2012-10-14  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use euidaccess, not access, when checking file permissions (Bug#12632).
+	* merge-gnulib (GNULIB_MODULES): Add euidaccess.
+	(GNULIB_TOOL_FLAGS): Avoid malloc-posix.
+
 2012-10-11  Kenichi Handa  <handa@gnu.org>
 
 	* charsets/mapconv: Adjusted for the change of mapfiles/*.gz to

=== modified file 'admin/merge-gnulib'
--- admin/merge-gnulib	2012-09-27 01:06:23 +0000
+++ admin/merge-gnulib	2012-10-12 23:28:31 +0000
@@ -28,7 +28,7 @@
 GNULIB_MODULES='
   alloca-opt c-ctype c-strcase
   careadlinkat crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512
-  dtoastr dtotimespec dup2 environ execinfo
+  dtoastr dtotimespec dup2 environ euidaccess execinfo
   filemode getloadavg getopt-gnu gettime gettimeofday
   ignore-value intprops largefile lstat
   manywarnings mktime pselect pthread_sigmask readlink
@@ -40,7 +40,7 @@
 
 GNULIB_TOOL_FLAGS='
   --avoid=errno --avoid=fcntl --avoid=fcntl-h --avoid=fstat
-  --avoid=msvc-inval --avoid=msvc-nothrow
+  --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow
   --avoid=raise --avoid=select --avoid=sigprocmask --avoid=sys_types
   --avoid=threadlib
   --conditional-dependencies --import --no-changelog --no-vc-files

=== modified file 'configure.ac'
--- configure.ac	2012-10-08 16:09:00 +0000
+++ configure.ac	2012-10-12 23:28:31 +0000
@@ -2872,7 +2872,7 @@
 AC_CHECK_FUNCS(gethostname \
 closedir getrusage get_current_dir_name \
 lrand48 setsid \
-fpathconf select euidaccess getpagesize setlocale \
+fpathconf select getpagesize setlocale \
 utimes getrlimit setrlimit setpgid getcwd shutdown getaddrinfo \
 __fpending strsignal setitimer \
 sendto recvfrom getsockname getpeername getifaddrs freeifaddrs \

=== added file 'lib/euidaccess.c'
--- lib/euidaccess.c	1970-01-01 00:00:00 +0000
+++ lib/euidaccess.c	2012-10-12 23:28:31 +0000
@@ -0,0 +1,221 @@
+/* euidaccess -- check if effective user id can access file
+
+   Copyright (C) 1990-1991, 1995, 1998, 2000, 2003-2006, 2008-2012 Free
+   Software Foundation, Inc.
+
+   This file is part of the GNU C Library.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by David MacKenzie and Torbjorn Granlund.
+   Adapted for GNU C library by Roland McGrath.  */
+
+#ifndef _LIBC
+# include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "root-uid.h"
+
+#if HAVE_LIBGEN_H
+# include <libgen.h>
+#endif
+
+#include <errno.h>
+#ifndef __set_errno
+# define __set_errno(val) errno = (val)
+#endif
+
+#if defined EACCES && !defined EACCESS
+# define EACCESS EACCES
+#endif
+
+#ifndef F_OK
+# define F_OK 0
+# define X_OK 1
+# define W_OK 2
+# define R_OK 4
+#endif
+
+
+#ifdef _LIBC
+
+# define access __access
+# define getuid __getuid
+# define getgid __getgid
+# define geteuid __geteuid
+# define getegid __getegid
+# define group_member __group_member
+# define euidaccess __euidaccess
+# undef stat
+# define stat stat64
+
+#endif
+
+/* Return 0 if the user has permission of type MODE on FILE;
+   otherwise, return -1 and set 'errno'.
+   Like access, except that it uses the effective user and group
+   id's instead of the real ones, and it does not always check for read-only
+   file system, text busy, etc.  */
+
+int
+euidaccess (const char *file, int mode)
+{
+#if HAVE_FACCESSAT                   /* glibc, AIX 7, Solaris 11, Cygwin 1.7 */
+  return faccessat (AT_FDCWD, file, mode, AT_EACCESS);
+#elif defined EFF_ONLY_OK               /* IRIX, OSF/1, Interix */
+  return access (file, mode | EFF_ONLY_OK);
+#elif defined ACC_SELF                  /* AIX */
+  return accessx (file, mode, ACC_SELF);
+#elif HAVE_EACCESS                      /* FreeBSD */
+  return eaccess (file, mode);
+#else       /* Mac OS X, NetBSD, OpenBSD, HP-UX, Solaris, Cygwin, mingw, BeOS */
+
+  uid_t uid = getuid ();
+  gid_t gid = getgid ();
+  uid_t euid = geteuid ();
+  gid_t egid = getegid ();
+  struct stat stats;
+
+# if HAVE_DECL_SETREGID && PREFER_NONREENTRANT_EUIDACCESS
+
+  /* Define PREFER_NONREENTRANT_EUIDACCESS if you prefer euidaccess to
+     return the correct result even if this would make it
+     nonreentrant.  Define this only if your entire application is
+     safe even if the uid or gid might temporarily change.  If your
+     application uses signal handlers or threads it is probably not
+     safe.  */
+
+  if (mode == F_OK)
+    return stat (file, &stats);
+  else
+    {
+      int result;
+      int saved_errno;
+
+      if (uid != euid)
+        setreuid (euid, uid);
+      if (gid != egid)
+        setregid (egid, gid);
+
+      result = access (file, mode);
+      saved_errno = errno;
+
+      /* Restore them.  */
+      if (uid != euid)
+        setreuid (uid, euid);
+      if (gid != egid)
+        setregid (gid, egid);
+
+      errno = saved_errno;
+      return result;
+    }
+
+# else
+
+  /* The following code assumes the traditional Unix model, and is not
+     correct on systems that have ACLs or the like.  However, it's
+     better than nothing, and it is reentrant.  */
+
+  unsigned int granted;
+  if (uid == euid && gid == egid)
+    /* If we are not set-uid or set-gid, access does the same.  */
+    return access (file, mode);
+
+  if (stat (file, &stats) != 0)
+    return -1;
+
+  /* The super-user can read and write any file, and execute any file
+     that anyone can execute.  */
+  if (euid == ROOT_UID
+      && ((mode & X_OK) == 0
+          || (stats.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))))
+    return 0;
+
+  /* Convert the mode to traditional form, clearing any bogus bits.  */
+  if (R_OK == 4 && W_OK == 2 && X_OK == 1 && F_OK == 0)
+    mode &= 7;
+  else
+    mode = ((mode & R_OK ? 4 : 0)
+            + (mode & W_OK ? 2 : 0)
+            + (mode & X_OK ? 1 : 0));
+
+  if (mode == 0)
+    return 0;                   /* The file exists.  */
+
+  /* Convert the file's permission bits to traditional form.  */
+  if (S_IRUSR == (4 << 6) && S_IWUSR == (2 << 6) && S_IXUSR == (1 << 6)
+      && S_IRGRP == (4 << 3) && S_IWGRP == (2 << 3) && S_IXGRP == (1 << 3)
+      && S_IROTH == (4 << 0) && S_IWOTH == (2 << 0) && S_IXOTH == (1 << 0))
+    granted = stats.st_mode;
+  else
+    granted = ((stats.st_mode & S_IRUSR ? 4 << 6 : 0)
+               + (stats.st_mode & S_IWUSR ? 2 << 6 : 0)
+               + (stats.st_mode & S_IXUSR ? 1 << 6 : 0)
+               + (stats.st_mode & S_IRGRP ? 4 << 3 : 0)
+               + (stats.st_mode & S_IWGRP ? 2 << 3 : 0)
+               + (stats.st_mode & S_IXGRP ? 1 << 3 : 0)
+               + (stats.st_mode & S_IROTH ? 4 << 0 : 0)
+               + (stats.st_mode & S_IWOTH ? 2 << 0 : 0)
+               + (stats.st_mode & S_IXOTH ? 1 << 0 : 0));
+
+  if (euid == stats.st_uid)
+    granted >>= 6;
+  else if (egid == stats.st_gid || group_member (stats.st_gid))
+    granted >>= 3;
+
+  if ((mode & ~granted) == 0)
+    return 0;
+  __set_errno (EACCESS);
+  return -1;
+
+# endif
+#endif
+}
+#undef euidaccess
+#ifdef weak_alias
+weak_alias (__euidaccess, euidaccess)
+#endif
+\f
+#ifdef TEST
+# include <error.h>
+# include <stdio.h>
+# include <stdlib.h>
+
+char *program_name;
+
+int
+main (int argc, char **argv)
+{
+  char *file;
+  int mode;
+  int err;
+
+  program_name = argv[0];
+  if (argc < 3)
+    abort ();
+  file = argv[1];
+  mode = atoi (argv[2]);
+
+  err = euidaccess (file, mode);
+  printf ("%d\n", err);
+  if (err != 0)
+    error (0, errno, "%s", file);
+  exit (0);
+}
+#endif

=== added file 'lib/getgroups.c'
--- lib/getgroups.c	1970-01-01 00:00:00 +0000
+++ lib/getgroups.c	2012-10-12 23:28:31 +0000
@@ -0,0 +1,116 @@
+/* provide consistent interface to getgroups for systems that don't allow N==0
+
+   Copyright (C) 1996, 1999, 2003, 2006-2012 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* written by Jim Meyering */
+
+#include <config.h>
+
+#include <unistd.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#if !HAVE_GETGROUPS
+
+/* Provide a stub that fails with ENOSYS, since there is no group
+   information available on mingw.  */
+int
+getgroups (int n _GL_UNUSED, GETGROUPS_T *groups _GL_UNUSED)
+{
+  errno = ENOSYS;
+  return -1;
+}
+
+#else /* HAVE_GETGROUPS */
+
+# undef getgroups
+# ifndef GETGROUPS_ZERO_BUG
+#  define GETGROUPS_ZERO_BUG 0
+# endif
+
+/* On at least Ultrix 4.3 and NextStep 3.2, getgroups (0, NULL) always
+   fails.  On other systems, it returns the number of supplemental
+   groups for the process.  This function handles that special case
+   and lets the system-provided function handle all others.  However,
+   it can fail with ENOMEM if memory is tight.  It is unspecified
+   whether the effective group id is included in the list.  */
+
+int
+rpl_getgroups (int n, gid_t *group)
+{
+  int n_groups;
+  GETGROUPS_T *gbuf;
+  int saved_errno;
+
+  if (n < 0)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  if (n != 0 || !GETGROUPS_ZERO_BUG)
+    {
+      int result;
+      if (sizeof *group == sizeof *gbuf)
+        return getgroups (n, (GETGROUPS_T *) group);
+
+      if (SIZE_MAX / sizeof *gbuf <= n)
+        {
+          errno = ENOMEM;
+          return -1;
+        }
+      gbuf = malloc (n * sizeof *gbuf);
+      if (!gbuf)
+        return -1;
+      result = getgroups (n, gbuf);
+      if (0 <= result)
+        {
+          n = result;
+          while (n--)
+            group[n] = gbuf[n];
+        }
+      saved_errno = errno;
+      free (gbuf);
+      errno == saved_errno;
+      return result;
+    }
+
+  n = 20;
+  while (1)
+    {
+      /* No need to worry about address arithmetic overflow here,
+         since the ancient systems that we're running on have low
+         limits on the number of secondary groups.  */
+      gbuf = malloc (n * sizeof *gbuf);
+      if (!gbuf)
+        return -1;
+      n_groups = getgroups (n, gbuf);
+      if (n_groups == -1 ? errno != EINVAL : n_groups < n)
+        break;
+      free (gbuf);
+      n *= 2;
+    }
+
+  saved_errno = errno;
+  free (gbuf);
+  errno = saved_errno;
+
+  return n_groups;
+}
+
+#endif /* HAVE_GETGROUPS */

=== modified file 'lib/gnulib.mk'
--- lib/gnulib.mk	2012-10-04 07:15:42 +0000
+++ lib/gnulib.mk	2012-10-12 23:28:31 +0000
@@ -21,7 +21,7 @@
 # the same distribution terms as the rest of that program.
 #
 # Generated by gnulib-tool.
-# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=errno --avoid=fcntl --avoid=fcntl-h --avoid=fstat --avoid=msvc-inval --avoid=msvc-nothrow --avoid=raise --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo filemode getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings mktime pselect pthread_sigmask readlink socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub utimens warnings
+# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=errno --avoid=fcntl --avoid=fcntl-h --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=raise --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ euidaccess execinfo filemode getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings mktime pselect pthread_sigmask readlink socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub utimens warnings
 
 
 MOSTLYCLEANFILES += core *.stackdump
@@ -150,6 +150,15 @@
 
 ## end   gnulib module dup2
 
+## begin gnulib module euidaccess
+
+
+EXTRA_DIST += euidaccess.c
+
+EXTRA_libgnu_a_SOURCES += euidaccess.c
+
+## end   gnulib module euidaccess
+
 ## begin gnulib module execinfo
 
 BUILT_SOURCES += $(EXECINFO_H)
@@ -183,6 +192,17 @@
 
 ## end   gnulib module filemode
 
+## begin gnulib module getgroups
+
+if gl_GNULIB_ENABLED_getgroups
+
+endif
+EXTRA_DIST += getgroups.c
+
+EXTRA_libgnu_a_SOURCES += getgroups.c
+
+## end   gnulib module getgroups
+
 ## begin gnulib module getloadavg
 
 
@@ -242,6 +262,17 @@
 
 ## end   gnulib module gettimeofday
 
+## begin gnulib module group-member
+
+if gl_GNULIB_ENABLED_a9786850e999ae65a836a6041e8e5ed1
+
+endif
+EXTRA_DIST += group-member.c
+
+EXTRA_libgnu_a_SOURCES += group-member.c
+
+## end   gnulib module group-member
+
 ## begin gnulib module ignore-value
 
 
@@ -354,6 +385,13 @@
 
 ## end   gnulib module readlink
 
+## begin gnulib module root-uid
+
+
+EXTRA_DIST += root-uid.h
+
+## end   gnulib module root-uid
+
 ## begin gnulib module signal-h
 
 BUILT_SOURCES += signal.h
@@ -1312,6 +1350,15 @@
 
 ## end   gnulib module verify
 
+## begin gnulib module xalloc-oversized
+
+if gl_GNULIB_ENABLED_682e609604ccaac6be382e4ee3a4eaec
+
+endif
+EXTRA_DIST += xalloc-oversized.h
+
+## end   gnulib module xalloc-oversized
+
 
 mostlyclean-local: mostlyclean-generic
 	@for dir in '' $(MOSTLYCLEANDIRS); do \

=== added file 'lib/group-member.c'
--- lib/group-member.c	1970-01-01 00:00:00 +0000
+++ lib/group-member.c	2012-10-12 23:28:31 +0000
@@ -0,0 +1,119 @@
+/* group-member.c -- determine whether group id is in calling user's group list
+
+   Copyright (C) 1994, 1997-1998, 2003, 2005-2006, 2009-2012 Free Software
+   Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <unistd.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdlib.h>
+
+#include "xalloc-oversized.h"
+
+/* Most processes have no more than this many groups, and for these
+   processes we can avoid using malloc.  */
+enum { GROUPBUF_SIZE = 100 };
+
+struct group_info
+  {
+    gid_t *group;
+    gid_t groupbuf[GROUPBUF_SIZE];
+  };
+
+static void
+free_group_info (struct group_info const *g)
+{
+  if (g->group != g->groupbuf)
+    free (g->group);
+}
+
+static int
+get_group_info (struct group_info *gi)
+{
+  int n_groups = getgroups (GROUPBUF_SIZE, gi->groupbuf);
+  gi->group = gi->groupbuf;
+
+  if (n_groups < 0)
+    {
+      int n_group_slots = getgroups (0, NULL);
+      if (0 <= n_group_slots
+          && ! xalloc_oversized (n_group_slots, sizeof *gi->group))
+        {
+          gi->group = malloc (n_group_slots * sizeof *gi->group);
+          if (gi->group)
+            n_groups = getgroups (n_group_slots, gi->group);
+        }
+    }
+
+  /* In case of error, the user loses.  */
+  return n_groups;
+}
+
+/* Return non-zero if GID is one that we have in our groups list.
+   Note that the groups list is not guaranteed to contain the current
+   or effective group ID, so they should generally be checked
+   separately.  */
+
+int
+group_member (gid_t gid)
+{
+  int i;
+  int found;
+  struct group_info gi;
+  int n_groups = get_group_info (&gi);
+
+  /* Search through the list looking for GID. */
+  found = 0;
+  for (i = 0; i < n_groups; i++)
+    {
+      if (gid == gi.group[i])
+        {
+          found = 1;
+          break;
+        }
+    }
+
+  free_group_info (&gi);
+
+  return found;
+}
+
+#ifdef TEST
+
+char *program_name;
+
+int
+main (int argc, char **argv)
+{
+  int i;
+
+  program_name = argv[0];
+
+  for (i = 1; i < argc; i++)
+    {
+      gid_t gid;
+
+      gid = atoi (argv[i]);
+      printf ("%d: %s\n", gid, group_member (gid) ? "yes" : "no");
+    }
+  exit (0);
+}
+
+#endif /* TEST */

=== added file 'lib/root-uid.h'
--- lib/root-uid.h	1970-01-01 00:00:00 +0000
+++ lib/root-uid.h	2012-10-12 23:28:31 +0000
@@ -0,0 +1,30 @@
+/* The user ID that always has appropriate privileges in the POSIX sense.
+
+   Copyright 2012 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   Written by Paul Eggert.  */
+
+#ifndef ROOT_UID_H_
+#define ROOT_UID_H_
+
+/* The user ID that always has appropriate privileges in the POSIX sense.  */
+#ifdef __TANDEM
+# define ROOT_UID 65535
+#else
+# define ROOT_UID 0
+#endif
+
+#endif

=== added file 'lib/xalloc-oversized.h'
--- lib/xalloc-oversized.h	1970-01-01 00:00:00 +0000
+++ lib/xalloc-oversized.h	2012-10-12 23:28:31 +0000
@@ -0,0 +1,38 @@
+/* xalloc-oversized.h -- memory allocation size checking
+
+   Copyright (C) 1990-2000, 2003-2004, 2006-2012 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef XALLOC_OVERSIZED_H_
+# define XALLOC_OVERSIZED_H_
+
+# include <stddef.h>
+
+/* Return 1 if an array of N objects, each of size S, cannot exist due
+   to size arithmetic overflow.  S must be positive and N must be
+   nonnegative.  This is a macro, not a function, so that it
+   works correctly even when SIZE_MAX < N.
+
+   By gnulib convention, SIZE_MAX represents overflow in size
+   calculations, so the conservative dividend to use here is
+   SIZE_MAX - 1, since SIZE_MAX might represent an overflowed value.
+   However, malloc (SIZE_MAX) fails on all known hosts where
+   sizeof (ptrdiff_t) <= sizeof (size_t), so do not bother to test for
+   exactly-SIZE_MAX allocations on such hosts; this avoids a test and
+   branch when S is known to be 1.  */
+# define xalloc_oversized(n, s) \
+    ((size_t) (sizeof (ptrdiff_t) <= sizeof (size_t) ? -1 : -2) / (s) < (n))
+
+#endif /* !XALLOC_OVERSIZED_H_ */

=== added file 'm4/euidaccess.m4'
--- m4/euidaccess.m4	1970-01-01 00:00:00 +0000
+++ m4/euidaccess.m4	2012-10-12 23:28:31 +0000
@@ -0,0 +1,52 @@
+# euidaccess.m4 serial 14
+dnl Copyright (C) 2002-2012 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_NONREENTRANT_EUIDACCESS],
+[
+  AC_REQUIRE([gl_FUNC_EUIDACCESS])
+  AC_DEFINE([PREFER_NONREENTRANT_EUIDACCESS], [1],
+    [Define this if you prefer euidaccess to return the correct result
+     even if this would make it nonreentrant.  Define this only if your
+     entire application is safe even if the uid or gid might temporarily
+     change.  If your application uses signal handlers or threads it
+     is probably not safe.])
+])
+
+AC_DEFUN([gl_FUNC_EUIDACCESS],
+[
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+
+  dnl Persuade glibc <unistd.h> to declare euidaccess().
+  AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
+
+  AC_CHECK_FUNCS([euidaccess])
+  if test $ac_cv_func_euidaccess = no; then
+    HAVE_EUIDACCESS=0
+  fi
+])
+
+# Prerequisites of lib/euidaccess.c.
+AC_DEFUN([gl_PREREQ_EUIDACCESS], [
+  dnl Prefer POSIX faccessat over non-standard euidaccess.
+  AC_CHECK_FUNCS_ONCE([faccessat])
+  dnl Try various other non-standard fallbacks.
+  AC_CHECK_HEADERS_ONCE([libgen.h])
+  AC_CHECK_DECLS_ONCE([setregid])
+  AC_REQUIRE([AC_FUNC_GETGROUPS])
+
+  # Solaris 9 and 10 need -lgen to get the eaccess function.
+  # Save and restore LIBS so -lgen isn't added to it.  Otherwise, *all*
+  # programs in the package would end up linked with that potentially-shared
+  # library, inducing unnecessary run-time overhead.
+  LIB_EACCESS=
+  AC_SUBST([LIB_EACCESS])
+  gl_saved_libs=$LIBS
+    AC_SEARCH_LIBS([eaccess], [gen],
+                   [test "$ac_cv_search_eaccess" = "none required" ||
+                    LIB_EACCESS=$ac_cv_search_eaccess])
+    AC_CHECK_FUNCS([eaccess])
+  LIBS=$gl_saved_libs
+])

=== added file 'm4/getgroups.m4'
--- m4/getgroups.m4	1970-01-01 00:00:00 +0000
+++ m4/getgroups.m4	2012-10-12 23:28:31 +0000
@@ -0,0 +1,107 @@
+# serial 18
+
+dnl From Jim Meyering.
+dnl A wrapper around AC_FUNC_GETGROUPS.
+
+# Copyright (C) 1996-1997, 1999-2004, 2008-2012 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+m4_version_prereq([2.70], [] ,[
+
+# This is taken from the following Autoconf patch:
+# http://git.savannah.gnu.org/gitweb/?p=autoconf.git;a=commitdiff;h=7fbb553727ed7e0e689a17594b58559ecf3ea6e9
+AC_DEFUN([AC_FUNC_GETGROUPS],
+[
+  AC_REQUIRE([AC_TYPE_GETGROUPS])dnl
+  AC_REQUIRE([AC_TYPE_SIZE_T])dnl
+  AC_REQUIRE([AC_CANONICAL_HOST])dnl for cross-compiles
+  AC_CHECK_FUNC([getgroups])
+
+  # If we don't yet have getgroups, see if it's in -lbsd.
+  # This is reported to be necessary on an ITOS 3000WS running SEIUX 3.1.
+  ac_save_LIBS=$LIBS
+  if test $ac_cv_func_getgroups = no; then
+    AC_CHECK_LIB(bsd, getgroups, [GETGROUPS_LIB=-lbsd])
+  fi
+
+  # Run the program to test the functionality of the system-supplied
+  # getgroups function only if there is such a function.
+  if test $ac_cv_func_getgroups = yes; then
+    AC_CACHE_CHECK([for working getgroups], [ac_cv_func_getgroups_works],
+      [AC_RUN_IFELSE(
+         [AC_LANG_PROGRAM(
+            [AC_INCLUDES_DEFAULT],
+            [[/* On Ultrix 4.3, getgroups (0, 0) always fails.  */
+              return getgroups (0, 0) == -1;]])
+         ],
+         [ac_cv_func_getgroups_works=yes],
+         [ac_cv_func_getgroups_works=no],
+         [case "$host_os" in # ((
+                    # Guess yes on glibc systems.
+            *-gnu*) ac_cv_func_getgroups_works="guessing yes" ;;
+                    # If we don't know, assume the worst.
+            *)      ac_cv_func_getgroups_works="guessing no" ;;
+          esac
+         ])
+      ])
+  else
+    ac_cv_func_getgroups_works=no
+  fi
+  case "$ac_cv_func_getgroups_works" in
+    *yes)
+      AC_DEFINE([HAVE_GETGROUPS], [1],
+        [Define to 1 if your system has a working `getgroups' function.])
+      ;;
+  esac
+  LIBS=$ac_save_LIBS
+])# AC_FUNC_GETGROUPS
+
+])
+
+AC_DEFUN([gl_FUNC_GETGROUPS],
+[
+  AC_REQUIRE([AC_TYPE_GETGROUPS])
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+
+  AC_FUNC_GETGROUPS
+  if test $ac_cv_func_getgroups != yes; then
+    HAVE_GETGROUPS=0
+  else
+    if test "$ac_cv_type_getgroups" != gid_t \
+       || { case "$ac_cv_func_getgroups_works" in
+              *yes) false;;
+              *) true;;
+            esac
+          }; then
+      REPLACE_GETGROUPS=1
+      AC_DEFINE([GETGROUPS_ZERO_BUG], [1], [Define this to 1 if
+        getgroups(0,NULL) does not return the number of groups.])
+    else
+      dnl Detect FreeBSD bug; POSIX requires getgroups(-1,ptr) to fail.
+      AC_CACHE_CHECK([whether getgroups handles negative values],
+        [gl_cv_func_getgroups_works],
+        [AC_RUN_IFELSE([AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT],
+          [[int size = getgroups (0, 0);
+            gid_t *list = malloc (size * sizeof *list);
+            return getgroups (-1, list) != -1;]])],
+          [gl_cv_func_getgroups_works=yes],
+          [gl_cv_func_getgroups_works=no],
+          [case "$host_os" in
+                     # Guess yes on glibc systems.
+             *-gnu*) gl_cv_func_getgroups_works="guessing yes" ;;
+                     # If we don't know, assume the worst.
+             *)      gl_cv_func_getgroups_works="guessing no" ;;
+           esac
+          ])])
+      case "$gl_cv_func_getgroups_works" in
+        *yes) ;;
+        *) REPLACE_GETGROUPS=1 ;;
+      esac
+    fi
+  fi
+  test -n "$GETGROUPS_LIB" && LIBS="$GETGROUPS_LIB $LIBS"
+])

=== modified file 'm4/gnulib-comp.m4'
--- m4/gnulib-comp.m4	2012-09-27 01:06:23 +0000
+++ m4/gnulib-comp.m4	2012-10-12 23:28:31 +0000
@@ -53,17 +53,20 @@
   # Code from module dtotimespec:
   # Code from module dup2:
   # Code from module environ:
+  # Code from module euidaccess:
   # Code from module execinfo:
   # Code from module extensions:
   AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
   # Code from module extern-inline:
   # Code from module filemode:
+  # Code from module getgroups:
   # Code from module getloadavg:
   # Code from module getopt-gnu:
   # Code from module getopt-posix:
   # Code from module gettext-h:
   # Code from module gettime:
   # Code from module gettimeofday:
+  # Code from module group-member:
   # Code from module ignore-value:
   # Code from module include_next:
   # Code from module intprops:
@@ -79,6 +82,7 @@
   # Code from module pselect:
   # Code from module pthread_sigmask:
   # Code from module readlink:
+  # Code from module root-uid:
   # Code from module signal-h:
   # Code from module snippet/_Noreturn:
   # Code from module snippet/arg-nonnull:
@@ -120,6 +124,7 @@
   # Code from module utimens:
   # Code from module verify:
   # Code from module warnings:
+  # Code from module xalloc-oversized:
 ])
 
 # This macro should be invoked from ./configure.ac, in the section
@@ -154,6 +159,12 @@
   gl_UNISTD_MODULE_INDICATOR([dup2])
   gl_ENVIRON
   gl_UNISTD_MODULE_INDICATOR([environ])
+  gl_FUNC_EUIDACCESS
+  if test $HAVE_EUIDACCESS = 0; then
+    AC_LIBOBJ([euidaccess])
+    gl_PREREQ_EUIDACCESS
+  fi
+  gl_UNISTD_MODULE_INDICATOR([euidaccess])
   gl_EXECINFO_H
   AC_REQUIRE([gl_EXTERN_INLINE])
   gl_FILEMODE
@@ -269,18 +280,32 @@
   gl_UNISTD_H
   gl_UTIMENS
   gl_gnulib_enabled_dosname=false
+  gl_gnulib_enabled_getgroups=false
   gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=false
+  gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1=false
   gl_gnulib_enabled_pathmax=false
   gl_gnulib_enabled_stat=false
   gl_gnulib_enabled_strtoll=false
   gl_gnulib_enabled_strtoull=false
   gl_gnulib_enabled_verify=false
+  gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=false
   func_gl_gnulib_m4code_dosname ()
   {
     if ! $gl_gnulib_enabled_dosname; then
       gl_gnulib_enabled_dosname=true
     fi
   }
+  func_gl_gnulib_m4code_getgroups ()
+  {
+    if ! $gl_gnulib_enabled_getgroups; then
+      gl_FUNC_GETGROUPS
+      if test $HAVE_GETGROUPS = 0 || test $REPLACE_GETGROUPS = 1; then
+        AC_LIBOBJ([getgroups])
+      fi
+      gl_UNISTD_MODULE_INDICATOR([getgroups])
+      gl_gnulib_enabled_getgroups=true
+    fi
+  }
   func_gl_gnulib_m4code_be453cec5eecf5731a274f2de7f2db36 ()
   {
     if ! $gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36; then
@@ -289,6 +314,24 @@
       gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=true
     fi
   }
+  func_gl_gnulib_m4code_a9786850e999ae65a836a6041e8e5ed1 ()
+  {
+    if ! $gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1; then
+      gl_FUNC_GROUP_MEMBER
+      if test $HAVE_GROUP_MEMBER = 0; then
+        AC_LIBOBJ([group-member])
+        gl_PREREQ_GROUP_MEMBER
+      fi
+      gl_UNISTD_MODULE_INDICATOR([group-member])
+      gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1=true
+      if test $HAVE_GROUP_MEMBER = 0; then
+        func_gl_gnulib_m4code_getgroups
+      fi
+      if test $HAVE_GROUP_MEMBER = 0; then
+        func_gl_gnulib_m4code_682e609604ccaac6be382e4ee3a4eaec
+      fi
+    fi
+  }
   func_gl_gnulib_m4code_pathmax ()
   {
     if ! $gl_gnulib_enabled_pathmax; then
@@ -347,6 +390,18 @@
       gl_gnulib_enabled_verify=true
     fi
   }
+  func_gl_gnulib_m4code_682e609604ccaac6be382e4ee3a4eaec ()
+  {
+    if ! $gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec; then
+      gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=true
+    fi
+  }
+  if test $HAVE_EUIDACCESS = 0; then
+    func_gl_gnulib_m4code_a9786850e999ae65a836a6041e8e5ed1
+  fi
+  if test $HAVE_EUIDACCESS = 0; then
+    func_gl_gnulib_m4code_stat
+  fi
   if test $REPLACE_GETOPT = 1; then
     func_gl_gnulib_m4code_be453cec5eecf5731a274f2de7f2db36
   fi
@@ -373,12 +428,15 @@
   fi
   m4_pattern_allow([^gl_GNULIB_ENABLED_])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_dosname], [$gl_gnulib_enabled_dosname])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_getgroups], [$gl_gnulib_enabled_getgroups])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_be453cec5eecf5731a274f2de7f2db36], [$gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_a9786850e999ae65a836a6041e8e5ed1], [$gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_pathmax], [$gl_gnulib_enabled_pathmax])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_stat], [$gl_gnulib_enabled_stat])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_strtoll], [$gl_gnulib_enabled_strtoll])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_strtoull], [$gl_gnulib_enabled_strtoull])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_verify], [$gl_gnulib_enabled_verify])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_682e609604ccaac6be382e4ee3a4eaec], [$gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec])
   # End of code from modules
   m4_ifval(gl_LIBSOURCES_LIST, [
     m4_syscmd([test ! -d ]m4_defn([gl_LIBSOURCES_DIR])[ ||
@@ -538,12 +596,14 @@
   lib/dtoastr.c
   lib/dtotimespec.c
   lib/dup2.c
+  lib/euidaccess.c
   lib/execinfo.c
   lib/execinfo.in.h
   lib/filemode.c
   lib/filemode.h
   lib/ftoastr.c
   lib/ftoastr.h
+  lib/getgroups.c
   lib/getloadavg.c
   lib/getopt.c
   lib/getopt.in.h
@@ -552,6 +612,7 @@
   lib/gettext.h
   lib/gettime.c
   lib/gettimeofday.c
+  lib/group-member.c
   lib/ignore-value.h
   lib/intprops.h
   lib/inttypes.in.h
@@ -564,6 +625,7 @@
   lib/pselect.c
   lib/pthread_sigmask.c
   lib/readlink.c
+  lib/root-uid.h
   lib/sha1.c
   lib/sha1.h
   lib/sha256.c
@@ -605,21 +667,25 @@
   lib/utimens.c
   lib/utimens.h
   lib/verify.h
+  lib/xalloc-oversized.h
   m4/00gnulib.m4
   m4/alloca.m4
   m4/c-strtod.m4
   m4/clock_time.m4
   m4/dup2.m4
   m4/environ.m4
+  m4/euidaccess.m4
   m4/execinfo.m4
   m4/extensions.m4
   m4/extern-inline.m4
   m4/filemode.m4
+  m4/getgroups.m4
   m4/getloadavg.m4
   m4/getopt.m4
   m4/gettime.m4
   m4/gettimeofday.m4
   m4/gnulib-common.m4
+  m4/group-member.m4
   m4/include_next.m4
   m4/inttypes.m4
   m4/largefile.m4

=== added file 'm4/group-member.m4'
--- m4/group-member.m4	1970-01-01 00:00:00 +0000
+++ m4/group-member.m4	2012-10-12 23:28:31 +0000
@@ -0,0 +1,29 @@
+# serial 14
+
+# Copyright (C) 1999-2001, 2003-2007, 2009-2012 Free Software Foundation, Inc.
+
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+dnl Written by Jim Meyering
+
+AC_DEFUN([gl_FUNC_GROUP_MEMBER],
+[
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+
+  dnl Persuade glibc <unistd.h> to declare group_member().
+  AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
+
+  dnl Do this replacement check manually because I want the hyphen
+  dnl (not the underscore) in the filename.
+  AC_CHECK_FUNC([group_member], , [
+    HAVE_GROUP_MEMBER=0
+  ])
+])
+
+# Prerequisites of lib/group-member.c.
+AC_DEFUN([gl_PREREQ_GROUP_MEMBER],
+[
+  AC_REQUIRE([AC_FUNC_GETGROUPS])
+])

=== modified file 'src/ChangeLog'
--- src/ChangeLog	2012-10-14 00:17:07 +0000
+++ src/ChangeLog	2012-10-14 06:02:52 +0000
@@ -1,3 +1,27 @@
+2012-10-14  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use euidaccess, not access, when checking file permissions (Bug#12632).
+	* Makefile.in (LIB_EACCESS): New macro.
+	(LIBES): Use it.
+	* callproc.c (init_callproc):
+	* charset.c (init_charset):
+	* fileio.c (check_existing) [!DOS_NT]:
+	(check_executable) [!DOS_NT && !HAVE_EUIDACCESS]:
+	* lread.c (openp, load_path_check):
+	* process.c (allocate_pty):
+	* xrdb.c (file_p):
+	Use euidaccess, not access.  Use symbolic names instead of integers
+	for the flags, as they're portable now.
+	* fileio.c (Ffile_readable_p) [!DOS_NT && !macintosh]:
+	Use euidaccess, not stat + open + close.
+	(file_directory_p): New function, which uses 'stat' on most places
+	but 'access' (for efficiency) if WINDOWSNT.
+	(Ffile_directory_p): Use it.
+	* lisp.h (file_directory_p): New decl.
+	* lread.c (openp): When opening a file, use fstat rather than
+	stat, as that avoids a permissions race.  When not opening a file,
+	use file_directory_p rather than stat.
+
 2012-10-13  Jan Djärv  <jan.h.d@swipnet.se>
 
 	* gtkutil.c (xg_set_widget_bg): Divide by 65535 (Bug#12612).

=== modified file 'src/Makefile.in'
--- src/Makefile.in	2012-10-08 12:53:18 +0000
+++ src/Makefile.in	2012-10-13 01:49:41 +0000
@@ -150,6 +150,7 @@
 M17N_FLT_LIBS = @M17N_FLT_LIBS@
 
 LIB_CLOCK_GETTIME=@LIB_CLOCK_GETTIME@
+LIB_EACCESS=@LIB_EACCESS@
 LIB_TIMER_TIME=@LIB_TIMER_TIME@
 
 DBUS_CFLAGS = @DBUS_CFLAGS@
@@ -392,7 +393,7 @@
 LIBES = $(LIBS) $(W32_LIBS) $(LIBX_BASE) $(LIBIMAGE) \
    $(LIBX_OTHER) $(LIBSOUND) \
    $(RSVG_LIBS) $(IMAGEMAGICK_LIBS) $(LIB_CLOCK_GETTIME) \
-   $(LIB_TIMER_TIME) $(DBUS_LIBS) \
+   $(LIB_EACCESS) $(LIB_TIMER_TIME) $(DBUS_LIBS) \
    $(LIB_EXECINFO) \
    $(LIBXML2_LIBS) $(LIBGPM) $(LIBRESOLV) $(LIBS_SYSTEM) \
    $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \

=== modified file 'src/callproc.c'
--- src/callproc.c	2012-09-23 22:25:22 +0000
+++ src/callproc.c	2012-10-12 23:28:31 +0000
@@ -1597,13 +1597,13 @@
 #endif
     {
       tempdir = Fdirectory_file_name (Vexec_directory);
-      if (access (SSDATA (tempdir), 0) < 0)
+      if (euidaccess (SSDATA (tempdir), F_OK) < 0)
 	dir_warning ("Warning: arch-dependent data dir (%s) does not exist.\n",
 		     Vexec_directory);
     }
 
   tempdir = Fdirectory_file_name (Vdata_directory);
-  if (access (SSDATA (tempdir), 0) < 0)
+  if (euidaccess (SSDATA (tempdir), F_OK) < 0)
     dir_warning ("Warning: arch-independent data dir (%s) does not exist.\n",
 		 Vdata_directory);
 

=== modified file 'src/charset.c'
--- src/charset.c	2012-10-01 06:36:54 +0000
+++ src/charset.c	2012-10-12 23:28:31 +0000
@@ -2293,7 +2293,7 @@
 {
   Lisp_Object tempdir;
   tempdir = Fexpand_file_name (build_string ("charsets"), Vdata_directory);
-  if (access (SSDATA (tempdir), 0) < 0)
+  if (euidaccess (SSDATA (tempdir), F_OK) != 0)
     {
       /* This used to be non-fatal (dir_warning), but it should not
          happen, and if it does sooner or later it will cause some

=== modified file 'src/fileio.c'
--- src/fileio.c	2012-10-13 08:55:26 +0000
+++ src/fileio.c	2012-10-14 06:02:52 +0000
@@ -2428,10 +2428,11 @@
   /* The full emulation of Posix 'stat' is too expensive on
      DOS/Windows, when all we want to know is whether the file exists.
      So we use 'access' instead, which is much more lightweight.  */
+  /* FIXME: an euidaccess implementation should be added to the
+     DOS/Windows ports and this #ifdef branch should be removed.  */
   return (access (filename, F_OK) >= 0);
 #else
-  struct stat st;
-  return (stat (filename, &st) >= 0);
+  return euidaccess (filename, F_OK) == 0;
 #endif
 }
 
@@ -2441,19 +2442,14 @@
 check_executable (char *filename)
 {
 #ifdef DOS_NT
+  /* FIXME: an euidaccess implementation should be added to the
+     DOS/Windows ports and this #ifdef branch should be removed.  */
   struct stat st;
   if (stat (filename, &st) < 0)
     return 0;
   return ((st.st_mode & S_IEXEC) != 0);
 #else /* not DOS_NT */
-#ifdef HAVE_EUIDACCESS
-  return (euidaccess (filename, 1) >= 0);
-#else
-  /* Access isn't quite right because it uses the real uid
-     and we really want to test with the effective uid.
-     But Unix doesn't give us a right way to do it.  */
-  return (access (filename, 1) >= 0);
-#endif
+  return euidaccess (filename, X_OK) == 0;
 #endif /* not DOS_NT */
 }
 
@@ -2463,13 +2459,14 @@
 check_writable (const char *filename)
 {
 #ifdef MSDOS
+  /* FIXME: an euidaccess implementation should be added to the
+     DOS/Windows ports and this #ifdef branch should be removed.  */
   struct stat st;
   if (stat (filename, &st) < 0)
     return 0;
   return (st.st_mode & S_IWRITE || S_ISDIR (st.st_mode));
 #else /* not MSDOS */
-#ifdef HAVE_EUIDACCESS
-  bool res = (euidaccess (filename, 2) >= 0);
+  bool res = euidaccess (filename, W_OK) == 0;
 #ifdef CYGWIN
   /* euidaccess may have returned failure because Cygwin couldn't
      determine the file's UID or GID; if so, we return success. */
@@ -2482,14 +2479,6 @@
     }
 #endif /* CYGWIN */
   return res;
-#else /* not HAVE_EUIDACCESS */
-  /* Access isn't quite right because it uses the real uid
-     and we really want to test with the effective uid.
-     But Unix doesn't give us a right way to do it.
-     Opening with O_WRONLY could work for an ordinary file,
-     but would lose for directories.  */
-  return (access (filename, 2) >= 0);
-#endif /* not HAVE_EUIDACCESS */
 #endif /* not MSDOS */
 }
 
@@ -2546,9 +2535,6 @@
 {
   Lisp_Object absname;
   Lisp_Object handler;
-  int desc;
-  int flags;
-  struct stat statbuf;
 
   CHECK_STRING (filename);
   absname = Fexpand_file_name (filename, Qnil);
@@ -2562,28 +2548,13 @@
   absname = ENCODE_FILE (absname);
 
 #if defined (DOS_NT) || defined (macintosh)
-  /* Under MS-DOS, Windows, and Macintosh, open does not work for
-     directories.  */
+  /* FIXME: an euidaccess implementation should be added to the
+     DOS/Windows ports and this #ifdef branch should be removed.  */
   if (access (SDATA (absname), 0) == 0)
     return Qt;
   return Qnil;
 #else /* not DOS_NT and not macintosh */
-  flags = O_RDONLY;
-#ifdef O_NONBLOCK
-  /* Opening a fifo without O_NONBLOCK can wait.
-     We don't want to wait.  But we don't want to mess wth O_NONBLOCK
-     except in the case of a fifo, on a system which handles it.  */
-  desc = stat (SSDATA (absname), &statbuf);
-  if (desc < 0)
-    return Qnil;
-  if (S_ISFIFO (statbuf.st_mode))
-    flags |= O_NONBLOCK;
-#endif
-  desc = emacs_open (SSDATA (absname), flags, 0);
-  if (desc < 0)
-    return Qnil;
-  emacs_close (desc);
-  return Qt;
+  return euidaccess (SSDATA (absname), R_OK) == 0 ? Qt : Qnil;
 #endif /* not DOS_NT and not macintosh */
 }
 
@@ -2702,8 +2673,7 @@
 See `file-symlink-p' to distinguish symlinks.  */)
   (Lisp_Object filename)
 {
-  register Lisp_Object absname;
-  struct stat st;
+  Lisp_Object absname;
   Lisp_Object handler;
 
   absname = expand_and_dir_to_file (filename, BVAR (current_buffer, directory));
@@ -2716,9 +2686,20 @@
 
   absname = ENCODE_FILE (absname);
 
-  if (stat (SSDATA (absname), &st) < 0)
-    return Qnil;
-  return S_ISDIR (st.st_mode) ? Qt : Qnil;
+  return file_directory_p (SSDATA (absname)) ? Qt : Qnil;
+}
+
+/* Return true if FILE is a directory or a symlink to a directory.  */
+bool
+file_directory_p (char const *file)
+{
+#ifdef WINDOWSNT
+  /* 'access' is cheaper than 'stat'.  */
+  return access (file, D_OK) == 0;
+#else
+  struct stat st;
+  return stat (file, &st) == 0 && S_ISDIR (st.st_mode);
+#endif
 }
 
 DEFUN ("file-accessible-directory-p", Ffile_accessible_directory_p,

=== modified file 'src/lisp.h'
--- src/lisp.h	2012-10-12 15:19:54 +0000
+++ src/lisp.h	2012-10-14 06:00:40 +0000
@@ -3179,6 +3179,7 @@
 extern Lisp_Object restore_point_unwind (Lisp_Object);
 extern _Noreturn void report_file_error (const char *, Lisp_Object);
 extern void internal_delete_file (Lisp_Object);
+extern bool file_directory_p (const char *);
 extern void syms_of_fileio (void);
 extern Lisp_Object make_temp_name (Lisp_Object, bool);
 extern Lisp_Object Qdelete_file;

=== modified file 'src/lread.c'
--- src/lread.c	2012-10-12 15:19:54 +0000
+++ src/lread.c	2012-10-14 06:00:40 +0000
@@ -1404,7 +1404,7 @@
 If SUFFIXES is non-nil, it should be a list of suffixes to append to
 file name when searching.
 If non-nil, PREDICATE is used instead of `file-readable-p'.
-PREDICATE can also be an integer to pass to the access(2) function,
+PREDICATE can also be an integer to pass to the euidaccess(2) function,
 in which case file-name-handlers are ignored.
 This function will normally skip directories, so if you want it to find
 directories, make sure the PREDICATE function returns `dir-ok' for them.  */)
@@ -1442,7 +1442,6 @@
 int
 openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes, Lisp_Object *storeptr, Lisp_Object predicate)
 {
-  int fd;
   ptrdiff_t fn_size = 100;
   char buf[100];
   char *fn = buf;
@@ -1497,7 +1496,6 @@
 	{
 	  ptrdiff_t fnlen, lsuffix = SBYTES (XCAR (tail));
 	  Lisp_Object handler;
-	  bool exists;
 
 	  /* Concatenate path element/specified name with the suffix.
 	     If the directory starts with /:, remove that.  */
@@ -1521,6 +1519,7 @@
 	  handler = Ffind_file_name_handler (string, Qfile_exists_p);
 	  if ((!NILP (handler) || !NILP (predicate)) && !NATNUMP (predicate))
             {
+	      bool exists;
 	      if (NILP (predicate))
 		exists = !NILP (Ffile_readable_p (string));
 	      else
@@ -1542,37 +1541,38 @@
 	    }
 	  else
 	    {
-#ifndef WINDOWSNT
-	      struct stat st;
-#endif
+	      int fd;
 	      const char *pfn;
 
 	      encoded_fn = ENCODE_FILE (string);
 	      pfn = SSDATA (encoded_fn);
-#ifdef WINDOWSNT
-	      exists = access (pfn, F_OK) == 0 && access (pfn, D_OK) < 0;
-#else
-	      exists = (stat (pfn, &st) == 0 && ! S_ISDIR (st.st_mode));
-#endif
-	      if (exists)
+
+	      /* Check that we can access or open it.  */
+	      if (NATNUMP (predicate))
+		fd = (((XFASTINT (predicate) & ~INT_MAX) == 0
+		       && euidaccess (pfn, XFASTINT (predicate)) == 0
+		       && ! file_directory_p (pfn))
+		      ? 1 : -1);
+	      else
 		{
-		  /* Check that we can access or open it.  */
-		  if (NATNUMP (predicate))
-		    fd = (((XFASTINT (predicate) & ~INT_MAX) == 0
-			   && access (pfn, XFASTINT (predicate)) == 0)
-			  ? 1 : -1);
-		  else
-		    fd = emacs_open (pfn, O_RDONLY, 0);
-
-		  if (fd >= 0)
+		  struct stat st;
+		  fd = emacs_open (pfn, O_RDONLY, 0);
+		  if (0 <= fd
+		      && (fstat (fd, &st) != 0 || S_ISDIR (st.st_mode)))
 		    {
-		      /* We succeeded; return this descriptor and filename.  */
-		      if (storeptr)
-			*storeptr = string;
-		      UNGCPRO;
-		      return fd;
+		      emacs_close (fd);
+		      fd = -1;
 		    }
 		}
+
+	      if (fd >= 0)
+		{
+		  /* We succeeded; return this descriptor and filename.  */
+		  if (storeptr)
+		    *storeptr = string;
+		  UNGCPRO;
+		  return fd;
+		}
 	    }
 	}
       if (absolute)
@@ -4088,7 +4088,7 @@
       if (STRINGP (dirfile))
         {
           dirfile = Fdirectory_file_name (dirfile);
-          if (access (SSDATA (dirfile), 0) < 0)
+          if (euidaccess (SSDATA (dirfile), F_OK) != 0)
             dir_warning ("Warning: Lisp directory `%s' does not exist.\n",
                          XCAR (path_tail));
         }

=== modified file 'src/process.c'
--- src/process.c	2012-10-13 00:52:01 +0000
+++ src/process.c	2012-10-13 00:55:20 +0000
@@ -663,7 +663,7 @@
 #else
 	    sprintf (pty_name, "/dev/tty%c%x", c, i);
 #endif /* no PTY_TTY_NAME_SPRINTF */
-	    if (access (pty_name, 6) != 0)
+	    if (euidaccess (pty_name, R_OK | W_OK) != 0)
 	      {
 		emacs_close (fd);
 # ifndef __sgi

=== modified file 'src/xrdb.c'
--- src/xrdb.c	2012-09-15 07:06:56 +0000
+++ src/xrdb.c	2012-10-12 23:28:31 +0000
@@ -268,7 +268,7 @@
 {
   struct stat status;
 
-  return (access (filename, 4) == 0             /* exists and is readable */
+  return (euidaccess (filename, R_OK) == 0      /* exists and is readable */
 	  && stat (filename, &status) == 0	/* get the status */
 	  && (S_ISDIR (status.st_mode)) == 0);	/* not a directory */
 }


^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-14  6:16     ` Paul Eggert
@ 2012-10-14  6:56       ` Eli Zaretskii
  2012-10-14 18:14         ` Paul Eggert
  0 siblings, 1 reply; 47+ messages in thread
From: Eli Zaretskii @ 2012-10-14  6:56 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 12632

> Date: Sat, 13 Oct 2012 23:16:44 -0700
> From: Paul Eggert <eggert@cs.ucla.edu>
> CC: 12632@debbugs.gnu.org
> 
> > How will the new code work if 'dir' is nil?
> 
> 'dir' can't be nil there.

file-name-directory can return nil, so 'dir' can be nil if the
function is called with a name of a non-exiting file that has no
leading directories.

> > Also, what about lread.c:openp, around line 1555: doesn't it want
> > 'euidaccess' as well, rather than 'stat'?
> 
> Sure, we can do that.  Done in the revised patch.
> [...]
> +
> +	      /* Check that we can access or open it.  */
> +	      if (NATNUMP (predicate))
> +		fd = (((XFASTINT (predicate) & ~INT_MAX) == 0
> +		       && euidaccess (pfn, XFASTINT (predicate)) == 0
> +		       && ! file_directory_p (pfn))
> +		      ? 1 : -1);
> +	      else

This won't compile on Windows, since there's no 'euidaccess' (yet).

>        if (STRINGP (dirfile))
>          {
>            dirfile = Fdirectory_file_name (dirfile);
> -          if (access (SSDATA (dirfile), 0) < 0)
> +          if (euidaccess (SSDATA (dirfile), F_OK) != 0)
>              dir_warning ("Warning: Lisp directory `%s' does not exist.\n",
>                           XCAR (path_tail));
>          }

Same here.

> > I don't understand why is it a good idea to use 'euidaccess' in
> > check_existing.  Isn't the fact of the mere existence of a file
> > independent of user's access rights?
> 
> No, because you cannot even stat a file that's in a directory that you
> can't search.  Using 'access' rather than 'euidaccess' might
> let a setuid Emacs search directories that it shouldn't be able
> to search, or vice versa.

But that's not a concern for check_existing, I think.  That's a
concern for check_writable, file-accessible-directory, etc.

IOW, Emacs should be able to test whether a file exists even if it
will be unable to access it later.

In any case, using 'euidaccess' here subtly changes the semantics of
file-exists-p, so if we decide to do that, it should be documented as
an incompatible change.





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-14  6:56       ` Eli Zaretskii
@ 2012-10-14 18:14         ` Paul Eggert
  2012-10-14 18:39           ` Eli Zaretskii
  0 siblings, 1 reply; 47+ messages in thread
From: Paul Eggert @ 2012-10-14 18:14 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 12632

[-- Attachment #1: Type: text/plain, Size: 7053 bytes --]

On 10/13/2012 11:56 PM, Eli Zaretskii wrote:

> This won't compile on Windows, since there's no 'euidaccess' (yet).

Thanks, that should be easy enough to fix, so I gave it a whirl
(patch below).

> Emacs should be able to test whether a file exists even if it
> will be unable to access it later.

Emacs cannot do that.  What 'access' does is ask, "If Emacs were
to issue the seteuid system call, and change the effective user
ID to the real user ID, would Emacs then be able to see that the
file exists?"  This does not test whether the file exists; it tests
only whether Emacs could see that the file exists in a hypothetical
situation that never actually happens (because Emacs never issues
the seteuid system call).  But this isn't what is wanted here:
what is wanted is a test whether Emacs can currently see that the
file exists, and that is what euidaccess does.

> In any case, using 'euidaccess' here subtly changes the semantics of
> file-exists-p

Currently file-exists-p uses 'stat', and 'euidaccess' uses
the same access check that 'stat' does -- they both use the
effective uid.  So the semantics aren't changing here.

>> > 'dir' can't be nil there.
> file-name-directory can return nil

It's an absolute file name, so file-name-directory can't return nil.
(This point is moot now, since the patch doesn't remove the
unnecessary NILP check.)

Here's the incremental patch to add euidaccess to Windows.
I'm attaching the entire revised patch, relative to trunk
bzr 110544.

=== modified file 'nt/ChangeLog'
--- nt/ChangeLog	2012-10-08 14:14:22 +0000
+++ nt/ChangeLog	2012-10-14 18:06:01 +0000
@@ -1,3 +1,9 @@
+2012-10-14  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use euidaccess, not access, when checking file permissions (Bug#12632).
+	* inc/ms-w32.h (euidaccess): Define this, not 'access', since
+	the mainline code now uses euidaccess.
+
 2012-10-08  Juanma Barranquero  <lekktu@gmail.com>
 
 	* config.nt: Sync with autogen/config.in.

=== modified file 'nt/inc/ms-w32.h'
--- nt/inc/ms-w32.h	2012-09-30 21:36:42 +0000
+++ nt/inc/ms-w32.h	2012-10-14 18:06:01 +0000
@@ -160,8 +160,6 @@
 #endif
 
 /* Calls that are emulated or shadowed.  */
-#undef access
-#define access  sys_access
 #undef chdir
 #define chdir   sys_chdir
 #undef chmod
@@ -176,6 +174,7 @@
 #define dup     sys_dup
 #undef dup2
 #define dup2    sys_dup2
+#define euidaccess  sys_access
 #define fopen   sys_fopen
 #define link    sys_link
 #define localtime sys_localtime

=== modified file 'src/ChangeLog'
--- src/ChangeLog	2012-10-14 06:02:52 +0000
+++ src/ChangeLog	2012-10-14 18:06:01 +0000
@@ -5,14 +5,13 @@
 	(LIBES): Use it.
 	* callproc.c (init_callproc):
 	* charset.c (init_charset):
-	* fileio.c (check_existing) [!DOS_NT]:
-	(check_executable) [!DOS_NT && !HAVE_EUIDACCESS]:
+	* fileio.c (check_existing, check_executable):
 	* lread.c (openp, load_path_check):
 	* process.c (allocate_pty):
 	* xrdb.c (file_p):
 	Use euidaccess, not access.  Use symbolic names instead of integers
 	for the flags, as they're portable now.
-	* fileio.c (Ffile_readable_p) [!DOS_NT && !macintosh]:
+	* fileio.c (Ffile_readable_p):
 	Use euidaccess, not stat + open + close.
 	(file_directory_p): New function, which uses 'stat' on most places
 	but 'access' (for efficiency) if WINDOWSNT.
@@ -21,6 +20,7 @@
 	* lread.c (openp): When opening a file, use fstat rather than
 	stat, as that avoids a permissions race.  When not opening a file,
 	use file_directory_p rather than stat.
+	* msdos.c (init_environment, readlink): Use sys_access, not access.
 
 2012-10-13  Jan Djärv  <jan.h.d@swipnet.se>
 

=== modified file 'src/fileio.c'
--- src/fileio.c	2012-10-14 06:02:52 +0000
+++ src/fileio.c	2012-10-14 18:06:01 +0000
@@ -2424,16 +2424,7 @@
 bool
 check_existing (const char *filename)
 {
-#ifdef DOS_NT
-  /* The full emulation of Posix 'stat' is too expensive on
-     DOS/Windows, when all we want to know is whether the file exists.
-     So we use 'access' instead, which is much more lightweight.  */
-  /* FIXME: an euidaccess implementation should be added to the
-     DOS/Windows ports and this #ifdef branch should be removed.  */
-  return (access (filename, F_OK) >= 0);
-#else
   return euidaccess (filename, F_OK) == 0;
-#endif
 }
 
 /* Return true if file FILENAME exists and can be executed.  */
@@ -2441,16 +2432,7 @@
 static bool
 check_executable (char *filename)
 {
-#ifdef DOS_NT
-  /* FIXME: an euidaccess implementation should be added to the
-     DOS/Windows ports and this #ifdef branch should be removed.  */
-  struct stat st;
-  if (stat (filename, &st) < 0)
-    return 0;
-  return ((st.st_mode & S_IEXEC) != 0);
-#else /* not DOS_NT */
   return euidaccess (filename, X_OK) == 0;
-#endif /* not DOS_NT */
 }
 
 /* Return true if file FILENAME exists and can be written.  */
@@ -2546,16 +2528,7 @@
     return call2 (handler, Qfile_readable_p, absname);
 
   absname = ENCODE_FILE (absname);
-
-#if defined (DOS_NT) || defined (macintosh)
-  /* FIXME: an euidaccess implementation should be added to the
-     DOS/Windows ports and this #ifdef branch should be removed.  */
-  if (access (SDATA (absname), 0) == 0)
-    return Qt;
-  return Qnil;
-#else /* not DOS_NT and not macintosh */
   return euidaccess (SSDATA (absname), R_OK) == 0 ? Qt : Qnil;
-#endif /* not DOS_NT and not macintosh */
 }
 
 /* Having this before file-symlink-p mysteriously caused it to be forgotten
@@ -2592,7 +2565,7 @@
   /* The read-only attribute of the parent directory doesn't affect
      whether a file or directory can be created within it.  Some day we
      should check ACLs though, which do affect this.  */
-  return (access (SDATA (dir), D_OK) < 0) ? Qnil : Qt;
+  return file_directory_p (SDATA (dir)) ? Qt : Qnil;
 #else
   return (check_writable (!NILP (dir) ? SSDATA (dir) : "")
 	  ? Qt : Qnil);
@@ -2694,8 +2667,8 @@
 file_directory_p (char const *file)
 {
 #ifdef WINDOWSNT
-  /* 'access' is cheaper than 'stat'.  */
-  return access (file, D_OK) == 0;
+  /* This is cheaper than 'stat'.  */
+  return euidaccess (file, D_OK) == 0;
 #else
   struct stat st;
   return stat (file, &st) == 0 && S_ISDIR (st.st_mode);

=== modified file 'src/msdos.c'
--- src/msdos.c	2012-09-23 08:44:20 +0000
+++ src/msdos.c	2012-10-14 18:06:01 +0000
@@ -3557,7 +3557,7 @@
 	 read-only filesystem, like CD-ROM or a write-protected floppy.
 	 The only way to be really sure is to actually create a file and
 	 see if it succeeds.  But I think that's too much to ask.  */
-      if (tmp && access (tmp, D_OK) == 0)
+      if (tmp && sys_access (tmp, D_OK) == 0)
 	{
 	  setenv ("TMPDIR", tmp, 1);
 	  break;
@@ -3935,7 +3935,7 @@
 readlink (const char *name, char *dummy1, size_t dummy2)
 {
   /* `access' is much faster than `stat' on MS-DOS.  */
-  if (access (name, F_OK) == 0)
+  if (sys_access (name, F_OK) == 0)
     errno = EINVAL;
   return -1;
 }



[-- Attachment #2: euidaccess.txt --]
[-- Type: text/plain, Size: 49280 bytes --]

=== modified file 'ChangeLog'
--- ChangeLog	2012-10-11 11:29:47 +0000
+++ ChangeLog	2012-10-14 06:03:50 +0000
@@ -1,3 +1,12 @@
+2012-10-14  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use euidaccess, not access, when checking file permissions (Bug#12632).
+	* configure.ac (euidaccess): Remove check; gnulib does this for us now.
+	* lib/euidaccess.c, lib/getgroups.c, lib/group-member.c:
+	* lib/root-uid.h, lib/xalloc-oversized.h, m4/euidaccess.m4:
+	* m4/getgroups.m4, m4/group-member.m4: New files, from gnulib.
+	* lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate.
+
 2012-10-11  Kenichi Handa  <handa@gnu.org>
 
 	* .bzrignore: Add several files under admin/charsets.

=== modified file 'admin/ChangeLog'
--- admin/ChangeLog	2012-10-14 08:01:30 +0000
+++ admin/ChangeLog	2012-10-14 18:08:30 +0000
@@ -1,3 +1,9 @@
+2012-10-14  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use euidaccess, not access, when checking file permissions (Bug#12632).
+	* merge-gnulib (GNULIB_MODULES): Add euidaccess.
+	(GNULIB_TOOL_FLAGS): Avoid malloc-posix.
+
 2012-10-12  Kenichi Handa  <handa@gnu.org>
 
 	* charsets/Makefile (JISC6226.map): Add missing mappings.

=== modified file 'admin/merge-gnulib'
--- admin/merge-gnulib	2012-09-27 01:06:23 +0000
+++ admin/merge-gnulib	2012-10-12 23:28:31 +0000
@@ -28,7 +28,7 @@
 GNULIB_MODULES='
   alloca-opt c-ctype c-strcase
   careadlinkat crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512
-  dtoastr dtotimespec dup2 environ execinfo
+  dtoastr dtotimespec dup2 environ euidaccess execinfo
   filemode getloadavg getopt-gnu gettime gettimeofday
   ignore-value intprops largefile lstat
   manywarnings mktime pselect pthread_sigmask readlink
@@ -40,7 +40,7 @@
 
 GNULIB_TOOL_FLAGS='
   --avoid=errno --avoid=fcntl --avoid=fcntl-h --avoid=fstat
-  --avoid=msvc-inval --avoid=msvc-nothrow
+  --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow
   --avoid=raise --avoid=select --avoid=sigprocmask --avoid=sys_types
   --avoid=threadlib
   --conditional-dependencies --import --no-changelog --no-vc-files

=== modified file 'configure.ac'
--- configure.ac	2012-10-08 16:09:00 +0000
+++ configure.ac	2012-10-12 23:28:31 +0000
@@ -2872,7 +2872,7 @@
 AC_CHECK_FUNCS(gethostname \
 closedir getrusage get_current_dir_name \
 lrand48 setsid \
-fpathconf select euidaccess getpagesize setlocale \
+fpathconf select getpagesize setlocale \
 utimes getrlimit setrlimit setpgid getcwd shutdown getaddrinfo \
 __fpending strsignal setitimer \
 sendto recvfrom getsockname getpeername getifaddrs freeifaddrs \

=== added file 'lib/euidaccess.c'
--- lib/euidaccess.c	1970-01-01 00:00:00 +0000
+++ lib/euidaccess.c	2012-10-12 23:28:31 +0000
@@ -0,0 +1,221 @@
+/* euidaccess -- check if effective user id can access file
+
+   Copyright (C) 1990-1991, 1995, 1998, 2000, 2003-2006, 2008-2012 Free
+   Software Foundation, Inc.
+
+   This file is part of the GNU C Library.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by David MacKenzie and Torbjorn Granlund.
+   Adapted for GNU C library by Roland McGrath.  */
+
+#ifndef _LIBC
+# include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "root-uid.h"
+
+#if HAVE_LIBGEN_H
+# include <libgen.h>
+#endif
+
+#include <errno.h>
+#ifndef __set_errno
+# define __set_errno(val) errno = (val)
+#endif
+
+#if defined EACCES && !defined EACCESS
+# define EACCESS EACCES
+#endif
+
+#ifndef F_OK
+# define F_OK 0
+# define X_OK 1
+# define W_OK 2
+# define R_OK 4
+#endif
+
+
+#ifdef _LIBC
+
+# define access __access
+# define getuid __getuid
+# define getgid __getgid
+# define geteuid __geteuid
+# define getegid __getegid
+# define group_member __group_member
+# define euidaccess __euidaccess
+# undef stat
+# define stat stat64
+
+#endif
+
+/* Return 0 if the user has permission of type MODE on FILE;
+   otherwise, return -1 and set 'errno'.
+   Like access, except that it uses the effective user and group
+   id's instead of the real ones, and it does not always check for read-only
+   file system, text busy, etc.  */
+
+int
+euidaccess (const char *file, int mode)
+{
+#if HAVE_FACCESSAT                   /* glibc, AIX 7, Solaris 11, Cygwin 1.7 */
+  return faccessat (AT_FDCWD, file, mode, AT_EACCESS);
+#elif defined EFF_ONLY_OK               /* IRIX, OSF/1, Interix */
+  return access (file, mode | EFF_ONLY_OK);
+#elif defined ACC_SELF                  /* AIX */
+  return accessx (file, mode, ACC_SELF);
+#elif HAVE_EACCESS                      /* FreeBSD */
+  return eaccess (file, mode);
+#else       /* Mac OS X, NetBSD, OpenBSD, HP-UX, Solaris, Cygwin, mingw, BeOS */
+
+  uid_t uid = getuid ();
+  gid_t gid = getgid ();
+  uid_t euid = geteuid ();
+  gid_t egid = getegid ();
+  struct stat stats;
+
+# if HAVE_DECL_SETREGID && PREFER_NONREENTRANT_EUIDACCESS
+
+  /* Define PREFER_NONREENTRANT_EUIDACCESS if you prefer euidaccess to
+     return the correct result even if this would make it
+     nonreentrant.  Define this only if your entire application is
+     safe even if the uid or gid might temporarily change.  If your
+     application uses signal handlers or threads it is probably not
+     safe.  */
+
+  if (mode == F_OK)
+    return stat (file, &stats);
+  else
+    {
+      int result;
+      int saved_errno;
+
+      if (uid != euid)
+        setreuid (euid, uid);
+      if (gid != egid)
+        setregid (egid, gid);
+
+      result = access (file, mode);
+      saved_errno = errno;
+
+      /* Restore them.  */
+      if (uid != euid)
+        setreuid (uid, euid);
+      if (gid != egid)
+        setregid (gid, egid);
+
+      errno = saved_errno;
+      return result;
+    }
+
+# else
+
+  /* The following code assumes the traditional Unix model, and is not
+     correct on systems that have ACLs or the like.  However, it's
+     better than nothing, and it is reentrant.  */
+
+  unsigned int granted;
+  if (uid == euid && gid == egid)
+    /* If we are not set-uid or set-gid, access does the same.  */
+    return access (file, mode);
+
+  if (stat (file, &stats) != 0)
+    return -1;
+
+  /* The super-user can read and write any file, and execute any file
+     that anyone can execute.  */
+  if (euid == ROOT_UID
+      && ((mode & X_OK) == 0
+          || (stats.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))))
+    return 0;
+
+  /* Convert the mode to traditional form, clearing any bogus bits.  */
+  if (R_OK == 4 && W_OK == 2 && X_OK == 1 && F_OK == 0)
+    mode &= 7;
+  else
+    mode = ((mode & R_OK ? 4 : 0)
+            + (mode & W_OK ? 2 : 0)
+            + (mode & X_OK ? 1 : 0));
+
+  if (mode == 0)
+    return 0;                   /* The file exists.  */
+
+  /* Convert the file's permission bits to traditional form.  */
+  if (S_IRUSR == (4 << 6) && S_IWUSR == (2 << 6) && S_IXUSR == (1 << 6)
+      && S_IRGRP == (4 << 3) && S_IWGRP == (2 << 3) && S_IXGRP == (1 << 3)
+      && S_IROTH == (4 << 0) && S_IWOTH == (2 << 0) && S_IXOTH == (1 << 0))
+    granted = stats.st_mode;
+  else
+    granted = ((stats.st_mode & S_IRUSR ? 4 << 6 : 0)
+               + (stats.st_mode & S_IWUSR ? 2 << 6 : 0)
+               + (stats.st_mode & S_IXUSR ? 1 << 6 : 0)
+               + (stats.st_mode & S_IRGRP ? 4 << 3 : 0)
+               + (stats.st_mode & S_IWGRP ? 2 << 3 : 0)
+               + (stats.st_mode & S_IXGRP ? 1 << 3 : 0)
+               + (stats.st_mode & S_IROTH ? 4 << 0 : 0)
+               + (stats.st_mode & S_IWOTH ? 2 << 0 : 0)
+               + (stats.st_mode & S_IXOTH ? 1 << 0 : 0));
+
+  if (euid == stats.st_uid)
+    granted >>= 6;
+  else if (egid == stats.st_gid || group_member (stats.st_gid))
+    granted >>= 3;
+
+  if ((mode & ~granted) == 0)
+    return 0;
+  __set_errno (EACCESS);
+  return -1;
+
+# endif
+#endif
+}
+#undef euidaccess
+#ifdef weak_alias
+weak_alias (__euidaccess, euidaccess)
+#endif
+\f
+#ifdef TEST
+# include <error.h>
+# include <stdio.h>
+# include <stdlib.h>
+
+char *program_name;
+
+int
+main (int argc, char **argv)
+{
+  char *file;
+  int mode;
+  int err;
+
+  program_name = argv[0];
+  if (argc < 3)
+    abort ();
+  file = argv[1];
+  mode = atoi (argv[2]);
+
+  err = euidaccess (file, mode);
+  printf ("%d\n", err);
+  if (err != 0)
+    error (0, errno, "%s", file);
+  exit (0);
+}
+#endif

=== added file 'lib/getgroups.c'
--- lib/getgroups.c	1970-01-01 00:00:00 +0000
+++ lib/getgroups.c	2012-10-12 23:28:31 +0000
@@ -0,0 +1,116 @@
+/* provide consistent interface to getgroups for systems that don't allow N==0
+
+   Copyright (C) 1996, 1999, 2003, 2006-2012 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* written by Jim Meyering */
+
+#include <config.h>
+
+#include <unistd.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#if !HAVE_GETGROUPS
+
+/* Provide a stub that fails with ENOSYS, since there is no group
+   information available on mingw.  */
+int
+getgroups (int n _GL_UNUSED, GETGROUPS_T *groups _GL_UNUSED)
+{
+  errno = ENOSYS;
+  return -1;
+}
+
+#else /* HAVE_GETGROUPS */
+
+# undef getgroups
+# ifndef GETGROUPS_ZERO_BUG
+#  define GETGROUPS_ZERO_BUG 0
+# endif
+
+/* On at least Ultrix 4.3 and NextStep 3.2, getgroups (0, NULL) always
+   fails.  On other systems, it returns the number of supplemental
+   groups for the process.  This function handles that special case
+   and lets the system-provided function handle all others.  However,
+   it can fail with ENOMEM if memory is tight.  It is unspecified
+   whether the effective group id is included in the list.  */
+
+int
+rpl_getgroups (int n, gid_t *group)
+{
+  int n_groups;
+  GETGROUPS_T *gbuf;
+  int saved_errno;
+
+  if (n < 0)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  if (n != 0 || !GETGROUPS_ZERO_BUG)
+    {
+      int result;
+      if (sizeof *group == sizeof *gbuf)
+        return getgroups (n, (GETGROUPS_T *) group);
+
+      if (SIZE_MAX / sizeof *gbuf <= n)
+        {
+          errno = ENOMEM;
+          return -1;
+        }
+      gbuf = malloc (n * sizeof *gbuf);
+      if (!gbuf)
+        return -1;
+      result = getgroups (n, gbuf);
+      if (0 <= result)
+        {
+          n = result;
+          while (n--)
+            group[n] = gbuf[n];
+        }
+      saved_errno = errno;
+      free (gbuf);
+      errno == saved_errno;
+      return result;
+    }
+
+  n = 20;
+  while (1)
+    {
+      /* No need to worry about address arithmetic overflow here,
+         since the ancient systems that we're running on have low
+         limits on the number of secondary groups.  */
+      gbuf = malloc (n * sizeof *gbuf);
+      if (!gbuf)
+        return -1;
+      n_groups = getgroups (n, gbuf);
+      if (n_groups == -1 ? errno != EINVAL : n_groups < n)
+        break;
+      free (gbuf);
+      n *= 2;
+    }
+
+  saved_errno = errno;
+  free (gbuf);
+  errno = saved_errno;
+
+  return n_groups;
+}
+
+#endif /* HAVE_GETGROUPS */

=== modified file 'lib/gnulib.mk'
--- lib/gnulib.mk	2012-10-04 07:15:42 +0000
+++ lib/gnulib.mk	2012-10-12 23:28:31 +0000
@@ -21,7 +21,7 @@
 # the same distribution terms as the rest of that program.
 #
 # Generated by gnulib-tool.
-# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=errno --avoid=fcntl --avoid=fcntl-h --avoid=fstat --avoid=msvc-inval --avoid=msvc-nothrow --avoid=raise --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo filemode getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings mktime pselect pthread_sigmask readlink socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub utimens warnings
+# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=errno --avoid=fcntl --avoid=fcntl-h --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=raise --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ euidaccess execinfo filemode getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings mktime pselect pthread_sigmask readlink socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub utimens warnings
 
 
 MOSTLYCLEANFILES += core *.stackdump
@@ -150,6 +150,15 @@
 
 ## end   gnulib module dup2
 
+## begin gnulib module euidaccess
+
+
+EXTRA_DIST += euidaccess.c
+
+EXTRA_libgnu_a_SOURCES += euidaccess.c
+
+## end   gnulib module euidaccess
+
 ## begin gnulib module execinfo
 
 BUILT_SOURCES += $(EXECINFO_H)
@@ -183,6 +192,17 @@
 
 ## end   gnulib module filemode
 
+## begin gnulib module getgroups
+
+if gl_GNULIB_ENABLED_getgroups
+
+endif
+EXTRA_DIST += getgroups.c
+
+EXTRA_libgnu_a_SOURCES += getgroups.c
+
+## end   gnulib module getgroups
+
 ## begin gnulib module getloadavg
 
 
@@ -242,6 +262,17 @@
 
 ## end   gnulib module gettimeofday
 
+## begin gnulib module group-member
+
+if gl_GNULIB_ENABLED_a9786850e999ae65a836a6041e8e5ed1
+
+endif
+EXTRA_DIST += group-member.c
+
+EXTRA_libgnu_a_SOURCES += group-member.c
+
+## end   gnulib module group-member
+
 ## begin gnulib module ignore-value
 
 
@@ -354,6 +385,13 @@
 
 ## end   gnulib module readlink
 
+## begin gnulib module root-uid
+
+
+EXTRA_DIST += root-uid.h
+
+## end   gnulib module root-uid
+
 ## begin gnulib module signal-h
 
 BUILT_SOURCES += signal.h
@@ -1312,6 +1350,15 @@
 
 ## end   gnulib module verify
 
+## begin gnulib module xalloc-oversized
+
+if gl_GNULIB_ENABLED_682e609604ccaac6be382e4ee3a4eaec
+
+endif
+EXTRA_DIST += xalloc-oversized.h
+
+## end   gnulib module xalloc-oversized
+
 
 mostlyclean-local: mostlyclean-generic
 	@for dir in '' $(MOSTLYCLEANDIRS); do \

=== added file 'lib/group-member.c'
--- lib/group-member.c	1970-01-01 00:00:00 +0000
+++ lib/group-member.c	2012-10-12 23:28:31 +0000
@@ -0,0 +1,119 @@
+/* group-member.c -- determine whether group id is in calling user's group list
+
+   Copyright (C) 1994, 1997-1998, 2003, 2005-2006, 2009-2012 Free Software
+   Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <unistd.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdlib.h>
+
+#include "xalloc-oversized.h"
+
+/* Most processes have no more than this many groups, and for these
+   processes we can avoid using malloc.  */
+enum { GROUPBUF_SIZE = 100 };
+
+struct group_info
+  {
+    gid_t *group;
+    gid_t groupbuf[GROUPBUF_SIZE];
+  };
+
+static void
+free_group_info (struct group_info const *g)
+{
+  if (g->group != g->groupbuf)
+    free (g->group);
+}
+
+static int
+get_group_info (struct group_info *gi)
+{
+  int n_groups = getgroups (GROUPBUF_SIZE, gi->groupbuf);
+  gi->group = gi->groupbuf;
+
+  if (n_groups < 0)
+    {
+      int n_group_slots = getgroups (0, NULL);
+      if (0 <= n_group_slots
+          && ! xalloc_oversized (n_group_slots, sizeof *gi->group))
+        {
+          gi->group = malloc (n_group_slots * sizeof *gi->group);
+          if (gi->group)
+            n_groups = getgroups (n_group_slots, gi->group);
+        }
+    }
+
+  /* In case of error, the user loses.  */
+  return n_groups;
+}
+
+/* Return non-zero if GID is one that we have in our groups list.
+   Note that the groups list is not guaranteed to contain the current
+   or effective group ID, so they should generally be checked
+   separately.  */
+
+int
+group_member (gid_t gid)
+{
+  int i;
+  int found;
+  struct group_info gi;
+  int n_groups = get_group_info (&gi);
+
+  /* Search through the list looking for GID. */
+  found = 0;
+  for (i = 0; i < n_groups; i++)
+    {
+      if (gid == gi.group[i])
+        {
+          found = 1;
+          break;
+        }
+    }
+
+  free_group_info (&gi);
+
+  return found;
+}
+
+#ifdef TEST
+
+char *program_name;
+
+int
+main (int argc, char **argv)
+{
+  int i;
+
+  program_name = argv[0];
+
+  for (i = 1; i < argc; i++)
+    {
+      gid_t gid;
+
+      gid = atoi (argv[i]);
+      printf ("%d: %s\n", gid, group_member (gid) ? "yes" : "no");
+    }
+  exit (0);
+}
+
+#endif /* TEST */

=== added file 'lib/root-uid.h'
--- lib/root-uid.h	1970-01-01 00:00:00 +0000
+++ lib/root-uid.h	2012-10-12 23:28:31 +0000
@@ -0,0 +1,30 @@
+/* The user ID that always has appropriate privileges in the POSIX sense.
+
+   Copyright 2012 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   Written by Paul Eggert.  */
+
+#ifndef ROOT_UID_H_
+#define ROOT_UID_H_
+
+/* The user ID that always has appropriate privileges in the POSIX sense.  */
+#ifdef __TANDEM
+# define ROOT_UID 65535
+#else
+# define ROOT_UID 0
+#endif
+
+#endif

=== added file 'lib/xalloc-oversized.h'
--- lib/xalloc-oversized.h	1970-01-01 00:00:00 +0000
+++ lib/xalloc-oversized.h	2012-10-12 23:28:31 +0000
@@ -0,0 +1,38 @@
+/* xalloc-oversized.h -- memory allocation size checking
+
+   Copyright (C) 1990-2000, 2003-2004, 2006-2012 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef XALLOC_OVERSIZED_H_
+# define XALLOC_OVERSIZED_H_
+
+# include <stddef.h>
+
+/* Return 1 if an array of N objects, each of size S, cannot exist due
+   to size arithmetic overflow.  S must be positive and N must be
+   nonnegative.  This is a macro, not a function, so that it
+   works correctly even when SIZE_MAX < N.
+
+   By gnulib convention, SIZE_MAX represents overflow in size
+   calculations, so the conservative dividend to use here is
+   SIZE_MAX - 1, since SIZE_MAX might represent an overflowed value.
+   However, malloc (SIZE_MAX) fails on all known hosts where
+   sizeof (ptrdiff_t) <= sizeof (size_t), so do not bother to test for
+   exactly-SIZE_MAX allocations on such hosts; this avoids a test and
+   branch when S is known to be 1.  */
+# define xalloc_oversized(n, s) \
+    ((size_t) (sizeof (ptrdiff_t) <= sizeof (size_t) ? -1 : -2) / (s) < (n))
+
+#endif /* !XALLOC_OVERSIZED_H_ */

=== added file 'm4/euidaccess.m4'
--- m4/euidaccess.m4	1970-01-01 00:00:00 +0000
+++ m4/euidaccess.m4	2012-10-12 23:28:31 +0000
@@ -0,0 +1,52 @@
+# euidaccess.m4 serial 14
+dnl Copyright (C) 2002-2012 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_NONREENTRANT_EUIDACCESS],
+[
+  AC_REQUIRE([gl_FUNC_EUIDACCESS])
+  AC_DEFINE([PREFER_NONREENTRANT_EUIDACCESS], [1],
+    [Define this if you prefer euidaccess to return the correct result
+     even if this would make it nonreentrant.  Define this only if your
+     entire application is safe even if the uid or gid might temporarily
+     change.  If your application uses signal handlers or threads it
+     is probably not safe.])
+])
+
+AC_DEFUN([gl_FUNC_EUIDACCESS],
+[
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+
+  dnl Persuade glibc <unistd.h> to declare euidaccess().
+  AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
+
+  AC_CHECK_FUNCS([euidaccess])
+  if test $ac_cv_func_euidaccess = no; then
+    HAVE_EUIDACCESS=0
+  fi
+])
+
+# Prerequisites of lib/euidaccess.c.
+AC_DEFUN([gl_PREREQ_EUIDACCESS], [
+  dnl Prefer POSIX faccessat over non-standard euidaccess.
+  AC_CHECK_FUNCS_ONCE([faccessat])
+  dnl Try various other non-standard fallbacks.
+  AC_CHECK_HEADERS_ONCE([libgen.h])
+  AC_CHECK_DECLS_ONCE([setregid])
+  AC_REQUIRE([AC_FUNC_GETGROUPS])
+
+  # Solaris 9 and 10 need -lgen to get the eaccess function.
+  # Save and restore LIBS so -lgen isn't added to it.  Otherwise, *all*
+  # programs in the package would end up linked with that potentially-shared
+  # library, inducing unnecessary run-time overhead.
+  LIB_EACCESS=
+  AC_SUBST([LIB_EACCESS])
+  gl_saved_libs=$LIBS
+    AC_SEARCH_LIBS([eaccess], [gen],
+                   [test "$ac_cv_search_eaccess" = "none required" ||
+                    LIB_EACCESS=$ac_cv_search_eaccess])
+    AC_CHECK_FUNCS([eaccess])
+  LIBS=$gl_saved_libs
+])

=== added file 'm4/getgroups.m4'
--- m4/getgroups.m4	1970-01-01 00:00:00 +0000
+++ m4/getgroups.m4	2012-10-12 23:28:31 +0000
@@ -0,0 +1,107 @@
+# serial 18
+
+dnl From Jim Meyering.
+dnl A wrapper around AC_FUNC_GETGROUPS.
+
+# Copyright (C) 1996-1997, 1999-2004, 2008-2012 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+m4_version_prereq([2.70], [] ,[
+
+# This is taken from the following Autoconf patch:
+# http://git.savannah.gnu.org/gitweb/?p=autoconf.git;a=commitdiff;h=7fbb553727ed7e0e689a17594b58559ecf3ea6e9
+AC_DEFUN([AC_FUNC_GETGROUPS],
+[
+  AC_REQUIRE([AC_TYPE_GETGROUPS])dnl
+  AC_REQUIRE([AC_TYPE_SIZE_T])dnl
+  AC_REQUIRE([AC_CANONICAL_HOST])dnl for cross-compiles
+  AC_CHECK_FUNC([getgroups])
+
+  # If we don't yet have getgroups, see if it's in -lbsd.
+  # This is reported to be necessary on an ITOS 3000WS running SEIUX 3.1.
+  ac_save_LIBS=$LIBS
+  if test $ac_cv_func_getgroups = no; then
+    AC_CHECK_LIB(bsd, getgroups, [GETGROUPS_LIB=-lbsd])
+  fi
+
+  # Run the program to test the functionality of the system-supplied
+  # getgroups function only if there is such a function.
+  if test $ac_cv_func_getgroups = yes; then
+    AC_CACHE_CHECK([for working getgroups], [ac_cv_func_getgroups_works],
+      [AC_RUN_IFELSE(
+         [AC_LANG_PROGRAM(
+            [AC_INCLUDES_DEFAULT],
+            [[/* On Ultrix 4.3, getgroups (0, 0) always fails.  */
+              return getgroups (0, 0) == -1;]])
+         ],
+         [ac_cv_func_getgroups_works=yes],
+         [ac_cv_func_getgroups_works=no],
+         [case "$host_os" in # ((
+                    # Guess yes on glibc systems.
+            *-gnu*) ac_cv_func_getgroups_works="guessing yes" ;;
+                    # If we don't know, assume the worst.
+            *)      ac_cv_func_getgroups_works="guessing no" ;;
+          esac
+         ])
+      ])
+  else
+    ac_cv_func_getgroups_works=no
+  fi
+  case "$ac_cv_func_getgroups_works" in
+    *yes)
+      AC_DEFINE([HAVE_GETGROUPS], [1],
+        [Define to 1 if your system has a working `getgroups' function.])
+      ;;
+  esac
+  LIBS=$ac_save_LIBS
+])# AC_FUNC_GETGROUPS
+
+])
+
+AC_DEFUN([gl_FUNC_GETGROUPS],
+[
+  AC_REQUIRE([AC_TYPE_GETGROUPS])
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+
+  AC_FUNC_GETGROUPS
+  if test $ac_cv_func_getgroups != yes; then
+    HAVE_GETGROUPS=0
+  else
+    if test "$ac_cv_type_getgroups" != gid_t \
+       || { case "$ac_cv_func_getgroups_works" in
+              *yes) false;;
+              *) true;;
+            esac
+          }; then
+      REPLACE_GETGROUPS=1
+      AC_DEFINE([GETGROUPS_ZERO_BUG], [1], [Define this to 1 if
+        getgroups(0,NULL) does not return the number of groups.])
+    else
+      dnl Detect FreeBSD bug; POSIX requires getgroups(-1,ptr) to fail.
+      AC_CACHE_CHECK([whether getgroups handles negative values],
+        [gl_cv_func_getgroups_works],
+        [AC_RUN_IFELSE([AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT],
+          [[int size = getgroups (0, 0);
+            gid_t *list = malloc (size * sizeof *list);
+            return getgroups (-1, list) != -1;]])],
+          [gl_cv_func_getgroups_works=yes],
+          [gl_cv_func_getgroups_works=no],
+          [case "$host_os" in
+                     # Guess yes on glibc systems.
+             *-gnu*) gl_cv_func_getgroups_works="guessing yes" ;;
+                     # If we don't know, assume the worst.
+             *)      gl_cv_func_getgroups_works="guessing no" ;;
+           esac
+          ])])
+      case "$gl_cv_func_getgroups_works" in
+        *yes) ;;
+        *) REPLACE_GETGROUPS=1 ;;
+      esac
+    fi
+  fi
+  test -n "$GETGROUPS_LIB" && LIBS="$GETGROUPS_LIB $LIBS"
+])

=== modified file 'm4/gnulib-comp.m4'
--- m4/gnulib-comp.m4	2012-09-27 01:06:23 +0000
+++ m4/gnulib-comp.m4	2012-10-12 23:28:31 +0000
@@ -53,17 +53,20 @@
   # Code from module dtotimespec:
   # Code from module dup2:
   # Code from module environ:
+  # Code from module euidaccess:
   # Code from module execinfo:
   # Code from module extensions:
   AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
   # Code from module extern-inline:
   # Code from module filemode:
+  # Code from module getgroups:
   # Code from module getloadavg:
   # Code from module getopt-gnu:
   # Code from module getopt-posix:
   # Code from module gettext-h:
   # Code from module gettime:
   # Code from module gettimeofday:
+  # Code from module group-member:
   # Code from module ignore-value:
   # Code from module include_next:
   # Code from module intprops:
@@ -79,6 +82,7 @@
   # Code from module pselect:
   # Code from module pthread_sigmask:
   # Code from module readlink:
+  # Code from module root-uid:
   # Code from module signal-h:
   # Code from module snippet/_Noreturn:
   # Code from module snippet/arg-nonnull:
@@ -120,6 +124,7 @@
   # Code from module utimens:
   # Code from module verify:
   # Code from module warnings:
+  # Code from module xalloc-oversized:
 ])
 
 # This macro should be invoked from ./configure.ac, in the section
@@ -154,6 +159,12 @@
   gl_UNISTD_MODULE_INDICATOR([dup2])
   gl_ENVIRON
   gl_UNISTD_MODULE_INDICATOR([environ])
+  gl_FUNC_EUIDACCESS
+  if test $HAVE_EUIDACCESS = 0; then
+    AC_LIBOBJ([euidaccess])
+    gl_PREREQ_EUIDACCESS
+  fi
+  gl_UNISTD_MODULE_INDICATOR([euidaccess])
   gl_EXECINFO_H
   AC_REQUIRE([gl_EXTERN_INLINE])
   gl_FILEMODE
@@ -269,18 +280,32 @@
   gl_UNISTD_H
   gl_UTIMENS
   gl_gnulib_enabled_dosname=false
+  gl_gnulib_enabled_getgroups=false
   gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=false
+  gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1=false
   gl_gnulib_enabled_pathmax=false
   gl_gnulib_enabled_stat=false
   gl_gnulib_enabled_strtoll=false
   gl_gnulib_enabled_strtoull=false
   gl_gnulib_enabled_verify=false
+  gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=false
   func_gl_gnulib_m4code_dosname ()
   {
     if ! $gl_gnulib_enabled_dosname; then
       gl_gnulib_enabled_dosname=true
     fi
   }
+  func_gl_gnulib_m4code_getgroups ()
+  {
+    if ! $gl_gnulib_enabled_getgroups; then
+      gl_FUNC_GETGROUPS
+      if test $HAVE_GETGROUPS = 0 || test $REPLACE_GETGROUPS = 1; then
+        AC_LIBOBJ([getgroups])
+      fi
+      gl_UNISTD_MODULE_INDICATOR([getgroups])
+      gl_gnulib_enabled_getgroups=true
+    fi
+  }
   func_gl_gnulib_m4code_be453cec5eecf5731a274f2de7f2db36 ()
   {
     if ! $gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36; then
@@ -289,6 +314,24 @@
       gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=true
     fi
   }
+  func_gl_gnulib_m4code_a9786850e999ae65a836a6041e8e5ed1 ()
+  {
+    if ! $gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1; then
+      gl_FUNC_GROUP_MEMBER
+      if test $HAVE_GROUP_MEMBER = 0; then
+        AC_LIBOBJ([group-member])
+        gl_PREREQ_GROUP_MEMBER
+      fi
+      gl_UNISTD_MODULE_INDICATOR([group-member])
+      gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1=true
+      if test $HAVE_GROUP_MEMBER = 0; then
+        func_gl_gnulib_m4code_getgroups
+      fi
+      if test $HAVE_GROUP_MEMBER = 0; then
+        func_gl_gnulib_m4code_682e609604ccaac6be382e4ee3a4eaec
+      fi
+    fi
+  }
   func_gl_gnulib_m4code_pathmax ()
   {
     if ! $gl_gnulib_enabled_pathmax; then
@@ -347,6 +390,18 @@
       gl_gnulib_enabled_verify=true
     fi
   }
+  func_gl_gnulib_m4code_682e609604ccaac6be382e4ee3a4eaec ()
+  {
+    if ! $gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec; then
+      gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=true
+    fi
+  }
+  if test $HAVE_EUIDACCESS = 0; then
+    func_gl_gnulib_m4code_a9786850e999ae65a836a6041e8e5ed1
+  fi
+  if test $HAVE_EUIDACCESS = 0; then
+    func_gl_gnulib_m4code_stat
+  fi
   if test $REPLACE_GETOPT = 1; then
     func_gl_gnulib_m4code_be453cec5eecf5731a274f2de7f2db36
   fi
@@ -373,12 +428,15 @@
   fi
   m4_pattern_allow([^gl_GNULIB_ENABLED_])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_dosname], [$gl_gnulib_enabled_dosname])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_getgroups], [$gl_gnulib_enabled_getgroups])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_be453cec5eecf5731a274f2de7f2db36], [$gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_a9786850e999ae65a836a6041e8e5ed1], [$gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_pathmax], [$gl_gnulib_enabled_pathmax])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_stat], [$gl_gnulib_enabled_stat])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_strtoll], [$gl_gnulib_enabled_strtoll])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_strtoull], [$gl_gnulib_enabled_strtoull])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_verify], [$gl_gnulib_enabled_verify])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_682e609604ccaac6be382e4ee3a4eaec], [$gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec])
   # End of code from modules
   m4_ifval(gl_LIBSOURCES_LIST, [
     m4_syscmd([test ! -d ]m4_defn([gl_LIBSOURCES_DIR])[ ||
@@ -538,12 +596,14 @@
   lib/dtoastr.c
   lib/dtotimespec.c
   lib/dup2.c
+  lib/euidaccess.c
   lib/execinfo.c
   lib/execinfo.in.h
   lib/filemode.c
   lib/filemode.h
   lib/ftoastr.c
   lib/ftoastr.h
+  lib/getgroups.c
   lib/getloadavg.c
   lib/getopt.c
   lib/getopt.in.h
@@ -552,6 +612,7 @@
   lib/gettext.h
   lib/gettime.c
   lib/gettimeofday.c
+  lib/group-member.c
   lib/ignore-value.h
   lib/intprops.h
   lib/inttypes.in.h
@@ -564,6 +625,7 @@
   lib/pselect.c
   lib/pthread_sigmask.c
   lib/readlink.c
+  lib/root-uid.h
   lib/sha1.c
   lib/sha1.h
   lib/sha256.c
@@ -605,21 +667,25 @@
   lib/utimens.c
   lib/utimens.h
   lib/verify.h
+  lib/xalloc-oversized.h
   m4/00gnulib.m4
   m4/alloca.m4
   m4/c-strtod.m4
   m4/clock_time.m4
   m4/dup2.m4
   m4/environ.m4
+  m4/euidaccess.m4
   m4/execinfo.m4
   m4/extensions.m4
   m4/extern-inline.m4
   m4/filemode.m4
+  m4/getgroups.m4
   m4/getloadavg.m4
   m4/getopt.m4
   m4/gettime.m4
   m4/gettimeofday.m4
   m4/gnulib-common.m4
+  m4/group-member.m4
   m4/include_next.m4
   m4/inttypes.m4
   m4/largefile.m4

=== added file 'm4/group-member.m4'
--- m4/group-member.m4	1970-01-01 00:00:00 +0000
+++ m4/group-member.m4	2012-10-12 23:28:31 +0000
@@ -0,0 +1,29 @@
+# serial 14
+
+# Copyright (C) 1999-2001, 2003-2007, 2009-2012 Free Software Foundation, Inc.
+
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+dnl Written by Jim Meyering
+
+AC_DEFUN([gl_FUNC_GROUP_MEMBER],
+[
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+
+  dnl Persuade glibc <unistd.h> to declare group_member().
+  AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
+
+  dnl Do this replacement check manually because I want the hyphen
+  dnl (not the underscore) in the filename.
+  AC_CHECK_FUNC([group_member], , [
+    HAVE_GROUP_MEMBER=0
+  ])
+])
+
+# Prerequisites of lib/group-member.c.
+AC_DEFUN([gl_PREREQ_GROUP_MEMBER],
+[
+  AC_REQUIRE([AC_FUNC_GETGROUPS])
+])

=== modified file 'nt/ChangeLog'
--- nt/ChangeLog	2012-10-08 14:14:22 +0000
+++ nt/ChangeLog	2012-10-14 18:06:01 +0000
@@ -1,3 +1,9 @@
+2012-10-14  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use euidaccess, not access, when checking file permissions (Bug#12632).
+	* inc/ms-w32.h (euidaccess): Define this, not 'access', since
+	the mainline code now uses euidaccess.
+
 2012-10-08  Juanma Barranquero  <lekktu@gmail.com>
 
 	* config.nt: Sync with autogen/config.in.

=== modified file 'nt/inc/ms-w32.h'
--- nt/inc/ms-w32.h	2012-09-30 21:36:42 +0000
+++ nt/inc/ms-w32.h	2012-10-14 18:06:01 +0000
@@ -160,8 +160,6 @@
 #endif
 
 /* Calls that are emulated or shadowed.  */
-#undef access
-#define access  sys_access
 #undef chdir
 #define chdir   sys_chdir
 #undef chmod
@@ -176,6 +174,7 @@
 #define dup     sys_dup
 #undef dup2
 #define dup2    sys_dup2
+#define euidaccess  sys_access
 #define fopen   sys_fopen
 #define link    sys_link
 #define localtime sys_localtime

=== modified file 'src/ChangeLog'
--- src/ChangeLog	2012-10-14 00:17:07 +0000
+++ src/ChangeLog	2012-10-14 18:06:01 +0000
@@ -1,3 +1,27 @@
+2012-10-14  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use euidaccess, not access, when checking file permissions (Bug#12632).
+	* Makefile.in (LIB_EACCESS): New macro.
+	(LIBES): Use it.
+	* callproc.c (init_callproc):
+	* charset.c (init_charset):
+	* fileio.c (check_existing, check_executable):
+	* lread.c (openp, load_path_check):
+	* process.c (allocate_pty):
+	* xrdb.c (file_p):
+	Use euidaccess, not access.  Use symbolic names instead of integers
+	for the flags, as they're portable now.
+	* fileio.c (Ffile_readable_p):
+	Use euidaccess, not stat + open + close.
+	(file_directory_p): New function, which uses 'stat' on most places
+	but 'access' (for efficiency) if WINDOWSNT.
+	(Ffile_directory_p): Use it.
+	* lisp.h (file_directory_p): New decl.
+	* lread.c (openp): When opening a file, use fstat rather than
+	stat, as that avoids a permissions race.  When not opening a file,
+	use file_directory_p rather than stat.
+	* msdos.c (init_environment, readlink): Use sys_access, not access.
+
 2012-10-13  Jan Djärv  <jan.h.d@swipnet.se>
 
 	* gtkutil.c (xg_set_widget_bg): Divide by 65535 (Bug#12612).

=== modified file 'src/Makefile.in'
--- src/Makefile.in	2012-10-08 12:53:18 +0000
+++ src/Makefile.in	2012-10-13 01:49:41 +0000
@@ -150,6 +150,7 @@
 M17N_FLT_LIBS = @M17N_FLT_LIBS@
 
 LIB_CLOCK_GETTIME=@LIB_CLOCK_GETTIME@
+LIB_EACCESS=@LIB_EACCESS@
 LIB_TIMER_TIME=@LIB_TIMER_TIME@
 
 DBUS_CFLAGS = @DBUS_CFLAGS@
@@ -392,7 +393,7 @@
 LIBES = $(LIBS) $(W32_LIBS) $(LIBX_BASE) $(LIBIMAGE) \
    $(LIBX_OTHER) $(LIBSOUND) \
    $(RSVG_LIBS) $(IMAGEMAGICK_LIBS) $(LIB_CLOCK_GETTIME) \
-   $(LIB_TIMER_TIME) $(DBUS_LIBS) \
+   $(LIB_EACCESS) $(LIB_TIMER_TIME) $(DBUS_LIBS) \
    $(LIB_EXECINFO) \
    $(LIBXML2_LIBS) $(LIBGPM) $(LIBRESOLV) $(LIBS_SYSTEM) \
    $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \

=== modified file 'src/callproc.c'
--- src/callproc.c	2012-09-23 22:25:22 +0000
+++ src/callproc.c	2012-10-12 23:28:31 +0000
@@ -1597,13 +1597,13 @@
 #endif
     {
       tempdir = Fdirectory_file_name (Vexec_directory);
-      if (access (SSDATA (tempdir), 0) < 0)
+      if (euidaccess (SSDATA (tempdir), F_OK) < 0)
 	dir_warning ("Warning: arch-dependent data dir (%s) does not exist.\n",
 		     Vexec_directory);
     }
 
   tempdir = Fdirectory_file_name (Vdata_directory);
-  if (access (SSDATA (tempdir), 0) < 0)
+  if (euidaccess (SSDATA (tempdir), F_OK) < 0)
     dir_warning ("Warning: arch-independent data dir (%s) does not exist.\n",
 		 Vdata_directory);
 

=== modified file 'src/charset.c'
--- src/charset.c	2012-10-01 06:36:54 +0000
+++ src/charset.c	2012-10-12 23:28:31 +0000
@@ -2293,7 +2293,7 @@
 {
   Lisp_Object tempdir;
   tempdir = Fexpand_file_name (build_string ("charsets"), Vdata_directory);
-  if (access (SSDATA (tempdir), 0) < 0)
+  if (euidaccess (SSDATA (tempdir), F_OK) != 0)
     {
       /* This used to be non-fatal (dir_warning), but it should not
          happen, and if it does sooner or later it will cause some

=== modified file 'src/fileio.c'
--- src/fileio.c	2012-10-13 08:55:26 +0000
+++ src/fileio.c	2012-10-14 18:06:01 +0000
@@ -2424,15 +2424,7 @@
 bool
 check_existing (const char *filename)
 {
-#ifdef DOS_NT
-  /* The full emulation of Posix 'stat' is too expensive on
-     DOS/Windows, when all we want to know is whether the file exists.
-     So we use 'access' instead, which is much more lightweight.  */
-  return (access (filename, F_OK) >= 0);
-#else
-  struct stat st;
-  return (stat (filename, &st) >= 0);
-#endif
+  return euidaccess (filename, F_OK) == 0;
 }
 
 /* Return true if file FILENAME exists and can be executed.  */
@@ -2440,21 +2432,7 @@
 static bool
 check_executable (char *filename)
 {
-#ifdef DOS_NT
-  struct stat st;
-  if (stat (filename, &st) < 0)
-    return 0;
-  return ((st.st_mode & S_IEXEC) != 0);
-#else /* not DOS_NT */
-#ifdef HAVE_EUIDACCESS
-  return (euidaccess (filename, 1) >= 0);
-#else
-  /* Access isn't quite right because it uses the real uid
-     and we really want to test with the effective uid.
-     But Unix doesn't give us a right way to do it.  */
-  return (access (filename, 1) >= 0);
-#endif
-#endif /* not DOS_NT */
+  return euidaccess (filename, X_OK) == 0;
 }
 
 /* Return true if file FILENAME exists and can be written.  */
@@ -2463,13 +2441,14 @@
 check_writable (const char *filename)
 {
 #ifdef MSDOS
+  /* FIXME: an euidaccess implementation should be added to the
+     DOS/Windows ports and this #ifdef branch should be removed.  */
   struct stat st;
   if (stat (filename, &st) < 0)
     return 0;
   return (st.st_mode & S_IWRITE || S_ISDIR (st.st_mode));
 #else /* not MSDOS */
-#ifdef HAVE_EUIDACCESS
-  bool res = (euidaccess (filename, 2) >= 0);
+  bool res = euidaccess (filename, W_OK) == 0;
 #ifdef CYGWIN
   /* euidaccess may have returned failure because Cygwin couldn't
      determine the file's UID or GID; if so, we return success. */
@@ -2482,14 +2461,6 @@
     }
 #endif /* CYGWIN */
   return res;
-#else /* not HAVE_EUIDACCESS */
-  /* Access isn't quite right because it uses the real uid
-     and we really want to test with the effective uid.
-     But Unix doesn't give us a right way to do it.
-     Opening with O_WRONLY could work for an ordinary file,
-     but would lose for directories.  */
-  return (access (filename, 2) >= 0);
-#endif /* not HAVE_EUIDACCESS */
 #endif /* not MSDOS */
 }
 
@@ -2546,9 +2517,6 @@
 {
   Lisp_Object absname;
   Lisp_Object handler;
-  int desc;
-  int flags;
-  struct stat statbuf;
 
   CHECK_STRING (filename);
   absname = Fexpand_file_name (filename, Qnil);
@@ -2560,31 +2528,7 @@
     return call2 (handler, Qfile_readable_p, absname);
 
   absname = ENCODE_FILE (absname);
-
-#if defined (DOS_NT) || defined (macintosh)
-  /* Under MS-DOS, Windows, and Macintosh, open does not work for
-     directories.  */
-  if (access (SDATA (absname), 0) == 0)
-    return Qt;
-  return Qnil;
-#else /* not DOS_NT and not macintosh */
-  flags = O_RDONLY;
-#ifdef O_NONBLOCK
-  /* Opening a fifo without O_NONBLOCK can wait.
-     We don't want to wait.  But we don't want to mess wth O_NONBLOCK
-     except in the case of a fifo, on a system which handles it.  */
-  desc = stat (SSDATA (absname), &statbuf);
-  if (desc < 0)
-    return Qnil;
-  if (S_ISFIFO (statbuf.st_mode))
-    flags |= O_NONBLOCK;
-#endif
-  desc = emacs_open (SSDATA (absname), flags, 0);
-  if (desc < 0)
-    return Qnil;
-  emacs_close (desc);
-  return Qt;
-#endif /* not DOS_NT and not macintosh */
+  return euidaccess (SSDATA (absname), R_OK) == 0 ? Qt : Qnil;
 }
 
 /* Having this before file-symlink-p mysteriously caused it to be forgotten
@@ -2621,7 +2565,7 @@
   /* The read-only attribute of the parent directory doesn't affect
      whether a file or directory can be created within it.  Some day we
      should check ACLs though, which do affect this.  */
-  return (access (SDATA (dir), D_OK) < 0) ? Qnil : Qt;
+  return file_directory_p (SDATA (dir)) ? Qt : Qnil;
 #else
   return (check_writable (!NILP (dir) ? SSDATA (dir) : "")
 	  ? Qt : Qnil);
@@ -2702,8 +2646,7 @@
 See `file-symlink-p' to distinguish symlinks.  */)
   (Lisp_Object filename)
 {
-  register Lisp_Object absname;
-  struct stat st;
+  Lisp_Object absname;
   Lisp_Object handler;
 
   absname = expand_and_dir_to_file (filename, BVAR (current_buffer, directory));
@@ -2716,9 +2659,20 @@
 
   absname = ENCODE_FILE (absname);
 
-  if (stat (SSDATA (absname), &st) < 0)
-    return Qnil;
-  return S_ISDIR (st.st_mode) ? Qt : Qnil;
+  return file_directory_p (SSDATA (absname)) ? Qt : Qnil;
+}
+
+/* Return true if FILE is a directory or a symlink to a directory.  */
+bool
+file_directory_p (char const *file)
+{
+#ifdef WINDOWSNT
+  /* This is cheaper than 'stat'.  */
+  return euidaccess (file, D_OK) == 0;
+#else
+  struct stat st;
+  return stat (file, &st) == 0 && S_ISDIR (st.st_mode);
+#endif
 }
 
 DEFUN ("file-accessible-directory-p", Ffile_accessible_directory_p,

=== modified file 'src/lisp.h'
--- src/lisp.h	2012-10-12 15:19:54 +0000
+++ src/lisp.h	2012-10-14 06:00:40 +0000
@@ -3179,6 +3179,7 @@
 extern Lisp_Object restore_point_unwind (Lisp_Object);
 extern _Noreturn void report_file_error (const char *, Lisp_Object);
 extern void internal_delete_file (Lisp_Object);
+extern bool file_directory_p (const char *);
 extern void syms_of_fileio (void);
 extern Lisp_Object make_temp_name (Lisp_Object, bool);
 extern Lisp_Object Qdelete_file;

=== modified file 'src/lread.c'
--- src/lread.c	2012-10-12 15:19:54 +0000
+++ src/lread.c	2012-10-14 06:00:40 +0000
@@ -1404,7 +1404,7 @@
 If SUFFIXES is non-nil, it should be a list of suffixes to append to
 file name when searching.
 If non-nil, PREDICATE is used instead of `file-readable-p'.
-PREDICATE can also be an integer to pass to the access(2) function,
+PREDICATE can also be an integer to pass to the euidaccess(2) function,
 in which case file-name-handlers are ignored.
 This function will normally skip directories, so if you want it to find
 directories, make sure the PREDICATE function returns `dir-ok' for them.  */)
@@ -1442,7 +1442,6 @@
 int
 openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes, Lisp_Object *storeptr, Lisp_Object predicate)
 {
-  int fd;
   ptrdiff_t fn_size = 100;
   char buf[100];
   char *fn = buf;
@@ -1497,7 +1496,6 @@
 	{
 	  ptrdiff_t fnlen, lsuffix = SBYTES (XCAR (tail));
 	  Lisp_Object handler;
-	  bool exists;
 
 	  /* Concatenate path element/specified name with the suffix.
 	     If the directory starts with /:, remove that.  */
@@ -1521,6 +1519,7 @@
 	  handler = Ffind_file_name_handler (string, Qfile_exists_p);
 	  if ((!NILP (handler) || !NILP (predicate)) && !NATNUMP (predicate))
             {
+	      bool exists;
 	      if (NILP (predicate))
 		exists = !NILP (Ffile_readable_p (string));
 	      else
@@ -1542,37 +1541,38 @@
 	    }
 	  else
 	    {
-#ifndef WINDOWSNT
-	      struct stat st;
-#endif
+	      int fd;
 	      const char *pfn;
 
 	      encoded_fn = ENCODE_FILE (string);
 	      pfn = SSDATA (encoded_fn);
-#ifdef WINDOWSNT
-	      exists = access (pfn, F_OK) == 0 && access (pfn, D_OK) < 0;
-#else
-	      exists = (stat (pfn, &st) == 0 && ! S_ISDIR (st.st_mode));
-#endif
-	      if (exists)
+
+	      /* Check that we can access or open it.  */
+	      if (NATNUMP (predicate))
+		fd = (((XFASTINT (predicate) & ~INT_MAX) == 0
+		       && euidaccess (pfn, XFASTINT (predicate)) == 0
+		       && ! file_directory_p (pfn))
+		      ? 1 : -1);
+	      else
 		{
-		  /* Check that we can access or open it.  */
-		  if (NATNUMP (predicate))
-		    fd = (((XFASTINT (predicate) & ~INT_MAX) == 0
-			   && access (pfn, XFASTINT (predicate)) == 0)
-			  ? 1 : -1);
-		  else
-		    fd = emacs_open (pfn, O_RDONLY, 0);
-
-		  if (fd >= 0)
+		  struct stat st;
+		  fd = emacs_open (pfn, O_RDONLY, 0);
+		  if (0 <= fd
+		      && (fstat (fd, &st) != 0 || S_ISDIR (st.st_mode)))
 		    {
-		      /* We succeeded; return this descriptor and filename.  */
-		      if (storeptr)
-			*storeptr = string;
-		      UNGCPRO;
-		      return fd;
+		      emacs_close (fd);
+		      fd = -1;
 		    }
 		}
+
+	      if (fd >= 0)
+		{
+		  /* We succeeded; return this descriptor and filename.  */
+		  if (storeptr)
+		    *storeptr = string;
+		  UNGCPRO;
+		  return fd;
+		}
 	    }
 	}
       if (absolute)
@@ -4088,7 +4088,7 @@
       if (STRINGP (dirfile))
         {
           dirfile = Fdirectory_file_name (dirfile);
-          if (access (SSDATA (dirfile), 0) < 0)
+          if (euidaccess (SSDATA (dirfile), F_OK) != 0)
             dir_warning ("Warning: Lisp directory `%s' does not exist.\n",
                          XCAR (path_tail));
         }

=== modified file 'src/msdos.c'
--- src/msdos.c	2012-09-23 08:44:20 +0000
+++ src/msdos.c	2012-10-14 18:06:01 +0000
@@ -3557,7 +3557,7 @@
 	 read-only filesystem, like CD-ROM or a write-protected floppy.
 	 The only way to be really sure is to actually create a file and
 	 see if it succeeds.  But I think that's too much to ask.  */
-      if (tmp && access (tmp, D_OK) == 0)
+      if (tmp && sys_access (tmp, D_OK) == 0)
 	{
 	  setenv ("TMPDIR", tmp, 1);
 	  break;
@@ -3935,7 +3935,7 @@
 readlink (const char *name, char *dummy1, size_t dummy2)
 {
   /* `access' is much faster than `stat' on MS-DOS.  */
-  if (access (name, F_OK) == 0)
+  if (sys_access (name, F_OK) == 0)
     errno = EINVAL;
   return -1;
 }

=== modified file 'src/process.c'
--- src/process.c	2012-10-13 00:52:01 +0000
+++ src/process.c	2012-10-13 00:55:20 +0000
@@ -663,7 +663,7 @@
 #else
 	    sprintf (pty_name, "/dev/tty%c%x", c, i);
 #endif /* no PTY_TTY_NAME_SPRINTF */
-	    if (access (pty_name, 6) != 0)
+	    if (euidaccess (pty_name, R_OK | W_OK) != 0)
 	      {
 		emacs_close (fd);
 # ifndef __sgi

=== modified file 'src/xrdb.c'
--- src/xrdb.c	2012-09-15 07:06:56 +0000
+++ src/xrdb.c	2012-10-12 23:28:31 +0000
@@ -268,7 +268,7 @@
 {
   struct stat status;
 
-  return (access (filename, 4) == 0             /* exists and is readable */
+  return (euidaccess (filename, R_OK) == 0      /* exists and is readable */
 	  && stat (filename, &status) == 0	/* get the status */
 	  && (S_ISDIR (status.st_mode)) == 0);	/* not a directory */
 }


^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-14 18:14         ` Paul Eggert
@ 2012-10-14 18:39           ` Eli Zaretskii
  2012-10-14 19:42             ` Paul Eggert
  0 siblings, 1 reply; 47+ messages in thread
From: Eli Zaretskii @ 2012-10-14 18:39 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 12632

> Date: Sun, 14 Oct 2012 11:14:39 -0700
> From: Paul Eggert <eggert@cs.ucla.edu>
> CC: 12632@debbugs.gnu.org
> 
> > Emacs should be able to test whether a file exists even if it
> > will be unable to access it later.
> 
> Emacs cannot do that.  What 'access' does is ask, "If Emacs were
> to issue the seteuid system call, and change the effective user
> ID to the real user ID, would Emacs then be able to see that the
> file exists?"  This does not test whether the file exists; it tests
> only whether Emacs could see that the file exists in a hypothetical
> situation that never actually happens (because Emacs never issues
> the seteuid system call).  But this isn't what is wanted here:
> what is wanted is a test whether Emacs can currently see that the
> file exists, and that is what euidaccess does.

I can't argue with you, because you seem to bring arguments out of
some inner knowledge.  The 'access' man page simply says this:

   F_OK tests for the existence of the file.

It says nothing about granting any permissions (unlike when it
describes R_OK, W_OK, and X_OK).

> === modified file 'src/msdos.c'
> --- src/msdos.c	2012-09-23 08:44:20 +0000
> +++ src/msdos.c	2012-10-14 18:06:01 +0000
> @@ -3557,7 +3557,7 @@
>  	 read-only filesystem, like CD-ROM or a write-protected floppy.
>  	 The only way to be really sure is to actually create a file and
>  	 see if it succeeds.  But I think that's too much to ask.  */
> -      if (tmp && access (tmp, D_OK) == 0)
> +      if (tmp && sys_access (tmp, D_OK) == 0)
>  	{
>  	  setenv ("TMPDIR", tmp, 1);
>  	  break;
> @@ -3935,7 +3935,7 @@
>  readlink (const char *name, char *dummy1, size_t dummy2)
>  {
>    /* `access' is much faster than `stat' on MS-DOS.  */
> -  if (access (name, F_OK) == 0)
> +  if (sys_access (name, F_OK) == 0)
>      errno = EINVAL;
>    return -1;
>  }

This part is wrong: the MSDOS build doesn't have sys_access, and
there's nothing wrong with its library's 'access' that would justify
such a replacement.  Only the MS-Windows build needs 'sys_access'.





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-14 18:39           ` Eli Zaretskii
@ 2012-10-14 19:42             ` Paul Eggert
  2012-10-14 20:10               ` Eli Zaretskii
  2012-10-14 20:17               ` Eli Zaretskii
  0 siblings, 2 replies; 47+ messages in thread
From: Paul Eggert @ 2012-10-14 19:42 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 12632

On 10/14/2012 11:39 AM, Eli Zaretskii wrote:
> The 'access' man page simply says this:
> 
>    F_OK tests for the existence of the file.
> 
> It says nothing about granting any permissions (unlike when it
> describes R_OK, W_OK, and X_OK).

One always needs search permissions when resolving file names, no matter
what the context, and the 'access' man page doesn't bother to document
that.  Here's an example to illustrate.  Compile the following program
on a GNUish host and put it into a file 'a.out'.

	#define _GNU_SOURCE
	#include <stdio.h>
	#include <unistd.h>
	#include <sys/stat.h>

	static void
	try (char const *file)
	{
	  struct stat st;
	  printf ("%8d %12d %11d    %s\n",
		  access (file, F_OK),
		  euidaccess (file, F_OK),
		  stat (file, &st),
		  file);
	}

	int
	main (int argc, char **argv)
	{
	  printf ("access(F_OK) euidaccess(F_OK) stat() filename\n");
	  while (*++argv)
	    try (*argv);
	  return 0;
	}

Now, make a.out setuid and owned by someone else, and set up
an environment where you're trying to access files in directories
that you cannot search, but the other guy can.  For example:

	$ sudo chown games a.out
	$ sudo chmod u+s a.out
	$ mkdir -m 700 eggert games uucp
	$ touch eggert/foo games/foo uucp/foo
	$ sudo chown games games
	$ sudo chown uucp uucp
	$ sudo ls -ld a.out eggert eggert/foo games games/foo uucp uucp/foo
	-rwsr-sr-x. 1 games  root 7440 Oct 14 12:21 a.out
	drwx------. 2 eggert root 4096 Oct 14 12:15 eggert
	----------. 1 root   root    0 Oct 14 12:15 eggert/foo
	drwx------. 2 games  root 4096 Oct 14 12:15 games
	----------. 1 root   root    0 Oct 14 12:15 games/foo
	drwx------. 2 uucp   root 4096 Oct 14 12:22 uucp
	-rw-r--r--. 1 root   root    0 Oct 14 12:22 uucp/foo
	$ ls -ld a.out eggert eggert/foo games games/foo uucp uucp/foo
	ls: cannot access games/foo: Permission denied
	ls: cannot access uucp/foo: Permission denied
	-rwsr-sr-x. 1 games  root 7440 Oct 14 12:21 a.out
	drwx------. 2 eggert root 4096 Oct 14 12:15 eggert
	----------. 1 root   root    0 Oct 14 12:15 eggert/foo
	drwx------. 2 games  root 4096 Oct 14 12:15 games
	drwx------. 2 uucp   root 4096 Oct 14 12:22 uucp
	$ ./a.out eggert eggert/foo games games/foo uucp uucp/foo
	access(F_OK) euidaccess(F_OK) stat() filename
	       0            0           0    eggert
	       0           -1          -1    eggert/foo
	       0            0           0    games
	      -1            0           0    games/foo
	       0            0           0    uucp
	      -1           -1          -1    uucp/foo

euidaccess always agrees with ls and with stat, whereas
access does not.  We want the semantics of ls and of stat
and of euidaccess, not the semantics of access.

> This part is wrong: the MSDOS build doesn't have sys_access

OK, thanks, I'll leave that part out.






^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-14 19:42             ` Paul Eggert
@ 2012-10-14 20:10               ` Eli Zaretskii
  2012-10-14 20:17               ` Eli Zaretskii
  1 sibling, 0 replies; 47+ messages in thread
From: Eli Zaretskii @ 2012-10-14 20:10 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 12632

> Date: Sun, 14 Oct 2012 12:42:40 -0700
> From: Paul Eggert <eggert@cs.ucla.edu>
> CC: 12632@debbugs.gnu.org
> 
> On 10/14/2012 11:39 AM, Eli Zaretskii wrote:
> > The 'access' man page simply says this:
> > 
> >    F_OK tests for the existence of the file.
> > 
> > It says nothing about granting any permissions (unlike when it
> > describes R_OK, W_OK, and X_OK).
> 
> One always needs search permissions when resolving file names

You are talking about the directories leading to the file.  I was
talking about the file itself.





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-14 19:42             ` Paul Eggert
  2012-10-14 20:10               ` Eli Zaretskii
@ 2012-10-14 20:17               ` Eli Zaretskii
  2012-10-14 20:40                 ` Paul Eggert
  1 sibling, 1 reply; 47+ messages in thread
From: Eli Zaretskii @ 2012-10-14 20:17 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 12632

> Date: Sun, 14 Oct 2012 12:42:40 -0700
> From: Paul Eggert <eggert@cs.ucla.edu>
> CC: 12632@debbugs.gnu.org
> 
> 	$ ./a.out eggert eggert/foo games games/foo uucp uucp/foo
> 	access(F_OK) euidaccess(F_OK) stat() filename
> 	       0            0           0    eggert
> 	       0           -1          -1    eggert/foo
> 	       0            0           0    games
> 	      -1            0           0    games/foo
> 	       0            0           0    uucp
> 	      -1           -1          -1    uucp/foo
> 
> euidaccess always agrees with ls and with stat, whereas
> access does not.

If euidaccess always agrees with stat, why does it make sense to
switch check_existing to using euidaccess, which is much less portable
and non-standard function, requiring yet another bunch of imports from
gnulib?





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-14 20:17               ` Eli Zaretskii
@ 2012-10-14 20:40                 ` Paul Eggert
  2012-10-14 20:53                   ` Eli Zaretskii
  0 siblings, 1 reply; 47+ messages in thread
From: Paul Eggert @ 2012-10-14 20:40 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 12632

On 10/14/2012 01:17 PM, Eli Zaretskii wrote:
> If euidaccess always agrees with stat, why does it make sense to
> switch check_existing to using euidaccess

euidaccess always agrees with 'stat' with respect to access checks,
but there are other reasons that 'stat' could fail, e.g.,
on a file that is too large for 'stat' to represent the size.
In cases such as these, we want euidaccess here, because
euidaccess shouldn't fail for these other reasons.

> You are talking about the directories leading to the file.  I was
> talking about the file itself.

OK, but in this context it doesn't make sense to talk about just
the file itself, since the file is being accessed via a name, and
one needs proper permissions to use the name.

For both access and euidaccess, the F_OK check means "I don't
care about the permissions about the file itself; all I care about
are the permissions on the file's name", as far as permissions
are concerned.





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-14 20:40                 ` Paul Eggert
@ 2012-10-14 20:53                   ` Eli Zaretskii
  2012-10-15  6:17                     ` Paul Eggert
  0 siblings, 1 reply; 47+ messages in thread
From: Eli Zaretskii @ 2012-10-14 20:53 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 12632

> Date: Sun, 14 Oct 2012 13:40:52 -0700
> From: Paul Eggert <eggert@cs.ucla.edu>
> CC: 12632@debbugs.gnu.org
> 
> On 10/14/2012 01:17 PM, Eli Zaretskii wrote:
> > If euidaccess always agrees with stat, why does it make sense to
> > switch check_existing to using euidaccess
> 
> euidaccess always agrees with 'stat' with respect to access checks,
> but there are other reasons that 'stat' could fail, e.g.,
> on a file that is too large for 'stat' to represent the size.

That sounds like a very weak reason to switch to a non-portable API,
especially since Emacs will be unable to do anything with such
imaginary files anyway.  And gnulib's euidaccess will also fail for
them in most non-trivial cases, because it calls stat.






^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-14 20:53                   ` Eli Zaretskii
@ 2012-10-15  6:17                     ` Paul Eggert
  2012-10-15 17:31                       ` Eli Zaretskii
  0 siblings, 1 reply; 47+ messages in thread
From: Paul Eggert @ 2012-10-15  6:17 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 12632

[-- Attachment #1: Type: text/plain, Size: 664 bytes --]

On 10/14/2012 01:53 PM, Eli Zaretskii wrote:
> That sounds like a very weak reason to switch to a non-portable API

Since the main objection here seems to be using the non-portable
function euidaccess, I changed the patch to use faccessat instead.
faccessat, unlike euidaccess, is standardized by POSIX; it's a tad
more-complicated to use but it is standard and it's no doubt
a better solution in the long run anyway.  The patch
uses the Gnulib substitute for faccessat for obsolescent POSIXish
hosts that lack faccessat.  For Windows the patch simply reworks the
existing substitute for 'access' so that it implements faccessat instead.

Revised patch attached.


[-- Attachment #2: faccessat.txt --]
[-- Type: text/plain, Size: 75350 bytes --]

=== modified file '.bzrignore'
--- .bzrignore	2012-10-11 11:26:26 +0000
+++ .bzrignore	2012-10-15 06:05:46 +0000
@@ -87,6 +87,7 @@
 lib/arg-nonnull.h
 lib/c++defs.h
 lib/execinfo.h
+lib/fcntl.h
 lib/getopt.h
 lib/inttypes.h
 lib/stdalign.h

=== modified file 'ChangeLog'
--- ChangeLog	2012-10-11 11:29:47 +0000
+++ ChangeLog	2012-10-15 06:05:46 +0000
@@ -1,3 +1,16 @@
+2012-10-15  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use faccessat, not access, when checking file permissions (Bug#12632).
+	* .bzrignore: Add lib/fcntl.h.
+	* configure.ac (euidaccess): Remove check; gnulib does this for us now.
+	(gl_FCNTL_O_FLAGS): Define a dummy version.
+	* lib/at-func.c, lib/euidaccess.c, lib/faccessat.c, lib/fcntl.in.h:
+	* lib/getgroups.c, lib/group-member.c, lib/root-uid.h:
+	* lib/xalloc-oversized.h, m4/euidaccess.m4, m4/faccessat.m4:
+	* m4/fcntl_h.m4, m4/getgroups.m4, m4/group-member.m4:
+	New files, from gnulib.
+	* lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate.
+
 2012-10-11  Kenichi Handa  <handa@gnu.org>
 
 	* .bzrignore: Add several files under admin/charsets.

=== modified file 'admin/ChangeLog'
--- admin/ChangeLog	2012-10-14 08:01:30 +0000
+++ admin/ChangeLog	2012-10-15 06:05:46 +0000
@@ -1,3 +1,11 @@
+2012-10-14  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use faccessat, not access, when checking file permissions (Bug#12632).
+	* merge-gnulib (GNULIB_MODULES): Add faccessat.
+	(GNULIB_TOOL_FLAGS): Avoid at-internal, fchdir, malloc-posix,
+	openat-die, openat-h, save-cwd.  Do not avoid fcntl-h.
+	Omit gnulib's m4/fcntl-o.m4.
+
 2012-10-12  Kenichi Handa  <handa@gnu.org>
 
 	* charsets/Makefile (JISC6226.map): Add missing mappings.

=== modified file 'admin/merge-gnulib'
--- admin/merge-gnulib	2012-09-27 01:06:23 +0000
+++ admin/merge-gnulib	2012-10-15 06:05:46 +0000
@@ -28,7 +28,7 @@
 GNULIB_MODULES='
   alloca-opt c-ctype c-strcase
   careadlinkat crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512
-  dtoastr dtotimespec dup2 environ execinfo
+  dtoastr dtotimespec dup2 environ execinfo faccessat
   filemode getloadavg getopt-gnu gettime gettimeofday
   ignore-value intprops largefile lstat
   manywarnings mktime pselect pthread_sigmask readlink
@@ -39,9 +39,12 @@
 '
 
 GNULIB_TOOL_FLAGS='
-  --avoid=errno --avoid=fcntl --avoid=fcntl-h --avoid=fstat
-  --avoid=msvc-inval --avoid=msvc-nothrow
-  --avoid=raise --avoid=select --avoid=sigprocmask --avoid=sys_types
+  --avoid=at-internal
+  --avoid=errno --avoid=fchdir --avoid=fcntl --avoid=fstat
+  --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow
+  --avoid=openat-die --avoid=openat-h
+  --avoid=raise
+  --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types
   --avoid=threadlib
   --conditional-dependencies --import --no-changelog --no-vc-files
   --makefile-name=gnulib.mk
@@ -85,7 +88,7 @@
 }
 
 "$gnulib_srcdir"/gnulib-tool --dir="$src" $GNULIB_TOOL_FLAGS $GNULIB_MODULES &&
-rm -- "$src"m4/gnulib-cache.m4 "$src"m4/warn-on-use.m4 &&
+rm -- "$src"m4/fcntl-o.m4 "$src"m4/gnulib-cache.m4 "$src"m4/warn-on-use.m4 &&
 cp -- "$gnulib_srcdir"/build-aux/texinfo.tex "$src"doc/misc &&
 cp -- "$gnulib_srcdir"/build-aux/move-if-change "$src"build-aux &&
 autoreconf -i -I m4 -- ${src:+"$src"}

=== modified file 'configure.ac'
--- configure.ac	2012-10-08 16:09:00 +0000
+++ configure.ac	2012-10-15 06:05:46 +0000
@@ -572,6 +572,8 @@
   test "x$NON_GCC_TEST_OPTIONS" != x && CC="$CC $NON_GCC_TEST_OPTIONS"
 fi
 
+# Avoid gnulib's tests for O_NOATIME and O_NOFOLLOW, as we don't use them.
+AC_DEFUN([gl_FCNTL_O_FLAGS])
 # Avoid gnulib's threadlib module, as we do threads our own way.
 AC_DEFUN([gl_THREADLIB])
 
@@ -2872,7 +2874,7 @@
 AC_CHECK_FUNCS(gethostname \
 closedir getrusage get_current_dir_name \
 lrand48 setsid \
-fpathconf select euidaccess getpagesize setlocale \
+fpathconf select getpagesize setlocale \
 utimes getrlimit setrlimit setpgid getcwd shutdown getaddrinfo \
 __fpending strsignal setitimer \
 sendto recvfrom getsockname getpeername getifaddrs freeifaddrs \

=== added file 'lib/at-func.c'
--- lib/at-func.c	1970-01-01 00:00:00 +0000
+++ lib/at-func.c	2012-10-15 06:05:46 +0000
@@ -0,0 +1,146 @@
+/* Define at-style functions like fstatat, unlinkat, fchownat, etc.
+   Copyright (C) 2006, 2009-2012 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* written by Jim Meyering */
+
+#include "dosname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */
+
+#ifdef GNULIB_SUPPORT_ONLY_AT_FDCWD
+# include <errno.h>
+# ifndef ENOTSUP
+#  define ENOTSUP EINVAL
+# endif
+#else
+# include "openat.h"
+# include "openat-priv.h"
+# include "save-cwd.h"
+#endif
+
+#ifdef AT_FUNC_USE_F1_COND
+# define CALL_FUNC(F)                           \
+  (flag == AT_FUNC_USE_F1_COND                  \
+    ? AT_FUNC_F1 (F AT_FUNC_POST_FILE_ARGS)     \
+    : AT_FUNC_F2 (F AT_FUNC_POST_FILE_ARGS))
+# define VALIDATE_FLAG(F)                       \
+  if (flag & ~AT_FUNC_USE_F1_COND)              \
+    {                                           \
+      errno = EINVAL;                           \
+      return FUNC_FAIL;                         \
+    }
+#else
+# define CALL_FUNC(F) (AT_FUNC_F1 (F AT_FUNC_POST_FILE_ARGS))
+# define VALIDATE_FLAG(F) /* empty */
+#endif
+
+#ifdef AT_FUNC_RESULT
+# define FUNC_RESULT AT_FUNC_RESULT
+#else
+# define FUNC_RESULT int
+#endif
+
+#ifdef AT_FUNC_FAIL
+# define FUNC_FAIL AT_FUNC_FAIL
+#else
+# define FUNC_FAIL -1
+#endif
+
+/* Call AT_FUNC_F1 to operate on FILE, which is in the directory
+   open on descriptor FD.  If AT_FUNC_USE_F1_COND is defined to a value,
+   AT_FUNC_POST_FILE_PARAM_DECLS must include a parameter named flag;
+   call AT_FUNC_F2 if FLAG is 0 or fail if FLAG contains more bits than
+   AT_FUNC_USE_F1_COND.  Return int and fail with -1 unless AT_FUNC_RESULT
+   or AT_FUNC_FAIL are defined.  If possible, do it without changing the
+   working directory.  Otherwise, resort to using save_cwd/fchdir,
+   then AT_FUNC_F?/restore_cwd.  If either the save_cwd or the restore_cwd
+   fails, then give a diagnostic and exit nonzero.  */
+FUNC_RESULT
+AT_FUNC_NAME (int fd, char const *file AT_FUNC_POST_FILE_PARAM_DECLS)
+{
+  VALIDATE_FLAG (flag);
+
+  if (fd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file))
+    return CALL_FUNC (file);
+
+#ifdef GNULIB_SUPPORT_ONLY_AT_FDCWD
+  errno = ENOTSUP;
+  return FUNC_FAIL;
+#else
+  {
+  /* Be careful to choose names unlikely to conflict with
+     AT_FUNC_POST_FILE_PARAM_DECLS.  */
+  struct saved_cwd saved_cwd;
+  int saved_errno;
+  FUNC_RESULT err;
+
+  {
+    char proc_buf[OPENAT_BUFFER_SIZE];
+    char *proc_file = openat_proc_name (proc_buf, fd, file);
+    if (proc_file)
+      {
+        FUNC_RESULT proc_result = CALL_FUNC (proc_file);
+        int proc_errno = errno;
+        if (proc_file != proc_buf)
+          free (proc_file);
+        /* If the syscall succeeds, or if it fails with an unexpected
+           errno value, then return right away.  Otherwise, fall through
+           and resort to using save_cwd/restore_cwd.  */
+        if (FUNC_FAIL != proc_result)
+          return proc_result;
+        if (! EXPECTED_ERRNO (proc_errno))
+          {
+            errno = proc_errno;
+            return proc_result;
+          }
+      }
+  }
+
+  if (save_cwd (&saved_cwd) != 0)
+    openat_save_fail (errno);
+  if (0 <= fd && fd == saved_cwd.desc)
+    {
+      /* If saving the working directory collides with the user's
+         requested fd, then the user's fd must have been closed to
+         begin with.  */
+      free_cwd (&saved_cwd);
+      errno = EBADF;
+      return FUNC_FAIL;
+    }
+
+  if (fchdir (fd) != 0)
+    {
+      saved_errno = errno;
+      free_cwd (&saved_cwd);
+      errno = saved_errno;
+      return FUNC_FAIL;
+    }
+
+  err = CALL_FUNC (file);
+  saved_errno = (err == FUNC_FAIL ? errno : 0);
+
+  if (restore_cwd (&saved_cwd) != 0)
+    openat_restore_fail (errno);
+
+  free_cwd (&saved_cwd);
+
+  if (saved_errno)
+    errno = saved_errno;
+  return err;
+  }
+#endif
+}
+#undef CALL_FUNC
+#undef FUNC_RESULT
+#undef FUNC_FAIL

=== added file 'lib/euidaccess.c'
--- lib/euidaccess.c	1970-01-01 00:00:00 +0000
+++ lib/euidaccess.c	2012-10-12 23:28:31 +0000
@@ -0,0 +1,221 @@
+/* euidaccess -- check if effective user id can access file
+
+   Copyright (C) 1990-1991, 1995, 1998, 2000, 2003-2006, 2008-2012 Free
+   Software Foundation, Inc.
+
+   This file is part of the GNU C Library.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by David MacKenzie and Torbjorn Granlund.
+   Adapted for GNU C library by Roland McGrath.  */
+
+#ifndef _LIBC
+# include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "root-uid.h"
+
+#if HAVE_LIBGEN_H
+# include <libgen.h>
+#endif
+
+#include <errno.h>
+#ifndef __set_errno
+# define __set_errno(val) errno = (val)
+#endif
+
+#if defined EACCES && !defined EACCESS
+# define EACCESS EACCES
+#endif
+
+#ifndef F_OK
+# define F_OK 0
+# define X_OK 1
+# define W_OK 2
+# define R_OK 4
+#endif
+
+
+#ifdef _LIBC
+
+# define access __access
+# define getuid __getuid
+# define getgid __getgid
+# define geteuid __geteuid
+# define getegid __getegid
+# define group_member __group_member
+# define euidaccess __euidaccess
+# undef stat
+# define stat stat64
+
+#endif
+
+/* Return 0 if the user has permission of type MODE on FILE;
+   otherwise, return -1 and set 'errno'.
+   Like access, except that it uses the effective user and group
+   id's instead of the real ones, and it does not always check for read-only
+   file system, text busy, etc.  */
+
+int
+euidaccess (const char *file, int mode)
+{
+#if HAVE_FACCESSAT                   /* glibc, AIX 7, Solaris 11, Cygwin 1.7 */
+  return faccessat (AT_FDCWD, file, mode, AT_EACCESS);
+#elif defined EFF_ONLY_OK               /* IRIX, OSF/1, Interix */
+  return access (file, mode | EFF_ONLY_OK);
+#elif defined ACC_SELF                  /* AIX */
+  return accessx (file, mode, ACC_SELF);
+#elif HAVE_EACCESS                      /* FreeBSD */
+  return eaccess (file, mode);
+#else       /* Mac OS X, NetBSD, OpenBSD, HP-UX, Solaris, Cygwin, mingw, BeOS */
+
+  uid_t uid = getuid ();
+  gid_t gid = getgid ();
+  uid_t euid = geteuid ();
+  gid_t egid = getegid ();
+  struct stat stats;
+
+# if HAVE_DECL_SETREGID && PREFER_NONREENTRANT_EUIDACCESS
+
+  /* Define PREFER_NONREENTRANT_EUIDACCESS if you prefer euidaccess to
+     return the correct result even if this would make it
+     nonreentrant.  Define this only if your entire application is
+     safe even if the uid or gid might temporarily change.  If your
+     application uses signal handlers or threads it is probably not
+     safe.  */
+
+  if (mode == F_OK)
+    return stat (file, &stats);
+  else
+    {
+      int result;
+      int saved_errno;
+
+      if (uid != euid)
+        setreuid (euid, uid);
+      if (gid != egid)
+        setregid (egid, gid);
+
+      result = access (file, mode);
+      saved_errno = errno;
+
+      /* Restore them.  */
+      if (uid != euid)
+        setreuid (uid, euid);
+      if (gid != egid)
+        setregid (gid, egid);
+
+      errno = saved_errno;
+      return result;
+    }
+
+# else
+
+  /* The following code assumes the traditional Unix model, and is not
+     correct on systems that have ACLs or the like.  However, it's
+     better than nothing, and it is reentrant.  */
+
+  unsigned int granted;
+  if (uid == euid && gid == egid)
+    /* If we are not set-uid or set-gid, access does the same.  */
+    return access (file, mode);
+
+  if (stat (file, &stats) != 0)
+    return -1;
+
+  /* The super-user can read and write any file, and execute any file
+     that anyone can execute.  */
+  if (euid == ROOT_UID
+      && ((mode & X_OK) == 0
+          || (stats.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))))
+    return 0;
+
+  /* Convert the mode to traditional form, clearing any bogus bits.  */
+  if (R_OK == 4 && W_OK == 2 && X_OK == 1 && F_OK == 0)
+    mode &= 7;
+  else
+    mode = ((mode & R_OK ? 4 : 0)
+            + (mode & W_OK ? 2 : 0)
+            + (mode & X_OK ? 1 : 0));
+
+  if (mode == 0)
+    return 0;                   /* The file exists.  */
+
+  /* Convert the file's permission bits to traditional form.  */
+  if (S_IRUSR == (4 << 6) && S_IWUSR == (2 << 6) && S_IXUSR == (1 << 6)
+      && S_IRGRP == (4 << 3) && S_IWGRP == (2 << 3) && S_IXGRP == (1 << 3)
+      && S_IROTH == (4 << 0) && S_IWOTH == (2 << 0) && S_IXOTH == (1 << 0))
+    granted = stats.st_mode;
+  else
+    granted = ((stats.st_mode & S_IRUSR ? 4 << 6 : 0)
+               + (stats.st_mode & S_IWUSR ? 2 << 6 : 0)
+               + (stats.st_mode & S_IXUSR ? 1 << 6 : 0)
+               + (stats.st_mode & S_IRGRP ? 4 << 3 : 0)
+               + (stats.st_mode & S_IWGRP ? 2 << 3 : 0)
+               + (stats.st_mode & S_IXGRP ? 1 << 3 : 0)
+               + (stats.st_mode & S_IROTH ? 4 << 0 : 0)
+               + (stats.st_mode & S_IWOTH ? 2 << 0 : 0)
+               + (stats.st_mode & S_IXOTH ? 1 << 0 : 0));
+
+  if (euid == stats.st_uid)
+    granted >>= 6;
+  else if (egid == stats.st_gid || group_member (stats.st_gid))
+    granted >>= 3;
+
+  if ((mode & ~granted) == 0)
+    return 0;
+  __set_errno (EACCESS);
+  return -1;
+
+# endif
+#endif
+}
+#undef euidaccess
+#ifdef weak_alias
+weak_alias (__euidaccess, euidaccess)
+#endif
+\f
+#ifdef TEST
+# include <error.h>
+# include <stdio.h>
+# include <stdlib.h>
+
+char *program_name;
+
+int
+main (int argc, char **argv)
+{
+  char *file;
+  int mode;
+  int err;
+
+  program_name = argv[0];
+  if (argc < 3)
+    abort ();
+  file = argv[1];
+  mode = atoi (argv[2]);
+
+  err = euidaccess (file, mode);
+  printf ("%d\n", err);
+  if (err != 0)
+    error (0, errno, "%s", file);
+  exit (0);
+}
+#endif

=== added file 'lib/faccessat.c'
--- lib/faccessat.c	1970-01-01 00:00:00 +0000
+++ lib/faccessat.c	2012-10-15 06:05:46 +0000
@@ -0,0 +1,45 @@
+/* Check the access rights of a file relative to an open directory.
+   Copyright (C) 2009-2012 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* written by Eric Blake */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#ifndef HAVE_ACCESS
+/* Mingw lacks access, but it also lacks real vs. effective ids, so
+   the gnulib euidaccess module is good enough.  */
+# undef access
+# define access euidaccess
+#endif
+
+/* Invoke access or euidaccess on file, FILE, using mode MODE, in the directory
+   open on descriptor FD.  If possible, do it without changing the
+   working directory.  Otherwise, resort to using save_cwd/fchdir, then
+   (access|euidaccess)/restore_cwd.  If either the save_cwd or the
+   restore_cwd fails, then give a diagnostic and exit nonzero.
+   Note that this implementation only supports AT_EACCESS, although some
+   native versions also support AT_SYMLINK_NOFOLLOW.  */
+
+#define AT_FUNC_NAME faccessat
+#define AT_FUNC_F1 euidaccess
+#define AT_FUNC_F2 access
+#define AT_FUNC_USE_F1_COND AT_EACCESS
+#define AT_FUNC_POST_FILE_PARAM_DECLS , int mode, int flag
+#define AT_FUNC_POST_FILE_ARGS        , mode
+#include "at-func.c"

=== added file 'lib/fcntl.in.h'
--- lib/fcntl.in.h	1970-01-01 00:00:00 +0000
+++ lib/fcntl.in.h	2012-10-15 06:05:46 +0000
@@ -0,0 +1,335 @@
+/* Like <fcntl.h>, but with non-working flags defined to 0.
+
+   Copyright (C) 2006-2012 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* written by Paul Eggert */
+
+#if __GNUC__ >= 3
+@PRAGMA_SYSTEM_HEADER@
+#endif
+@PRAGMA_COLUMNS@
+
+#if defined __need_system_fcntl_h
+/* Special invocation convention.  */
+
+/* Needed before <sys/stat.h>.
+   May also define off_t to a 64-bit type on native Windows.  */
+#include <sys/types.h>
+/* On some systems other than glibc, <sys/stat.h> is a prerequisite of
+   <fcntl.h>.  On glibc systems, we would like to avoid namespace pollution.
+   But on glibc systems, <fcntl.h> includes <sys/stat.h> inside an
+   extern "C" { ... } block, which leads to errors in C++ mode with the
+   overridden <sys/stat.h> from gnulib.  These errors are known to be gone
+   with g++ version >= 4.3.  */
+#if !(defined __GLIBC__ || defined __UCLIBC__) || (defined __cplusplus && defined GNULIB_NAMESPACE && !(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)))
+# include <sys/stat.h>
+#endif
+#@INCLUDE_NEXT@ @NEXT_FCNTL_H@
+
+#else
+/* Normal invocation convention.  */
+
+#ifndef _@GUARD_PREFIX@_FCNTL_H
+
+/* Needed before <sys/stat.h>.
+   May also define off_t to a 64-bit type on native Windows.  */
+#include <sys/types.h>
+/* On some systems other than glibc, <sys/stat.h> is a prerequisite of
+   <fcntl.h>.  On glibc systems, we would like to avoid namespace pollution.
+   But on glibc systems, <fcntl.h> includes <sys/stat.h> inside an
+   extern "C" { ... } block, which leads to errors in C++ mode with the
+   overridden <sys/stat.h> from gnulib.  These errors are known to be gone
+   with g++ version >= 4.3.  */
+#if !(defined __GLIBC__ || defined __UCLIBC__) || (defined __cplusplus && defined GNULIB_NAMESPACE && !(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)))
+# include <sys/stat.h>
+#endif
+/* The include_next requires a split double-inclusion guard.  */
+#@INCLUDE_NEXT@ @NEXT_FCNTL_H@
+
+#ifndef _@GUARD_PREFIX@_FCNTL_H
+#define _@GUARD_PREFIX@_FCNTL_H
+
+#ifndef __GLIBC__ /* Avoid namespace pollution on glibc systems.  */
+# include <unistd.h>
+#endif
+
+/* Native Windows platforms declare open(), creat() in <io.h>.  */
+#if (@GNULIB_OPEN@ || defined GNULIB_POSIXCHECK) \
+    && ((defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__)
+# include <io.h>
+#endif
+
+
+/* The definitions of _GL_FUNCDECL_RPL etc. are copied here.  */
+
+/* The definition of _GL_ARG_NONNULL is copied here.  */
+
+/* The definition of _GL_WARN_ON_USE is copied here.  */
+
+
+/* Declare overridden functions.  */
+
+#if @GNULIB_FCNTL@
+# if @REPLACE_FCNTL@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef fcntl
+#   define fcntl rpl_fcntl
+#  endif
+_GL_FUNCDECL_RPL (fcntl, int, (int fd, int action, ...));
+_GL_CXXALIAS_RPL (fcntl, int, (int fd, int action, ...));
+# else
+#  if !@HAVE_FCNTL@
+_GL_FUNCDECL_SYS (fcntl, int, (int fd, int action, ...));
+#  endif
+_GL_CXXALIAS_SYS (fcntl, int, (int fd, int action, ...));
+# endif
+_GL_CXXALIASWARN (fcntl);
+#elif defined GNULIB_POSIXCHECK
+# undef fcntl
+# if HAVE_RAW_DECL_FCNTL
+_GL_WARN_ON_USE (fcntl, "fcntl is not always POSIX compliant - "
+                 "use gnulib module fcntl for portability");
+# endif
+#endif
+
+#if @GNULIB_OPEN@
+# if @REPLACE_OPEN@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef open
+#   define open rpl_open
+#  endif
+_GL_FUNCDECL_RPL (open, int, (const char *filename, int flags, ...)
+                             _GL_ARG_NONNULL ((1)));
+_GL_CXXALIAS_RPL (open, int, (const char *filename, int flags, ...));
+# else
+_GL_CXXALIAS_SYS (open, int, (const char *filename, int flags, ...));
+# endif
+/* On HP-UX 11, in C++ mode, open() is defined as an inline function with a
+   default argument.  _GL_CXXALIASWARN does not work in this case.  */
+# if !defined __hpux
+_GL_CXXALIASWARN (open);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef open
+/* Assume open is always declared.  */
+_GL_WARN_ON_USE (open, "open is not always POSIX compliant - "
+                 "use gnulib module open for portability");
+#endif
+
+#if @GNULIB_OPENAT@
+# if @REPLACE_OPENAT@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef openat
+#   define openat rpl_openat
+#  endif
+_GL_FUNCDECL_RPL (openat, int,
+                  (int fd, char const *file, int flags, /* mode_t mode */ ...)
+                  _GL_ARG_NONNULL ((2)));
+_GL_CXXALIAS_RPL (openat, int,
+                  (int fd, char const *file, int flags, /* mode_t mode */ ...));
+# else
+#  if !@HAVE_OPENAT@
+_GL_FUNCDECL_SYS (openat, int,
+                  (int fd, char const *file, int flags, /* mode_t mode */ ...)
+                  _GL_ARG_NONNULL ((2)));
+#  endif
+_GL_CXXALIAS_SYS (openat, int,
+                  (int fd, char const *file, int flags, /* mode_t mode */ ...));
+# endif
+_GL_CXXALIASWARN (openat);
+#elif defined GNULIB_POSIXCHECK
+# undef openat
+# if HAVE_RAW_DECL_OPENAT
+_GL_WARN_ON_USE (openat, "openat is not portable - "
+                 "use gnulib module openat for portability");
+# endif
+#endif
+
+
+/* Fix up the FD_* macros, only known to be missing on mingw.  */
+
+#ifndef FD_CLOEXEC
+# define FD_CLOEXEC 1
+#endif
+
+/* Fix up the supported F_* macros.  Intentionally leave other F_*
+   macros undefined.  Only known to be missing on mingw.  */
+
+#ifndef F_DUPFD_CLOEXEC
+# define F_DUPFD_CLOEXEC 0x40000000
+/* Witness variable: 1 if gnulib defined F_DUPFD_CLOEXEC, 0 otherwise.  */
+# define GNULIB_defined_F_DUPFD_CLOEXEC 1
+#else
+# define GNULIB_defined_F_DUPFD_CLOEXEC 0
+#endif
+
+#ifndef F_DUPFD
+# define F_DUPFD 1
+#endif
+
+#ifndef F_GETFD
+# define F_GETFD 2
+#endif
+
+/* Fix up the O_* macros.  */
+
+#if !defined O_DIRECT && defined O_DIRECTIO
+/* Tru64 spells it 'O_DIRECTIO'.  */
+# define O_DIRECT O_DIRECTIO
+#endif
+
+#if !defined O_CLOEXEC && defined O_NOINHERIT
+/* Mingw spells it 'O_NOINHERIT'.  */
+# define O_CLOEXEC O_NOINHERIT
+#endif
+
+#ifndef O_CLOEXEC
+# define O_CLOEXEC 0
+#endif
+
+#ifndef O_DIRECT
+# define O_DIRECT 0
+#endif
+
+#ifndef O_DIRECTORY
+# define O_DIRECTORY 0
+#endif
+
+#ifndef O_DSYNC
+# define O_DSYNC 0
+#endif
+
+#ifndef O_EXEC
+# define O_EXEC O_RDONLY /* This is often close enough in older systems.  */
+#endif
+
+#ifndef O_NDELAY
+# define O_NDELAY 0
+#endif
+
+#ifndef O_NOATIME
+# define O_NOATIME 0
+#endif
+
+#ifndef O_NONBLOCK
+# define O_NONBLOCK O_NDELAY
+#endif
+
+/* If the gnulib module 'nonblocking' is in use, guarantee a working non-zero
+   value of O_NONBLOCK.  Otherwise, O_NONBLOCK is defined (above) to O_NDELAY
+   or to 0 as fallback.  */
+#if @GNULIB_NONBLOCKING@
+# if O_NONBLOCK
+#  define GNULIB_defined_O_NONBLOCK 0
+# else
+#  define GNULIB_defined_O_NONBLOCK 1
+#  undef O_NONBLOCK
+#  define O_NONBLOCK 0x40000000
+# endif
+#endif
+
+#ifndef O_NOCTTY
+# define O_NOCTTY 0
+#endif
+
+#ifndef O_NOFOLLOW
+# define O_NOFOLLOW 0
+#endif
+
+#ifndef O_NOLINKS
+# define O_NOLINKS 0
+#endif
+
+#ifndef O_RSYNC
+# define O_RSYNC 0
+#endif
+
+#ifndef O_SEARCH
+# define O_SEARCH O_RDONLY /* This is often close enough in older systems.  */
+#endif
+
+#ifndef O_SYNC
+# define O_SYNC 0
+#endif
+
+#ifndef O_TTY_INIT
+# define O_TTY_INIT 0
+#endif
+
+#if O_ACCMODE != (O_RDONLY | O_WRONLY | O_RDWR | O_EXEC | O_SEARCH)
+# undef O_ACCMODE
+# define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR | O_EXEC | O_SEARCH)
+#endif
+
+/* For systems that distinguish between text and binary I/O.
+   O_BINARY is usually declared in fcntl.h  */
+#if !defined O_BINARY && defined _O_BINARY
+  /* For MSC-compatible compilers.  */
+# define O_BINARY _O_BINARY
+# define O_TEXT _O_TEXT
+#endif
+
+#if defined __BEOS__ || defined __HAIKU__
+  /* BeOS 5 and Haiku have O_BINARY and O_TEXT, but they have no effect.  */
+# undef O_BINARY
+# undef O_TEXT
+#endif
+
+#ifndef O_BINARY
+# define O_BINARY 0
+# define O_TEXT 0
+#endif
+
+/* Fix up the AT_* macros.  */
+
+/* Work around a bug in Solaris 9 and 10: AT_FDCWD is positive.  Its
+   value exceeds INT_MAX, so its use as an int doesn't conform to the
+   C standard, and GCC and Sun C complain in some cases.  If the bug
+   is present, undef AT_FDCWD here, so it can be redefined below.  */
+#if 0 < AT_FDCWD && AT_FDCWD == 0xffd19553
+# undef AT_FDCWD
+#endif
+
+/* Use the same bit pattern as Solaris 9, but with the proper
+   signedness.  The bit pattern is important, in case this actually is
+   Solaris with the above workaround.  */
+#ifndef AT_FDCWD
+# define AT_FDCWD (-3041965)
+#endif
+
+/* Use the same values as Solaris 9.  This shouldn't matter, but
+   there's no real reason to differ.  */
+#ifndef AT_SYMLINK_NOFOLLOW
+# define AT_SYMLINK_NOFOLLOW 4096
+#endif
+
+#ifndef AT_REMOVEDIR
+# define AT_REMOVEDIR 1
+#endif
+
+/* Solaris 9 lacks these two, so just pick unique values.  */
+#ifndef AT_SYMLINK_FOLLOW
+# define AT_SYMLINK_FOLLOW 2
+#endif
+
+#ifndef AT_EACCESS
+# define AT_EACCESS 4
+#endif
+
+
+#endif /* _@GUARD_PREFIX@_FCNTL_H */
+#endif /* _@GUARD_PREFIX@_FCNTL_H */
+#endif

=== added file 'lib/getgroups.c'
--- lib/getgroups.c	1970-01-01 00:00:00 +0000
+++ lib/getgroups.c	2012-10-12 23:28:31 +0000
@@ -0,0 +1,116 @@
+/* provide consistent interface to getgroups for systems that don't allow N==0
+
+   Copyright (C) 1996, 1999, 2003, 2006-2012 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* written by Jim Meyering */
+
+#include <config.h>
+
+#include <unistd.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#if !HAVE_GETGROUPS
+
+/* Provide a stub that fails with ENOSYS, since there is no group
+   information available on mingw.  */
+int
+getgroups (int n _GL_UNUSED, GETGROUPS_T *groups _GL_UNUSED)
+{
+  errno = ENOSYS;
+  return -1;
+}
+
+#else /* HAVE_GETGROUPS */
+
+# undef getgroups
+# ifndef GETGROUPS_ZERO_BUG
+#  define GETGROUPS_ZERO_BUG 0
+# endif
+
+/* On at least Ultrix 4.3 and NextStep 3.2, getgroups (0, NULL) always
+   fails.  On other systems, it returns the number of supplemental
+   groups for the process.  This function handles that special case
+   and lets the system-provided function handle all others.  However,
+   it can fail with ENOMEM if memory is tight.  It is unspecified
+   whether the effective group id is included in the list.  */
+
+int
+rpl_getgroups (int n, gid_t *group)
+{
+  int n_groups;
+  GETGROUPS_T *gbuf;
+  int saved_errno;
+
+  if (n < 0)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  if (n != 0 || !GETGROUPS_ZERO_BUG)
+    {
+      int result;
+      if (sizeof *group == sizeof *gbuf)
+        return getgroups (n, (GETGROUPS_T *) group);
+
+      if (SIZE_MAX / sizeof *gbuf <= n)
+        {
+          errno = ENOMEM;
+          return -1;
+        }
+      gbuf = malloc (n * sizeof *gbuf);
+      if (!gbuf)
+        return -1;
+      result = getgroups (n, gbuf);
+      if (0 <= result)
+        {
+          n = result;
+          while (n--)
+            group[n] = gbuf[n];
+        }
+      saved_errno = errno;
+      free (gbuf);
+      errno == saved_errno;
+      return result;
+    }
+
+  n = 20;
+  while (1)
+    {
+      /* No need to worry about address arithmetic overflow here,
+         since the ancient systems that we're running on have low
+         limits on the number of secondary groups.  */
+      gbuf = malloc (n * sizeof *gbuf);
+      if (!gbuf)
+        return -1;
+      n_groups = getgroups (n, gbuf);
+      if (n_groups == -1 ? errno != EINVAL : n_groups < n)
+        break;
+      free (gbuf);
+      n *= 2;
+    }
+
+  saved_errno = errno;
+  free (gbuf);
+  errno = saved_errno;
+
+  return n_groups;
+}
+
+#endif /* HAVE_GETGROUPS */

=== modified file 'lib/gnulib.mk'
--- lib/gnulib.mk	2012-10-04 07:15:42 +0000
+++ lib/gnulib.mk	2012-10-15 06:05:46 +0000
@@ -21,7 +21,7 @@
 # the same distribution terms as the rest of that program.
 #
 # Generated by gnulib-tool.
-# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=errno --avoid=fcntl --avoid=fcntl-h --avoid=fstat --avoid=msvc-inval --avoid=msvc-nothrow --avoid=raise --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo filemode getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings mktime pselect pthread_sigmask readlink socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub utimens warnings
+# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=at-internal --avoid=errno --avoid=fchdir --avoid=fcntl --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=openat-die --avoid=openat-h --avoid=raise --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat filemode getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings mktime pselect pthread_sigmask readlink socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub utimens warnings
 
 
 MOSTLYCLEANFILES += core *.stackdump
@@ -150,6 +150,17 @@
 
 ## end   gnulib module dup2
 
+## begin gnulib module euidaccess
+
+if gl_GNULIB_ENABLED_euidaccess
+
+endif
+EXTRA_DIST += euidaccess.c
+
+EXTRA_libgnu_a_SOURCES += euidaccess.c
+
+## end   gnulib module euidaccess
+
 ## begin gnulib module execinfo
 
 BUILT_SOURCES += $(EXECINFO_H)
@@ -175,6 +186,50 @@
 
 ## end   gnulib module execinfo
 
+## begin gnulib module faccessat
+
+
+EXTRA_DIST += at-func.c faccessat.c
+
+EXTRA_libgnu_a_SOURCES += at-func.c faccessat.c
+
+## end   gnulib module faccessat
+
+## begin gnulib module fcntl-h
+
+BUILT_SOURCES += fcntl.h
+
+# We need the following in order to create <fcntl.h> when the system
+# doesn't have one that works with the given compiler.
+fcntl.h: fcntl.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) $(WARN_ON_USE_H)
+	$(AM_V_GEN)rm -f $@-t $@ && \
+	{ echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \
+	  sed -e 's|@''GUARD_PREFIX''@|GL|g' \
+	      -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \
+	      -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
+	      -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \
+	      -e 's|@''NEXT_FCNTL_H''@|$(NEXT_FCNTL_H)|g' \
+	      -e 's/@''GNULIB_FCNTL''@/$(GNULIB_FCNTL)/g' \
+	      -e 's/@''GNULIB_NONBLOCKING''@/$(GNULIB_NONBLOCKING)/g' \
+	      -e 's/@''GNULIB_OPEN''@/$(GNULIB_OPEN)/g' \
+	      -e 's/@''GNULIB_OPENAT''@/$(GNULIB_OPENAT)/g' \
+	      -e 's|@''HAVE_FCNTL''@|$(HAVE_FCNTL)|g' \
+	      -e 's|@''HAVE_OPENAT''@|$(HAVE_OPENAT)|g' \
+	      -e 's|@''REPLACE_FCNTL''@|$(REPLACE_FCNTL)|g' \
+	      -e 's|@''REPLACE_OPEN''@|$(REPLACE_OPEN)|g' \
+	      -e 's|@''REPLACE_OPENAT''@|$(REPLACE_OPENAT)|g' \
+	      -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \
+	      -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \
+	      -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \
+	      < $(srcdir)/fcntl.in.h; \
+	} > $@-t && \
+	mv $@-t $@
+MOSTLYCLEANFILES += fcntl.h fcntl.h-t
+
+EXTRA_DIST += fcntl.in.h
+
+## end   gnulib module fcntl-h
+
 ## begin gnulib module filemode
 
 libgnu_a_SOURCES += filemode.c
@@ -183,6 +238,17 @@
 
 ## end   gnulib module filemode
 
+## begin gnulib module getgroups
+
+if gl_GNULIB_ENABLED_getgroups
+
+endif
+EXTRA_DIST += getgroups.c
+
+EXTRA_libgnu_a_SOURCES += getgroups.c
+
+## end   gnulib module getgroups
+
 ## begin gnulib module getloadavg
 
 
@@ -242,6 +308,17 @@
 
 ## end   gnulib module gettimeofday
 
+## begin gnulib module group-member
+
+if gl_GNULIB_ENABLED_a9786850e999ae65a836a6041e8e5ed1
+
+endif
+EXTRA_DIST += group-member.c
+
+EXTRA_libgnu_a_SOURCES += group-member.c
+
+## end   gnulib module group-member
+
 ## begin gnulib module ignore-value
 
 
@@ -354,6 +431,15 @@
 
 ## end   gnulib module readlink
 
+## begin gnulib module root-uid
+
+if gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c
+
+endif
+EXTRA_DIST += root-uid.h
+
+## end   gnulib module root-uid
+
 ## begin gnulib module signal-h
 
 BUILT_SOURCES += signal.h
@@ -1312,6 +1398,15 @@
 
 ## end   gnulib module verify
 
+## begin gnulib module xalloc-oversized
+
+if gl_GNULIB_ENABLED_682e609604ccaac6be382e4ee3a4eaec
+
+endif
+EXTRA_DIST += xalloc-oversized.h
+
+## end   gnulib module xalloc-oversized
+
 
 mostlyclean-local: mostlyclean-generic
 	@for dir in '' $(MOSTLYCLEANDIRS); do \

=== added file 'lib/group-member.c'
--- lib/group-member.c	1970-01-01 00:00:00 +0000
+++ lib/group-member.c	2012-10-12 23:28:31 +0000
@@ -0,0 +1,119 @@
+/* group-member.c -- determine whether group id is in calling user's group list
+
+   Copyright (C) 1994, 1997-1998, 2003, 2005-2006, 2009-2012 Free Software
+   Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <unistd.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdlib.h>
+
+#include "xalloc-oversized.h"
+
+/* Most processes have no more than this many groups, and for these
+   processes we can avoid using malloc.  */
+enum { GROUPBUF_SIZE = 100 };
+
+struct group_info
+  {
+    gid_t *group;
+    gid_t groupbuf[GROUPBUF_SIZE];
+  };
+
+static void
+free_group_info (struct group_info const *g)
+{
+  if (g->group != g->groupbuf)
+    free (g->group);
+}
+
+static int
+get_group_info (struct group_info *gi)
+{
+  int n_groups = getgroups (GROUPBUF_SIZE, gi->groupbuf);
+  gi->group = gi->groupbuf;
+
+  if (n_groups < 0)
+    {
+      int n_group_slots = getgroups (0, NULL);
+      if (0 <= n_group_slots
+          && ! xalloc_oversized (n_group_slots, sizeof *gi->group))
+        {
+          gi->group = malloc (n_group_slots * sizeof *gi->group);
+          if (gi->group)
+            n_groups = getgroups (n_group_slots, gi->group);
+        }
+    }
+
+  /* In case of error, the user loses.  */
+  return n_groups;
+}
+
+/* Return non-zero if GID is one that we have in our groups list.
+   Note that the groups list is not guaranteed to contain the current
+   or effective group ID, so they should generally be checked
+   separately.  */
+
+int
+group_member (gid_t gid)
+{
+  int i;
+  int found;
+  struct group_info gi;
+  int n_groups = get_group_info (&gi);
+
+  /* Search through the list looking for GID. */
+  found = 0;
+  for (i = 0; i < n_groups; i++)
+    {
+      if (gid == gi.group[i])
+        {
+          found = 1;
+          break;
+        }
+    }
+
+  free_group_info (&gi);
+
+  return found;
+}
+
+#ifdef TEST
+
+char *program_name;
+
+int
+main (int argc, char **argv)
+{
+  int i;
+
+  program_name = argv[0];
+
+  for (i = 1; i < argc; i++)
+    {
+      gid_t gid;
+
+      gid = atoi (argv[i]);
+      printf ("%d: %s\n", gid, group_member (gid) ? "yes" : "no");
+    }
+  exit (0);
+}
+
+#endif /* TEST */

=== added file 'lib/root-uid.h'
--- lib/root-uid.h	1970-01-01 00:00:00 +0000
+++ lib/root-uid.h	2012-10-12 23:28:31 +0000
@@ -0,0 +1,30 @@
+/* The user ID that always has appropriate privileges in the POSIX sense.
+
+   Copyright 2012 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   Written by Paul Eggert.  */
+
+#ifndef ROOT_UID_H_
+#define ROOT_UID_H_
+
+/* The user ID that always has appropriate privileges in the POSIX sense.  */
+#ifdef __TANDEM
+# define ROOT_UID 65535
+#else
+# define ROOT_UID 0
+#endif
+
+#endif

=== added file 'lib/xalloc-oversized.h'
--- lib/xalloc-oversized.h	1970-01-01 00:00:00 +0000
+++ lib/xalloc-oversized.h	2012-10-12 23:28:31 +0000
@@ -0,0 +1,38 @@
+/* xalloc-oversized.h -- memory allocation size checking
+
+   Copyright (C) 1990-2000, 2003-2004, 2006-2012 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef XALLOC_OVERSIZED_H_
+# define XALLOC_OVERSIZED_H_
+
+# include <stddef.h>
+
+/* Return 1 if an array of N objects, each of size S, cannot exist due
+   to size arithmetic overflow.  S must be positive and N must be
+   nonnegative.  This is a macro, not a function, so that it
+   works correctly even when SIZE_MAX < N.
+
+   By gnulib convention, SIZE_MAX represents overflow in size
+   calculations, so the conservative dividend to use here is
+   SIZE_MAX - 1, since SIZE_MAX might represent an overflowed value.
+   However, malloc (SIZE_MAX) fails on all known hosts where
+   sizeof (ptrdiff_t) <= sizeof (size_t), so do not bother to test for
+   exactly-SIZE_MAX allocations on such hosts; this avoids a test and
+   branch when S is known to be 1.  */
+# define xalloc_oversized(n, s) \
+    ((size_t) (sizeof (ptrdiff_t) <= sizeof (size_t) ? -1 : -2) / (s) < (n))
+
+#endif /* !XALLOC_OVERSIZED_H_ */

=== added file 'm4/euidaccess.m4'
--- m4/euidaccess.m4	1970-01-01 00:00:00 +0000
+++ m4/euidaccess.m4	2012-10-12 23:28:31 +0000
@@ -0,0 +1,52 @@
+# euidaccess.m4 serial 14
+dnl Copyright (C) 2002-2012 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_NONREENTRANT_EUIDACCESS],
+[
+  AC_REQUIRE([gl_FUNC_EUIDACCESS])
+  AC_DEFINE([PREFER_NONREENTRANT_EUIDACCESS], [1],
+    [Define this if you prefer euidaccess to return the correct result
+     even if this would make it nonreentrant.  Define this only if your
+     entire application is safe even if the uid or gid might temporarily
+     change.  If your application uses signal handlers or threads it
+     is probably not safe.])
+])
+
+AC_DEFUN([gl_FUNC_EUIDACCESS],
+[
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+
+  dnl Persuade glibc <unistd.h> to declare euidaccess().
+  AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
+
+  AC_CHECK_FUNCS([euidaccess])
+  if test $ac_cv_func_euidaccess = no; then
+    HAVE_EUIDACCESS=0
+  fi
+])
+
+# Prerequisites of lib/euidaccess.c.
+AC_DEFUN([gl_PREREQ_EUIDACCESS], [
+  dnl Prefer POSIX faccessat over non-standard euidaccess.
+  AC_CHECK_FUNCS_ONCE([faccessat])
+  dnl Try various other non-standard fallbacks.
+  AC_CHECK_HEADERS_ONCE([libgen.h])
+  AC_CHECK_DECLS_ONCE([setregid])
+  AC_REQUIRE([AC_FUNC_GETGROUPS])
+
+  # Solaris 9 and 10 need -lgen to get the eaccess function.
+  # Save and restore LIBS so -lgen isn't added to it.  Otherwise, *all*
+  # programs in the package would end up linked with that potentially-shared
+  # library, inducing unnecessary run-time overhead.
+  LIB_EACCESS=
+  AC_SUBST([LIB_EACCESS])
+  gl_saved_libs=$LIBS
+    AC_SEARCH_LIBS([eaccess], [gen],
+                   [test "$ac_cv_search_eaccess" = "none required" ||
+                    LIB_EACCESS=$ac_cv_search_eaccess])
+    AC_CHECK_FUNCS([eaccess])
+  LIBS=$gl_saved_libs
+])

=== added file 'm4/faccessat.m4'
--- m4/faccessat.m4	1970-01-01 00:00:00 +0000
+++ m4/faccessat.m4	2012-10-15 06:05:46 +0000
@@ -0,0 +1,28 @@
+# serial 6
+# See if we need to provide faccessat replacement.
+
+dnl Copyright (C) 2009-2012 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+# Written by Eric Blake.
+
+AC_DEFUN([gl_FUNC_FACCESSAT],
+[
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+
+  dnl Persuade glibc <unistd.h> to declare faccessat().
+  AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+
+  AC_CHECK_FUNCS_ONCE([faccessat])
+  if test $ac_cv_func_faccessat = no; then
+    HAVE_FACCESSAT=0
+  fi
+])
+
+# Prerequisites of lib/faccessat.m4.
+AC_DEFUN([gl_PREREQ_FACCESSAT],
+[
+  AC_CHECK_FUNCS([access])
+])

=== added file 'm4/fcntl_h.m4'
--- m4/fcntl_h.m4	1970-01-01 00:00:00 +0000
+++ m4/fcntl_h.m4	2012-10-15 06:05:46 +0000
@@ -0,0 +1,50 @@
+# serial 15
+# Configure fcntl.h.
+dnl Copyright (C) 2006-2007, 2009-2012 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl Written by Paul Eggert.
+
+AC_DEFUN([gl_FCNTL_H],
+[
+  AC_REQUIRE([gl_FCNTL_H_DEFAULTS])
+  AC_REQUIRE([gl_FCNTL_O_FLAGS])
+  gl_NEXT_HEADERS([fcntl.h])
+
+  dnl Ensure the type pid_t gets defined.
+  AC_REQUIRE([AC_TYPE_PID_T])
+
+  dnl Ensure the type mode_t gets defined.
+  AC_REQUIRE([AC_TYPE_MODE_T])
+
+  dnl Check for declarations of anything we want to poison if the
+  dnl corresponding gnulib module is not in use, if it is not common
+  dnl enough to be declared everywhere.
+  gl_WARN_ON_USE_PREPARE([[#include <fcntl.h>
+    ]], [fcntl openat])
+])
+
+AC_DEFUN([gl_FCNTL_MODULE_INDICATOR],
+[
+  dnl Use AC_REQUIRE here, so that the default settings are expanded once only.
+  AC_REQUIRE([gl_FCNTL_H_DEFAULTS])
+  gl_MODULE_INDICATOR_SET_VARIABLE([$1])
+  dnl Define it also as a C macro, for the benefit of the unit tests.
+  gl_MODULE_INDICATOR_FOR_TESTS([$1])
+])
+
+AC_DEFUN([gl_FCNTL_H_DEFAULTS],
+[
+  GNULIB_FCNTL=0;        AC_SUBST([GNULIB_FCNTL])
+  GNULIB_NONBLOCKING=0;  AC_SUBST([GNULIB_NONBLOCKING])
+  GNULIB_OPEN=0;         AC_SUBST([GNULIB_OPEN])
+  GNULIB_OPENAT=0;       AC_SUBST([GNULIB_OPENAT])
+  dnl Assume proper GNU behavior unless another module says otherwise.
+  HAVE_FCNTL=1;          AC_SUBST([HAVE_FCNTL])
+  HAVE_OPENAT=1;         AC_SUBST([HAVE_OPENAT])
+  REPLACE_FCNTL=0;       AC_SUBST([REPLACE_FCNTL])
+  REPLACE_OPEN=0;        AC_SUBST([REPLACE_OPEN])
+  REPLACE_OPENAT=0;      AC_SUBST([REPLACE_OPENAT])
+])

=== added file 'm4/getgroups.m4'
--- m4/getgroups.m4	1970-01-01 00:00:00 +0000
+++ m4/getgroups.m4	2012-10-12 23:28:31 +0000
@@ -0,0 +1,107 @@
+# serial 18
+
+dnl From Jim Meyering.
+dnl A wrapper around AC_FUNC_GETGROUPS.
+
+# Copyright (C) 1996-1997, 1999-2004, 2008-2012 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+m4_version_prereq([2.70], [] ,[
+
+# This is taken from the following Autoconf patch:
+# http://git.savannah.gnu.org/gitweb/?p=autoconf.git;a=commitdiff;h=7fbb553727ed7e0e689a17594b58559ecf3ea6e9
+AC_DEFUN([AC_FUNC_GETGROUPS],
+[
+  AC_REQUIRE([AC_TYPE_GETGROUPS])dnl
+  AC_REQUIRE([AC_TYPE_SIZE_T])dnl
+  AC_REQUIRE([AC_CANONICAL_HOST])dnl for cross-compiles
+  AC_CHECK_FUNC([getgroups])
+
+  # If we don't yet have getgroups, see if it's in -lbsd.
+  # This is reported to be necessary on an ITOS 3000WS running SEIUX 3.1.
+  ac_save_LIBS=$LIBS
+  if test $ac_cv_func_getgroups = no; then
+    AC_CHECK_LIB(bsd, getgroups, [GETGROUPS_LIB=-lbsd])
+  fi
+
+  # Run the program to test the functionality of the system-supplied
+  # getgroups function only if there is such a function.
+  if test $ac_cv_func_getgroups = yes; then
+    AC_CACHE_CHECK([for working getgroups], [ac_cv_func_getgroups_works],
+      [AC_RUN_IFELSE(
+         [AC_LANG_PROGRAM(
+            [AC_INCLUDES_DEFAULT],
+            [[/* On Ultrix 4.3, getgroups (0, 0) always fails.  */
+              return getgroups (0, 0) == -1;]])
+         ],
+         [ac_cv_func_getgroups_works=yes],
+         [ac_cv_func_getgroups_works=no],
+         [case "$host_os" in # ((
+                    # Guess yes on glibc systems.
+            *-gnu*) ac_cv_func_getgroups_works="guessing yes" ;;
+                    # If we don't know, assume the worst.
+            *)      ac_cv_func_getgroups_works="guessing no" ;;
+          esac
+         ])
+      ])
+  else
+    ac_cv_func_getgroups_works=no
+  fi
+  case "$ac_cv_func_getgroups_works" in
+    *yes)
+      AC_DEFINE([HAVE_GETGROUPS], [1],
+        [Define to 1 if your system has a working `getgroups' function.])
+      ;;
+  esac
+  LIBS=$ac_save_LIBS
+])# AC_FUNC_GETGROUPS
+
+])
+
+AC_DEFUN([gl_FUNC_GETGROUPS],
+[
+  AC_REQUIRE([AC_TYPE_GETGROUPS])
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+
+  AC_FUNC_GETGROUPS
+  if test $ac_cv_func_getgroups != yes; then
+    HAVE_GETGROUPS=0
+  else
+    if test "$ac_cv_type_getgroups" != gid_t \
+       || { case "$ac_cv_func_getgroups_works" in
+              *yes) false;;
+              *) true;;
+            esac
+          }; then
+      REPLACE_GETGROUPS=1
+      AC_DEFINE([GETGROUPS_ZERO_BUG], [1], [Define this to 1 if
+        getgroups(0,NULL) does not return the number of groups.])
+    else
+      dnl Detect FreeBSD bug; POSIX requires getgroups(-1,ptr) to fail.
+      AC_CACHE_CHECK([whether getgroups handles negative values],
+        [gl_cv_func_getgroups_works],
+        [AC_RUN_IFELSE([AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT],
+          [[int size = getgroups (0, 0);
+            gid_t *list = malloc (size * sizeof *list);
+            return getgroups (-1, list) != -1;]])],
+          [gl_cv_func_getgroups_works=yes],
+          [gl_cv_func_getgroups_works=no],
+          [case "$host_os" in
+                     # Guess yes on glibc systems.
+             *-gnu*) gl_cv_func_getgroups_works="guessing yes" ;;
+                     # If we don't know, assume the worst.
+             *)      gl_cv_func_getgroups_works="guessing no" ;;
+           esac
+          ])])
+      case "$gl_cv_func_getgroups_works" in
+        *yes) ;;
+        *) REPLACE_GETGROUPS=1 ;;
+      esac
+    fi
+  fi
+  test -n "$GETGROUPS_LIB" && LIBS="$GETGROUPS_LIB $LIBS"
+])

=== modified file 'm4/gnulib-comp.m4'
--- m4/gnulib-comp.m4	2012-09-27 01:06:23 +0000
+++ m4/gnulib-comp.m4	2012-10-15 06:05:46 +0000
@@ -53,17 +53,22 @@
   # Code from module dtotimespec:
   # Code from module dup2:
   # Code from module environ:
+  # Code from module euidaccess:
   # Code from module execinfo:
   # Code from module extensions:
   AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
   # Code from module extern-inline:
+  # Code from module faccessat:
+  # Code from module fcntl-h:
   # Code from module filemode:
+  # Code from module getgroups:
   # Code from module getloadavg:
   # Code from module getopt-gnu:
   # Code from module getopt-posix:
   # Code from module gettext-h:
   # Code from module gettime:
   # Code from module gettimeofday:
+  # Code from module group-member:
   # Code from module ignore-value:
   # Code from module include_next:
   # Code from module intprops:
@@ -79,6 +84,7 @@
   # Code from module pselect:
   # Code from module pthread_sigmask:
   # Code from module readlink:
+  # Code from module root-uid:
   # Code from module signal-h:
   # Code from module snippet/_Noreturn:
   # Code from module snippet/arg-nonnull:
@@ -120,6 +126,7 @@
   # Code from module utimens:
   # Code from module verify:
   # Code from module warnings:
+  # Code from module xalloc-oversized:
 ])
 
 # This macro should be invoked from ./configure.ac, in the section
@@ -156,6 +163,14 @@
   gl_UNISTD_MODULE_INDICATOR([environ])
   gl_EXECINFO_H
   AC_REQUIRE([gl_EXTERN_INLINE])
+  gl_FUNC_FACCESSAT
+  if test $HAVE_FACCESSAT = 0; then
+    AC_LIBOBJ([faccessat])
+    gl_PREREQ_FACCESSAT
+  fi
+  gl_MODULE_INDICATOR([faccessat])
+  gl_UNISTD_MODULE_INDICATOR([faccessat])
+  gl_FCNTL_H
   gl_FILEMODE
   gl_GETLOADAVG
   if test $HAVE_GETLOADAVG = 0; then
@@ -269,18 +284,53 @@
   gl_UNISTD_H
   gl_UTIMENS
   gl_gnulib_enabled_dosname=false
+  gl_gnulib_enabled_euidaccess=false
+  gl_gnulib_enabled_getgroups=false
   gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=false
+  gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1=false
   gl_gnulib_enabled_pathmax=false
+  gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=false
   gl_gnulib_enabled_stat=false
   gl_gnulib_enabled_strtoll=false
   gl_gnulib_enabled_strtoull=false
   gl_gnulib_enabled_verify=false
+  gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=false
   func_gl_gnulib_m4code_dosname ()
   {
     if ! $gl_gnulib_enabled_dosname; then
       gl_gnulib_enabled_dosname=true
     fi
   }
+  func_gl_gnulib_m4code_euidaccess ()
+  {
+    if ! $gl_gnulib_enabled_euidaccess; then
+      gl_FUNC_EUIDACCESS
+      if test $HAVE_EUIDACCESS = 0; then
+        AC_LIBOBJ([euidaccess])
+        gl_PREREQ_EUIDACCESS
+      fi
+      gl_UNISTD_MODULE_INDICATOR([euidaccess])
+      gl_gnulib_enabled_euidaccess=true
+      if test $HAVE_EUIDACCESS = 0; then
+        func_gl_gnulib_m4code_a9786850e999ae65a836a6041e8e5ed1
+      fi
+      func_gl_gnulib_m4code_6099e9737f757db36c47fa9d9f02e88c
+      if test $HAVE_EUIDACCESS = 0; then
+        func_gl_gnulib_m4code_stat
+      fi
+    fi
+  }
+  func_gl_gnulib_m4code_getgroups ()
+  {
+    if ! $gl_gnulib_enabled_getgroups; then
+      gl_FUNC_GETGROUPS
+      if test $HAVE_GETGROUPS = 0 || test $REPLACE_GETGROUPS = 1; then
+        AC_LIBOBJ([getgroups])
+      fi
+      gl_UNISTD_MODULE_INDICATOR([getgroups])
+      gl_gnulib_enabled_getgroups=true
+    fi
+  }
   func_gl_gnulib_m4code_be453cec5eecf5731a274f2de7f2db36 ()
   {
     if ! $gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36; then
@@ -289,6 +339,24 @@
       gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=true
     fi
   }
+  func_gl_gnulib_m4code_a9786850e999ae65a836a6041e8e5ed1 ()
+  {
+    if ! $gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1; then
+      gl_FUNC_GROUP_MEMBER
+      if test $HAVE_GROUP_MEMBER = 0; then
+        AC_LIBOBJ([group-member])
+        gl_PREREQ_GROUP_MEMBER
+      fi
+      gl_UNISTD_MODULE_INDICATOR([group-member])
+      gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1=true
+      if test $HAVE_GROUP_MEMBER = 0; then
+        func_gl_gnulib_m4code_getgroups
+      fi
+      if test $HAVE_GROUP_MEMBER = 0; then
+        func_gl_gnulib_m4code_682e609604ccaac6be382e4ee3a4eaec
+      fi
+    fi
+  }
   func_gl_gnulib_m4code_pathmax ()
   {
     if ! $gl_gnulib_enabled_pathmax; then
@@ -296,6 +364,12 @@
       gl_gnulib_enabled_pathmax=true
     fi
   }
+  func_gl_gnulib_m4code_6099e9737f757db36c47fa9d9f02e88c ()
+  {
+    if ! $gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c; then
+      gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=true
+    fi
+  }
   func_gl_gnulib_m4code_stat ()
   {
     if ! $gl_gnulib_enabled_stat; then
@@ -347,6 +421,18 @@
       gl_gnulib_enabled_verify=true
     fi
   }
+  func_gl_gnulib_m4code_682e609604ccaac6be382e4ee3a4eaec ()
+  {
+    if ! $gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec; then
+      gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=true
+    fi
+  }
+  if test $HAVE_FACCESSAT = 0; then
+    func_gl_gnulib_m4code_dosname
+  fi
+  if test $HAVE_FACCESSAT = 0; then
+    func_gl_gnulib_m4code_euidaccess
+  fi
   if test $REPLACE_GETOPT = 1; then
     func_gl_gnulib_m4code_be453cec5eecf5731a274f2de7f2db36
   fi
@@ -373,12 +459,17 @@
   fi
   m4_pattern_allow([^gl_GNULIB_ENABLED_])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_dosname], [$gl_gnulib_enabled_dosname])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_euidaccess], [$gl_gnulib_enabled_euidaccess])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_getgroups], [$gl_gnulib_enabled_getgroups])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_be453cec5eecf5731a274f2de7f2db36], [$gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_a9786850e999ae65a836a6041e8e5ed1], [$gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_pathmax], [$gl_gnulib_enabled_pathmax])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c], [$gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_stat], [$gl_gnulib_enabled_stat])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_strtoll], [$gl_gnulib_enabled_strtoll])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_strtoull], [$gl_gnulib_enabled_strtoull])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_verify], [$gl_gnulib_enabled_verify])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_682e609604ccaac6be382e4ee3a4eaec], [$gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec])
   # End of code from modules
   m4_ifval(gl_LIBSOURCES_LIST, [
     m4_syscmd([test ! -d ]m4_defn([gl_LIBSOURCES_DIR])[ ||
@@ -527,6 +618,7 @@
   lib/alloca.in.h
   lib/allocator.c
   lib/allocator.h
+  lib/at-func.c
   lib/c-ctype.c
   lib/c-ctype.h
   lib/c-strcase.h
@@ -538,12 +630,16 @@
   lib/dtoastr.c
   lib/dtotimespec.c
   lib/dup2.c
+  lib/euidaccess.c
   lib/execinfo.c
   lib/execinfo.in.h
+  lib/faccessat.c
+  lib/fcntl.in.h
   lib/filemode.c
   lib/filemode.h
   lib/ftoastr.c
   lib/ftoastr.h
+  lib/getgroups.c
   lib/getloadavg.c
   lib/getopt.c
   lib/getopt.in.h
@@ -552,6 +648,7 @@
   lib/gettext.h
   lib/gettime.c
   lib/gettimeofday.c
+  lib/group-member.c
   lib/ignore-value.h
   lib/intprops.h
   lib/inttypes.in.h
@@ -564,6 +661,7 @@
   lib/pselect.c
   lib/pthread_sigmask.c
   lib/readlink.c
+  lib/root-uid.h
   lib/sha1.c
   lib/sha1.h
   lib/sha256.c
@@ -605,21 +703,28 @@
   lib/utimens.c
   lib/utimens.h
   lib/verify.h
+  lib/xalloc-oversized.h
   m4/00gnulib.m4
   m4/alloca.m4
   m4/c-strtod.m4
   m4/clock_time.m4
   m4/dup2.m4
   m4/environ.m4
+  m4/euidaccess.m4
   m4/execinfo.m4
   m4/extensions.m4
   m4/extern-inline.m4
+  m4/faccessat.m4
+  m4/fcntl-o.m4
+  m4/fcntl_h.m4
   m4/filemode.m4
+  m4/getgroups.m4
   m4/getloadavg.m4
   m4/getopt.m4
   m4/gettime.m4
   m4/gettimeofday.m4
   m4/gnulib-common.m4
+  m4/group-member.m4
   m4/include_next.m4
   m4/inttypes.m4
   m4/largefile.m4

=== added file 'm4/group-member.m4'
--- m4/group-member.m4	1970-01-01 00:00:00 +0000
+++ m4/group-member.m4	2012-10-12 23:28:31 +0000
@@ -0,0 +1,29 @@
+# serial 14
+
+# Copyright (C) 1999-2001, 2003-2007, 2009-2012 Free Software Foundation, Inc.
+
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+dnl Written by Jim Meyering
+
+AC_DEFUN([gl_FUNC_GROUP_MEMBER],
+[
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+
+  dnl Persuade glibc <unistd.h> to declare group_member().
+  AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
+
+  dnl Do this replacement check manually because I want the hyphen
+  dnl (not the underscore) in the filename.
+  AC_CHECK_FUNC([group_member], , [
+    HAVE_GROUP_MEMBER=0
+  ])
+])
+
+# Prerequisites of lib/group-member.c.
+AC_DEFUN([gl_PREREQ_GROUP_MEMBER],
+[
+  AC_REQUIRE([AC_FUNC_GETGROUPS])
+])

=== modified file 'nt/ChangeLog'
--- nt/ChangeLog	2012-10-08 14:14:22 +0000
+++ nt/ChangeLog	2012-10-15 06:05:46 +0000
@@ -1,3 +1,10 @@
+2012-10-15  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use faccessat, not access, when checking file permissions (Bug#12632).
+	* inc/ms-w32.h (AT_FDCWD, AT_EACCESS): New symbols.
+	(access): Remove.
+	(faccessat): New macro.
+
 2012-10-08  Juanma Barranquero  <lekktu@gmail.com>
 
 	* config.nt: Sync with autogen/config.in.

=== modified file 'nt/inc/ms-w32.h'
--- nt/inc/ms-w32.h	2012-09-30 21:36:42 +0000
+++ nt/inc/ms-w32.h	2012-10-15 06:05:46 +0000
@@ -139,6 +139,10 @@
 #define MAXPATHLEN      _MAX_PATH
 #endif
 
+/* Use values compatible with gnulib, as there's no reason to differ.  */
+#define AT_FDCWD (-3041965)
+#define AT_EACCESS 4
+
 #ifdef HAVE_NTGUI
 #define HAVE_WINDOW_SYSTEM 1
 #define HAVE_MENUS 1
@@ -160,8 +164,6 @@
 #endif
 
 /* Calls that are emulated or shadowed.  */
-#undef access
-#define access  sys_access
 #undef chdir
 #define chdir   sys_chdir
 #undef chmod
@@ -176,6 +178,7 @@
 #define dup     sys_dup
 #undef dup2
 #define dup2    sys_dup2
+#define faccessat  sys_faccessat
 #define fopen   sys_fopen
 #define link    sys_link
 #define localtime sys_localtime

=== modified file 'src/ChangeLog'
--- src/ChangeLog	2012-10-15 01:38:07 +0000
+++ src/ChangeLog	2012-10-15 06:07:05 +0000
@@ -1,3 +1,30 @@
+2012-10-15  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use faccessat, not access, when checking file permissions (Bug#12632).
+	* Makefile.in (LIB_EACCESS): New macro.
+	(LIBES): Use it.
+	* callproc.c (init_callproc):
+	* charset.c (init_charset):
+	* fileio.c (check_existing, check_executable):
+	* lread.c (openp, load_path_check):
+	* process.c (allocate_pty):
+	* xrdb.c (file_p):
+	Use faccessat, not access or euidaccess.  Use symbolic names
+	instead of integers for the flags, as they're portable now.
+	* charset.c, xrdb.c: Include <fcntl.h>, for the new flags used.
+	* fileio.c (Ffile_readable_p):
+	Use faccessat, not stat + open + close.
+	(file_directory_p): New function, which uses 'stat' on most places
+	but 'access' (for efficiency) if WINDOWSNT.
+	* fileio.c (Ffile_directory_p, Fset_file_times):
+	* xrdb.c (file_p): Use file_directory_p.
+	* lisp.h (file_directory_p): New decl.
+	* lread.c (openp): When opening a file, use fstat rather than
+	stat, as that avoids a permissions race.  When not opening a file,
+	use file_directory_p rather than stat.
+	* w32.c (sys_faccessat): Rename from sys_access and switch to
+	faccessat's API.
+
 2012-10-15  Daniel Colascione  <dancol@dancol.org>
 
 	* dbusbind.c: Fix cygw32 build break when compiling with dbus

=== modified file 'src/Makefile.in'
--- src/Makefile.in	2012-10-08 12:53:18 +0000
+++ src/Makefile.in	2012-10-13 01:49:41 +0000
@@ -150,6 +150,7 @@
 M17N_FLT_LIBS = @M17N_FLT_LIBS@
 
 LIB_CLOCK_GETTIME=@LIB_CLOCK_GETTIME@
+LIB_EACCESS=@LIB_EACCESS@
 LIB_TIMER_TIME=@LIB_TIMER_TIME@
 
 DBUS_CFLAGS = @DBUS_CFLAGS@
@@ -392,7 +393,7 @@
 LIBES = $(LIBS) $(W32_LIBS) $(LIBX_BASE) $(LIBIMAGE) \
    $(LIBX_OTHER) $(LIBSOUND) \
    $(RSVG_LIBS) $(IMAGEMAGICK_LIBS) $(LIB_CLOCK_GETTIME) \
-   $(LIB_TIMER_TIME) $(DBUS_LIBS) \
+   $(LIB_EACCESS) $(LIB_TIMER_TIME) $(DBUS_LIBS) \
    $(LIB_EXECINFO) \
    $(LIBXML2_LIBS) $(LIBGPM) $(LIBRESOLV) $(LIBS_SYSTEM) \
    $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \

=== modified file 'src/callproc.c'
--- src/callproc.c	2012-09-23 22:25:22 +0000
+++ src/callproc.c	2012-10-15 06:05:46 +0000
@@ -1597,13 +1597,13 @@
 #endif
     {
       tempdir = Fdirectory_file_name (Vexec_directory);
-      if (access (SSDATA (tempdir), 0) < 0)
+      if (faccessat (AT_FDCWD, SSDATA (tempdir), F_OK, AT_EACCESS) != 0)
 	dir_warning ("Warning: arch-dependent data dir (%s) does not exist.\n",
 		     Vexec_directory);
     }
 
   tempdir = Fdirectory_file_name (Vdata_directory);
-  if (access (SSDATA (tempdir), 0) < 0)
+  if (faccessat (AT_FDCWD, SSDATA (tempdir), F_OK, AT_EACCESS) != 0)
     dir_warning ("Warning: arch-independent data dir (%s) does not exist.\n",
 		 Vdata_directory);
 

=== modified file 'src/charset.c'
--- src/charset.c	2012-10-01 06:36:54 +0000
+++ src/charset.c	2012-10-15 06:05:46 +0000
@@ -28,6 +28,7 @@
 
 #define CHARSET_INLINE EXTERN_INLINE
 
+#include <fcntl.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <limits.h>
@@ -2293,7 +2294,7 @@
 {
   Lisp_Object tempdir;
   tempdir = Fexpand_file_name (build_string ("charsets"), Vdata_directory);
-  if (access (SSDATA (tempdir), 0) < 0)
+  if (faccessat (AT_FDCWD, SSDATA (tempdir), F_OK, AT_EACCESS) != 0)
     {
       /* This used to be non-fatal (dir_warning), but it should not
          happen, and if it does sooner or later it will cause some

=== modified file 'src/conf_post.h'
--- src/conf_post.h	2012-10-08 22:14:39 +0000
+++ src/conf_post.h	2012-10-15 06:05:46 +0000
@@ -169,6 +169,10 @@
 #endif
 #endif
 
+/* Tell gnulib to omit support for openat-related functions having a
+   first argument other than AT_FDCWD.  */
+#define GNULIB_SUPPORT_ONLY_AT_FDCWD
+
 #include <string.h>
 #include <stdlib.h>
 

=== modified file 'src/fileio.c'
--- src/fileio.c	2012-10-13 08:55:26 +0000
+++ src/fileio.c	2012-10-15 06:05:46 +0000
@@ -2424,15 +2424,7 @@
 bool
 check_existing (const char *filename)
 {
-#ifdef DOS_NT
-  /* The full emulation of Posix 'stat' is too expensive on
-     DOS/Windows, when all we want to know is whether the file exists.
-     So we use 'access' instead, which is much more lightweight.  */
-  return (access (filename, F_OK) >= 0);
-#else
-  struct stat st;
-  return (stat (filename, &st) >= 0);
-#endif
+  return faccessat (AT_FDCWD, filename, F_OK, AT_EACCESS) == 0;
 }
 
 /* Return true if file FILENAME exists and can be executed.  */
@@ -2440,21 +2432,7 @@
 static bool
 check_executable (char *filename)
 {
-#ifdef DOS_NT
-  struct stat st;
-  if (stat (filename, &st) < 0)
-    return 0;
-  return ((st.st_mode & S_IEXEC) != 0);
-#else /* not DOS_NT */
-#ifdef HAVE_EUIDACCESS
-  return (euidaccess (filename, 1) >= 0);
-#else
-  /* Access isn't quite right because it uses the real uid
-     and we really want to test with the effective uid.
-     But Unix doesn't give us a right way to do it.  */
-  return (access (filename, 1) >= 0);
-#endif
-#endif /* not DOS_NT */
+  return faccessat (AT_FDCWD, filename, X_OK, AT_EACCESS) == 0;
 }
 
 /* Return true if file FILENAME exists and can be written.  */
@@ -2463,15 +2441,16 @@
 check_writable (const char *filename)
 {
 #ifdef MSDOS
+  /* FIXME: an faccessat implementation should be added to the
+     DOS/Windows ports and this #ifdef branch should be removed.  */
   struct stat st;
   if (stat (filename, &st) < 0)
     return 0;
   return (st.st_mode & S_IWRITE || S_ISDIR (st.st_mode));
 #else /* not MSDOS */
-#ifdef HAVE_EUIDACCESS
-  bool res = (euidaccess (filename, 2) >= 0);
+  bool res = faccessat (AT_FDCWD, filename, W_OK, AT_EACCESS) == 0;
 #ifdef CYGWIN
-  /* euidaccess may have returned failure because Cygwin couldn't
+  /* faccessat may have returned failure because Cygwin couldn't
      determine the file's UID or GID; if so, we return success. */
   if (!res)
     {
@@ -2482,14 +2461,6 @@
     }
 #endif /* CYGWIN */
   return res;
-#else /* not HAVE_EUIDACCESS */
-  /* Access isn't quite right because it uses the real uid
-     and we really want to test with the effective uid.
-     But Unix doesn't give us a right way to do it.
-     Opening with O_WRONLY could work for an ordinary file,
-     but would lose for directories.  */
-  return (access (filename, 2) >= 0);
-#endif /* not HAVE_EUIDACCESS */
 #endif /* not MSDOS */
 }
 
@@ -2546,9 +2517,6 @@
 {
   Lisp_Object absname;
   Lisp_Object handler;
-  int desc;
-  int flags;
-  struct stat statbuf;
 
   CHECK_STRING (filename);
   absname = Fexpand_file_name (filename, Qnil);
@@ -2560,31 +2528,8 @@
     return call2 (handler, Qfile_readable_p, absname);
 
   absname = ENCODE_FILE (absname);
-
-#if defined (DOS_NT) || defined (macintosh)
-  /* Under MS-DOS, Windows, and Macintosh, open does not work for
-     directories.  */
-  if (access (SDATA (absname), 0) == 0)
-    return Qt;
-  return Qnil;
-#else /* not DOS_NT and not macintosh */
-  flags = O_RDONLY;
-#ifdef O_NONBLOCK
-  /* Opening a fifo without O_NONBLOCK can wait.
-     We don't want to wait.  But we don't want to mess wth O_NONBLOCK
-     except in the case of a fifo, on a system which handles it.  */
-  desc = stat (SSDATA (absname), &statbuf);
-  if (desc < 0)
-    return Qnil;
-  if (S_ISFIFO (statbuf.st_mode))
-    flags |= O_NONBLOCK;
-#endif
-  desc = emacs_open (SSDATA (absname), flags, 0);
-  if (desc < 0)
-    return Qnil;
-  emacs_close (desc);
-  return Qt;
-#endif /* not DOS_NT and not macintosh */
+  return (faccessat (AT_FDCWD, SSDATA (absname), R_OK, AT_EACCESS) == 0
+	  ? Qt : Qnil);
 }
 
 /* Having this before file-symlink-p mysteriously caused it to be forgotten
@@ -2621,7 +2566,7 @@
   /* The read-only attribute of the parent directory doesn't affect
      whether a file or directory can be created within it.  Some day we
      should check ACLs though, which do affect this.  */
-  return (access (SDATA (dir), D_OK) < 0) ? Qnil : Qt;
+  return file_directory_p (SDATA (dir)) ? Qt : Qnil;
 #else
   return (check_writable (!NILP (dir) ? SSDATA (dir) : "")
 	  ? Qt : Qnil);
@@ -2702,8 +2647,7 @@
 See `file-symlink-p' to distinguish symlinks.  */)
   (Lisp_Object filename)
 {
-  register Lisp_Object absname;
-  struct stat st;
+  Lisp_Object absname;
   Lisp_Object handler;
 
   absname = expand_and_dir_to_file (filename, BVAR (current_buffer, directory));
@@ -2716,9 +2660,20 @@
 
   absname = ENCODE_FILE (absname);
 
-  if (stat (SSDATA (absname), &st) < 0)
-    return Qnil;
-  return S_ISDIR (st.st_mode) ? Qt : Qnil;
+  return file_directory_p (SSDATA (absname)) ? Qt : Qnil;
+}
+
+/* Return true if FILE is a directory or a symlink to a directory.  */
+bool
+file_directory_p (char const *file)
+{
+#ifdef WINDOWSNT
+  /* This is cheaper than 'stat'.  */
+  return faccessat (AT_FDCWD, file, D_OK, AT_EACCESS) == 0;
+#else
+  struct stat st;
+  return stat (file, &st) == 0 && S_ISDIR (st.st_mode);
+#endif
 }
 
 DEFUN ("file-accessible-directory-p", Ffile_accessible_directory_p,
@@ -3043,10 +2998,8 @@
     if (set_file_times (-1, SSDATA (encoded_absname), t, t))
       {
 #ifdef MSDOS
-        struct stat st;
-
         /* Setting times on a directory always fails.  */
-        if (stat (SSDATA (encoded_absname), &st) == 0 && S_ISDIR (st.st_mode))
+        if (file_directory_p (SSDATA (encoded_absname)))
           return Qnil;
 #endif
         report_file_error ("Setting file times", Fcons (absname, Qnil));

=== modified file 'src/lisp.h'
--- src/lisp.h	2012-10-12 15:19:54 +0000
+++ src/lisp.h	2012-10-14 06:00:40 +0000
@@ -3179,6 +3179,7 @@
 extern Lisp_Object restore_point_unwind (Lisp_Object);
 extern _Noreturn void report_file_error (const char *, Lisp_Object);
 extern void internal_delete_file (Lisp_Object);
+extern bool file_directory_p (const char *);
 extern void syms_of_fileio (void);
 extern Lisp_Object make_temp_name (Lisp_Object, bool);
 extern Lisp_Object Qdelete_file;

=== modified file 'src/lread.c'
--- src/lread.c	2012-10-12 15:19:54 +0000
+++ src/lread.c	2012-10-15 06:05:46 +0000
@@ -1404,7 +1404,7 @@
 If SUFFIXES is non-nil, it should be a list of suffixes to append to
 file name when searching.
 If non-nil, PREDICATE is used instead of `file-readable-p'.
-PREDICATE can also be an integer to pass to the access(2) function,
+PREDICATE can also be an integer to pass to the faccessat(2) function,
 in which case file-name-handlers are ignored.
 This function will normally skip directories, so if you want it to find
 directories, make sure the PREDICATE function returns `dir-ok' for them.  */)
@@ -1442,7 +1442,6 @@
 int
 openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes, Lisp_Object *storeptr, Lisp_Object predicate)
 {
-  int fd;
   ptrdiff_t fn_size = 100;
   char buf[100];
   char *fn = buf;
@@ -1497,7 +1496,6 @@
 	{
 	  ptrdiff_t fnlen, lsuffix = SBYTES (XCAR (tail));
 	  Lisp_Object handler;
-	  bool exists;
 
 	  /* Concatenate path element/specified name with the suffix.
 	     If the directory starts with /:, remove that.  */
@@ -1521,6 +1519,7 @@
 	  handler = Ffind_file_name_handler (string, Qfile_exists_p);
 	  if ((!NILP (handler) || !NILP (predicate)) && !NATNUMP (predicate))
             {
+	      bool exists;
 	      if (NILP (predicate))
 		exists = !NILP (Ffile_readable_p (string));
 	      else
@@ -1542,37 +1541,40 @@
 	    }
 	  else
 	    {
-#ifndef WINDOWSNT
-	      struct stat st;
-#endif
+	      int fd;
 	      const char *pfn;
 
 	      encoded_fn = ENCODE_FILE (string);
 	      pfn = SSDATA (encoded_fn);
-#ifdef WINDOWSNT
-	      exists = access (pfn, F_OK) == 0 && access (pfn, D_OK) < 0;
-#else
-	      exists = (stat (pfn, &st) == 0 && ! S_ISDIR (st.st_mode));
-#endif
-	      if (exists)
+
+	      /* Check that we can access or open it.  */
+	      if (NATNUMP (predicate))
+		fd = (((XFASTINT (predicate) & ~INT_MAX) == 0
+		       && (faccessat (AT_FDCWD, pfn, XFASTINT (predicate),
+				      AT_EACCESS)
+			   == 0)
+		       && ! file_directory_p (pfn))
+		      ? 1 : -1);
+	      else
 		{
-		  /* Check that we can access or open it.  */
-		  if (NATNUMP (predicate))
-		    fd = (((XFASTINT (predicate) & ~INT_MAX) == 0
-			   && access (pfn, XFASTINT (predicate)) == 0)
-			  ? 1 : -1);
-		  else
-		    fd = emacs_open (pfn, O_RDONLY, 0);
-
-		  if (fd >= 0)
+		  struct stat st;
+		  fd = emacs_open (pfn, O_RDONLY, 0);
+		  if (0 <= fd
+		      && (fstat (fd, &st) != 0 || S_ISDIR (st.st_mode)))
 		    {
-		      /* We succeeded; return this descriptor and filename.  */
-		      if (storeptr)
-			*storeptr = string;
-		      UNGCPRO;
-		      return fd;
+		      emacs_close (fd);
+		      fd = -1;
 		    }
 		}
+
+	      if (fd >= 0)
+		{
+		  /* We succeeded; return this descriptor and filename.  */
+		  if (storeptr)
+		    *storeptr = string;
+		  UNGCPRO;
+		  return fd;
+		}
 	    }
 	}
       if (absolute)
@@ -4088,7 +4090,7 @@
       if (STRINGP (dirfile))
         {
           dirfile = Fdirectory_file_name (dirfile);
-          if (access (SSDATA (dirfile), 0) < 0)
+          if (faccessat (AT_FDCWD, SSDATA (dirfile), F_OK, AT_EACCESS) != 0)
             dir_warning ("Warning: Lisp directory `%s' does not exist.\n",
                          XCAR (path_tail));
         }

=== modified file 'src/process.c'
--- src/process.c	2012-10-13 00:52:01 +0000
+++ src/process.c	2012-10-15 06:05:46 +0000
@@ -663,7 +663,7 @@
 #else
 	    sprintf (pty_name, "/dev/tty%c%x", c, i);
 #endif /* no PTY_TTY_NAME_SPRINTF */
-	    if (access (pty_name, 6) != 0)
+	    if (faccessat (AT_FDCWD, pty_name, R_OK | W_OK, AT_EACCESS) != 0)
 	      {
 		emacs_close (fd);
 # ifndef __sgi

=== modified file 'src/w32.c'
--- src/w32.c	2012-10-12 15:30:06 +0000
+++ src/w32.c	2012-10-15 06:05:46 +0000
@@ -2707,10 +2707,16 @@
    long file names.  */
 
 int
-sys_access (const char * path, int mode)
+sys_faccessat (int dirfd, const char * path, int mode, int flags)
 {
   DWORD attributes;
 
+  if (dirfd != AT_FDCWD)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
   /* MSVCRT implementation of 'access' doesn't recognize D_OK, and its
      newer versions blow up when passed D_OK.  */
   path = map_w32_filename (path, NULL);

=== modified file 'src/xrdb.c'
--- src/xrdb.c	2012-09-15 07:06:56 +0000
+++ src/xrdb.c	2012-10-15 06:05:46 +0000
@@ -21,6 +21,7 @@
 
 #include <config.h>
 
+#include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
 #include <epaths.h>
@@ -266,11 +267,8 @@
 static int
 file_p (const char *filename)
 {
-  struct stat status;
-
-  return (access (filename, 4) == 0             /* exists and is readable */
-	  && stat (filename, &status) == 0	/* get the status */
-	  && (S_ISDIR (status.st_mode)) == 0);	/* not a directory */
+  return (faccessat (AT_FDCWD, filename, R_OK, AT_EACCESS) == 0
+	  && ! file_directory_p (filename));
 }
 
 


^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-15  6:17                     ` Paul Eggert
@ 2012-10-15 17:31                       ` Eli Zaretskii
  2012-10-15 21:38                         ` Paul Eggert
  0 siblings, 1 reply; 47+ messages in thread
From: Eli Zaretskii @ 2012-10-15 17:31 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 12632

> Date: Sun, 14 Oct 2012 23:17:16 -0700
> From: Paul Eggert <eggert@cs.ucla.edu>
> CC: 12632@debbugs.gnu.org
> 
> On 10/14/2012 01:53 PM, Eli Zaretskii wrote:
> > That sounds like a very weak reason to switch to a non-portable API
> 
> Since the main objection here seems to be using the non-portable
> function euidaccess, I changed the patch to use faccessat instead.

Well, actually I thought we should stay with 'stat'.

> faccessat, unlike euidaccess, is standardized by POSIX; it's a tad
> more-complicated to use but it is standard and it's no doubt
> a better solution in the long run anyway.  The patch
> uses the Gnulib substitute for faccessat for obsolescent POSIXish
> hosts that lack faccessat.

But those which have 'faccessat' probably also don't have the problems
in 'stat' with huge files etc.  So 'stat' is still better, IMO,
because it is very efficient (since it's so ubiquitous in many
programs).

> For Windows the patch simply reworks the existing substitute for
> 'access' so that it implements faccessat instead.

Thanks, but you need to change all the calls to sys_access inside
w32.c, or else Emacs won't link on Windows.





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-15 17:31                       ` Eli Zaretskii
@ 2012-10-15 21:38                         ` Paul Eggert
  2012-10-16  3:46                           ` Eli Zaretskii
  0 siblings, 1 reply; 47+ messages in thread
From: Paul Eggert @ 2012-10-15 21:38 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 12632

On 10/15/2012 10:31 AM, Eli Zaretskii wrote:
> Well, actually I thought we should stay with 'stat'.

We cannot stay with 'stat' everywhere, since 'stat' does not tell us
whether a file is readable or writeable or executable.  We must use
faccessat (or something like it, e.g., euidaccess) when we're
implementing functions like check_writable, check_executable, or
basically any function that uses R_OK, W_OK, or X_OK.  It's true that
we could use 'stat' instead of faccessat(..., F_OK, ...), but the
question then arises, why bother to make a special case for F_OK?

> So 'stat' is still better, IMO, because it is very efficient

Why should 'stat' be more efficient than faccessat?  'stat' has more
work to do if successful, as it needs to gather and copy a 'struct
stat' from kernel space to user space.  faccessat doesn't need to do that.

I did try timing the two, and found that on some hosts 'stat' is
faster, and on others 'faccessat' is.  Presumably 'stat' is faster on
platforms where, as you say, people have worked harder to tune it.
But the differences are not universal enough, or large enough, to say
that 'stat' is very efficient and faccessat is not.

> you need to change all the calls to sys_access inside
> w32.c, or else Emacs won't link on Windows.

Thanks, the following further patch should do that.

=== modified file 'src/ChangeLog'
--- src/ChangeLog	2012-10-15 06:07:05 +0000
+++ src/ChangeLog	2012-10-15 21:35:25 +0000
@@ -23,7 +23,7 @@
 	stat, as that avoids a permissions race.  When not opening a file,
 	use file_directory_p rather than stat.
 	* w32.c (sys_faccessat): Rename from sys_access and switch to
-	faccessat's API.
+	faccessat's API.  All uses changed.
 
 2012-10-15  Daniel Colascione  <dancol@dancol.org>
 

=== modified file 'src/w32.c'
--- src/w32.c	2012-10-15 06:05:46 +0000
+++ src/w32.c	2012-10-15 21:35:25 +0000
@@ -1590,7 +1590,7 @@
 	 see if it succeeds.  But I think that's too much to ask.  */
 
       /* MSVCRT's _access crashes with D_OK.  */
-      if (tmp && sys_access (tmp, D_OK) == 0)
+      if (tmp && sys_faccessat (AT_FDCWD, tmp, D_OK, AT_EACCESS) == 0)
 	{
 	  char * var = alloca (strlen (tmp) + 8);
 	  sprintf (var, "TMPDIR=%s", tmp);
@@ -2959,7 +2959,7 @@
 	{
 	  int save_errno = errno;
 	  p[0] = first_char[i];
-	  if (sys_access (template, 0) < 0)
+	  if (sys_faccessat (AT_FDCWD, template, F_OK, AT_EACCESS) < 0)
 	    {
 	      errno = save_errno;
 	      return template;
@@ -4010,7 +4010,7 @@
     {
       /* Non-absolute FILENAME is understood as being relative to
 	 LINKNAME's directory.  We need to prepend that directory to
-	 FILENAME to get correct results from sys_access below, since
+	 FILENAME to get correct results from sys_faccessat below, since
 	 otherwise it will interpret FILENAME relative to the
 	 directory where the Emacs process runs.  Note that
 	 make-symbolic-link always makes sure LINKNAME is a fully
@@ -4024,10 +4024,10 @@
 	strncpy (tem, linkfn, p - linkfn);
       tem[p - linkfn] = '\0';
       strcat (tem, filename);
-      dir_access = sys_access (tem, D_OK);
+      dir_access = sys_faccessat (AT_FDCWD, tem, D_OK, AT_EACCESS);
     }
   else
-    dir_access = sys_access (filename, D_OK);
+    dir_access = sys_faccessat (AT_FDCWD, filename, D_OK, AT_EACCESS);
 
   /* Since Windows distinguishes between symlinks to directories and
      to files, we provide a kludgy feature: if FILENAME doesn't







^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-15 21:38                         ` Paul Eggert
@ 2012-10-16  3:46                           ` Eli Zaretskii
  2012-10-16  6:00                             ` Paul Eggert
  0 siblings, 1 reply; 47+ messages in thread
From: Eli Zaretskii @ 2012-10-16  3:46 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 12632

> Date: Mon, 15 Oct 2012 14:38:05 -0700
> From: Paul Eggert <eggert@cs.ucla.edu>
> CC: 12632@debbugs.gnu.org
> 
> On 10/15/2012 10:31 AM, Eli Zaretskii wrote:
> > Well, actually I thought we should stay with 'stat'.
> 
> We cannot stay with 'stat' everywhere, since 'stat' does not tell us
> whether a file is readable or writeable or executable.  We must use
> faccessat (or something like it, e.g., euidaccess) when we're
> implementing functions like check_writable, check_executable, or
> basically any function that uses R_OK, W_OK, or X_OK.

I was talking about check_existing.

> It's true that we could use 'stat' instead of faccessat(..., F_OK,
> ...), but the question then arises, why bother to make a special
> case for F_OK?

Because it's more universally available, and I believe also more
efficient.

> > So 'stat' is still better, IMO, because it is very efficient
> 
> Why should 'stat' be more efficient than faccessat?

Because it's used a lot.





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-16  3:46                           ` Eli Zaretskii
@ 2012-10-16  6:00                             ` Paul Eggert
  2012-10-16 16:36                               ` Eli Zaretskii
  0 siblings, 1 reply; 47+ messages in thread
From: Paul Eggert @ 2012-10-16  6:00 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 12632

On 10/15/2012 08:46 PM, Eli Zaretskii wrote:
>> why bother to make a special case for F_OK?
> Because it's more universally available

faccessat is just as available, on the platforms that
the (patched) Emacs will run on: it's available either
natively or via supplied emulation.

> I believe also more efficient.

I thought the point of Bug#12587 was that on Windows,
'stat' can be way slower than faccessat, so much
so that Emacs appears unresponsive.

'stat' is also slower than faccessat on Solaris,
though the performance difference is not as great
as it appears to be on Windows.  'stat' is faster
than faccessat on GNU/Linux, true, but the
performance difference isn't great enough to make
a special case just for that platform.





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-16  6:00                             ` Paul Eggert
@ 2012-10-16 16:36                               ` Eli Zaretskii
  2012-10-19 17:01                                 ` Paul Eggert
  0 siblings, 1 reply; 47+ messages in thread
From: Eli Zaretskii @ 2012-10-16 16:36 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 12632

> Date: Mon, 15 Oct 2012 23:00:34 -0700
> From: Paul Eggert <eggert@cs.ucla.edu>
> CC: 12632@debbugs.gnu.org
> 
> > I believe also more efficient.
> 
> I thought the point of Bug#12587 was that on Windows,
> 'stat' can be way slower than faccessat, so much
> so that Emacs appears unresponsive.

Granted, I was talking only about Posix platforms.





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-16 16:36                               ` Eli Zaretskii
@ 2012-10-19 17:01                                 ` Paul Eggert
  2012-10-19 18:41                                   ` Eli Zaretskii
  0 siblings, 1 reply; 47+ messages in thread
From: Paul Eggert @ 2012-10-19 17:01 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 12632-done

On 10/16/2012 09:36 AM, Eli Zaretskii wrote:
>> > I thought the point of Bug#12587 was that on Windows,
>> > 'stat' can be way slower than faccessat, so much
>> > so that Emacs appears unresponsive.
> Granted, I was talking only about Posix platforms.

I'd rather avoid the situation where we have code like this:

  #if defined WINDOWSNT || defined __sun
    use faccessat
  #elif defined __linux__
    use stat
  #else
    /* not sure which is faster */
  #endif

unless there's a reasonably significant performance advantage
in doing so.  The performance advantage of stat on GNU/Linux
is relatively minor, whereas the performance advantage of
faccessat on Windows is major.  Since it's simpler to use faccessat
everywhere, I installed a patch to do that as trunk bzr 110591
and am marking this bug as done.  In the unlikely event that we
run into significant performance issues because of this we can
easily switch to the more-complex approach.





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-19 17:01                                 ` Paul Eggert
@ 2012-10-19 18:41                                   ` Eli Zaretskii
  2012-10-19 18:54                                     ` Paul Eggert
  0 siblings, 1 reply; 47+ messages in thread
From: Eli Zaretskii @ 2012-10-19 18:41 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 12632

> Date: Fri, 19 Oct 2012 10:01:23 -0700
> From: Paul Eggert <eggert@cs.ucla.edu>
> CC: 12632-done@debbugs.gnu.org
> 
> Since it's simpler to use faccessat everywhere, I installed a patch
> to do that as trunk bzr 110591 and am marking this bug as done.

Why are we doing such changes during a feature freeze?





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-19 18:41                                   ` Eli Zaretskii
@ 2012-10-19 18:54                                     ` Paul Eggert
  2012-10-19 19:05                                       ` Glenn Morris
  2012-10-19 19:10                                       ` Eli Zaretskii
  0 siblings, 2 replies; 47+ messages in thread
From: Paul Eggert @ 2012-10-19 18:54 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 12632

On 10/19/2012 11:41 AM, Eli Zaretskii wrote:
> Why are we doing such changes during a feature freeze?

To fix a bug.  It's similar to trunk bzr 110520,
another bug fix in this area, installed during
the feature freeze.





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-19 18:54                                     ` Paul Eggert
@ 2012-10-19 19:05                                       ` Glenn Morris
  2012-10-19 19:36                                         ` Paul Eggert
  2012-10-19 19:10                                       ` Eli Zaretskii
  1 sibling, 1 reply; 47+ messages in thread
From: Glenn Morris @ 2012-10-19 19:05 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 12632

Paul Eggert wrote:

> On 10/19/2012 11:41 AM, Eli Zaretskii wrote:
>> Why are we doing such changes during a feature freeze?
>
> To fix a bug.

If a bug is long-standing and/or minor, and if the fix for it involves
non-trivial modifications, then it can be better to leave it unfixed (at
this stage in the release process), in case a new, more serious or
subtle problem is inadvertently introduced.

Your initial report said "setuid Emacs is mishandling file permissions
on POSIXish platforms that lack 'euidaccess'" and "setuid Emacs is
mishandling these file permissions even on GNUish platforms.".

I doubt anyone is running setuid Emacs anywhere, so this seems like a
theoretical concern.





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-19 18:54                                     ` Paul Eggert
  2012-10-19 19:05                                       ` Glenn Morris
@ 2012-10-19 19:10                                       ` Eli Zaretskii
  1 sibling, 0 replies; 47+ messages in thread
From: Eli Zaretskii @ 2012-10-19 19:10 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 12632

> Date: Fri, 19 Oct 2012 11:54:23 -0700
> From: Paul Eggert <eggert@cs.ucla.edu>
> CC: 12632@debbugs.gnu.org
> 
> On 10/19/2012 11:41 AM, Eli Zaretskii wrote:
> > Why are we doing such changes during a feature freeze?
> 
> To fix a bug.  It's similar to trunk bzr 110520,

No, it isn't.





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-19 19:05                                       ` Glenn Morris
@ 2012-10-19 19:36                                         ` Paul Eggert
  2012-10-20  2:25                                           ` Richard Stallman
  2012-10-21  1:44                                           ` Glenn Morris
  0 siblings, 2 replies; 47+ messages in thread
From: Paul Eggert @ 2012-10-19 19:36 UTC (permalink / raw)
  To: Glenn Morris; +Cc: 12632

On 10/19/2012 12:05 PM, Glenn Morris wrote:

> I doubt anyone is running setuid Emacs anywhere

People do it all the time, often unwittingly, typically by
having setuid or settgid scripts that end up invoking an
editor.  I've run into the problem myself.

But I agree that this bug is not a new one.  The first bug
report I could find for it was from Chris Torek, dated 1983!
I suppose that if Emacs users have lived with this security
hole for three decades, they can live with it for a while
longer.  So I reverted the change from the trunk.

Here's a copy of Torek's report:

http://www.megalextoria.com/usenet-archive/news005f1/b12/net.emacs/00000104.html





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-19 19:36                                         ` Paul Eggert
@ 2012-10-20  2:25                                           ` Richard Stallman
  2012-10-20  4:36                                             ` Paul Eggert
  2012-10-21  1:44                                           ` Glenn Morris
  1 sibling, 1 reply; 47+ messages in thread
From: Richard Stallman @ 2012-10-20  2:25 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 12632

    But I agree that this bug is not a new one.  The first bug
    report I could find for it was from Chris Torek, dated 1983!

Since I started writing GNU Emacs in 1984, that must have been
rather prescient on his part.  Or maybe he was using some
other Emacs version.

-- 
Dr Richard Stallman
President, Free Software Foundation
51 Franklin St
Boston MA 02110
USA
www.fsf.org  www.gnu.org
Skype: No way! That's nonfree (freedom-denying) software.
  Use Ekiga or an ordinary phone call






^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-20  2:25                                           ` Richard Stallman
@ 2012-10-20  4:36                                             ` Paul Eggert
  0 siblings, 0 replies; 47+ messages in thread
From: Paul Eggert @ 2012-10-20  4:36 UTC (permalink / raw)
  To: rms; +Cc: 12632

On 10/19/2012 07:25 PM, Richard Stallman wrote:
> that must have been rather prescient on his part.

Ha!  I should have known there was something odd about
an Emacs bug report dated 1983.

For what it's worth, I have verified that the bug
exists in the oldest version of GNU Emacs that I found
at gnu.org, namely GNU Emacs 18.59 (dated October 1992).
I suspect that the bug goes back to the first version
of GNU Emacs, though.  At least, I hope so: it's not
often that one gets to fix a bug that old.

> maybe he was using some other Emacs version.

Torek's Usenet posting (Article-I.D.: sri-arpa.858,
Posted: Fri Apr  8 14:18:56 1983) doesn't say which Emacs
he was writing about.  Most likely it was Gosling's Emacs;
this was back when that was freely redistributable.

It's not surprising that Gosling Emacs (assuming that's what
it was) had the same bug then that GNU Emacs does now.  It's
a common bug associated with the use of the 'access' system call.






^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-19 19:36                                         ` Paul Eggert
  2012-10-20  2:25                                           ` Richard Stallman
@ 2012-10-21  1:44                                           ` Glenn Morris
  2012-10-21  2:52                                             ` Paul Eggert
  1 sibling, 1 reply; 47+ messages in thread
From: Glenn Morris @ 2012-10-21  1:44 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 12632

Paul Eggert wrote:

> People do it all the time, often unwittingly, typically by
> having setuid or settgid scripts that end up invoking an
> editor.  I've run into the problem myself.

I thought setuid shell scripts were not supported on most UNIXs?






^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-21  1:44                                           ` Glenn Morris
@ 2012-10-21  2:52                                             ` Paul Eggert
  2012-10-21  4:24                                               ` Glenn Morris
  0 siblings, 1 reply; 47+ messages in thread
From: Paul Eggert @ 2012-10-21  2:52 UTC (permalink / raw)
  To: Glenn Morris; +Cc: 12632

On 10/20/2012 06:44 PM, Glenn Morris wrote:
> I thought setuid shell scripts were not supported on most UNIXs?

Yes, nowadays it's typically disabled, but there are obvious
workarounds and people use them.  They'll create a tiny
executable that is setuid, which turns around and invokes
the script.  It's not something one would recommend for
highly secure environments, but that doesn't mean
it doesn't happen.





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-21  2:52                                             ` Paul Eggert
@ 2012-10-21  4:24                                               ` Glenn Morris
  2012-10-22  6:03                                                 ` Paul Eggert
  0 siblings, 1 reply; 47+ messages in thread
From: Glenn Morris @ 2012-10-21  4:24 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 12632

Paul Eggert wrote:

> On 10/20/2012 06:44 PM, Glenn Morris wrote:
>> I thought setuid shell scripts were not supported on most UNIXs?
>
> Yes, nowadays it's typically disabled, but there are obvious
> workarounds and people use them. They'll create a tiny executable that
> is setuid, which turns around and invokes the script.

I really can't see how this is something that "People do [...] all the
time, often unwittingly"; but obviously at this point I'm just arguing
for the sake of it...





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-21  4:24                                               ` Glenn Morris
@ 2012-10-22  6:03                                                 ` Paul Eggert
  2012-10-22 17:19                                                   ` Eli Zaretskii
  0 siblings, 1 reply; 47+ messages in thread
From: Paul Eggert @ 2012-10-22  6:03 UTC (permalink / raw)
  To: Glenn Morris; +Cc: 12632

[-- Attachment #1: Type: text/plain, Size: 961 bytes --]

On 10/20/2012 09:24 PM, Glenn Morris wrote:
> I really can't see how this is something that "People do [...] all the
> time, often unwittingly"

Yes, you're right, I was exaggerating -- it used to be more
common, but it's rarer nowadays.

Anyway, now that this patch is waiting until after the feature
freeze, it can accommodate some cleanup changes.  I've attached
the revised version.  It speeds up file-accessible-directory-p
by doing it with one system call rather than two, avoids the
unnecessary NILP check in file-writable-p that we discussed earlier,
fixes a bug where file-writable-p thinks that one can create a file
in a directory even when one cannot, does a better job of checking
whether directories are searchable rather than merely whether they
exist, and closes a couple more races related to permissions checking.
None of this is urgent but I figured I'd write it up while I was
thinking about it.

This patch is relative to trunk bzr 110616.

[-- Attachment #2: euidaccess.txt --]
[-- Type: text/plain, Size: 95453 bytes --]

=== modified file '.bzrignore'
--- .bzrignore	2012-10-19 19:25:18 +0000
+++ .bzrignore	2012-10-20 01:13:35 +0000
@@ -88,6 +88,7 @@
 lib/arg-nonnull.h
 lib/c++defs.h
 lib/execinfo.h
+lib/fcntl.h
 lib/getopt.h
 lib/inttypes.h
 lib/stdalign.h

=== modified file 'ChangeLog'
--- ChangeLog	2012-10-21 01:35:46 +0000
+++ ChangeLog	2012-10-22 02:43:16 +0000
@@ -1,3 +1,16 @@
+2012-10-22  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use faccessat, not access, when checking file permissions (Bug#12632).
+	* .bzrignore: Add lib/fcntl.h.
+	* configure.ac (euidaccess): Remove check; gnulib does this for us now.
+	(gl_FCNTL_O_FLAGS): Define a dummy version.
+	* lib/at-func.c, lib/euidaccess.c, lib/faccessat.c, lib/fcntl.in.h:
+	* lib/getgroups.c, lib/group-member.c, lib/root-uid.h:
+	* lib/xalloc-oversized.h, m4/euidaccess.m4, m4/faccessat.m4:
+	* m4/fcntl_h.m4, m4/getgroups.m4, m4/group-member.m4:
+	New files, from gnulib.
+	* lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate.
+
 2012-10-21  Glenn Morris  <rgm@gnu.org>
 
 	* Makefile.in (install-etc): Don't install emacs22 icons.

=== modified file 'admin/ChangeLog'
--- admin/ChangeLog	2012-10-19 19:25:18 +0000
+++ admin/ChangeLog	2012-10-22 02:43:16 +0000
@@ -1,3 +1,11 @@
+2012-10-22  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use faccessat, not access, when checking file permissions (Bug#12632).
+	* merge-gnulib (GNULIB_MODULES): Add faccessat.
+	(GNULIB_TOOL_FLAGS): Avoid at-internal, fchdir, malloc-posix,
+	openat-die, openat-h, save-cwd.  Do not avoid fcntl-h.
+	Omit gnulib's m4/fcntl-o.m4.
+
 2012-10-12  Kenichi Handa  <handa@gnu.org>
 
 	* charsets/Makefile (JISC6226.map): Add missing mappings.

=== modified file 'admin/merge-gnulib'
--- admin/merge-gnulib	2012-10-19 19:25:18 +0000
+++ admin/merge-gnulib	2012-10-20 01:13:35 +0000
@@ -28,7 +28,7 @@
 GNULIB_MODULES='
   alloca-opt c-ctype c-strcase
   careadlinkat crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512
-  dtoastr dtotimespec dup2 environ execinfo
+  dtoastr dtotimespec dup2 environ execinfo faccessat
   filemode getloadavg getopt-gnu gettime gettimeofday
   ignore-value intprops largefile lstat
   manywarnings mktime pselect pthread_sigmask readlink
@@ -39,9 +39,12 @@
 '
 
 GNULIB_TOOL_FLAGS='
-  --avoid=errno --avoid=fcntl --avoid=fcntl-h --avoid=fstat
-  --avoid=msvc-inval --avoid=msvc-nothrow
-  --avoid=raise --avoid=select --avoid=sigprocmask --avoid=sys_types
+  --avoid=at-internal
+  --avoid=errno --avoid=fchdir --avoid=fcntl --avoid=fstat
+  --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow
+  --avoid=openat-die --avoid=openat-h
+  --avoid=raise
+  --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types
   --avoid=threadlib
   --conditional-dependencies --import --no-changelog --no-vc-files
   --makefile-name=gnulib.mk
@@ -85,7 +88,7 @@
 }
 
 "$gnulib_srcdir"/gnulib-tool --dir="$src" $GNULIB_TOOL_FLAGS $GNULIB_MODULES &&
-rm -- "$src"m4/gnulib-cache.m4 "$src"m4/warn-on-use.m4 &&
+rm -- "$src"m4/fcntl-o.m4 "$src"m4/gnulib-cache.m4 "$src"m4/warn-on-use.m4 &&
 cp -- "$gnulib_srcdir"/build-aux/texinfo.tex "$src"doc/misc &&
 cp -- "$gnulib_srcdir"/build-aux/move-if-change "$src"build-aux &&
 autoreconf -i -I m4 -- ${src:+"$src"}

=== modified file 'configure.ac'
--- configure.ac	2012-10-21 07:23:34 +0000
+++ configure.ac	2012-10-22 02:43:16 +0000
@@ -572,6 +572,8 @@
   test "x$NON_GCC_TEST_OPTIONS" != x && CC="$CC $NON_GCC_TEST_OPTIONS"
 fi
 
+# Avoid gnulib's tests for O_NOATIME and O_NOFOLLOW, as we don't use them.
+AC_DEFUN([gl_FCNTL_O_FLAGS])
 # Avoid gnulib's threadlib module, as we do threads our own way.
 AC_DEFUN([gl_THREADLIB])
 
@@ -2872,7 +2874,7 @@
 AC_CHECK_FUNCS(gethostname \
 closedir getrusage get_current_dir_name \
 lrand48 setsid \
-fpathconf select euidaccess getpagesize setlocale \
+fpathconf select getpagesize setlocale \
 utimes getrlimit setrlimit setpgid getcwd shutdown getaddrinfo \
 __fpending strsignal setitimer \
 sendto recvfrom getsockname getpeername getifaddrs freeifaddrs \

=== added file 'lib/at-func.c'
--- lib/at-func.c	1970-01-01 00:00:00 +0000
+++ lib/at-func.c	2012-10-15 06:05:46 +0000
@@ -0,0 +1,146 @@
+/* Define at-style functions like fstatat, unlinkat, fchownat, etc.
+   Copyright (C) 2006, 2009-2012 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* written by Jim Meyering */
+
+#include "dosname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */
+
+#ifdef GNULIB_SUPPORT_ONLY_AT_FDCWD
+# include <errno.h>
+# ifndef ENOTSUP
+#  define ENOTSUP EINVAL
+# endif
+#else
+# include "openat.h"
+# include "openat-priv.h"
+# include "save-cwd.h"
+#endif
+
+#ifdef AT_FUNC_USE_F1_COND
+# define CALL_FUNC(F)                           \
+  (flag == AT_FUNC_USE_F1_COND                  \
+    ? AT_FUNC_F1 (F AT_FUNC_POST_FILE_ARGS)     \
+    : AT_FUNC_F2 (F AT_FUNC_POST_FILE_ARGS))
+# define VALIDATE_FLAG(F)                       \
+  if (flag & ~AT_FUNC_USE_F1_COND)              \
+    {                                           \
+      errno = EINVAL;                           \
+      return FUNC_FAIL;                         \
+    }
+#else
+# define CALL_FUNC(F) (AT_FUNC_F1 (F AT_FUNC_POST_FILE_ARGS))
+# define VALIDATE_FLAG(F) /* empty */
+#endif
+
+#ifdef AT_FUNC_RESULT
+# define FUNC_RESULT AT_FUNC_RESULT
+#else
+# define FUNC_RESULT int
+#endif
+
+#ifdef AT_FUNC_FAIL
+# define FUNC_FAIL AT_FUNC_FAIL
+#else
+# define FUNC_FAIL -1
+#endif
+
+/* Call AT_FUNC_F1 to operate on FILE, which is in the directory
+   open on descriptor FD.  If AT_FUNC_USE_F1_COND is defined to a value,
+   AT_FUNC_POST_FILE_PARAM_DECLS must include a parameter named flag;
+   call AT_FUNC_F2 if FLAG is 0 or fail if FLAG contains more bits than
+   AT_FUNC_USE_F1_COND.  Return int and fail with -1 unless AT_FUNC_RESULT
+   or AT_FUNC_FAIL are defined.  If possible, do it without changing the
+   working directory.  Otherwise, resort to using save_cwd/fchdir,
+   then AT_FUNC_F?/restore_cwd.  If either the save_cwd or the restore_cwd
+   fails, then give a diagnostic and exit nonzero.  */
+FUNC_RESULT
+AT_FUNC_NAME (int fd, char const *file AT_FUNC_POST_FILE_PARAM_DECLS)
+{
+  VALIDATE_FLAG (flag);
+
+  if (fd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file))
+    return CALL_FUNC (file);
+
+#ifdef GNULIB_SUPPORT_ONLY_AT_FDCWD
+  errno = ENOTSUP;
+  return FUNC_FAIL;
+#else
+  {
+  /* Be careful to choose names unlikely to conflict with
+     AT_FUNC_POST_FILE_PARAM_DECLS.  */
+  struct saved_cwd saved_cwd;
+  int saved_errno;
+  FUNC_RESULT err;
+
+  {
+    char proc_buf[OPENAT_BUFFER_SIZE];
+    char *proc_file = openat_proc_name (proc_buf, fd, file);
+    if (proc_file)
+      {
+        FUNC_RESULT proc_result = CALL_FUNC (proc_file);
+        int proc_errno = errno;
+        if (proc_file != proc_buf)
+          free (proc_file);
+        /* If the syscall succeeds, or if it fails with an unexpected
+           errno value, then return right away.  Otherwise, fall through
+           and resort to using save_cwd/restore_cwd.  */
+        if (FUNC_FAIL != proc_result)
+          return proc_result;
+        if (! EXPECTED_ERRNO (proc_errno))
+          {
+            errno = proc_errno;
+            return proc_result;
+          }
+      }
+  }
+
+  if (save_cwd (&saved_cwd) != 0)
+    openat_save_fail (errno);
+  if (0 <= fd && fd == saved_cwd.desc)
+    {
+      /* If saving the working directory collides with the user's
+         requested fd, then the user's fd must have been closed to
+         begin with.  */
+      free_cwd (&saved_cwd);
+      errno = EBADF;
+      return FUNC_FAIL;
+    }
+
+  if (fchdir (fd) != 0)
+    {
+      saved_errno = errno;
+      free_cwd (&saved_cwd);
+      errno = saved_errno;
+      return FUNC_FAIL;
+    }
+
+  err = CALL_FUNC (file);
+  saved_errno = (err == FUNC_FAIL ? errno : 0);
+
+  if (restore_cwd (&saved_cwd) != 0)
+    openat_restore_fail (errno);
+
+  free_cwd (&saved_cwd);
+
+  if (saved_errno)
+    errno = saved_errno;
+  return err;
+  }
+#endif
+}
+#undef CALL_FUNC
+#undef FUNC_RESULT
+#undef FUNC_FAIL

=== added file 'lib/euidaccess.c'
--- lib/euidaccess.c	1970-01-01 00:00:00 +0000
+++ lib/euidaccess.c	2012-10-12 23:28:31 +0000
@@ -0,0 +1,221 @@
+/* euidaccess -- check if effective user id can access file
+
+   Copyright (C) 1990-1991, 1995, 1998, 2000, 2003-2006, 2008-2012 Free
+   Software Foundation, Inc.
+
+   This file is part of the GNU C Library.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by David MacKenzie and Torbjorn Granlund.
+   Adapted for GNU C library by Roland McGrath.  */
+
+#ifndef _LIBC
+# include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "root-uid.h"
+
+#if HAVE_LIBGEN_H
+# include <libgen.h>
+#endif
+
+#include <errno.h>
+#ifndef __set_errno
+# define __set_errno(val) errno = (val)
+#endif
+
+#if defined EACCES && !defined EACCESS
+# define EACCESS EACCES
+#endif
+
+#ifndef F_OK
+# define F_OK 0
+# define X_OK 1
+# define W_OK 2
+# define R_OK 4
+#endif
+
+
+#ifdef _LIBC
+
+# define access __access
+# define getuid __getuid
+# define getgid __getgid
+# define geteuid __geteuid
+# define getegid __getegid
+# define group_member __group_member
+# define euidaccess __euidaccess
+# undef stat
+# define stat stat64
+
+#endif
+
+/* Return 0 if the user has permission of type MODE on FILE;
+   otherwise, return -1 and set 'errno'.
+   Like access, except that it uses the effective user and group
+   id's instead of the real ones, and it does not always check for read-only
+   file system, text busy, etc.  */
+
+int
+euidaccess (const char *file, int mode)
+{
+#if HAVE_FACCESSAT                   /* glibc, AIX 7, Solaris 11, Cygwin 1.7 */
+  return faccessat (AT_FDCWD, file, mode, AT_EACCESS);
+#elif defined EFF_ONLY_OK               /* IRIX, OSF/1, Interix */
+  return access (file, mode | EFF_ONLY_OK);
+#elif defined ACC_SELF                  /* AIX */
+  return accessx (file, mode, ACC_SELF);
+#elif HAVE_EACCESS                      /* FreeBSD */
+  return eaccess (file, mode);
+#else       /* Mac OS X, NetBSD, OpenBSD, HP-UX, Solaris, Cygwin, mingw, BeOS */
+
+  uid_t uid = getuid ();
+  gid_t gid = getgid ();
+  uid_t euid = geteuid ();
+  gid_t egid = getegid ();
+  struct stat stats;
+
+# if HAVE_DECL_SETREGID && PREFER_NONREENTRANT_EUIDACCESS
+
+  /* Define PREFER_NONREENTRANT_EUIDACCESS if you prefer euidaccess to
+     return the correct result even if this would make it
+     nonreentrant.  Define this only if your entire application is
+     safe even if the uid or gid might temporarily change.  If your
+     application uses signal handlers or threads it is probably not
+     safe.  */
+
+  if (mode == F_OK)
+    return stat (file, &stats);
+  else
+    {
+      int result;
+      int saved_errno;
+
+      if (uid != euid)
+        setreuid (euid, uid);
+      if (gid != egid)
+        setregid (egid, gid);
+
+      result = access (file, mode);
+      saved_errno = errno;
+
+      /* Restore them.  */
+      if (uid != euid)
+        setreuid (uid, euid);
+      if (gid != egid)
+        setregid (gid, egid);
+
+      errno = saved_errno;
+      return result;
+    }
+
+# else
+
+  /* The following code assumes the traditional Unix model, and is not
+     correct on systems that have ACLs or the like.  However, it's
+     better than nothing, and it is reentrant.  */
+
+  unsigned int granted;
+  if (uid == euid && gid == egid)
+    /* If we are not set-uid or set-gid, access does the same.  */
+    return access (file, mode);
+
+  if (stat (file, &stats) != 0)
+    return -1;
+
+  /* The super-user can read and write any file, and execute any file
+     that anyone can execute.  */
+  if (euid == ROOT_UID
+      && ((mode & X_OK) == 0
+          || (stats.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))))
+    return 0;
+
+  /* Convert the mode to traditional form, clearing any bogus bits.  */
+  if (R_OK == 4 && W_OK == 2 && X_OK == 1 && F_OK == 0)
+    mode &= 7;
+  else
+    mode = ((mode & R_OK ? 4 : 0)
+            + (mode & W_OK ? 2 : 0)
+            + (mode & X_OK ? 1 : 0));
+
+  if (mode == 0)
+    return 0;                   /* The file exists.  */
+
+  /* Convert the file's permission bits to traditional form.  */
+  if (S_IRUSR == (4 << 6) && S_IWUSR == (2 << 6) && S_IXUSR == (1 << 6)
+      && S_IRGRP == (4 << 3) && S_IWGRP == (2 << 3) && S_IXGRP == (1 << 3)
+      && S_IROTH == (4 << 0) && S_IWOTH == (2 << 0) && S_IXOTH == (1 << 0))
+    granted = stats.st_mode;
+  else
+    granted = ((stats.st_mode & S_IRUSR ? 4 << 6 : 0)
+               + (stats.st_mode & S_IWUSR ? 2 << 6 : 0)
+               + (stats.st_mode & S_IXUSR ? 1 << 6 : 0)
+               + (stats.st_mode & S_IRGRP ? 4 << 3 : 0)
+               + (stats.st_mode & S_IWGRP ? 2 << 3 : 0)
+               + (stats.st_mode & S_IXGRP ? 1 << 3 : 0)
+               + (stats.st_mode & S_IROTH ? 4 << 0 : 0)
+               + (stats.st_mode & S_IWOTH ? 2 << 0 : 0)
+               + (stats.st_mode & S_IXOTH ? 1 << 0 : 0));
+
+  if (euid == stats.st_uid)
+    granted >>= 6;
+  else if (egid == stats.st_gid || group_member (stats.st_gid))
+    granted >>= 3;
+
+  if ((mode & ~granted) == 0)
+    return 0;
+  __set_errno (EACCESS);
+  return -1;
+
+# endif
+#endif
+}
+#undef euidaccess
+#ifdef weak_alias
+weak_alias (__euidaccess, euidaccess)
+#endif
+\f
+#ifdef TEST
+# include <error.h>
+# include <stdio.h>
+# include <stdlib.h>
+
+char *program_name;
+
+int
+main (int argc, char **argv)
+{
+  char *file;
+  int mode;
+  int err;
+
+  program_name = argv[0];
+  if (argc < 3)
+    abort ();
+  file = argv[1];
+  mode = atoi (argv[2]);
+
+  err = euidaccess (file, mode);
+  printf ("%d\n", err);
+  if (err != 0)
+    error (0, errno, "%s", file);
+  exit (0);
+}
+#endif

=== added file 'lib/faccessat.c'
--- lib/faccessat.c	1970-01-01 00:00:00 +0000
+++ lib/faccessat.c	2012-10-15 06:05:46 +0000
@@ -0,0 +1,45 @@
+/* Check the access rights of a file relative to an open directory.
+   Copyright (C) 2009-2012 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* written by Eric Blake */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#ifndef HAVE_ACCESS
+/* Mingw lacks access, but it also lacks real vs. effective ids, so
+   the gnulib euidaccess module is good enough.  */
+# undef access
+# define access euidaccess
+#endif
+
+/* Invoke access or euidaccess on file, FILE, using mode MODE, in the directory
+   open on descriptor FD.  If possible, do it without changing the
+   working directory.  Otherwise, resort to using save_cwd/fchdir, then
+   (access|euidaccess)/restore_cwd.  If either the save_cwd or the
+   restore_cwd fails, then give a diagnostic and exit nonzero.
+   Note that this implementation only supports AT_EACCESS, although some
+   native versions also support AT_SYMLINK_NOFOLLOW.  */
+
+#define AT_FUNC_NAME faccessat
+#define AT_FUNC_F1 euidaccess
+#define AT_FUNC_F2 access
+#define AT_FUNC_USE_F1_COND AT_EACCESS
+#define AT_FUNC_POST_FILE_PARAM_DECLS , int mode, int flag
+#define AT_FUNC_POST_FILE_ARGS        , mode
+#include "at-func.c"

=== added file 'lib/fcntl.in.h'
--- lib/fcntl.in.h	1970-01-01 00:00:00 +0000
+++ lib/fcntl.in.h	2012-10-16 01:07:08 +0000
@@ -0,0 +1,347 @@
+/* Like <fcntl.h>, but with non-working flags defined to 0.
+
+   Copyright (C) 2006-2012 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* written by Paul Eggert */
+
+#if __GNUC__ >= 3
+@PRAGMA_SYSTEM_HEADER@
+#endif
+@PRAGMA_COLUMNS@
+
+#if defined __need_system_fcntl_h
+/* Special invocation convention.  */
+
+/* Needed before <sys/stat.h>.
+   May also define off_t to a 64-bit type on native Windows.  */
+#include <sys/types.h>
+/* On some systems other than glibc, <sys/stat.h> is a prerequisite of
+   <fcntl.h>.  On glibc systems, we would like to avoid namespace pollution.
+   But on glibc systems, <fcntl.h> includes <sys/stat.h> inside an
+   extern "C" { ... } block, which leads to errors in C++ mode with the
+   overridden <sys/stat.h> from gnulib.  These errors are known to be gone
+   with g++ version >= 4.3.  */
+#if !(defined __GLIBC__ || defined __UCLIBC__) || (defined __cplusplus && defined GNULIB_NAMESPACE && !(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)))
+# include <sys/stat.h>
+#endif
+#@INCLUDE_NEXT@ @NEXT_FCNTL_H@
+
+#else
+/* Normal invocation convention.  */
+
+#ifndef _@GUARD_PREFIX@_FCNTL_H
+
+/* Needed before <sys/stat.h>.
+   May also define off_t to a 64-bit type on native Windows.  */
+#include <sys/types.h>
+/* On some systems other than glibc, <sys/stat.h> is a prerequisite of
+   <fcntl.h>.  On glibc systems, we would like to avoid namespace pollution.
+   But on glibc systems, <fcntl.h> includes <sys/stat.h> inside an
+   extern "C" { ... } block, which leads to errors in C++ mode with the
+   overridden <sys/stat.h> from gnulib.  These errors are known to be gone
+   with g++ version >= 4.3.  */
+#if !(defined __GLIBC__ || defined __UCLIBC__) || (defined __cplusplus && defined GNULIB_NAMESPACE && !(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)))
+# include <sys/stat.h>
+#endif
+/* The include_next requires a split double-inclusion guard.  */
+#@INCLUDE_NEXT@ @NEXT_FCNTL_H@
+
+#ifndef _@GUARD_PREFIX@_FCNTL_H
+#define _@GUARD_PREFIX@_FCNTL_H
+
+#ifndef __GLIBC__ /* Avoid namespace pollution on glibc systems.  */
+# include <unistd.h>
+#endif
+
+/* Native Windows platforms declare open(), creat() in <io.h>.  */
+#if (@GNULIB_OPEN@ || defined GNULIB_POSIXCHECK) \
+    && ((defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__)
+# include <io.h>
+#endif
+
+
+/* The definitions of _GL_FUNCDECL_RPL etc. are copied here.  */
+
+/* The definition of _GL_ARG_NONNULL is copied here.  */
+
+/* The definition of _GL_WARN_ON_USE is copied here.  */
+
+
+/* Declare overridden functions.  */
+
+#if @GNULIB_FCNTL@
+# if @REPLACE_FCNTL@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef fcntl
+#   define fcntl rpl_fcntl
+#  endif
+_GL_FUNCDECL_RPL (fcntl, int, (int fd, int action, ...));
+_GL_CXXALIAS_RPL (fcntl, int, (int fd, int action, ...));
+# else
+#  if !@HAVE_FCNTL@
+_GL_FUNCDECL_SYS (fcntl, int, (int fd, int action, ...));
+#  endif
+_GL_CXXALIAS_SYS (fcntl, int, (int fd, int action, ...));
+# endif
+_GL_CXXALIASWARN (fcntl);
+#elif defined GNULIB_POSIXCHECK
+# undef fcntl
+# if HAVE_RAW_DECL_FCNTL
+_GL_WARN_ON_USE (fcntl, "fcntl is not always POSIX compliant - "
+                 "use gnulib module fcntl for portability");
+# endif
+#endif
+
+#if @GNULIB_OPEN@
+# if @REPLACE_OPEN@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef open
+#   define open rpl_open
+#  endif
+_GL_FUNCDECL_RPL (open, int, (const char *filename, int flags, ...)
+                             _GL_ARG_NONNULL ((1)));
+_GL_CXXALIAS_RPL (open, int, (const char *filename, int flags, ...));
+# else
+_GL_CXXALIAS_SYS (open, int, (const char *filename, int flags, ...));
+# endif
+/* On HP-UX 11, in C++ mode, open() is defined as an inline function with a
+   default argument.  _GL_CXXALIASWARN does not work in this case.  */
+# if !defined __hpux
+_GL_CXXALIASWARN (open);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef open
+/* Assume open is always declared.  */
+_GL_WARN_ON_USE (open, "open is not always POSIX compliant - "
+                 "use gnulib module open for portability");
+#endif
+
+#if @GNULIB_OPENAT@
+# if @REPLACE_OPENAT@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef openat
+#   define openat rpl_openat
+#  endif
+_GL_FUNCDECL_RPL (openat, int,
+                  (int fd, char const *file, int flags, /* mode_t mode */ ...)
+                  _GL_ARG_NONNULL ((2)));
+_GL_CXXALIAS_RPL (openat, int,
+                  (int fd, char const *file, int flags, /* mode_t mode */ ...));
+# else
+#  if !@HAVE_OPENAT@
+_GL_FUNCDECL_SYS (openat, int,
+                  (int fd, char const *file, int flags, /* mode_t mode */ ...)
+                  _GL_ARG_NONNULL ((2)));
+#  endif
+_GL_CXXALIAS_SYS (openat, int,
+                  (int fd, char const *file, int flags, /* mode_t mode */ ...));
+# endif
+_GL_CXXALIASWARN (openat);
+#elif defined GNULIB_POSIXCHECK
+# undef openat
+# if HAVE_RAW_DECL_OPENAT
+_GL_WARN_ON_USE (openat, "openat is not portable - "
+                 "use gnulib module openat for portability");
+# endif
+#endif
+
+
+/* Fix up the FD_* macros, only known to be missing on mingw.  */
+
+#ifndef FD_CLOEXEC
+# define FD_CLOEXEC 1
+#endif
+
+/* Fix up the supported F_* macros.  Intentionally leave other F_*
+   macros undefined.  Only known to be missing on mingw.  */
+
+#ifndef F_DUPFD_CLOEXEC
+# define F_DUPFD_CLOEXEC 0x40000000
+/* Witness variable: 1 if gnulib defined F_DUPFD_CLOEXEC, 0 otherwise.  */
+# define GNULIB_defined_F_DUPFD_CLOEXEC 1
+#else
+# define GNULIB_defined_F_DUPFD_CLOEXEC 0
+#endif
+
+#ifndef F_DUPFD
+# define F_DUPFD 1
+#endif
+
+#ifndef F_GETFD
+# define F_GETFD 2
+#endif
+
+/* Fix up the O_* macros.  */
+
+#if !defined O_DIRECT && defined O_DIRECTIO
+/* Tru64 spells it 'O_DIRECTIO'.  */
+# define O_DIRECT O_DIRECTIO
+#endif
+
+#if !defined O_CLOEXEC && defined O_NOINHERIT
+/* Mingw spells it 'O_NOINHERIT'.  */
+# define O_CLOEXEC O_NOINHERIT
+#endif
+
+#ifndef O_CLOEXEC
+# define O_CLOEXEC 0
+#endif
+
+#ifndef O_DIRECT
+# define O_DIRECT 0
+#endif
+
+#ifndef O_DIRECTORY
+# define O_DIRECTORY 0
+#endif
+
+#ifndef O_DSYNC
+# define O_DSYNC 0
+#endif
+
+#ifndef O_EXEC
+# define O_EXEC O_RDONLY /* This is often close enough in older systems.  */
+#endif
+
+#ifndef O_IGNORE_CTTY
+# define O_IGNORE_CTTY 0
+#endif
+
+#ifndef O_NDELAY
+# define O_NDELAY 0
+#endif
+
+#ifndef O_NOATIME
+# define O_NOATIME 0
+#endif
+
+#ifndef O_NONBLOCK
+# define O_NONBLOCK O_NDELAY
+#endif
+
+/* If the gnulib module 'nonblocking' is in use, guarantee a working non-zero
+   value of O_NONBLOCK.  Otherwise, O_NONBLOCK is defined (above) to O_NDELAY
+   or to 0 as fallback.  */
+#if @GNULIB_NONBLOCKING@
+# if O_NONBLOCK
+#  define GNULIB_defined_O_NONBLOCK 0
+# else
+#  define GNULIB_defined_O_NONBLOCK 1
+#  undef O_NONBLOCK
+#  define O_NONBLOCK 0x40000000
+# endif
+#endif
+
+#ifndef O_NOCTTY
+# define O_NOCTTY 0
+#endif
+
+#ifndef O_NOFOLLOW
+# define O_NOFOLLOW 0
+#endif
+
+#ifndef O_NOLINK
+# define O_NOLINK 0
+#endif
+
+#ifndef O_NOLINKS
+# define O_NOLINKS 0
+#endif
+
+#ifndef O_NOTRANS
+# define O_NOTRANS 0
+#endif
+
+#ifndef O_RSYNC
+# define O_RSYNC 0
+#endif
+
+#ifndef O_SEARCH
+# define O_SEARCH O_RDONLY /* This is often close enough in older systems.  */
+#endif
+
+#ifndef O_SYNC
+# define O_SYNC 0
+#endif
+
+#ifndef O_TTY_INIT
+# define O_TTY_INIT 0
+#endif
+
+#if O_ACCMODE != (O_RDONLY | O_WRONLY | O_RDWR | O_EXEC | O_SEARCH)
+# undef O_ACCMODE
+# define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR | O_EXEC | O_SEARCH)
+#endif
+
+/* For systems that distinguish between text and binary I/O.
+   O_BINARY is usually declared in fcntl.h  */
+#if !defined O_BINARY && defined _O_BINARY
+  /* For MSC-compatible compilers.  */
+# define O_BINARY _O_BINARY
+# define O_TEXT _O_TEXT
+#endif
+
+#if defined __BEOS__ || defined __HAIKU__
+  /* BeOS 5 and Haiku have O_BINARY and O_TEXT, but they have no effect.  */
+# undef O_BINARY
+# undef O_TEXT
+#endif
+
+#ifndef O_BINARY
+# define O_BINARY 0
+# define O_TEXT 0
+#endif
+
+/* Fix up the AT_* macros.  */
+
+/* Work around a bug in Solaris 9 and 10: AT_FDCWD is positive.  Its
+   value exceeds INT_MAX, so its use as an int doesn't conform to the
+   C standard, and GCC and Sun C complain in some cases.  If the bug
+   is present, undef AT_FDCWD here, so it can be redefined below.  */
+#if 0 < AT_FDCWD && AT_FDCWD == 0xffd19553
+# undef AT_FDCWD
+#endif
+
+/* Use the same bit pattern as Solaris 9, but with the proper
+   signedness.  The bit pattern is important, in case this actually is
+   Solaris with the above workaround.  */
+#ifndef AT_FDCWD
+# define AT_FDCWD (-3041965)
+#endif
+
+/* Use the same values as Solaris 9.  This shouldn't matter, but
+   there's no real reason to differ.  */
+#ifndef AT_SYMLINK_NOFOLLOW
+# define AT_SYMLINK_NOFOLLOW 4096
+#endif
+
+#ifndef AT_REMOVEDIR
+# define AT_REMOVEDIR 1
+#endif
+
+/* Solaris 9 lacks these two, so just pick unique values.  */
+#ifndef AT_SYMLINK_FOLLOW
+# define AT_SYMLINK_FOLLOW 2
+#endif
+
+#ifndef AT_EACCESS
+# define AT_EACCESS 4
+#endif
+
+
+#endif /* _@GUARD_PREFIX@_FCNTL_H */
+#endif /* _@GUARD_PREFIX@_FCNTL_H */
+#endif

=== added file 'lib/getgroups.c'
--- lib/getgroups.c	1970-01-01 00:00:00 +0000
+++ lib/getgroups.c	2012-10-12 23:28:31 +0000
@@ -0,0 +1,116 @@
+/* provide consistent interface to getgroups for systems that don't allow N==0
+
+   Copyright (C) 1996, 1999, 2003, 2006-2012 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* written by Jim Meyering */
+
+#include <config.h>
+
+#include <unistd.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#if !HAVE_GETGROUPS
+
+/* Provide a stub that fails with ENOSYS, since there is no group
+   information available on mingw.  */
+int
+getgroups (int n _GL_UNUSED, GETGROUPS_T *groups _GL_UNUSED)
+{
+  errno = ENOSYS;
+  return -1;
+}
+
+#else /* HAVE_GETGROUPS */
+
+# undef getgroups
+# ifndef GETGROUPS_ZERO_BUG
+#  define GETGROUPS_ZERO_BUG 0
+# endif
+
+/* On at least Ultrix 4.3 and NextStep 3.2, getgroups (0, NULL) always
+   fails.  On other systems, it returns the number of supplemental
+   groups for the process.  This function handles that special case
+   and lets the system-provided function handle all others.  However,
+   it can fail with ENOMEM if memory is tight.  It is unspecified
+   whether the effective group id is included in the list.  */
+
+int
+rpl_getgroups (int n, gid_t *group)
+{
+  int n_groups;
+  GETGROUPS_T *gbuf;
+  int saved_errno;
+
+  if (n < 0)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  if (n != 0 || !GETGROUPS_ZERO_BUG)
+    {
+      int result;
+      if (sizeof *group == sizeof *gbuf)
+        return getgroups (n, (GETGROUPS_T *) group);
+
+      if (SIZE_MAX / sizeof *gbuf <= n)
+        {
+          errno = ENOMEM;
+          return -1;
+        }
+      gbuf = malloc (n * sizeof *gbuf);
+      if (!gbuf)
+        return -1;
+      result = getgroups (n, gbuf);
+      if (0 <= result)
+        {
+          n = result;
+          while (n--)
+            group[n] = gbuf[n];
+        }
+      saved_errno = errno;
+      free (gbuf);
+      errno == saved_errno;
+      return result;
+    }
+
+  n = 20;
+  while (1)
+    {
+      /* No need to worry about address arithmetic overflow here,
+         since the ancient systems that we're running on have low
+         limits on the number of secondary groups.  */
+      gbuf = malloc (n * sizeof *gbuf);
+      if (!gbuf)
+        return -1;
+      n_groups = getgroups (n, gbuf);
+      if (n_groups == -1 ? errno != EINVAL : n_groups < n)
+        break;
+      free (gbuf);
+      n *= 2;
+    }
+
+  saved_errno = errno;
+  free (gbuf);
+  errno = saved_errno;
+
+  return n_groups;
+}
+
+#endif /* HAVE_GETGROUPS */

=== modified file 'lib/gnulib.mk'
--- lib/gnulib.mk	2012-10-19 19:25:18 +0000
+++ lib/gnulib.mk	2012-10-20 01:13:35 +0000
@@ -21,7 +21,7 @@
 # the same distribution terms as the rest of that program.
 #
 # Generated by gnulib-tool.
-# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=errno --avoid=fcntl --avoid=fcntl-h --avoid=fstat --avoid=msvc-inval --avoid=msvc-nothrow --avoid=raise --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo filemode getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings mktime pselect pthread_sigmask readlink socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub utimens warnings
+# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=at-internal --avoid=errno --avoid=fchdir --avoid=fcntl --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=openat-die --avoid=openat-h --avoid=raise --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat filemode getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings mktime pselect pthread_sigmask readlink socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub utimens warnings
 
 
 MOSTLYCLEANFILES += core *.stackdump
@@ -150,6 +150,17 @@
 
 ## end   gnulib module dup2
 
+## begin gnulib module euidaccess
+
+if gl_GNULIB_ENABLED_euidaccess
+
+endif
+EXTRA_DIST += euidaccess.c
+
+EXTRA_libgnu_a_SOURCES += euidaccess.c
+
+## end   gnulib module euidaccess
+
 ## begin gnulib module execinfo
 
 BUILT_SOURCES += $(EXECINFO_H)
@@ -175,6 +186,50 @@
 
 ## end   gnulib module execinfo
 
+## begin gnulib module faccessat
+
+
+EXTRA_DIST += at-func.c faccessat.c
+
+EXTRA_libgnu_a_SOURCES += at-func.c faccessat.c
+
+## end   gnulib module faccessat
+
+## begin gnulib module fcntl-h
+
+BUILT_SOURCES += fcntl.h
+
+# We need the following in order to create <fcntl.h> when the system
+# doesn't have one that works with the given compiler.
+fcntl.h: fcntl.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) $(WARN_ON_USE_H)
+	$(AM_V_GEN)rm -f $@-t $@ && \
+	{ echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \
+	  sed -e 's|@''GUARD_PREFIX''@|GL|g' \
+	      -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \
+	      -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
+	      -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \
+	      -e 's|@''NEXT_FCNTL_H''@|$(NEXT_FCNTL_H)|g' \
+	      -e 's/@''GNULIB_FCNTL''@/$(GNULIB_FCNTL)/g' \
+	      -e 's/@''GNULIB_NONBLOCKING''@/$(GNULIB_NONBLOCKING)/g' \
+	      -e 's/@''GNULIB_OPEN''@/$(GNULIB_OPEN)/g' \
+	      -e 's/@''GNULIB_OPENAT''@/$(GNULIB_OPENAT)/g' \
+	      -e 's|@''HAVE_FCNTL''@|$(HAVE_FCNTL)|g' \
+	      -e 's|@''HAVE_OPENAT''@|$(HAVE_OPENAT)|g' \
+	      -e 's|@''REPLACE_FCNTL''@|$(REPLACE_FCNTL)|g' \
+	      -e 's|@''REPLACE_OPEN''@|$(REPLACE_OPEN)|g' \
+	      -e 's|@''REPLACE_OPENAT''@|$(REPLACE_OPENAT)|g' \
+	      -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \
+	      -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \
+	      -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \
+	      < $(srcdir)/fcntl.in.h; \
+	} > $@-t && \
+	mv $@-t $@
+MOSTLYCLEANFILES += fcntl.h fcntl.h-t
+
+EXTRA_DIST += fcntl.in.h
+
+## end   gnulib module fcntl-h
+
 ## begin gnulib module filemode
 
 libgnu_a_SOURCES += filemode.c
@@ -183,6 +238,17 @@
 
 ## end   gnulib module filemode
 
+## begin gnulib module getgroups
+
+if gl_GNULIB_ENABLED_getgroups
+
+endif
+EXTRA_DIST += getgroups.c
+
+EXTRA_libgnu_a_SOURCES += getgroups.c
+
+## end   gnulib module getgroups
+
 ## begin gnulib module getloadavg
 
 
@@ -242,6 +308,17 @@
 
 ## end   gnulib module gettimeofday
 
+## begin gnulib module group-member
+
+if gl_GNULIB_ENABLED_a9786850e999ae65a836a6041e8e5ed1
+
+endif
+EXTRA_DIST += group-member.c
+
+EXTRA_libgnu_a_SOURCES += group-member.c
+
+## end   gnulib module group-member
+
 ## begin gnulib module ignore-value
 
 
@@ -354,6 +431,15 @@
 
 ## end   gnulib module readlink
 
+## begin gnulib module root-uid
+
+if gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c
+
+endif
+EXTRA_DIST += root-uid.h
+
+## end   gnulib module root-uid
+
 ## begin gnulib module signal-h
 
 BUILT_SOURCES += signal.h
@@ -1312,6 +1398,15 @@
 
 ## end   gnulib module verify
 
+## begin gnulib module xalloc-oversized
+
+if gl_GNULIB_ENABLED_682e609604ccaac6be382e4ee3a4eaec
+
+endif
+EXTRA_DIST += xalloc-oversized.h
+
+## end   gnulib module xalloc-oversized
+
 
 mostlyclean-local: mostlyclean-generic
 	@for dir in '' $(MOSTLYCLEANDIRS); do \

=== added file 'lib/group-member.c'
--- lib/group-member.c	1970-01-01 00:00:00 +0000
+++ lib/group-member.c	2012-10-12 23:28:31 +0000
@@ -0,0 +1,119 @@
+/* group-member.c -- determine whether group id is in calling user's group list
+
+   Copyright (C) 1994, 1997-1998, 2003, 2005-2006, 2009-2012 Free Software
+   Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <unistd.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdlib.h>
+
+#include "xalloc-oversized.h"
+
+/* Most processes have no more than this many groups, and for these
+   processes we can avoid using malloc.  */
+enum { GROUPBUF_SIZE = 100 };
+
+struct group_info
+  {
+    gid_t *group;
+    gid_t groupbuf[GROUPBUF_SIZE];
+  };
+
+static void
+free_group_info (struct group_info const *g)
+{
+  if (g->group != g->groupbuf)
+    free (g->group);
+}
+
+static int
+get_group_info (struct group_info *gi)
+{
+  int n_groups = getgroups (GROUPBUF_SIZE, gi->groupbuf);
+  gi->group = gi->groupbuf;
+
+  if (n_groups < 0)
+    {
+      int n_group_slots = getgroups (0, NULL);
+      if (0 <= n_group_slots
+          && ! xalloc_oversized (n_group_slots, sizeof *gi->group))
+        {
+          gi->group = malloc (n_group_slots * sizeof *gi->group);
+          if (gi->group)
+            n_groups = getgroups (n_group_slots, gi->group);
+        }
+    }
+
+  /* In case of error, the user loses.  */
+  return n_groups;
+}
+
+/* Return non-zero if GID is one that we have in our groups list.
+   Note that the groups list is not guaranteed to contain the current
+   or effective group ID, so they should generally be checked
+   separately.  */
+
+int
+group_member (gid_t gid)
+{
+  int i;
+  int found;
+  struct group_info gi;
+  int n_groups = get_group_info (&gi);
+
+  /* Search through the list looking for GID. */
+  found = 0;
+  for (i = 0; i < n_groups; i++)
+    {
+      if (gid == gi.group[i])
+        {
+          found = 1;
+          break;
+        }
+    }
+
+  free_group_info (&gi);
+
+  return found;
+}
+
+#ifdef TEST
+
+char *program_name;
+
+int
+main (int argc, char **argv)
+{
+  int i;
+
+  program_name = argv[0];
+
+  for (i = 1; i < argc; i++)
+    {
+      gid_t gid;
+
+      gid = atoi (argv[i]);
+      printf ("%d: %s\n", gid, group_member (gid) ? "yes" : "no");
+    }
+  exit (0);
+}
+
+#endif /* TEST */

=== added file 'lib/root-uid.h'
--- lib/root-uid.h	1970-01-01 00:00:00 +0000
+++ lib/root-uid.h	2012-10-12 23:28:31 +0000
@@ -0,0 +1,30 @@
+/* The user ID that always has appropriate privileges in the POSIX sense.
+
+   Copyright 2012 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   Written by Paul Eggert.  */
+
+#ifndef ROOT_UID_H_
+#define ROOT_UID_H_
+
+/* The user ID that always has appropriate privileges in the POSIX sense.  */
+#ifdef __TANDEM
+# define ROOT_UID 65535
+#else
+# define ROOT_UID 0
+#endif
+
+#endif

=== added file 'lib/xalloc-oversized.h'
--- lib/xalloc-oversized.h	1970-01-01 00:00:00 +0000
+++ lib/xalloc-oversized.h	2012-10-12 23:28:31 +0000
@@ -0,0 +1,38 @@
+/* xalloc-oversized.h -- memory allocation size checking
+
+   Copyright (C) 1990-2000, 2003-2004, 2006-2012 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef XALLOC_OVERSIZED_H_
+# define XALLOC_OVERSIZED_H_
+
+# include <stddef.h>
+
+/* Return 1 if an array of N objects, each of size S, cannot exist due
+   to size arithmetic overflow.  S must be positive and N must be
+   nonnegative.  This is a macro, not a function, so that it
+   works correctly even when SIZE_MAX < N.
+
+   By gnulib convention, SIZE_MAX represents overflow in size
+   calculations, so the conservative dividend to use here is
+   SIZE_MAX - 1, since SIZE_MAX might represent an overflowed value.
+   However, malloc (SIZE_MAX) fails on all known hosts where
+   sizeof (ptrdiff_t) <= sizeof (size_t), so do not bother to test for
+   exactly-SIZE_MAX allocations on such hosts; this avoids a test and
+   branch when S is known to be 1.  */
+# define xalloc_oversized(n, s) \
+    ((size_t) (sizeof (ptrdiff_t) <= sizeof (size_t) ? -1 : -2) / (s) < (n))
+
+#endif /* !XALLOC_OVERSIZED_H_ */

=== added file 'm4/euidaccess.m4'
--- m4/euidaccess.m4	1970-01-01 00:00:00 +0000
+++ m4/euidaccess.m4	2012-10-17 02:11:22 +0000
@@ -0,0 +1,52 @@
+# euidaccess.m4 serial 15
+dnl Copyright (C) 2002-2012 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_NONREENTRANT_EUIDACCESS],
+[
+  AC_REQUIRE([gl_FUNC_EUIDACCESS])
+  AC_CHECK_DECLS([setregid])
+  AC_DEFINE([PREFER_NONREENTRANT_EUIDACCESS], [1],
+    [Define this if you prefer euidaccess to return the correct result
+     even if this would make it nonreentrant.  Define this only if your
+     entire application is safe even if the uid or gid might temporarily
+     change.  If your application uses signal handlers or threads it
+     is probably not safe.])
+])
+
+AC_DEFUN([gl_FUNC_EUIDACCESS],
+[
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+
+  dnl Persuade glibc <unistd.h> to declare euidaccess().
+  AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
+
+  AC_CHECK_FUNCS([euidaccess])
+  if test $ac_cv_func_euidaccess = no; then
+    HAVE_EUIDACCESS=0
+  fi
+])
+
+# Prerequisites of lib/euidaccess.c.
+AC_DEFUN([gl_PREREQ_EUIDACCESS], [
+  dnl Prefer POSIX faccessat over non-standard euidaccess.
+  AC_CHECK_FUNCS_ONCE([faccessat])
+  dnl Try various other non-standard fallbacks.
+  AC_CHECK_HEADERS([libgen.h])
+  AC_FUNC_GETGROUPS
+
+  # Solaris 9 and 10 need -lgen to get the eaccess function.
+  # Save and restore LIBS so -lgen isn't added to it.  Otherwise, *all*
+  # programs in the package would end up linked with that potentially-shared
+  # library, inducing unnecessary run-time overhead.
+  LIB_EACCESS=
+  AC_SUBST([LIB_EACCESS])
+  gl_saved_libs=$LIBS
+    AC_SEARCH_LIBS([eaccess], [gen],
+                   [test "$ac_cv_search_eaccess" = "none required" ||
+                    LIB_EACCESS=$ac_cv_search_eaccess])
+    AC_CHECK_FUNCS([eaccess])
+  LIBS=$gl_saved_libs
+])

=== added file 'm4/faccessat.m4'
--- m4/faccessat.m4	1970-01-01 00:00:00 +0000
+++ m4/faccessat.m4	2012-10-15 06:05:46 +0000
@@ -0,0 +1,28 @@
+# serial 6
+# See if we need to provide faccessat replacement.
+
+dnl Copyright (C) 2009-2012 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+# Written by Eric Blake.
+
+AC_DEFUN([gl_FUNC_FACCESSAT],
+[
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+
+  dnl Persuade glibc <unistd.h> to declare faccessat().
+  AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+
+  AC_CHECK_FUNCS_ONCE([faccessat])
+  if test $ac_cv_func_faccessat = no; then
+    HAVE_FACCESSAT=0
+  fi
+])
+
+# Prerequisites of lib/faccessat.m4.
+AC_DEFUN([gl_PREREQ_FACCESSAT],
+[
+  AC_CHECK_FUNCS([access])
+])

=== added file 'm4/fcntl_h.m4'
--- m4/fcntl_h.m4	1970-01-01 00:00:00 +0000
+++ m4/fcntl_h.m4	2012-10-15 06:05:46 +0000
@@ -0,0 +1,50 @@
+# serial 15
+# Configure fcntl.h.
+dnl Copyright (C) 2006-2007, 2009-2012 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl Written by Paul Eggert.
+
+AC_DEFUN([gl_FCNTL_H],
+[
+  AC_REQUIRE([gl_FCNTL_H_DEFAULTS])
+  AC_REQUIRE([gl_FCNTL_O_FLAGS])
+  gl_NEXT_HEADERS([fcntl.h])
+
+  dnl Ensure the type pid_t gets defined.
+  AC_REQUIRE([AC_TYPE_PID_T])
+
+  dnl Ensure the type mode_t gets defined.
+  AC_REQUIRE([AC_TYPE_MODE_T])
+
+  dnl Check for declarations of anything we want to poison if the
+  dnl corresponding gnulib module is not in use, if it is not common
+  dnl enough to be declared everywhere.
+  gl_WARN_ON_USE_PREPARE([[#include <fcntl.h>
+    ]], [fcntl openat])
+])
+
+AC_DEFUN([gl_FCNTL_MODULE_INDICATOR],
+[
+  dnl Use AC_REQUIRE here, so that the default settings are expanded once only.
+  AC_REQUIRE([gl_FCNTL_H_DEFAULTS])
+  gl_MODULE_INDICATOR_SET_VARIABLE([$1])
+  dnl Define it also as a C macro, for the benefit of the unit tests.
+  gl_MODULE_INDICATOR_FOR_TESTS([$1])
+])
+
+AC_DEFUN([gl_FCNTL_H_DEFAULTS],
+[
+  GNULIB_FCNTL=0;        AC_SUBST([GNULIB_FCNTL])
+  GNULIB_NONBLOCKING=0;  AC_SUBST([GNULIB_NONBLOCKING])
+  GNULIB_OPEN=0;         AC_SUBST([GNULIB_OPEN])
+  GNULIB_OPENAT=0;       AC_SUBST([GNULIB_OPENAT])
+  dnl Assume proper GNU behavior unless another module says otherwise.
+  HAVE_FCNTL=1;          AC_SUBST([HAVE_FCNTL])
+  HAVE_OPENAT=1;         AC_SUBST([HAVE_OPENAT])
+  REPLACE_FCNTL=0;       AC_SUBST([REPLACE_FCNTL])
+  REPLACE_OPEN=0;        AC_SUBST([REPLACE_OPEN])
+  REPLACE_OPENAT=0;      AC_SUBST([REPLACE_OPENAT])
+])

=== added file 'm4/getgroups.m4'
--- m4/getgroups.m4	1970-01-01 00:00:00 +0000
+++ m4/getgroups.m4	2012-10-12 23:28:31 +0000
@@ -0,0 +1,107 @@
+# serial 18
+
+dnl From Jim Meyering.
+dnl A wrapper around AC_FUNC_GETGROUPS.
+
+# Copyright (C) 1996-1997, 1999-2004, 2008-2012 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+m4_version_prereq([2.70], [] ,[
+
+# This is taken from the following Autoconf patch:
+# http://git.savannah.gnu.org/gitweb/?p=autoconf.git;a=commitdiff;h=7fbb553727ed7e0e689a17594b58559ecf3ea6e9
+AC_DEFUN([AC_FUNC_GETGROUPS],
+[
+  AC_REQUIRE([AC_TYPE_GETGROUPS])dnl
+  AC_REQUIRE([AC_TYPE_SIZE_T])dnl
+  AC_REQUIRE([AC_CANONICAL_HOST])dnl for cross-compiles
+  AC_CHECK_FUNC([getgroups])
+
+  # If we don't yet have getgroups, see if it's in -lbsd.
+  # This is reported to be necessary on an ITOS 3000WS running SEIUX 3.1.
+  ac_save_LIBS=$LIBS
+  if test $ac_cv_func_getgroups = no; then
+    AC_CHECK_LIB(bsd, getgroups, [GETGROUPS_LIB=-lbsd])
+  fi
+
+  # Run the program to test the functionality of the system-supplied
+  # getgroups function only if there is such a function.
+  if test $ac_cv_func_getgroups = yes; then
+    AC_CACHE_CHECK([for working getgroups], [ac_cv_func_getgroups_works],
+      [AC_RUN_IFELSE(
+         [AC_LANG_PROGRAM(
+            [AC_INCLUDES_DEFAULT],
+            [[/* On Ultrix 4.3, getgroups (0, 0) always fails.  */
+              return getgroups (0, 0) == -1;]])
+         ],
+         [ac_cv_func_getgroups_works=yes],
+         [ac_cv_func_getgroups_works=no],
+         [case "$host_os" in # ((
+                    # Guess yes on glibc systems.
+            *-gnu*) ac_cv_func_getgroups_works="guessing yes" ;;
+                    # If we don't know, assume the worst.
+            *)      ac_cv_func_getgroups_works="guessing no" ;;
+          esac
+         ])
+      ])
+  else
+    ac_cv_func_getgroups_works=no
+  fi
+  case "$ac_cv_func_getgroups_works" in
+    *yes)
+      AC_DEFINE([HAVE_GETGROUPS], [1],
+        [Define to 1 if your system has a working `getgroups' function.])
+      ;;
+  esac
+  LIBS=$ac_save_LIBS
+])# AC_FUNC_GETGROUPS
+
+])
+
+AC_DEFUN([gl_FUNC_GETGROUPS],
+[
+  AC_REQUIRE([AC_TYPE_GETGROUPS])
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+
+  AC_FUNC_GETGROUPS
+  if test $ac_cv_func_getgroups != yes; then
+    HAVE_GETGROUPS=0
+  else
+    if test "$ac_cv_type_getgroups" != gid_t \
+       || { case "$ac_cv_func_getgroups_works" in
+              *yes) false;;
+              *) true;;
+            esac
+          }; then
+      REPLACE_GETGROUPS=1
+      AC_DEFINE([GETGROUPS_ZERO_BUG], [1], [Define this to 1 if
+        getgroups(0,NULL) does not return the number of groups.])
+    else
+      dnl Detect FreeBSD bug; POSIX requires getgroups(-1,ptr) to fail.
+      AC_CACHE_CHECK([whether getgroups handles negative values],
+        [gl_cv_func_getgroups_works],
+        [AC_RUN_IFELSE([AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT],
+          [[int size = getgroups (0, 0);
+            gid_t *list = malloc (size * sizeof *list);
+            return getgroups (-1, list) != -1;]])],
+          [gl_cv_func_getgroups_works=yes],
+          [gl_cv_func_getgroups_works=no],
+          [case "$host_os" in
+                     # Guess yes on glibc systems.
+             *-gnu*) gl_cv_func_getgroups_works="guessing yes" ;;
+                     # If we don't know, assume the worst.
+             *)      gl_cv_func_getgroups_works="guessing no" ;;
+           esac
+          ])])
+      case "$gl_cv_func_getgroups_works" in
+        *yes) ;;
+        *) REPLACE_GETGROUPS=1 ;;
+      esac
+    fi
+  fi
+  test -n "$GETGROUPS_LIB" && LIBS="$GETGROUPS_LIB $LIBS"
+])

=== modified file 'm4/gnulib-comp.m4'
--- m4/gnulib-comp.m4	2012-10-19 19:25:18 +0000
+++ m4/gnulib-comp.m4	2012-10-20 01:13:35 +0000
@@ -53,17 +53,22 @@
   # Code from module dtotimespec:
   # Code from module dup2:
   # Code from module environ:
+  # Code from module euidaccess:
   # Code from module execinfo:
   # Code from module extensions:
   AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
   # Code from module extern-inline:
+  # Code from module faccessat:
+  # Code from module fcntl-h:
   # Code from module filemode:
+  # Code from module getgroups:
   # Code from module getloadavg:
   # Code from module getopt-gnu:
   # Code from module getopt-posix:
   # Code from module gettext-h:
   # Code from module gettime:
   # Code from module gettimeofday:
+  # Code from module group-member:
   # Code from module ignore-value:
   # Code from module include_next:
   # Code from module intprops:
@@ -79,6 +84,7 @@
   # Code from module pselect:
   # Code from module pthread_sigmask:
   # Code from module readlink:
+  # Code from module root-uid:
   # Code from module signal-h:
   # Code from module snippet/_Noreturn:
   # Code from module snippet/arg-nonnull:
@@ -120,6 +126,7 @@
   # Code from module utimens:
   # Code from module verify:
   # Code from module warnings:
+  # Code from module xalloc-oversized:
 ])
 
 # This macro should be invoked from ./configure.ac, in the section
@@ -156,6 +163,14 @@
   gl_UNISTD_MODULE_INDICATOR([environ])
   gl_EXECINFO_H
   AC_REQUIRE([gl_EXTERN_INLINE])
+  gl_FUNC_FACCESSAT
+  if test $HAVE_FACCESSAT = 0; then
+    AC_LIBOBJ([faccessat])
+    gl_PREREQ_FACCESSAT
+  fi
+  gl_MODULE_INDICATOR([faccessat])
+  gl_UNISTD_MODULE_INDICATOR([faccessat])
+  gl_FCNTL_H
   gl_FILEMODE
   gl_GETLOADAVG
   if test $HAVE_GETLOADAVG = 0; then
@@ -269,18 +284,53 @@
   gl_UNISTD_H
   gl_UTIMENS
   gl_gnulib_enabled_dosname=false
+  gl_gnulib_enabled_euidaccess=false
+  gl_gnulib_enabled_getgroups=false
   gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=false
+  gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1=false
   gl_gnulib_enabled_pathmax=false
+  gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=false
   gl_gnulib_enabled_stat=false
   gl_gnulib_enabled_strtoll=false
   gl_gnulib_enabled_strtoull=false
   gl_gnulib_enabled_verify=false
+  gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=false
   func_gl_gnulib_m4code_dosname ()
   {
     if ! $gl_gnulib_enabled_dosname; then
       gl_gnulib_enabled_dosname=true
     fi
   }
+  func_gl_gnulib_m4code_euidaccess ()
+  {
+    if ! $gl_gnulib_enabled_euidaccess; then
+      gl_FUNC_EUIDACCESS
+      if test $HAVE_EUIDACCESS = 0; then
+        AC_LIBOBJ([euidaccess])
+        gl_PREREQ_EUIDACCESS
+      fi
+      gl_UNISTD_MODULE_INDICATOR([euidaccess])
+      gl_gnulib_enabled_euidaccess=true
+      if test $HAVE_EUIDACCESS = 0; then
+        func_gl_gnulib_m4code_a9786850e999ae65a836a6041e8e5ed1
+      fi
+      func_gl_gnulib_m4code_6099e9737f757db36c47fa9d9f02e88c
+      if test $HAVE_EUIDACCESS = 0; then
+        func_gl_gnulib_m4code_stat
+      fi
+    fi
+  }
+  func_gl_gnulib_m4code_getgroups ()
+  {
+    if ! $gl_gnulib_enabled_getgroups; then
+      gl_FUNC_GETGROUPS
+      if test $HAVE_GETGROUPS = 0 || test $REPLACE_GETGROUPS = 1; then
+        AC_LIBOBJ([getgroups])
+      fi
+      gl_UNISTD_MODULE_INDICATOR([getgroups])
+      gl_gnulib_enabled_getgroups=true
+    fi
+  }
   func_gl_gnulib_m4code_be453cec5eecf5731a274f2de7f2db36 ()
   {
     if ! $gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36; then
@@ -289,6 +339,24 @@
       gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=true
     fi
   }
+  func_gl_gnulib_m4code_a9786850e999ae65a836a6041e8e5ed1 ()
+  {
+    if ! $gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1; then
+      gl_FUNC_GROUP_MEMBER
+      if test $HAVE_GROUP_MEMBER = 0; then
+        AC_LIBOBJ([group-member])
+        gl_PREREQ_GROUP_MEMBER
+      fi
+      gl_UNISTD_MODULE_INDICATOR([group-member])
+      gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1=true
+      if test $HAVE_GROUP_MEMBER = 0; then
+        func_gl_gnulib_m4code_getgroups
+      fi
+      if test $HAVE_GROUP_MEMBER = 0; then
+        func_gl_gnulib_m4code_682e609604ccaac6be382e4ee3a4eaec
+      fi
+    fi
+  }
   func_gl_gnulib_m4code_pathmax ()
   {
     if ! $gl_gnulib_enabled_pathmax; then
@@ -296,6 +364,12 @@
       gl_gnulib_enabled_pathmax=true
     fi
   }
+  func_gl_gnulib_m4code_6099e9737f757db36c47fa9d9f02e88c ()
+  {
+    if ! $gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c; then
+      gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=true
+    fi
+  }
   func_gl_gnulib_m4code_stat ()
   {
     if ! $gl_gnulib_enabled_stat; then
@@ -347,6 +421,18 @@
       gl_gnulib_enabled_verify=true
     fi
   }
+  func_gl_gnulib_m4code_682e609604ccaac6be382e4ee3a4eaec ()
+  {
+    if ! $gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec; then
+      gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=true
+    fi
+  }
+  if test $HAVE_FACCESSAT = 0; then
+    func_gl_gnulib_m4code_dosname
+  fi
+  if test $HAVE_FACCESSAT = 0; then
+    func_gl_gnulib_m4code_euidaccess
+  fi
   if test $REPLACE_GETOPT = 1; then
     func_gl_gnulib_m4code_be453cec5eecf5731a274f2de7f2db36
   fi
@@ -373,12 +459,17 @@
   fi
   m4_pattern_allow([^gl_GNULIB_ENABLED_])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_dosname], [$gl_gnulib_enabled_dosname])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_euidaccess], [$gl_gnulib_enabled_euidaccess])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_getgroups], [$gl_gnulib_enabled_getgroups])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_be453cec5eecf5731a274f2de7f2db36], [$gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_a9786850e999ae65a836a6041e8e5ed1], [$gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_pathmax], [$gl_gnulib_enabled_pathmax])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c], [$gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_stat], [$gl_gnulib_enabled_stat])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_strtoll], [$gl_gnulib_enabled_strtoll])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_strtoull], [$gl_gnulib_enabled_strtoull])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_verify], [$gl_gnulib_enabled_verify])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_682e609604ccaac6be382e4ee3a4eaec], [$gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec])
   # End of code from modules
   m4_ifval(gl_LIBSOURCES_LIST, [
     m4_syscmd([test ! -d ]m4_defn([gl_LIBSOURCES_DIR])[ ||
@@ -527,6 +618,7 @@
   lib/alloca.in.h
   lib/allocator.c
   lib/allocator.h
+  lib/at-func.c
   lib/c-ctype.c
   lib/c-ctype.h
   lib/c-strcase.h
@@ -538,12 +630,16 @@
   lib/dtoastr.c
   lib/dtotimespec.c
   lib/dup2.c
+  lib/euidaccess.c
   lib/execinfo.c
   lib/execinfo.in.h
+  lib/faccessat.c
+  lib/fcntl.in.h
   lib/filemode.c
   lib/filemode.h
   lib/ftoastr.c
   lib/ftoastr.h
+  lib/getgroups.c
   lib/getloadavg.c
   lib/getopt.c
   lib/getopt.in.h
@@ -552,6 +648,7 @@
   lib/gettext.h
   lib/gettime.c
   lib/gettimeofday.c
+  lib/group-member.c
   lib/ignore-value.h
   lib/intprops.h
   lib/inttypes.in.h
@@ -564,6 +661,7 @@
   lib/pselect.c
   lib/pthread_sigmask.c
   lib/readlink.c
+  lib/root-uid.h
   lib/sha1.c
   lib/sha1.h
   lib/sha256.c
@@ -605,21 +703,28 @@
   lib/utimens.c
   lib/utimens.h
   lib/verify.h
+  lib/xalloc-oversized.h
   m4/00gnulib.m4
   m4/alloca.m4
   m4/c-strtod.m4
   m4/clock_time.m4
   m4/dup2.m4
   m4/environ.m4
+  m4/euidaccess.m4
   m4/execinfo.m4
   m4/extensions.m4
   m4/extern-inline.m4
+  m4/faccessat.m4
+  m4/fcntl-o.m4
+  m4/fcntl_h.m4
   m4/filemode.m4
+  m4/getgroups.m4
   m4/getloadavg.m4
   m4/getopt.m4
   m4/gettime.m4
   m4/gettimeofday.m4
   m4/gnulib-common.m4
+  m4/group-member.m4
   m4/include_next.m4
   m4/inttypes.m4
   m4/largefile.m4

=== added file 'm4/group-member.m4'
--- m4/group-member.m4	1970-01-01 00:00:00 +0000
+++ m4/group-member.m4	2012-10-12 23:28:31 +0000
@@ -0,0 +1,29 @@
+# serial 14
+
+# Copyright (C) 1999-2001, 2003-2007, 2009-2012 Free Software Foundation, Inc.
+
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+dnl Written by Jim Meyering
+
+AC_DEFUN([gl_FUNC_GROUP_MEMBER],
+[
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+
+  dnl Persuade glibc <unistd.h> to declare group_member().
+  AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
+
+  dnl Do this replacement check manually because I want the hyphen
+  dnl (not the underscore) in the filename.
+  AC_CHECK_FUNC([group_member], , [
+    HAVE_GROUP_MEMBER=0
+  ])
+])
+
+# Prerequisites of lib/group-member.c.
+AC_DEFUN([gl_PREREQ_GROUP_MEMBER],
+[
+  AC_REQUIRE([AC_FUNC_GETGROUPS])
+])

=== modified file 'nt/ChangeLog'
--- nt/ChangeLog	2012-10-19 19:25:18 +0000
+++ nt/ChangeLog	2012-10-22 02:43:16 +0000
@@ -1,3 +1,10 @@
+2012-10-22  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use faccessat, not access, when checking file permissions (Bug#12632).
+	* inc/ms-w32.h (AT_FDCWD, AT_EACCESS): New symbols.
+	(access): Remove.
+	(faccessat): New macro.
+
 2012-10-17  Eli Zaretskii  <eliz@gnu.org>
 
 	* inc/pwd.h (getuid, geteuid): Add prototypes.

=== modified file 'nt/inc/ms-w32.h'
--- nt/inc/ms-w32.h	2012-10-19 19:25:18 +0000
+++ nt/inc/ms-w32.h	2012-10-20 01:13:35 +0000
@@ -124,6 +124,10 @@
 #define MAXPATHLEN      _MAX_PATH
 #endif
 
+/* Use values compatible with gnulib, as there's no reason to differ.  */
+#define AT_FDCWD (-3041965)
+#define AT_EACCESS 4
+
 #ifdef HAVE_NTGUI
 #define HAVE_WINDOW_SYSTEM 1
 #define HAVE_MENUS 1
@@ -145,8 +149,6 @@
 #endif
 
 /* Calls that are emulated or shadowed.  */
-#undef access
-#define access  sys_access
 #undef chdir
 #define chdir   sys_chdir
 #undef chmod
@@ -161,6 +163,7 @@
 #define dup     sys_dup
 #undef dup2
 #define dup2    sys_dup2
+#define faccessat  sys_faccessat
 #define fopen   sys_fopen
 #define link    sys_link
 #define localtime sys_localtime

=== modified file 'src/ChangeLog'
--- src/ChangeLog	2012-10-21 18:48:11 +0000
+++ src/ChangeLog	2012-10-22 06:00:54 +0000
@@ -1,3 +1,68 @@
+2012-10-22  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use faccessat, not access, when checking file permissions (Bug#12632).
+	This fixes a bug that has been present in Emacs since its creation.
+	It was reported by Chris Torek in 1983 even before GNU Emacs existed,
+	which must set some sort of record.  (Torek's bug report was against
+	a predecessor of GNU Emacs, but GNU Emacs happened to have the
+	same common flaw.)  See Torek's Usenet posting
+	"setuid/setgid programs & Emacs" Article-I.D.: sri-arpa.858
+	Posted: Fri Apr  8 14:18:56 1983.
+	(LIBES): Use it.
+	* callproc.c (init_callproc):
+	* charset.c (init_charset):
+	* fileio.c (check_existing, check_executable, check_writable)
+	(Ffile_readable_p):
+	* lread.c (openp, load_path_check):
+	* process.c (allocate_pty):
+	* xrdb.c (file_p):
+	Use effective UID when checking permissions, not real UID.
+	* callproc.c (init_callproc):
+	* charset.c (init_charset):
+	* lread.c (load_path_check, init_lread):
+	Test whether directories are accessible, not merely whether they exist.
+	* conf_post.h (GNULIB_SUPPORT_ONLY_AT_FDCWD): New macro.
+	* fileio.c (check_existing, check_executable, check_writable)
+	(Ffile_readable_p):
+	Use symbolic names instead of integers for the flags, as they're
+	portable now.
+	(check_writable): Set errno on failure.
+	(Ffile_readable_p): Use faccessat, not stat + open + close.
+	(Ffile_writable_p): No need to call check_existing + check_writable.
+	Just call check_writable and then look at errno.  This saves a syscall.
+	dir should never be nil; replace an unnecessary runtime check
+	with an eassert.  When checking the parent directory of a nonexistent
+	file, check that the directory is searchable as well as writable, as
+	we can't create files in unsearchable directories.
+	(file_directory_p): New function, which uses 'stat' on most platforms
+	but faccessat with D_OK (for efficiency) if WINDOWSNT.
+	(Ffile_directory_p, Fset_file_times): Use it.
+	(file_accessible_directory_p): New function, which uses a single
+	syscall for efficiency.
+	(Ffile_accessible_directory_p): Use it.
+	* xrdb.c (file_p): Use file_directory_p.
+	* lisp.h (file_directory_p, file_accessible_directory_p): New decls.
+	* lread.c (openp): When opening a file, use fstat rather than
+	stat, as that avoids a permissions race.  When not opening a file,
+	use file_directory_p rather than stat.
+	(dir_warning): First arg is now a usage string, not a format.
+	Use errno.  All uses changed.
+	* nsterm.m (ns_term_init): Remove unnecessary call to file-readable
+	that merely introduced a race.
+	* process.c, sysdep.c, term.c: All uses of '#ifdef O_NONBLOCK'
+	changed to '#if O_NONBLOCK', to accommodate gnulib O_* style,
+	and similarly for the other O_* flags.
+	* w32.c (sys_faccessat): Rename from sys_access and switch to
+	faccessat's API.  All uses changed.
+	* xrdb.c: Do not include <sys/stat.h>; no longer needed.
+	(magic_db): Rename from magic_file_p.
+	(magic_db, search_magic_path): Return an XrmDatabase rather than a
+	char *, so that we don't have to test for file existence
+	separately from opening the file for reading.  This removes a race
+	fixes a permission-checking problem, and simplifies the code.
+	All uses changed.
+	(file_p): Remove; no longer needed.
+
 2012-10-21  Jan Djärv  <jan.h.d@swipnet.se>
 
 	* nsfont.m (nsfont_open, ns_glyph_metrics): Force integer advancement

=== modified file 'src/Makefile.in'
--- src/Makefile.in	2012-10-19 19:25:18 +0000
+++ src/Makefile.in	2012-10-20 01:13:35 +0000
@@ -150,6 +150,7 @@
 M17N_FLT_LIBS = @M17N_FLT_LIBS@
 
 LIB_CLOCK_GETTIME=@LIB_CLOCK_GETTIME@
+LIB_EACCESS=@LIB_EACCESS@
 LIB_TIMER_TIME=@LIB_TIMER_TIME@
 
 DBUS_CFLAGS = @DBUS_CFLAGS@
@@ -392,7 +393,7 @@
 LIBES = $(LIBS) $(W32_LIBS) $(LIBX_BASE) $(LIBIMAGE) \
    $(LIBX_OTHER) $(LIBSOUND) \
    $(RSVG_LIBS) $(IMAGEMAGICK_LIBS) $(LIB_CLOCK_GETTIME) \
-   $(LIB_TIMER_TIME) $(DBUS_LIBS) \
+   $(LIB_EACCESS) $(LIB_TIMER_TIME) $(DBUS_LIBS) \
    $(LIB_EXECINFO) \
    $(LIBXML2_LIBS) $(LIBGPM) $(LIBRESOLV) $(LIBS_SYSTEM) \
    $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \

=== modified file 'src/callproc.c'
--- src/callproc.c	2012-10-19 19:25:18 +0000
+++ src/callproc.c	2012-10-22 02:36:17 +0000
@@ -1597,15 +1597,13 @@
 #endif
     {
       tempdir = Fdirectory_file_name (Vexec_directory);
-      if (access (SSDATA (tempdir), 0) < 0)
-	dir_warning ("Warning: arch-dependent data dir (%s) does not exist.\n",
-		     Vexec_directory);
+      if (! file_accessible_directory_p (SSDATA (tempdir)))
+	dir_warning ("arch-dependent data dir", Vexec_directory);
     }
 
   tempdir = Fdirectory_file_name (Vdata_directory);
-  if (access (SSDATA (tempdir), 0) < 0)
-    dir_warning ("Warning: arch-independent data dir (%s) does not exist.\n",
-		 Vdata_directory);
+  if (! file_accessible_directory_p (SSDATA (tempdir)))
+    dir_warning ("arch-independent data dir", Vdata_directory);
 
   sh = (char *) getenv ("SHELL");
   Vshell_file_name = build_string (sh ? sh : "/bin/sh");
@@ -1614,7 +1612,7 @@
   Vshared_game_score_directory = Qnil;
 #else
   Vshared_game_score_directory = build_string (PATH_GAME);
-  if (NILP (Ffile_directory_p (Vshared_game_score_directory)))
+  if (NILP (Ffile_accessible_directory_p (Vshared_game_score_directory)))
     Vshared_game_score_directory = Qnil;
 #endif
 }

=== modified file 'src/charset.c'
--- src/charset.c	2012-10-19 19:25:18 +0000
+++ src/charset.c	2012-10-22 04:53:32 +0000
@@ -2293,7 +2293,7 @@
 {
   Lisp_Object tempdir;
   tempdir = Fexpand_file_name (build_string ("charsets"), Vdata_directory);
-  if (access (SSDATA (tempdir), 0) < 0)
+  if (! file_accessible_directory_p (SSDATA (tempdir)))
     {
       /* This used to be non-fatal (dir_warning), but it should not
          happen, and if it does sooner or later it will cause some

=== modified file 'src/conf_post.h'
--- src/conf_post.h	2012-10-19 19:25:18 +0000
+++ src/conf_post.h	2012-10-20 01:13:35 +0000
@@ -169,6 +169,10 @@
 #endif
 #endif
 
+/* Tell gnulib to omit support for openat-related functions having a
+   first argument other than AT_FDCWD.  */
+#define GNULIB_SUPPORT_ONLY_AT_FDCWD
+
 #include <string.h>
 #include <stdlib.h>
 

=== modified file 'src/fileio.c'
--- src/fileio.c	2012-10-19 19:25:18 +0000
+++ src/fileio.c	2012-10-22 02:36:17 +0000
@@ -2425,15 +2425,7 @@
 bool
 check_existing (const char *filename)
 {
-#ifdef DOS_NT
-  /* The full emulation of Posix 'stat' is too expensive on
-     DOS/Windows, when all we want to know is whether the file exists.
-     So we use 'access' instead, which is much more lightweight.  */
-  return (access (filename, F_OK) >= 0);
-#else
-  struct stat st;
-  return (stat (filename, &st) >= 0);
-#endif
+  return faccessat (AT_FDCWD, filename, F_OK, AT_EACCESS) == 0;
 }
 
 /* Return true if file FILENAME exists and can be executed.  */
@@ -2441,56 +2433,40 @@
 static bool
 check_executable (char *filename)
 {
-#ifdef DOS_NT
-  struct stat st;
-  if (stat (filename, &st) < 0)
-    return 0;
-  return ((st.st_mode & S_IEXEC) != 0);
-#else /* not DOS_NT */
-#ifdef HAVE_EUIDACCESS
-  return (euidaccess (filename, 1) >= 0);
-#else
-  /* Access isn't quite right because it uses the real uid
-     and we really want to test with the effective uid.
-     But Unix doesn't give us a right way to do it.  */
-  return (access (filename, 1) >= 0);
-#endif
-#endif /* not DOS_NT */
+  return faccessat (AT_FDCWD, filename, X_OK, AT_EACCESS) == 0;
 }
 
-/* Return true if file FILENAME exists and can be written.  */
+/* Return true if file FILENAME exists and can be accessed
+   according to AMODE, which should include W_OK.
+   On failure, return false and set errno.  */
 
 static bool
-check_writable (const char *filename)
+check_writable (const char *filename, int amode)
 {
 #ifdef MSDOS
+  /* FIXME: an faccessat implementation should be added to the
+     DOS/Windows ports and this #ifdef branch should be removed.  */
   struct stat st;
   if (stat (filename, &st) < 0)
     return 0;
+  errno = EPERM;
   return (st.st_mode & S_IWRITE || S_ISDIR (st.st_mode));
 #else /* not MSDOS */
-#ifdef HAVE_EUIDACCESS
-  bool res = (euidaccess (filename, 2) >= 0);
+  bool res = faccessat (AT_FDCWD, filename, amode, AT_EACCESS) == 0;
 #ifdef CYGWIN
-  /* euidaccess may have returned failure because Cygwin couldn't
+  /* faccessat may have returned failure because Cygwin couldn't
      determine the file's UID or GID; if so, we return success. */
   if (!res)
     {
+      int faccessat_errno = errno;
       struct stat st;
       if (stat (filename, &st) < 0)
         return 0;
       res = (st.st_uid == -1 || st.st_gid == -1);
+      errno = faccessat_errno;
     }
 #endif /* CYGWIN */
   return res;
-#else /* not HAVE_EUIDACCESS */
-  /* Access isn't quite right because it uses the real uid
-     and we really want to test with the effective uid.
-     But Unix doesn't give us a right way to do it.
-     Opening with O_WRONLY could work for an ordinary file,
-     but would lose for directories.  */
-  return (access (filename, 2) >= 0);
-#endif /* not HAVE_EUIDACCESS */
 #endif /* not MSDOS */
 }
 
@@ -2547,9 +2523,6 @@
 {
   Lisp_Object absname;
   Lisp_Object handler;
-  int desc;
-  int flags;
-  struct stat statbuf;
 
   CHECK_STRING (filename);
   absname = Fexpand_file_name (filename, Qnil);
@@ -2561,35 +2534,10 @@
     return call2 (handler, Qfile_readable_p, absname);
 
   absname = ENCODE_FILE (absname);
-
-#if defined (DOS_NT) || defined (macintosh)
-  /* Under MS-DOS, Windows, and Macintosh, open does not work for
-     directories.  */
-  if (access (SDATA (absname), 0) == 0)
-    return Qt;
-  return Qnil;
-#else /* not DOS_NT and not macintosh */
-  flags = O_RDONLY;
-#ifdef O_NONBLOCK
-  /* Opening a fifo without O_NONBLOCK can wait.
-     We don't want to wait.  But we don't want to mess wth O_NONBLOCK
-     except in the case of a fifo, on a system which handles it.  */
-  desc = stat (SSDATA (absname), &statbuf);
-  if (desc < 0)
-    return Qnil;
-  if (S_ISFIFO (statbuf.st_mode))
-    flags |= O_NONBLOCK;
-#endif
-  desc = emacs_open (SSDATA (absname), flags, 0);
-  if (desc < 0)
-    return Qnil;
-  emacs_close (desc);
-  return Qt;
-#endif /* not DOS_NT and not macintosh */
+  return (faccessat (AT_FDCWD, SSDATA (absname), R_OK, AT_EACCESS) == 0
+	  ? Qt : Qnil);
 }
 
-/* Having this before file-symlink-p mysteriously caused it to be forgotten
-   on the RT/PC.  */
 DEFUN ("file-writable-p", Ffile_writable_p, Sfile_writable_p, 1, 1, 0,
        doc: /* Return t if file FILENAME can be written or created by you.  */)
   (Lisp_Object filename)
@@ -2607,14 +2555,15 @@
     return call2 (handler, Qfile_writable_p, absname);
 
   encoded = ENCODE_FILE (absname);
-  if (check_existing (SSDATA (encoded)))
-    return (check_writable (SSDATA (encoded))
-	    ? Qt : Qnil);
+  if (check_writable (SSDATA (encoded), W_OK))
+    return Qt;
+  if (errno != ENOENT)
+    return Qnil;
 
   dir = Ffile_name_directory (absname);
+  eassert (!NILP (dir));
 #ifdef MSDOS
-  if (!NILP (dir))
-    dir = Fdirectory_file_name (dir);
+  dir = Fdirectory_file_name (dir);
 #endif /* MSDOS */
 
   dir = ENCODE_FILE (dir);
@@ -2622,10 +2571,9 @@
   /* The read-only attribute of the parent directory doesn't affect
      whether a file or directory can be created within it.  Some day we
      should check ACLs though, which do affect this.  */
-  return (access (SDATA (dir), D_OK) < 0) ? Qnil : Qt;
+  return file_directory_p (SDATA (dir)) ? Qt : Qnil;
 #else
-  return (check_writable (!NILP (dir) ? SSDATA (dir) : "")
-	  ? Qt : Qnil);
+  return check_writable (SSDATA (dir), W_OK | X_OK) ? Qt : Qnil;
 #endif
 }
 \f
@@ -2703,8 +2651,7 @@
 See `file-symlink-p' to distinguish symlinks.  */)
   (Lisp_Object filename)
 {
-  register Lisp_Object absname;
-  struct stat st;
+  Lisp_Object absname;
   Lisp_Object handler;
 
   absname = expand_and_dir_to_file (filename, BVAR (current_buffer, directory));
@@ -2717,9 +2664,20 @@
 
   absname = ENCODE_FILE (absname);
 
-  if (stat (SSDATA (absname), &st) < 0)
-    return Qnil;
-  return S_ISDIR (st.st_mode) ? Qt : Qnil;
+  return file_directory_p (SSDATA (absname)) ? Qt : Qnil;
+}
+
+/* Return true if FILE is a directory or a symlink to a directory.  */
+bool
+file_directory_p (char const *file)
+{
+#ifdef WINDOWSNT
+  /* This is cheaper than 'stat'.  */
+  return faccessat (AT_FDCWD, file, D_OK, AT_EACCESS) == 0;
+#else
+  struct stat st;
+  return stat (file, &st) == 0 && S_ISDIR (st.st_mode);
+#endif
 }
 
 DEFUN ("file-accessible-directory-p", Ffile_accessible_directory_p,
@@ -2733,21 +2691,49 @@
 searchable directory.  */)
   (Lisp_Object filename)
 {
+  Lisp_Object absname;
   Lisp_Object handler;
-  bool tem;
-  struct gcpro gcpro1;
+
+  CHECK_STRING (filename);
+  absname = Fexpand_file_name (filename, Qnil);
 
   /* If the file name has special constructs in it,
      call the corresponding file handler.  */
-  handler = Ffind_file_name_handler (filename, Qfile_accessible_directory_p);
+  handler = Ffind_file_name_handler (absname, Qfile_accessible_directory_p);
   if (!NILP (handler))
-    return call2 (handler, Qfile_accessible_directory_p, filename);
-
-  GCPRO1 (filename);
-  tem = (NILP (Ffile_directory_p (filename))
-	 || NILP (Ffile_executable_p (filename)));
-  UNGCPRO;
-  return tem ? Qnil : Qt;
+    return call2 (handler, Qfile_accessible_directory_p, absname);
+
+  absname = ENCODE_FILE (absname);
+  return file_accessible_directory_p (SSDATA (absname)) ? Qt : Qnil;
+}
+
+/* If FILE is a searchable directory or a symlink to a
+   searchable directory, return true.  Otherwise return
+   false and set errno to an error number.  */
+bool
+file_accessible_directory_p (char const *file)
+{
+  ptrdiff_t len = strlen (file);
+  bool ok;
+  int faccessat_errno;
+  USE_SAFE_ALLOCA;
+
+  /* Normally a file F is an accessible directory if F/. is accessible.
+     But omit the "/." if F is empty, as "" is not "/."; and omit the
+     "/" if F ends in "/", as on some platforms "/" != "//".  */
+  if (len)
+    {
+      char *buf = SAFE_ALLOCA (len + 3);
+      memcpy (buf, file, len);
+      strcpy (buf + len, "/." + (file[len - 1] == '/'));
+      file = buf;
+    }
+
+  ok = faccessat (AT_FDCWD, file, F_OK, AT_EACCESS) == 0;
+  faccessat_errno = errno;
+  SAFE_FREE ();
+  errno = faccessat_errno;
+  return ok;
 }
 
 DEFUN ("file-regular-p", Ffile_regular_p, Sfile_regular_p, 1, 1, 0,
@@ -3044,10 +3030,8 @@
     if (set_file_times (-1, SSDATA (encoded_absname), t, t))
       {
 #ifdef MSDOS
-        struct stat st;
-
         /* Setting times on a directory always fails.  */
-        if (stat (SSDATA (encoded_absname), &st) == 0 && S_ISDIR (st.st_mode))
+        if (file_directory_p (SSDATA (encoded_absname)))
           return Qnil;
 #endif
         report_file_error ("Setting file times", Fcons (absname, Qnil));

=== modified file 'src/lisp.h'
--- src/lisp.h	2012-10-21 07:23:34 +0000
+++ src/lisp.h	2012-10-22 02:43:16 +0000
@@ -3227,6 +3227,8 @@
 extern Lisp_Object restore_point_unwind (Lisp_Object);
 extern _Noreturn void report_file_error (const char *, Lisp_Object);
 extern void internal_delete_file (Lisp_Object);
+extern bool file_directory_p (const char *);
+extern bool file_accessible_directory_p (const char *);
 extern void syms_of_fileio (void);
 extern Lisp_Object make_temp_name (Lisp_Object, bool);
 extern Lisp_Object Qdelete_file;

=== modified file 'src/lread.c'
--- src/lread.c	2012-10-20 12:50:49 +0000
+++ src/lread.c	2012-10-22 02:43:16 +0000
@@ -1403,7 +1403,7 @@
 If SUFFIXES is non-nil, it should be a list of suffixes to append to
 file name when searching.
 If non-nil, PREDICATE is used instead of `file-readable-p'.
-PREDICATE can also be an integer to pass to the access(2) function,
+PREDICATE can also be an integer to pass to the faccessat(2) function,
 in which case file-name-handlers are ignored.
 This function will normally skip directories, so if you want it to find
 directories, make sure the PREDICATE function returns `dir-ok' for them.  */)
@@ -1441,7 +1441,6 @@
 int
 openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes, Lisp_Object *storeptr, Lisp_Object predicate)
 {
-  int fd;
   ptrdiff_t fn_size = 100;
   char buf[100];
   char *fn = buf;
@@ -1496,7 +1495,6 @@
 	{
 	  ptrdiff_t fnlen, lsuffix = SBYTES (XCAR (tail));
 	  Lisp_Object handler;
-	  bool exists;
 
 	  /* Concatenate path element/specified name with the suffix.
 	     If the directory starts with /:, remove that.  */
@@ -1520,6 +1518,7 @@
 	  handler = Ffind_file_name_handler (string, Qfile_exists_p);
 	  if ((!NILP (handler) || !NILP (predicate)) && !NATNUMP (predicate))
             {
+	      bool exists;
 	      if (NILP (predicate))
 		exists = !NILP (Ffile_readable_p (string));
 	      else
@@ -1541,37 +1540,40 @@
 	    }
 	  else
 	    {
-#ifndef WINDOWSNT
-	      struct stat st;
-#endif
+	      int fd;
 	      const char *pfn;
 
 	      encoded_fn = ENCODE_FILE (string);
 	      pfn = SSDATA (encoded_fn);
-#ifdef WINDOWSNT
-	      exists = access (pfn, F_OK) == 0 && access (pfn, D_OK) < 0;
-#else
-	      exists = (stat (pfn, &st) == 0 && ! S_ISDIR (st.st_mode));
-#endif
-	      if (exists)
+
+	      /* Check that we can access or open it.  */
+	      if (NATNUMP (predicate))
+		fd = (((XFASTINT (predicate) & ~INT_MAX) == 0
+		       && (faccessat (AT_FDCWD, pfn, XFASTINT (predicate),
+				      AT_EACCESS)
+			   == 0)
+		       && ! file_directory_p (pfn))
+		      ? 1 : -1);
+	      else
 		{
-		  /* Check that we can access or open it.  */
-		  if (NATNUMP (predicate))
-		    fd = (((XFASTINT (predicate) & ~INT_MAX) == 0
-			   && access (pfn, XFASTINT (predicate)) == 0)
-			  ? 1 : -1);
-		  else
-		    fd = emacs_open (pfn, O_RDONLY, 0);
-
-		  if (fd >= 0)
+		  struct stat st;
+		  fd = emacs_open (pfn, O_RDONLY, 0);
+		  if (0 <= fd
+		      && (fstat (fd, &st) != 0 || S_ISDIR (st.st_mode)))
 		    {
-		      /* We succeeded; return this descriptor and filename.  */
-		      if (storeptr)
-			*storeptr = string;
-		      UNGCPRO;
-		      return fd;
+		      emacs_close (fd);
+		      fd = -1;
 		    }
 		}
+
+	      if (fd >= 0)
+		{
+		  /* We succeeded; return this descriptor and filename.  */
+		  if (storeptr)
+		    *storeptr = string;
+		  UNGCPRO;
+		  return fd;
+		}
 	    }
 	}
       if (absolute)
@@ -4087,9 +4089,8 @@
       if (STRINGP (dirfile))
         {
           dirfile = Fdirectory_file_name (dirfile);
-          if (access (SSDATA (dirfile), 0) < 0)
-            dir_warning ("Warning: Lisp directory `%s' does not exist.\n",
-                         XCAR (path_tail));
+          if (! file_accessible_directory_p (SSDATA (dirfile)))
+            dir_warning ("Lisp directory", XCAR (path_tail));
         }
     }
 }
@@ -4201,11 +4202,11 @@
 	      Lisp_Object tem, tem1;
 
               /* Add to the path the lisp subdir of the installation
-                 dir, if it exists.  Note: in out-of-tree builds,
+                 dir, if it is accessible.  Note: in out-of-tree builds,
                  this directory is empty save for Makefile.  */
               tem = Fexpand_file_name (build_string ("lisp"),
                                        Vinstallation_directory);
-              tem1 = Ffile_exists_p (tem);
+              tem1 = Ffile_accessible_directory_p (tem);
               if (!NILP (tem1))
                 {
                   if (NILP (Fmember (tem, Vload_path)))
@@ -4222,10 +4223,10 @@
                    Lisp dirs instead.  */
                 Vload_path = nconc2 (Vload_path, dump_path);
 
-              /* Add leim under the installation dir, if it exists. */
+              /* Add leim under the installation dir, if it is accessible. */
               tem = Fexpand_file_name (build_string ("leim"),
                                        Vinstallation_directory);
-              tem1 = Ffile_exists_p (tem);
+              tem1 = Ffile_accessible_directory_p (tem);
               if (!NILP (tem1))
                 {
                   if (NILP (Fmember (tem, Vload_path)))
@@ -4237,7 +4238,7 @@
                 {
                   tem = Fexpand_file_name (build_string ("site-lisp"),
                                            Vinstallation_directory);
-                  tem1 = Ffile_exists_p (tem);
+                  tem1 = Ffile_accessible_directory_p (tem);
                   if (!NILP (tem1))
                     {
                       if (NILP (Fmember (tem, Vload_path)))
@@ -4282,7 +4283,7 @@
                         {
                           tem = Fexpand_file_name (build_string ("site-lisp"),
                                                    Vsource_directory);
-                          tem1 = Ffile_exists_p (tem);
+                          tem1 = Ffile_accessible_directory_p (tem);
                           if (!NILP (tem1))
                             {
                               if (NILP (Fmember (tem, Vload_path)))
@@ -4338,21 +4339,27 @@
   Vloads_in_progress = Qnil;
 }
 
-/* Print a warning, using format string FORMAT, that directory DIRNAME
-   does not exist.  Print it on stderr and put it in *Messages*.  */
+/* Print a warning that directory intended for use USE and with name
+   DIRNAME cannot be accessed.  On entry, errno should correspond to
+   the access failure.  Print the warning on stderr and put it in
+   *Messages*.  */
 
 void
-dir_warning (const char *format, Lisp_Object dirname)
+dir_warning (char const *use, Lisp_Object dirname)
 {
-  fprintf (stderr, format, SDATA (dirname));
+  static char const format[] = "Warning: %s `%s': %s\n";
+  char const *diagnostic = strerror (errno);
+  fprintf (stderr, format, use, SSDATA (dirname), diagnostic);
 
   /* Don't log the warning before we've initialized!!  */
   if (initialized)
     {
       USE_SAFE_ALLOCA;
-      char *buffer = SAFE_ALLOCA (SBYTES (dirname)
-				  + strlen (format) - (sizeof "%s" - 1) + 1);
-      ptrdiff_t message_len = esprintf (buffer, format, SDATA (dirname));
+      char *buffer = SAFE_ALLOCA (sizeof format - 3 * (sizeof "%s" - 1)
+				  + strlen (use) + SBYTES (dirname)
+				  + strlen (diagnostic));
+      ptrdiff_t message_len = esprintf (buffer, format, use, SSDATA (dirname),
+					diagnostic);
       message_dolog (buffer, message_len, 0, STRING_MULTIBYTE (dirname));
       SAFE_FREE ();
     }

=== modified file 'src/nsterm.m'
--- src/nsterm.m	2012-10-18 18:54:37 +0000
+++ src/nsterm.m	2012-10-22 02:36:17 +0000
@@ -4099,8 +4099,6 @@
 
         color_file = Fexpand_file_name (build_string ("rgb.txt"),
                          Fsymbol_value (intern ("data-directory")));
-        if (NILP (Ffile_readable_p (color_file)))
-          fatal ("Could not find %s.\n", SDATA (color_file));
 
         color_map = Fx_load_color_file (color_file);
         if (NILP (color_map))

=== modified file 'src/process.c'
--- src/process.c	2012-10-19 19:25:18 +0000
+++ src/process.c	2012-10-20 01:13:35 +0000
@@ -204,7 +204,7 @@
 #ifndef NON_BLOCKING_CONNECT
 #ifdef HAVE_SELECT
 #if defined (HAVE_GETPEERNAME) || defined (GNU_LINUX)
-#if defined (O_NONBLOCK) || defined (O_NDELAY)
+#if O_NONBLOCK || O_NDELAY
 #if defined (EWOULDBLOCK) || defined (EINPROGRESS)
 #define NON_BLOCKING_CONNECT
 #endif /* EWOULDBLOCK || EINPROGRESS */
@@ -651,7 +651,7 @@
 	PTY_OPEN;
 #else /* no PTY_OPEN */
 	{
-#  ifdef O_NONBLOCK
+#  if O_NONBLOCK
 	  fd = emacs_open (pty_name, O_RDWR | O_NONBLOCK, 0);
 #  else
 	  fd = emacs_open (pty_name, O_RDWR | O_NDELAY, 0);
@@ -668,7 +668,7 @@
 #else
 	    sprintf (pty_name, "/dev/tty%c%x", c, i);
 #endif /* no PTY_TTY_NAME_SPRINTF */
-	    if (access (pty_name, 6) != 0)
+	    if (faccessat (AT_FDCWD, pty_name, R_OK | W_OK, AT_EACCESS) != 0)
 	      {
 		emacs_close (fd);
 # ifndef __sgi
@@ -1621,7 +1621,7 @@
 #if ! defined (USG) || defined (USG_SUBTTY_WORKS)
       /* On most USG systems it does not work to open the pty's tty here,
 	 then close it and reopen it in the child.  */
-#ifdef O_NOCTTY
+#if O_NOCTTY
       /* Don't let this terminal become our controlling terminal
 	 (in case we don't have one).  */
       forkout = forkin = emacs_open (pty_name, O_RDWR | O_NOCTTY, 0);
@@ -1675,11 +1675,11 @@
     }
 #endif
 
-#ifdef O_NONBLOCK
+#if O_NONBLOCK
   fcntl (inchannel, F_SETFL, O_NONBLOCK);
   fcntl (outchannel, F_SETFL, O_NONBLOCK);
 #else
-#ifdef O_NDELAY
+#if O_NDELAY
   fcntl (inchannel, F_SETFL, O_NDELAY);
   fcntl (outchannel, F_SETFL, O_NDELAY);
 #endif
@@ -1967,7 +1967,7 @@
 #if ! defined (USG) || defined (USG_SUBTTY_WORKS)
       /* On most USG systems it does not work to open the pty's tty here,
 	 then close it and reopen it in the child.  */
-#ifdef O_NOCTTY
+#if O_NOCTTY
       /* Don't let this terminal become our controlling terminal
 	 (in case we don't have one).  */
       int forkout = emacs_open (pty_name, O_RDWR | O_NOCTTY, 0);
@@ -1987,11 +1987,11 @@
     }
 #endif /* HAVE_PTYS */
 
-#ifdef O_NONBLOCK
+#if O_NONBLOCK
   fcntl (inchannel, F_SETFL, O_NONBLOCK);
   fcntl (outchannel, F_SETFL, O_NONBLOCK);
 #else
-#ifdef O_NDELAY
+#if O_NDELAY
   fcntl (inchannel, F_SETFL, O_NDELAY);
   fcntl (outchannel, F_SETFL, O_NDELAY);
 #endif
@@ -2951,7 +2951,7 @@
     {
       /* Don't support network sockets when non-blocking mode is
 	 not available, since a blocked Emacs is not useful.  */
-#if !defined (O_NONBLOCK) && !defined (O_NDELAY)
+#if !O_NONBLOCK && !O_NDELAY
       error ("Network servers not supported");
 #else
       is_server = 1;
@@ -3217,7 +3217,7 @@
 #ifdef NON_BLOCKING_CONNECT
       if (is_non_blocking_client)
 	{
-#ifdef O_NONBLOCK
+#if O_NONBLOCK
 	  ret = fcntl (s, F_SETFL, O_NONBLOCK);
 #else
 	  ret = fcntl (s, F_SETFL, O_NDELAY);
@@ -3434,10 +3434,10 @@
 
   chan_process[inch] = proc;
 
-#ifdef O_NONBLOCK
+#if O_NONBLOCK
   fcntl (inch, F_SETFL, O_NONBLOCK);
 #else
-#ifdef O_NDELAY
+#if O_NDELAY
   fcntl (inch, F_SETFL, O_NDELAY);
 #endif
 #endif
@@ -4169,10 +4169,10 @@
 
   chan_process[s] = proc;
 
-#ifdef O_NONBLOCK
+#if O_NONBLOCK
   fcntl (s, F_SETFL, O_NONBLOCK);
 #else
-#ifdef O_NDELAY
+#if O_NDELAY
   fcntl (s, F_SETFL, O_NDELAY);
 #endif
 #endif
@@ -4873,11 +4873,11 @@
 #endif
 	      /* ISC 4.1 defines both EWOULDBLOCK and O_NONBLOCK,
 		 and Emacs uses O_NONBLOCK, so what we get is EAGAIN.  */
-#ifdef O_NONBLOCK
+#if O_NONBLOCK
 	      else if (nread == -1 && errno == EAGAIN)
 		;
 #else
-#ifdef O_NDELAY
+#if O_NDELAY
 	      else if (nread == -1 && errno == EAGAIN)
 		;
 	      /* Note that we cannot distinguish between no input
@@ -7363,7 +7363,7 @@
 #ifdef HAVE_GETSOCKNAME
    ADD_SUBFEATURE (QCservice, Qt);
 #endif
-#if defined (O_NONBLOCK) || defined (O_NDELAY)
+#if O_NONBLOCK || O_NDELAY
    ADD_SUBFEATURE (QCserver, Qt);
 #endif
 

=== modified file 'src/sysdep.c'
--- src/sysdep.c	2012-10-19 19:25:18 +0000
+++ src/sysdep.c	2012-10-20 01:13:35 +0000
@@ -1216,7 +1216,7 @@
              old_fcntl_owner[fileno (tty_out->input)]);
     }
 #endif /* F_SETOWN */
-#ifdef O_NDELAY
+#if O_NDELAY
   fcntl (fileno (tty_out->input), F_SETFL,
          fcntl (fileno (tty_out->input), F_GETFL, 0) & ~O_NDELAY);
 #endif
@@ -2312,12 +2312,12 @@
 
   fd = emacs_open ((char*) port,
 		   O_RDWR
-#ifdef O_NONBLOCK
+#if O_NONBLOCK
 		   | O_NONBLOCK
 #else
 		   | O_NDELAY
 #endif
-#ifdef O_NOCTTY
+#if O_NOCTTY
 		   | O_NOCTTY
 #endif
 		   , 0);

=== modified file 'src/term.c'
--- src/term.c	2012-10-19 19:25:18 +0000
+++ src/term.c	2012-10-20 01:13:35 +0000
@@ -3023,7 +3023,7 @@
     int fd;
     FILE *file;
 
-#ifdef O_IGNORE_CTTY
+#if O_IGNORE_CTTY
     if (!ctty)
       /* Open the terminal device.  Don't recognize it as our
          controlling terminal, and don't make it the controlling tty
@@ -3054,7 +3054,7 @@
                      name);
       }
 
-#ifndef O_IGNORE_CTTY
+#if !O_IGNORE_CTTY
     if (!ctty)
       dissociate_if_controlling_tty (fd);
 #endif

=== modified file 'src/w32.c'
--- src/w32.c	2012-10-19 19:25:18 +0000
+++ src/w32.c	2012-10-20 01:13:35 +0000
@@ -1597,7 +1597,7 @@
 	 see if it succeeds.  But I think that's too much to ask.  */
 
       /* MSVCRT's _access crashes with D_OK.  */
-      if (tmp && sys_access (tmp, D_OK) == 0)
+      if (tmp && sys_faccessat (AT_FDCWD, tmp, D_OK, AT_EACCESS) == 0)
 	{
 	  char * var = alloca (strlen (tmp) + 8);
 	  sprintf (var, "TMPDIR=%s", tmp);
@@ -2714,10 +2714,16 @@
    long file names.  */
 
 int
-sys_access (const char * path, int mode)
+sys_faccessat (int dirfd, const char * path, int mode, int flags)
 {
   DWORD attributes;
 
+  if (dirfd != AT_FDCWD)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
   /* MSVCRT implementation of 'access' doesn't recognize D_OK, and its
      newer versions blow up when passed D_OK.  */
   path = map_w32_filename (path, NULL);
@@ -2960,7 +2966,7 @@
 	{
 	  int save_errno = errno;
 	  p[0] = first_char[i];
-	  if (sys_access (template, 0) < 0)
+	  if (sys_faccessat (AT_FDCWD, template, F_OK, AT_EACCESS) < 0)
 	    {
 	      errno = save_errno;
 	      return template;
@@ -4011,7 +4017,7 @@
     {
       /* Non-absolute FILENAME is understood as being relative to
 	 LINKNAME's directory.  We need to prepend that directory to
-	 FILENAME to get correct results from sys_access below, since
+	 FILENAME to get correct results from sys_faccessat below, since
 	 otherwise it will interpret FILENAME relative to the
 	 directory where the Emacs process runs.  Note that
 	 make-symbolic-link always makes sure LINKNAME is a fully
@@ -4025,10 +4031,10 @@
 	strncpy (tem, linkfn, p - linkfn);
       tem[p - linkfn] = '\0';
       strcat (tem, filename);
-      dir_access = sys_access (tem, D_OK);
+      dir_access = sys_faccessat (AT_FDCWD, tem, D_OK, AT_EACCESS);
     }
   else
-    dir_access = sys_access (filename, D_OK);
+    dir_access = sys_faccessat (AT_FDCWD, filename, D_OK, AT_EACCESS);
 
   /* Since Windows distinguishes between symlinks to directories and
      to files, we provide a kludgy feature: if FILENAME doesn't

=== modified file 'src/xrdb.c'
--- src/xrdb.c	2012-10-19 19:25:18 +0000
+++ src/xrdb.c	2012-10-22 05:29:15 +0000
@@ -41,7 +41,6 @@
 #ifdef HAVE_PWD_H
 #include <pwd.h>
 #endif
-#include <sys/stat.h>
 
 #ifdef USE_MOTIF
 /* For Vdouble_click_time.  */
@@ -50,7 +49,6 @@
 
 char *x_get_string_resource (XrmDatabase rdb, const char *name,
 			     const char *class);
-static int file_p (const char *filename);
 
 \f
 /* X file search path processing.  */
@@ -108,7 +106,7 @@
 		database associated with display.
 		(This is x_customization_string.)
 
-   Return the expanded file name if it exists and is readable, and
+   Return the resource database if its file was read successfully, and
    refers to %L only when the LANG environment variable is set, or
    otherwise provided by X.
 
@@ -117,10 +115,11 @@
 
    Return NULL otherwise.  */
 
-static char *
-magic_file_p (const char *string, ptrdiff_t string_len, const char *class,
-	      const char *escaped_suffix)
+static XrmDatabase
+magic_db (const char *string, ptrdiff_t string_len, const char *class,
+	  const char *escaped_suffix)
 {
+  XrmDatabase db;
   char *lang = getenv ("LANG");
 
   ptrdiff_t path_size = 100;
@@ -217,14 +216,9 @@
     }
 
   path[path_len] = '\0';
-
-  if (! file_p (path))
-    {
-      xfree (path);
-      return NULL;
-    }
-
-  return path;
+  db = XrmGetFileDatabase (path);
+  xfree (path);
+  return db;
 }
 
 
@@ -258,22 +252,11 @@
 }
 
 
-static int
-file_p (const char *filename)
-{
-  struct stat status;
-
-  return (access (filename, 4) == 0             /* exists and is readable */
-	  && stat (filename, &status) == 0	/* get the status */
-	  && (S_ISDIR (status.st_mode)) == 0);	/* not a directory */
-}
-
-
 /* Find the first element of SEARCH_PATH which exists and is readable,
    after expanding the %-escapes.  Return 0 if we didn't find any, and
    the path name of the one we found otherwise.  */
 
-static char *
+static XrmDatabase
 search_magic_path (const char *search_path, const char *class,
 		   const char *escaped_suffix)
 {
@@ -286,18 +269,16 @@
 
       if (p > s)
 	{
-	  char *path = magic_file_p (s, p - s, class, escaped_suffix);
-	  if (path)
-	    return path;
+	  XrmDatabase db = magic_db (s, p - s, class, escaped_suffix);
+	  if (db)
+	    return db;
 	}
       else if (*p == ':')
 	{
-	  char *path;
-
-	  s = "%N%S";
-	  path = magic_file_p (s, strlen (s), class, escaped_suffix);
-	  if (path)
-	    return path;
+	  static char const ns[] = "%N%S";
+	  XrmDatabase db = magic_db (ns, strlen (ns), class, escaped_suffix);
+	  if (db)
+	    return db;
 	}
 
       if (*p == ':')
@@ -312,21 +293,12 @@
 static XrmDatabase
 get_system_app (const char *class)
 {
-  XrmDatabase db = NULL;
   const char *path;
-  char *p;
 
   path = getenv ("XFILESEARCHPATH");
   if (! path) path = PATH_X_DEFAULTS;
 
-  p = search_magic_path (path, class, 0);
-  if (p)
-    {
-      db = XrmGetFileDatabase (p);
-      xfree (p);
-    }
-
-  return db;
+  return search_magic_path (path, class, 0);
 }
 
 
@@ -340,35 +312,40 @@
 static XrmDatabase
 get_user_app (const char *class)
 {
+  XrmDatabase db = 0;
   const char *path;
-  char *file = 0;
-  char *free_it = 0;
 
   /* Check for XUSERFILESEARCHPATH.  It is a path of complete file
      names, not directories.  */
-  if (((path = getenv ("XUSERFILESEARCHPATH"))
-       && (file = search_magic_path (path, class, 0)))
+  path = getenv ("XUSERFILESEARCHPATH");
+  if (path)
+    db = search_magic_path (path, class, 0);
 
+  if (! db)
+    {
       /* Check for APPLRESDIR; it is a path of directories.  In each,
 	 we have to search for LANG/CLASS and then CLASS.  */
-      || ((path = getenv ("XAPPLRESDIR"))
-	  && ((file = search_magic_path (path, class, "/%L/%N"))
-	      || (file = search_magic_path (path, class, "/%N"))))
+      path = getenv ("XAPPLRESDIR");
+      if (path)
+	{
+	  db = search_magic_path (path, class, "/%L/%N");
+	  if (!db)
+	    db = search_magic_path (path, class, "/%N");
+	}
+    }
 
+  if (! db)
+    {
       /* Check in the home directory.  This is a bit of a hack; let's
 	 hope one's home directory doesn't contain any %-escapes.  */
-      || (free_it = gethomedir (),
-	  ((file = search_magic_path (free_it, class, "%L/%N"))
-	   || (file = search_magic_path (free_it, class, "%N")))))
-    {
-      XrmDatabase db = XrmGetFileDatabase (file);
-      xfree (file);
-      xfree (free_it);
-      return db;
+      char *home = gethomedir ();
+      db = search_magic_path (home, class, "%L/%N");
+      if (! db)
+	db = search_magic_path (home, class, "%N");
+      xfree (home);
     }
 
-  xfree (free_it);
-  return NULL;
+  return db;
 }
 
 


^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-22  6:03                                                 ` Paul Eggert
@ 2012-10-22 17:19                                                   ` Eli Zaretskii
  2012-10-22 20:33                                                     ` Paul Eggert
  0 siblings, 1 reply; 47+ messages in thread
From: Eli Zaretskii @ 2012-10-22 17:19 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 12632

> Date: Sun, 21 Oct 2012 23:03:30 -0700
> From: Paul Eggert <eggert@cs.ucla.edu>
> Cc: 12632@debbugs.gnu.org
> 
> +/* If FILE is a searchable directory or a symlink to a
> +   searchable directory, return true.  Otherwise return
> +   false and set errno to an error number.  */
> +bool
> +file_accessible_directory_p (char const *file)
> +{
> +  ptrdiff_t len = strlen (file);
> +  bool ok;
> +  int faccessat_errno;
> +  USE_SAFE_ALLOCA;
> +
> +  /* Normally a file F is an accessible directory if F/. is accessible.
> +     But omit the "/." if F is empty, as "" is not "/."; and omit the
> +     "/" if F ends in "/", as on some platforms "/" != "//".  */
> +  if (len)
> +    {
> +      char *buf = SAFE_ALLOCA (len + 3);
> +      memcpy (buf, file, len);
> +      strcpy (buf + len, "/." + (file[len - 1] == '/'));
> +      file = buf;
> +    }

I think this should use IS_DIRECTORY_SEP instead of a literal '/'.

>  int
> -sys_access (const char * path, int mode)
> +sys_faccessat (int dirfd, const char * path, int mode, int flags)
>  {
>    DWORD attributes;
>  
> +  if (dirfd != AT_FDCWD)
> +    {
> +      errno = EINVAL;
> +      return -1;
> +    }

My reading of Posix is that this should set errno to EBADF, not
EINVAL.

There's also a problem that sys_faccessat won't have a prototype, so
compiling with -std=gnu99 will issue a warning, but that's not fatal,
and we can add that later.





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-22 17:19                                                   ` Eli Zaretskii
@ 2012-10-22 20:33                                                     ` Paul Eggert
  2012-10-22 21:04                                                       ` Eli Zaretskii
  0 siblings, 1 reply; 47+ messages in thread
From: Paul Eggert @ 2012-10-22 20:33 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 12632

On 10/22/2012 10:19 AM, Eli Zaretskii wrote:
> My reading of Posix is that this should set errno to EBADF, not
> EINVAL.

Thanks, I'll fix that.

>> +  /* Normally a file F is an accessible directory if F/. is accessible.
>> +     But omit the "/." if F is empty, as "" is not "/."; and omit the
>> +     "/" if F ends in "/", as on some platforms "/" != "//".  */
>> +  if (len)
>> +    {
>> +      char *buf = SAFE_ALLOCA (len + 3);
>> +      memcpy (buf, file, len);
>> +      strcpy (buf + len, "/." + (file[len - 1] == '/'));
>> +      file = buf;
>> +    }
> 
> I think this should use IS_DIRECTORY_SEP instead of a literal '/'.

It should work as-is, no?  It's true that on Windows, backslash is
also a directory separator.  But I don't see any harm done if we append
'/.' to a file name that ends in backslash.  For example, if the file
name is 'FOO\' and we append '/.' to make it 'FOO\/.', the test should
succeed if FOO is a searchable directory and should fail otherwise, which
is what is wanted here.

Another way to put it is that the test against trailing slash is only
for the benefit of platforms where '/' != '//', and Windows is not
such a platform.






^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-22 20:33                                                     ` Paul Eggert
@ 2012-10-22 21:04                                                       ` Eli Zaretskii
  2012-10-22 21:30                                                         ` Paul Eggert
  0 siblings, 1 reply; 47+ messages in thread
From: Eli Zaretskii @ 2012-10-22 21:04 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 12632

> Date: Mon, 22 Oct 2012 13:33:34 -0700
> From: Paul Eggert <eggert@cs.ucla.edu>
> CC: rgm@gnu.org, 12632@debbugs.gnu.org
> 
> >> +  /* Normally a file F is an accessible directory if F/. is accessible.
> >> +     But omit the "/." if F is empty, as "" is not "/."; and omit the
> >> +     "/" if F ends in "/", as on some platforms "/" != "//".  */
> >> +  if (len)
> >> +    {
> >> +      char *buf = SAFE_ALLOCA (len + 3);
> >> +      memcpy (buf, file, len);
> >> +      strcpy (buf + len, "/." + (file[len - 1] == '/'));
> >> +      file = buf;
> >> +    }
> > 
> > I think this should use IS_DIRECTORY_SEP instead of a literal '/'.
> 
> It should work as-is, no?  It's true that on Windows, backslash is
> also a directory separator.  But I don't see any harm done if we append
> '/.' to a file name that ends in backslash.

I meant the test (file[len - 1] == '/').

> For example, if the file name is 'FOO\' and we append '/.' to make
> it 'FOO\/.', the test should succeed if FOO is a searchable
> directory

It doesn't necessarily succeed.  Windows file APIs are picky wrt
trailing slashes.  And the result is ugly, and the code looks wrong.

We use IS_DIRECTORY_SEP in other places, so why avoid it here?

> Another way to put it is that the test against trailing slash is only
> for the benefit of platforms where '/' != '//', and Windows is not
> such a platform.

Yes, Windows _is_ such a platform, "//foo/bar" and "/foo/bar" are very
different things there.





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-22 21:04                                                       ` Eli Zaretskii
@ 2012-10-22 21:30                                                         ` Paul Eggert
  2012-10-23  0:40                                                           ` Stefan Monnier
  2012-10-23  3:47                                                           ` Eli Zaretskii
  0 siblings, 2 replies; 47+ messages in thread
From: Paul Eggert @ 2012-10-22 21:30 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 12632

On 10/22/2012 02:04 PM, Eli Zaretskii wrote:
> Windows _is_ such a platform, "//foo/bar" and "/foo/bar" are very
> different things there.

What is the difference, from the point of view of the test?
The test is concerned only with trailing separators, not with
leading ones, so it shouldn't matter that "//foo/bar" and "/foo/bar"
might be different.

Is it possible in Windows that the searchability of the file named "//"
differs from the searchability of "/"?  Or that the searchability
of "\/" differs from that of "\"?  Could you explain a scenario
where that might happen?  (By "searchable", I mean that the file
is a directory and one can resolve file names within that directory.)
Emacs currently assumes that all directories are searchable on
Windows, so any such scenario would break Emacs elsewhere, right?
If "/" and "//" and "\/" and "\" are always searchable,
it shouldn't matter which Emacs tested.

> Windows file APIs are picky wrt trailing slashes.

Can you give an example of how that might affect the test?
It's OK if Windows is picky about trailing slashes -- POSIX platforms
are too -- all that matters is whether the pickiness derails the test.

> We use IS_DIRECTORY_SEP in other places, so why avoid it here?

If it's not needed, it makes the code a bit harder to read.





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-22 21:30                                                         ` Paul Eggert
@ 2012-10-23  0:40                                                           ` Stefan Monnier
  2012-10-23  1:46                                                             ` Paul Eggert
  2012-10-23  3:47                                                           ` Eli Zaretskii
  1 sibling, 1 reply; 47+ messages in thread
From: Stefan Monnier @ 2012-10-23  0:40 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 12632

>> We use IS_DIRECTORY_SEP in other places, so why avoid it here?
> If it's not needed, it makes the code a bit harder to read.

I don't see why.  The name says very clearly what it is intended to do.
On the contrary, it seems more clear than comparing with '/' which only
makes sense if you happen to know that / is the special char used as
directory-separator.

        Stefan





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-23  0:40                                                           ` Stefan Monnier
@ 2012-10-23  1:46                                                             ` Paul Eggert
  2012-10-23  3:49                                                               ` Eli Zaretskii
  0 siblings, 1 reply; 47+ messages in thread
From: Paul Eggert @ 2012-10-23  1:46 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: 12632

On 10/22/2012 05:40 PM, Stefan Monnier wrote:
>>> We use IS_DIRECTORY_SEP in other places, so why avoid it here?
>> > If it's not needed, it makes the code a bit harder to read.
> I don't see why.  The name says very clearly what it is intended to do.
> On the contrary, it seems more clear than comparing with '/' which only
> makes sense if you happen to know that / is the special char used as
> directory-separator.

Using IS_DIRECTORY_SEP indicates that it's necessary to
process a file name using a system-dependent interpretation
rather than the standard POSIX interpretation.  When
plain X=='/' would do, it can mislead the reader if the code
does IS_DIRECTORY_SEP (X), as the reader may naturally (and
mistakenly) infer that the code was written that way because
it must treat '\' specially on Windows.  It's primarily
this clarity, not the brevity, that I was referring to.






^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-22 21:30                                                         ` Paul Eggert
  2012-10-23  0:40                                                           ` Stefan Monnier
@ 2012-10-23  3:47                                                           ` Eli Zaretskii
  2012-10-23  5:07                                                             ` Paul Eggert
  1 sibling, 1 reply; 47+ messages in thread
From: Eli Zaretskii @ 2012-10-23  3:47 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 12632

> Date: Mon, 22 Oct 2012 14:30:41 -0700
> From: Paul Eggert <eggert@cs.ucla.edu>
> CC: rgm@gnu.org, 12632@debbugs.gnu.org
> 
> Is it possible in Windows that the searchability of the file named "//"
> differs from the searchability of "/"?  Or that the searchability
> of "\/" differs from that of "\"?

Both is true.  // or \\ or \/ starts a UNC, and Windows expects the
following to be the name of a remote machine.  / or \ is just the root
directory of the current drive.

> Emacs currently assumes that all directories are searchable on
> Windows, so any such scenario would break Emacs elsewhere, right?

expand-file-name already handles the UNC case.

> > Windows file APIs are picky wrt trailing slashes.
> 
> Can you give an example of how that might affect the test?

The call to faccessat could fail, just because of the "\/." at the
end of the file name.

> > We use IS_DIRECTORY_SEP in other places, so why avoid it here?
> 
> If it's not needed, it makes the code a bit harder to read.

I disagree.





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-23  1:46                                                             ` Paul Eggert
@ 2012-10-23  3:49                                                               ` Eli Zaretskii
  0 siblings, 0 replies; 47+ messages in thread
From: Eli Zaretskii @ 2012-10-23  3:49 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 12632

> Date: Mon, 22 Oct 2012 18:46:56 -0700
> From: Paul Eggert <eggert@cs.ucla.edu>
> CC: Eli Zaretskii <eliz@gnu.org>, 12632@debbugs.gnu.org
> 
> On 10/22/2012 05:40 PM, Stefan Monnier wrote:
> >>> We use IS_DIRECTORY_SEP in other places, so why avoid it here?
> >> > If it's not needed, it makes the code a bit harder to read.
> > I don't see why.  The name says very clearly what it is intended to do.
> > On the contrary, it seems more clear than comparing with '/' which only
> > makes sense if you happen to know that / is the special char used as
> > directory-separator.
> 
> Using IS_DIRECTORY_SEP indicates that it's necessary to
> process a file name using a system-dependent interpretation
> rather than the standard POSIX interpretation.  When
> plain X=='/' would do, it can mislead the reader if the code
> does IS_DIRECTORY_SEP (X), as the reader may naturally (and
> mistakenly) infer that the code was written that way because
> it must treat '\' specially on Windows.

Which is the case, so there's no misleading.

> It's primarily this clarity, not the brevity, that I was referring
> to.

This argument is a waste of everybody's time.  Feel free to leave the
test as it is, I will fix that after you commit the changeset.





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-23  3:47                                                           ` Eli Zaretskii
@ 2012-10-23  5:07                                                             ` Paul Eggert
  2012-10-23 16:44                                                               ` Eli Zaretskii
  0 siblings, 1 reply; 47+ messages in thread
From: Paul Eggert @ 2012-10-23  5:07 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 12632

On 10/22/2012 08:47 PM, Eli Zaretskii wrote:
>> > Is it possible in Windows that the searchability of the file named "//"
>> > differs from the searchability of "/"?  Or that the searchability
>> > of "\/" differs from that of "\"?
> Both is true.  // or \\ or \/ starts a UNC, and Windows expects the
> following to be the name of a remote machine.  / or \ is just the root
> directory of the current drive.

Sorry, apparently I wasn't clear.  I wasn't asking about names like
//remotemachine or /directory, as names like that should not be
affected by this issue.  I was asking about bare // and bare /
and their aliases with backslash.  Let me try to be more concrete.
Which of the following calls can fail on Windows in the current
Emacs trunk, and why?

   sys_access ("/", D_OK)     sys_access ("/.", D_OK)
   sys_access ("\\", D_OK)    sys_access ("\\.", D_OK)
   sys_access ("//", D_OK)    sys_access ("//.", D_OK)
   sys_access ("/\\", D_OK)   sys_access ("/\\.", D_OK)
   sys_access ("\\/", D_OK)   sys_access ("\\/.", D_OK)
   sys_access ("\\\\", D_OK)  sys_access ("\\\\.", D_OK)
   sys_access ("///", D_OK)   sys_access ("///.", D_OK)

>> Can you give an example of how that might affect the test?
> 
> The call to faccessat could fail, just because of the "\/." at the
> end of the file name.

Sorry, I don't follow this example.  The code doesn't append
backslash-slash-dot; it appends slash-dot.  Are you saying that
in the current trunk, sys_access ("\\", D_OK) can succeed
but sys_access ("\\/.", D_OK) can fail when presented with
the same file system?





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-23  5:07                                                             ` Paul Eggert
@ 2012-10-23 16:44                                                               ` Eli Zaretskii
  2012-10-23 19:27                                                                 ` Paul Eggert
  0 siblings, 1 reply; 47+ messages in thread
From: Eli Zaretskii @ 2012-10-23 16:44 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 12632

> Date: Mon, 22 Oct 2012 22:07:16 -0700
> From: Paul Eggert <eggert@cs.ucla.edu>
> CC: rgm@gnu.org, 12632@debbugs.gnu.org
> 
> Which of the following calls can fail on Windows in the current
> Emacs trunk, and why?
> 
>    sys_access ("/", D_OK)     sys_access ("/.", D_OK)
>    sys_access ("\\", D_OK)    sys_access ("\\.", D_OK)
>    sys_access ("//", D_OK)    sys_access ("//.", D_OK)
>    sys_access ("/\\", D_OK)   sys_access ("/\\.", D_OK)
>    sys_access ("\\/", D_OK)   sys_access ("\\/.", D_OK)
>    sys_access ("\\\\", D_OK)  sys_access ("\\\\.", D_OK)
>    sys_access ("///", D_OK)   sys_access ("///.", D_OK)

Sorry, I cannot afford doing this research.  Some of these file names
have no meaning at all, so they are prone to undefined behavior.
Others, like "//.", are downright dangerous, because "\\.\" begins a
device name on Windows.  With these arcana notoriously
under-documented by MS, it is anybody's guess what such names can do
in what APIs.

I'm trying to avoid potential problems before they have a chance to
screw some user.  I don't see a reason to seek a 110% provable test
case, when the issue at hand is a single use of a macro that is used
all over the place in Emacs.

There isn't a single comparison with a literal '/' in fileio.c, with
the sole exception of supporting the "/:" magic.  More than 50
instances of IS_DIRECTORY_SEP, just in that one file (and several
dozens more elsewhere in Emacs), are the evidence that
IS_DIRECTORY_SEP _is_ the norm, whereas a literal slash is an
exception.  Contrary to your original claim, using '/' will confuse
the reader into thinking that this particular function does something
special, like the code which looks for the "/:" magic, while using
IS_DIRECTORY_SEP will look like any other code we have, and will also
DTRT instead of relying on "maybe it will work".

> > The call to faccessat could fail, just because of the "\/." at the
> > end of the file name.
> 
> Sorry, I don't follow this example.  The code doesn't append
> backslash-slash-dot; it appends slash-dot.  Are you saying that
> in the current trunk, sys_access ("\\", D_OK) can succeed
> but sys_access ("\\/.", D_OK) can fail when presented with
> the same file system?

No, I'm saying that if the function is called with "d:\foo\", it will
call faccessat with "d:\foo\/." as the argument, which has "\/." at
the end of the file name.  Testing with IS_DIRECTORY_SEP will avoid
this and call faccessat with "d:\foo/.".  The latter is a valid file
name, while the former is not.





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-23 16:44                                                               ` Eli Zaretskii
@ 2012-10-23 19:27                                                                 ` Paul Eggert
  2012-10-23 19:50                                                                   ` Eli Zaretskii
  2012-10-23 23:15                                                                   ` Andy Moreton
  0 siblings, 2 replies; 47+ messages in thread
From: Paul Eggert @ 2012-10-23 19:27 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 12632

On 10/23/2012 09:44 AM, Eli Zaretskii wrote:
> Others, like "//.", are downright dangerous, because "\\.\" begins a
> device name on Windows.  With these arcana notoriously
> under-documented by MS, it is anybody's guess what such names can do
> in what APIs.

OK, thanks for explaining: I did not know about that syntax,
or about the behavior being undocumented and undefined.
Also, come to think of it, there will be problems with
drive prefixes.

Rather than try to fix these problems, which are on a platform
I'm not familiar with, I'll change the proposed code to
do the following.  This should be safe since it is what the
trunk currently does now (modulo some refactoring) when on
DOS or Windows platforms.  And it can be tuned for DOS and
Windows later, as needed.

/* If FILE is a searchable directory or a symlink to a
   searchable directory, return true.  Otherwise return
   false and set errno to an error number.  */
bool
file_accessible_directory_p (char const *file)
{
#ifdef DOS_NT
  /* File names may have drive prefixes and "/" is not the only
     separator, so the POSIXish approach doesn't work in general.
     Use a straightforward approach instead.  */
  return file_directory_p (file) && check_executable (file);
#else
  /* On POSIXish platforms, use just one system call; this avoids a
     race and is typically faster.  */
  ptrdiff_t len = strlen (file);
  char const *dir;
  bool ok;
  int saved_errno;
  USE_SAFE_ALLOCA;

  /* Normally a file "FOO" is an accessible directory if "FOO/." exists.
     There are three exceptions: "", "/", and "//".  Leave "" alone,
     as it's invalid.  Append only "." to the other two exceptions as
     "/" and "//" are distinct on some platforms, whereas "/", "///",
     "////", etc. are all equivalent.  */
  if (! len)
    dir = file;
  else
    {
      /* Just check for trailing '/' when deciding whether to append '/'.
	 That's simpler than testing the two special cases "/" and "//",
	 and it's a safe optimization here.  */
      char *buf = SAFE_ALLOCA (len + 3);
      memcpy (buf, file, len);
      strcpy (buf + len, "/." + (file[len - 1] == '/'));
      dir = buf;
    }

  ok = check_existing (dir);
  saved_errno = errno;
  SAFE_FREE ();
  errno = saved_errno;
  return ok;
#endif
}






^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-23 19:27                                                                 ` Paul Eggert
@ 2012-10-23 19:50                                                                   ` Eli Zaretskii
  2012-10-23 20:01                                                                     ` Paul Eggert
  2012-10-23 23:15                                                                   ` Andy Moreton
  1 sibling, 1 reply; 47+ messages in thread
From: Eli Zaretskii @ 2012-10-23 19:50 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 12632

> Date: Tue, 23 Oct 2012 12:27:21 -0700
> From: Paul Eggert <eggert@cs.ucla.edu>
> CC: rgm@gnu.org, 12632@debbugs.gnu.org
> 
> Rather than try to fix these problems, which are on a platform
> I'm not familiar with, I'll change the proposed code to
> do the following.  This should be safe since it is what the
> trunk currently does now (modulo some refactoring) when on
> DOS or Windows platforms.  And it can be tuned for DOS and
> Windows later, as needed.
> 
> /* If FILE is a searchable directory or a symlink to a
>    searchable directory, return true.  Otherwise return
>    false and set errno to an error number.  */
> bool
> file_accessible_directory_p (char const *file)
> {
> #ifdef DOS_NT
>   /* File names may have drive prefixes and "/" is not the only
>      separator, so the POSIXish approach doesn't work in general.
>      Use a straightforward approach instead.  */
>   return file_directory_p (file) && check_executable (file);

Just file_directory_p is enough, the other test adds no useful
information that isn't already gathered by the first test.  (The
executable bit of a directory is invented on DOS_NT platforms, it
doesn't exist on disk.)





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-23 19:50                                                                   ` Eli Zaretskii
@ 2012-10-23 20:01                                                                     ` Paul Eggert
  0 siblings, 0 replies; 47+ messages in thread
From: Paul Eggert @ 2012-10-23 20:01 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 12632

On 10/23/2012 12:50 PM, Eli Zaretskii wrote:
> Just file_directory_p is enough

Thanks, I'll simplify that part to:

bool
file_accessible_directory_p (char const *file)
{
#ifdef DOS_NT
  /* There's no need to test whether FILE is searchable, as the
     searchable/executable bit is invented on DOS_NT platforms.  */
  return file_directory_p (file);
#else
  ... same as before ...
#endif
}





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-23 19:27                                                                 ` Paul Eggert
  2012-10-23 19:50                                                                   ` Eli Zaretskii
@ 2012-10-23 23:15                                                                   ` Andy Moreton
  2012-10-24  3:51                                                                     ` Eli Zaretskii
  1 sibling, 1 reply; 47+ messages in thread
From: Andy Moreton @ 2012-10-23 23:15 UTC (permalink / raw)
  To: 12632

On Tue 23 Oct 2012, Paul Eggert wrote:

> On 10/23/2012 09:44 AM, Eli Zaretskii wrote:
>> Others, like "//.", are downright dangerous, because "\\.\" begins a
>> device name on Windows.  With these arcana notoriously
>> under-documented by MS, it is anybody's guess what such names can do
>> in what APIs.
>
> OK, thanks for explaining: I did not know about that syntax,
> or about the behavior being undocumented and undefined.
> Also, come to think of it, there will be problems with
> drive prefixes.

FYI it is documented - see "Win32 Device Namespaces":
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx

Of course as Eli suggests, this documentation may not be the whole
story, as you still need to know which names and namespaces are
supported by a given API (and on which versions of Windows).

    AndyM







^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: file permissions checking mishandled when setuid
  2012-10-23 23:15                                                                   ` Andy Moreton
@ 2012-10-24  3:51                                                                     ` Eli Zaretskii
  0 siblings, 0 replies; 47+ messages in thread
From: Eli Zaretskii @ 2012-10-24  3:51 UTC (permalink / raw)
  To: Andy Moreton; +Cc: 12632

> From: Andy Moreton <andrewjmoreton@gmail.com>
> Date: Wed, 24 Oct 2012 00:15:09 +0100
> 
> FYI it is documented - see "Win32 Device Namespaces":
> http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
> 
> Of course as Eli suggests, this documentation may not be the whole
> story, as you still need to know which names and namespaces are
> supported by a given API (and on which versions of Windows).

That's one thing that's missing, yes.  Another is how exactly a file
name of an arbitrary form is normalized by each API, which is exactly
the issue here.





^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: updated version of the patch
  2012-10-13  1:58 bug#12632: file permissions checking mishandled when setuid Paul Eggert
  2012-10-13  7:23 ` Eli Zaretskii
@ 2012-11-13  2:19 ` Paul Eggert
  2012-11-14  5:10   ` Paul Eggert
  1 sibling, 1 reply; 47+ messages in thread
From: Paul Eggert @ 2012-11-13  2:19 UTC (permalink / raw)
  To: 12632

[-- Attachment #1: Type: text/plain, Size: 205 bytes --]

I updated the patch so that it applies cleanly to the
Emacs trunk (bzr 110875) and would like to install it
soon.  This reflects the comments about the previous patch.
Revised patch (compressed) attached.

[-- Attachment #2: euidaccess.txt.gz --]
[-- Type: application/x-gzip, Size: 25975 bytes --]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* bug#12632: updated version of the patch
  2012-11-13  2:19 ` bug#12632: updated version of the patch Paul Eggert
@ 2012-11-14  5:10   ` Paul Eggert
  0 siblings, 0 replies; 47+ messages in thread
From: Paul Eggert @ 2012-11-14  5:10 UTC (permalink / raw)
  To: 12632-done

I installed the patch as trunk bzr 110889 and
am marking this as done.





^ permalink raw reply	[flat|nested] 47+ messages in thread

end of thread, other threads:[~2012-11-14  5:10 UTC | newest]

Thread overview: 47+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-10-13  1:58 bug#12632: file permissions checking mishandled when setuid Paul Eggert
2012-10-13  7:23 ` Eli Zaretskii
2012-10-13  8:36   ` Eli Zaretskii
2012-10-14  6:16     ` Paul Eggert
2012-10-14  6:56       ` Eli Zaretskii
2012-10-14 18:14         ` Paul Eggert
2012-10-14 18:39           ` Eli Zaretskii
2012-10-14 19:42             ` Paul Eggert
2012-10-14 20:10               ` Eli Zaretskii
2012-10-14 20:17               ` Eli Zaretskii
2012-10-14 20:40                 ` Paul Eggert
2012-10-14 20:53                   ` Eli Zaretskii
2012-10-15  6:17                     ` Paul Eggert
2012-10-15 17:31                       ` Eli Zaretskii
2012-10-15 21:38                         ` Paul Eggert
2012-10-16  3:46                           ` Eli Zaretskii
2012-10-16  6:00                             ` Paul Eggert
2012-10-16 16:36                               ` Eli Zaretskii
2012-10-19 17:01                                 ` Paul Eggert
2012-10-19 18:41                                   ` Eli Zaretskii
2012-10-19 18:54                                     ` Paul Eggert
2012-10-19 19:05                                       ` Glenn Morris
2012-10-19 19:36                                         ` Paul Eggert
2012-10-20  2:25                                           ` Richard Stallman
2012-10-20  4:36                                             ` Paul Eggert
2012-10-21  1:44                                           ` Glenn Morris
2012-10-21  2:52                                             ` Paul Eggert
2012-10-21  4:24                                               ` Glenn Morris
2012-10-22  6:03                                                 ` Paul Eggert
2012-10-22 17:19                                                   ` Eli Zaretskii
2012-10-22 20:33                                                     ` Paul Eggert
2012-10-22 21:04                                                       ` Eli Zaretskii
2012-10-22 21:30                                                         ` Paul Eggert
2012-10-23  0:40                                                           ` Stefan Monnier
2012-10-23  1:46                                                             ` Paul Eggert
2012-10-23  3:49                                                               ` Eli Zaretskii
2012-10-23  3:47                                                           ` Eli Zaretskii
2012-10-23  5:07                                                             ` Paul Eggert
2012-10-23 16:44                                                               ` Eli Zaretskii
2012-10-23 19:27                                                                 ` Paul Eggert
2012-10-23 19:50                                                                   ` Eli Zaretskii
2012-10-23 20:01                                                                     ` Paul Eggert
2012-10-23 23:15                                                                   ` Andy Moreton
2012-10-24  3:51                                                                     ` Eli Zaretskii
2012-10-19 19:10                                       ` Eli Zaretskii
2012-11-13  2:19 ` bug#12632: updated version of the patch Paul Eggert
2012-11-14  5:10   ` Paul Eggert

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).