]> Savannah Git Hosting - gnulib.git/commitdiff
fchmodat: AT_SYMLINK_NOFOLLOW fix for non-symlinks
authorPaul Eggert <eggert@cs.ucla.edu>
Sat, 8 Feb 2020 00:34:12 +0000 (16:34 -0800)
committerPaul Eggert <eggert@cs.ucla.edu>
Sat, 8 Feb 2020 00:35:18 +0000 (16:35 -0800)
Fix lchmod, and fchmodat with AT_SYMLINK_NOFOLLOW, so that
they act like chmod on non-symlinks.
* NEWS:
* doc/glibc-functions/lchmod.texi (lchmod):
* doc/posix-functions/fchmodat.texi (fchmodat):
Mention this.
* lib/fchmodat.c: Define __need_system_sys_stat_h before including
config.h, and undef it after including sys/stat.h the first time.
Include fcntl.h, stdio.h, unistd.h, intprops.h, and include
sys/stat.h a second time after defining orig_fchmodat.
(orig_fchmodat) [HAVE_FCHMODAT]: New function.
(fchmodat) [HAVE_FCHMODAT]: Work around the AT_SYMLINK_NOFOLLOW bug.
* lib/lchmod.c: New file.
* lib/sys_stat.in.h (fchmodat, lchmod):
Support replacing these functions.
* m4/fchmodat.m4 (gl_FUNC_FCHMODAT): If fchmodat exists,
test that AT_SYMLINK_NOFOLLOW works on non-symlinks.
* m4/lchmod.m4 (gl_FUNC_LCHMOD): Check for lstat.
Test that lchmod works on non-symlinks.
* m4/sys_stat_h.m4 (gl_SYS_STAT_H_DEFAULTS):
Default REPLACE_FCHMODAT and REPLACE_LCHMOD to 0.
* modules/fchmodat (Depends-on): Add fstatat, intprops, lchmod, unistd.
(Depends-on, configure.ac): Check REPLACE_FCHMODAT too.
* modules/lchmod (Files): Add lib/lchmod.c.
(Depends-on): Add errno, fcntl-h, fchmodat, intprops, lstat, unistd.
(configure.ac): Compile lchmod.c if needed.
(lib_SOURCES): Add lchmod.c.
* modules/sys_stat (sys/stat.h): Substitute REPLACE_FCHMODAT
and REPLACE_LCHMOD.
* tests/test-fchmodat.c: Include fcntl.h, sys/stat.h.
(main): Test fchmodat with AT_SYMLINK_NOFOLLOW on non-symlinks.

14 files changed:
ChangeLog
NEWS
doc/glibc-functions/lchmod.texi
doc/posix-functions/fchmodat.texi
lib/fchmodat.c
lib/lchmod.c [new file with mode: 0644]
lib/sys_stat.in.h
m4/fchmodat.m4
m4/lchmod.m4
m4/sys_stat_h.m4
modules/fchmodat
modules/lchmod
modules/sys_stat
tests/test-fchmodat.c

index 99e0c2e9e015b042ce79ff1fec26e2610a2222d7..71dcaba6c4da6bd5199d981ac1e249504279e0ac 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,38 @@
+2020-02-07  Paul Eggert  <eggert@cs.ucla.edu>
+
+       fchmodat: AT_SYMLINK_NOFOLLOW fix for non-symlinks
+       Fix lchmod, and fchmodat with AT_SYMLINK_NOFOLLOW, so that
+       they act like chmod on non-symlinks.
+       * NEWS:
+       * doc/glibc-functions/lchmod.texi (lchmod):
+       * doc/posix-functions/fchmodat.texi (fchmodat):
+       Mention this.
+       * lib/fchmodat.c: Define __need_system_sys_stat_h before including
+       config.h, and undef it after including sys/stat.h the first time.
+       Include fcntl.h, stdio.h, unistd.h, intprops.h, and include
+       sys/stat.h a second time after defining orig_fchmodat.
+       (orig_fchmodat) [HAVE_FCHMODAT]: New function.
+       (fchmodat) [HAVE_FCHMODAT]: Work around the AT_SYMLINK_NOFOLLOW bug.
+       * lib/lchmod.c: New file.
+       * lib/sys_stat.in.h (fchmodat, lchmod):
+       Support replacing these functions.
+       * m4/fchmodat.m4 (gl_FUNC_FCHMODAT): If fchmodat exists,
+       test that AT_SYMLINK_NOFOLLOW works on non-symlinks.
+       * m4/lchmod.m4 (gl_FUNC_LCHMOD): Check for lstat.
+       Test that lchmod works on non-symlinks.
+       * m4/sys_stat_h.m4 (gl_SYS_STAT_H_DEFAULTS):
+       Default REPLACE_FCHMODAT and REPLACE_LCHMOD to 0.
+       * modules/fchmodat (Depends-on): Add fstatat, intprops, lchmod, unistd.
+       (Depends-on, configure.ac): Check REPLACE_FCHMODAT too.
+       * modules/lchmod (Files): Add lib/lchmod.c.
+       (Depends-on): Add errno, fcntl-h, fchmodat, intprops, lstat, unistd.
+       (configure.ac): Compile lchmod.c if needed.
+       (lib_SOURCES): Add lchmod.c.
+       * modules/sys_stat (sys/stat.h): Substitute REPLACE_FCHMODAT
+       and REPLACE_LCHMOD.
+       * tests/test-fchmodat.c: Include fcntl.h, sys/stat.h.
+       (main): Test fchmodat with AT_SYMLINK_NOFOLLOW on non-symlinks.
+
 2020-02-05  Marc Dionne  <marc.dionne@auristor.com>  (tiny change)
 
        mountlist: Consider AFS filesystems as remote
diff --git a/NEWS b/NEWS
index dc5cc71f96ab505839cd8476acda7ba4608cf27c..bc81dfc288cb431ab2bd55cf3c847c89fef0d4fd 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -58,6 +58,13 @@ User visible incompatible changes
 
 Date        Modules         Changes
 
+2020-02-07  fchmodat        When applied to non-symlinks, these now act like
+            lchmod          chmod (the BSD behavior, which POSIX requires for
+                            fchmodat + AT_SYMLINK_NOFOLLOW), instead of failing
+                            (the GNU/Linux behavior through glibc 2.31).
+                            Future versions of GNU/Linux are planned to act as
+                            per POSIX and BSD.
+
 2020-01-15  gc-pbkdf2-sha1  This module is deprecated.  Use gc-pbkdf2 instead.
 
 2019-12-12  dfa             Its API now uses ptrdiff_t instead of size_t.
index aea782dc2492b17a697c22b5e3e7e4702ff3d0be..6cc48b45393615364377ce2b6c4df6bfd01918ad 100644 (file)
@@ -9,6 +9,10 @@ Portability problems fixed by Gnulib:
 @item
 This function is missing on some platforms:
 OpenBSD 3.8, Minix 3.1.8, AIX 5.1, IRIX 6.5, Solaris 11.4, Cygwin, mingw, MSVC 14, Android 9.0.
+@item
+This function always fails with @code{errno} set to @code{ENOSYS},
+even when the file is not a symbolic link:
+GNU/Linux with glibc 2.31.
 @end itemize
 
 Portability problems not fixed by Gnulib:
index 6add5ba8998801e38c096ddfed7d68811379bbb8..4d190310e997c7f4a3d53ae49312fa2104e638dc 100644 (file)
@@ -9,6 +9,11 @@ Gnulib module: fchmodat
 Portability problems fixed by Gnulib:
 @itemize
 @item
+When given the @code{AT_SYMLINK_NOFOLLOW} flag,
+this function fails with @code{errno} set to @code{ENOTSUP},
+even when the file is not a symbolic link:
+GNU/Linux and Cygwin with glibc 2.31.
+@item
 This function is missing on some platforms:
 glibc 2.3.6, Mac OS X 10.5, FreeBSD 6.0, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8,
 AIX 5.1, HP-UX 11, IRIX 6.5, Solaris 10, Cygwin 1.5.x, mingw, MSVC 14.
@@ -19,9 +24,5 @@ Portability problems not fixed by Gnulib:
 @itemize
 @item
 Some platforms do not allow changing the access bits on symbolic
-links.  POSIX states that @code{fchmodat(@dots{},AT_SYMLINK_NOFOLLOW)}
-may fail with @code{EOPNOTSUPP} when called on a symlink, but some
-platforms, as well as the gnulib replacement, fail for any use of
-AT_SYMLINK_NOFOLLOW even if the target was not a symlink:
-glibc, Cygwin.
+links.
 @end itemize
index 0d4891b3e435a13a75d6b668d1b14c83a51df36b..c6b8ef768efb48235b02ce1f130f350e42d52fd3 100644 (file)
 
 /* written by Jim Meyering */
 
+/* If the user's config.h happens to include <sys/stat.h>, let it include only
+   the system's <sys/stat.h> here, so that orig_fchmodat doesn't recurse to
+   rpl_fchmodat.  */
+#define __need_system_sys_stat_h
 #include <config.h>
 
 /* Specification.  */
 #include <sys/stat.h>
+#undef __need_system_sys_stat_h
 
 #include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
 #include <stdlib.h>
+#include <unistd.h>
 
-#ifndef HAVE_LCHMOD
-/* Use a different name, to avoid conflicting with any
-   system-supplied declaration.  */
-# undef lchmod
-# define lchmod lchmod_rpl
+#if HAVE_FCHMODAT
 static int
-lchmod (char const *f _GL_UNUSED, mode_t m _GL_UNUSED)
+orig_fchmodat (int dir, char const *file, mode_t mode, int flags)
 {
-  errno = ENOSYS;
-  return -1;
+  return fchmodat (dir, file, mode, flags);
 }
 #endif
 
-/* Solaris 10 has no function like this.
-   Invoke chmod or lchmod on file, FILE, using mode MODE, in the directory
+#ifdef __osf__
+/* Write "sys/stat.h" here, not <sys/stat.h>, otherwise OSF/1 5.1 DTK cc
+   eliminates this include because of the preliminary #include <sys/stat.h>
+   above.  */
+# include "sys/stat.h"
+#else
+# include <sys/stat.h>
+#endif
+
+#include <intprops.h>
+
+/* Invoke chmod or lchmod on 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 (chmod|lchmod)/restore_cwd.  If either the save_cwd or the
@@ -46,10 +59,52 @@ lchmod (char const *f _GL_UNUSED, mode_t m _GL_UNUSED)
    Note that an attempt to use a FLAG value of AT_SYMLINK_NOFOLLOW
    on a system without lchmod support causes this function to fail.  */
 
-#define AT_FUNC_NAME fchmodat
-#define AT_FUNC_F1 lchmod
-#define AT_FUNC_F2 chmod
-#define AT_FUNC_USE_F1_COND AT_SYMLINK_NOFOLLOW
-#define AT_FUNC_POST_FILE_PARAM_DECLS , mode_t mode, int flag
-#define AT_FUNC_POST_FILE_ARGS        , mode
-#include "at-func.c"
+#if HAVE_FCHMODAT
+int
+fchmodat (int dir, char const *file, mode_t mode, int flags)
+{
+  if (flags & AT_SYMLINK_NOFOLLOW)
+    {
+# ifdef O_PATH
+      int fd = openat (dir, file, O_PATH | O_NOFOLLOW | O_CLOEXEC);
+      if (fd < 0)
+        return fd;
+      static char const fmt[] = "/proc/self/fd/%d";
+      char buf[sizeof fmt - sizeof "%d" + INT_BUFSIZE_BOUND (int)];
+      sprintf (buf, fmt, fd);
+      int chmod_result = chmod (buf, mode);
+      int chmod_errno = errno;
+      close (fd);
+      if (chmod_result == 0)
+        return chmod_result;
+      if (chmod_errno != ENOENT)
+        {
+          errno = chmod_errno;
+          return chmod_result;
+        }
+      /* /proc is not mounted; fall back on racy implementation.  */
+# endif
+
+      struct stat st;
+      int fstatat_result = fstatat (dir, file, &st, AT_SYMLINK_NOFOLLOW);
+      if (fstatat_result != 0)
+        return fstatat_result;
+      if (S_ISLNK (st.st_mode))
+        {
+          errno = EOPNOTSUPP;
+          return -1;
+        }
+      flags &= ~AT_SYMLINK_NOFOLLOW;
+    }
+
+  return orig_fchmodat (dir, file, mode, flags);
+}
+#else
+# define AT_FUNC_NAME fchmodat
+# define AT_FUNC_F1 lchmod
+# define AT_FUNC_F2 chmod
+# define AT_FUNC_USE_F1_COND AT_SYMLINK_NOFOLLOW
+# define AT_FUNC_POST_FILE_PARAM_DECLS , mode_t mode, int flag
+# define AT_FUNC_POST_FILE_ARGS        , mode
+# include "at-func.c"
+#endif
diff --git a/lib/lchmod.c b/lib/lchmod.c
new file mode 100644 (file)
index 0000000..cc260ce
--- /dev/null
@@ -0,0 +1,72 @@
+/* Implement lchmod on platforms where it does not work correctly.
+
+   Copyright 2020 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 <https://www.gnu.org/licenses/>.  */
+
+/* written by Paul Eggert */
+
+#include <config.h>
+
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <intprops.h>
+
+/* Work like lchmod, except when FILE is a symbolic link.
+   In that case, set errno to EOPNOTSUPP and return -1.  */
+
+int
+lchmod (char const *file, mode_t mode)
+{
+#if HAVE_FCHMODAT
+  return fchmodat (AT_FDCWD, file, mode, AT_SYMLINK_NOFOLLOW);
+#elif defined O_PATH && defined AT_FDCWD
+  int fd = openat (AT_FDCWD, file, O_PATH | O_NOFOLLOW | O_CLOEXEC);
+  if (fd < 0)
+    return fd;
+  static char const fmt[] = "/proc/self/fd/%d";
+  char buf[sizeof fmt - sizeof "%d" + INT_BUFSIZE_BOUND (int)];
+  sprintf (buf, fmt, fd);
+  int chmod_result = chmod (buf, mode);
+  int chmod_errno = errno;
+  close (fd);
+  if (chmod_result == 0)
+    return chmod_result;
+  if (chmod_errno != ENOENT)
+    {
+      errno = chmod_errno;
+      return chmod_result;
+    }
+  /* /proc is not mounted; fall back on racy implementation.  */
+#endif
+
+#if HAVE_LSTAT
+  struct stat st;
+  int lstat_result = lstat (file, &st);
+  if (lstat_result != 0)
+    return lstat_result;
+  if (S_ISLNK (st.st_mode))
+    {
+      errno = EOPNOTSUPP;
+      return -1;
+    }
+#endif
+
+  return chmod (file, mode);
+}
index 5c891e7b899152fd5de0097a97dd0dbe721b723b..4f9eb5976fb8f9a40bdcb5af26068189c586160f 100644 (file)
@@ -392,13 +392,25 @@ struct stat
 
 
 #if @GNULIB_FCHMODAT@
-# if !@HAVE_FCHMODAT@
+# if @REPLACE_FCHMODAT@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef fchmodat
+#   define fchmodat rpl_fchmodat
+#  endif
+_GL_FUNCDECL_RPL (fchmodat, int,
+                  (int fd, char const *file, mode_t mode, int flag)
+                  _GL_ARG_NONNULL ((2)));
+_GL_CXXALIAS_RPL (fchmodat, int,
+                  (int fd, char const *file, mode_t mode, int flag));
+# else
+#  if !@HAVE_FCHMODAT@
 _GL_FUNCDECL_SYS (fchmodat, int,
                   (int fd, char const *file, mode_t mode, int flag)
                   _GL_ARG_NONNULL ((2)));
-# endif
+#  endif
 _GL_CXXALIAS_SYS (fchmodat, int,
                   (int fd, char const *file, mode_t mode, int flag));
+# endif
 _GL_CXXALIASWARN (fchmodat);
 #elif defined GNULIB_POSIXCHECK
 # undef fchmodat
@@ -502,31 +514,24 @@ _GL_WARN_ON_USE (futimens, "futimens is not portable - "
 #if @GNULIB_LCHMOD@
 /* Change the mode of FILENAME to MODE, without dereferencing it if FILENAME
    denotes a symbolic link.  */
-# if !@HAVE_LCHMOD@
-/* The lchmod replacement follows symbolic links.  Callers should take
-   this into account; lchmod should be applied only to arguments that
-   are known to not be symbolic links.  On hosts that lack lchmod,
-   this can lead to race conditions between the check and the
-   invocation of lchmod, but we know of no workarounds that are
-   reliable in general.  You might try requesting support for lchmod
-   from your operating system supplier.  */
+# if @REPLACE_LCHMOD@
 #  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
-#   define lchmod chmod
+#   undef lchmod
+#   define lchmod rpl_lchmod
 #  endif
-/* Need to cast, because on mingw, the second parameter of chmod is
-                                                int mode.  */
-_GL_CXXALIAS_RPL_CAST_1 (lchmod, chmod, int,
-                         (const char *filename, mode_t mode));
+_GL_FUNCDECL_RPL (lchmod, int,
+                  (char const *filename, mode_t mode)
+                  _GL_ARG_NONNULL ((1)));
+_GL_CXXALIAS_RPL (lchmod, int,
+                  (char const *filename, mode_t mode));
 # else
-#  if 0 /* assume already declared */
+#  if !@HAVE_LCHMOD@
 _GL_FUNCDECL_SYS (lchmod, int, (const char *filename, mode_t mode)
                                _GL_ARG_NONNULL ((1)));
 #  endif
 _GL_CXXALIAS_SYS (lchmod, int, (const char *filename, mode_t mode));
 # endif
-# if @HAVE_LCHMOD@
 _GL_CXXALIASWARN (lchmod);
-# endif
 #elif defined GNULIB_POSIXCHECK
 # undef lchmod
 # if HAVE_RAW_DECL_LCHMOD
index 460a4dc17155c2a7a8ddfc13592976ff33929026..8195ef6680564d756d75515c0803ac19f4cacd10 100644 (file)
@@ -1,4 +1,4 @@
-# fchmodat.m4 serial 1
+# fchmodat.m4 serial 2
 dnl Copyright (C) 2004-2020 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -13,5 +13,51 @@ AC_DEFUN([gl_FUNC_FCHMODAT],
   AC_CHECK_FUNCS_ONCE([fchmodat lchmod])
   if test $ac_cv_func_fchmodat != yes; then
     HAVE_FCHMODAT=0
+  else
+    AC_CACHE_CHECK(
+      [whether fchmodat+AT_SYMLINK_NOFOLLOW works on non-symlinks],
+      [gl_cv_func_fchmodat_works],
+      [AC_RUN_IFELSE(
+         [AC_LANG_PROGRAM(
+            [
+              AC_INCLUDES_DEFAULT[
+              #include <fcntl.h>
+              #ifndef S_IRUSR
+               #define S_IRUSR 0400
+              #endif
+              #ifndef S_IWUSR
+               #define S_IWUSR 0200
+              #endif
+              #ifndef S_IRWXU
+               #define S_IRWXU 0700
+              #endif
+              #ifndef S_IRWXG
+               #define S_IRWXG 0070
+              #endif
+              #ifndef S_IRWXO
+               #define S_IRWXO 0007
+              #endif
+            ]],
+            [[
+              int permissive = S_IRWXU | S_IRWXG | S_IRWXO;
+              int desired = S_IRUSR | S_IWUSR;
+              static char const f[] = "conftest.fchmodat";
+              struct stat st;
+              if (creat (f, permissive) < 0)
+                return 1;
+              if (fchmodat (AT_FDCWD, f, desired, AT_SYMLINK_NOFOLLOW) != 0)
+                return 1;
+              if (stat (f, &st) != 0)
+                return 1;
+              return ! ((st.st_mode & permissive) == desired);
+            ]])],
+         [gl_cv_func_fchmodat_works=yes],
+         [gl_cv_func_fchmodat_works=no],
+         [gl_cv_func_fchmodat_works=$gl_cross_guess_normal])
+       rm -f conftest.fchmodat])
+    case $gl_cv_func_fchmodat_works in
+      *yes) ;;
+      *) REPLACE_FCHMODAT=1;;
+    esac
   fi
 ])
index eeca7ac20ec009ce06558cc21d1a8d1837bbd430..68dab7a4ac484a9a359e1b7ebdf5329de38348ba 100644 (file)
@@ -1,4 +1,4 @@
-#serial 3
+#serial 4
 
 dnl Copyright (C) 2005-2006, 2008-2020 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
@@ -6,7 +6,7 @@ dnl gives unlimited permission to copy and/or distribute it,
 dnl with or without modifications, as long as this notice is preserved.
 
 dnl From Paul Eggert.
-dnl Provide a replacement for lchmod on hosts that lack it.
+dnl Provide a replacement for lchmod on hosts that lack a working version.
 
 AC_DEFUN([gl_FUNC_LCHMOD],
 [
@@ -15,8 +15,52 @@ AC_DEFUN([gl_FUNC_LCHMOD],
   dnl Persuade glibc <sys/stat.h> to declare lchmod().
   AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
 
-  AC_CHECK_FUNCS_ONCE([lchmod])
-  if test $ac_cv_func_lchmod = no; then
+  AC_CHECK_FUNCS_ONCE([fchmodat lchmod lstat])
+  if test "$ac_cv_func_lchmod" = no; then
     HAVE_LCHMOD=0
+  else
+    AC_CACHE_CHECK([whether lchmod works on non-symlinks],
+      [gl_cv_func_lchmod_works],
+      [AC_RUN_IFELSE(
+         [AC_LANG_PROGRAM(
+            [
+              AC_INCLUDES_DEFAULT[
+              #ifndef S_IRUSR
+               #define S_IRUSR 0400
+              #endif
+              #ifndef S_IWUSR
+               #define S_IWUSR 0200
+              #endif
+              #ifndef S_IRWXU
+               #define S_IRWXU 0700
+              #endif
+              #ifndef S_IRWXG
+               #define S_IRWXG 0070
+              #endif
+              #ifndef S_IRWXO
+               #define S_IRWXO 0007
+              #endif
+            ]],
+            [[
+              int permissive = S_IRWXU | S_IRWXG | S_IRWXO;
+              int desired = S_IRUSR | S_IWUSR;
+              static char const f[] = "conftest.lchmod";
+              struct stat st;
+              if (creat (f, permissive) < 0)
+                return 1;
+              if (lchmod (f, desired) != 0)
+                return 1;
+              if (stat (f, &st) != 0)
+                return 1;
+              return ! ((st.st_mode & permissive) == desired);
+            ]])],
+         [gl_cv_func_lchmod_works=yes],
+         [gl_cv_func_lchmod_works=no],
+         [gl_cv_func_lchmod_works=$gl_cross_guess_normal])
+       rm -f conftest.lchmod])
+    case $gl_cv_func_lchmod_works in
+      *yes) ;;
+      *) REPLACE_LCHMOD=1;;
+    esac
   fi
 ])
index d63df9ebffdf038562dd10ac36d22780d07256ab..a5f35d46dd9b96c88f3f79589e2580f675042c6b 100644 (file)
@@ -1,4 +1,4 @@
-# sys_stat_h.m4 serial 31   -*- Autoconf -*-
+# sys_stat_h.m4 serial 32   -*- Autoconf -*-
 dnl Copyright (C) 2006-2020 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -88,9 +88,11 @@ AC_DEFUN([gl_SYS_STAT_H_DEFAULTS],
   HAVE_MKNOD=1;         AC_SUBST([HAVE_MKNOD])
   HAVE_MKNODAT=1;       AC_SUBST([HAVE_MKNODAT])
   HAVE_UTIMENSAT=1;     AC_SUBST([HAVE_UTIMENSAT])
+  REPLACE_FCHMODAT=0;   AC_SUBST([REPLACE_FCHMODAT])
   REPLACE_FSTAT=0;      AC_SUBST([REPLACE_FSTAT])
   REPLACE_FSTATAT=0;    AC_SUBST([REPLACE_FSTATAT])
   REPLACE_FUTIMENS=0;   AC_SUBST([REPLACE_FUTIMENS])
+  REPLACE_LCHMOD=0;     AC_SUBST([REPLACE_LCHMOD])
   REPLACE_LSTAT=0;      AC_SUBST([REPLACE_LSTAT])
   REPLACE_MKDIR=0;      AC_SUBST([REPLACE_MKDIR])
   REPLACE_MKFIFO=0;     AC_SUBST([REPLACE_MKFIFO])
index 7a5e1a6886a7d180ba4378037b677041784cf99a..b0f06e862578a9f1ac21dafc957a0c5397cd70ca 100644 (file)
@@ -12,17 +12,21 @@ sys_stat
 extensions
 at-internal     [test $HAVE_FCHMODAT = 0]
 dosname         [test $HAVE_FCHMODAT = 0]
-errno           [test $HAVE_FCHMODAT = 0]
+errno           [test $HAVE_FCHMODAT = 0 || test $REPLACE_FCHMODAT = 1]
 extern-inline   [test $HAVE_FCHMODAT = 0]
 fchdir          [test $HAVE_FCHMODAT = 0]
-fcntl-h         [test $HAVE_FCHMODAT = 0]
+fcntl-h         [test $HAVE_FCHMODAT = 0 || test $REPLACE_FCHMODAT = 1]
+fstatat         [test $REPLACE_FCHMODAT = 1]
+intprops        [test $HAVE_FCHMODAT = 0 || test $REPLACE_FCHMODAT = 1]
+lchmod          [test $HAVE_FCHMODAT = 0]
 openat-die      [test $HAVE_FCHMODAT = 0]
 openat-h        [test $HAVE_FCHMODAT = 0]
 save-cwd        [test $HAVE_FCHMODAT = 0]
+unistd          [test $HAVE_FCHMODAT = 0 || test $REPLACE_FCHMODAT = 1]
 
 configure.ac:
 gl_FUNC_FCHMODAT
-if test $HAVE_FCHMODAT = 0; then
+if test $HAVE_FCHMODAT = 0 || test $REPLACE_FCHMODAT = 1; then
   AC_LIBOBJ([fchmodat])
 fi
 gl_MODULE_INDICATOR([fchmodat]) dnl for lib/openat.h
index a0ac7a534664ae1368bd5916a85a688655844e16..e1054f6a4a4a5965f6c419313ed345c16630f6c5 100644 (file)
@@ -2,17 +2,28 @@ Description:
 lchmod that is actually chmod (!) on hosts lacking lchmod
 
 Files:
+lib/lchmod.c
 m4/lchmod.m4
 
 Depends-on:
-sys_stat
+errno         [test $HAVE_LCHMOD = 0 || test $REPLACE_LCHMOD = 1]
 extensions
+fcntl-h       [test $HAVE_LCHMOD = 0 || test $REPLACE_LCHMOD = 1]
+fchmodat      [test $HAVE_LCHMOD = 0 || test $REPLACE_LCHMOD = 1]
+intprops      [test $HAVE_LCHMOD = 0 || test $REPLACE_LCHMOD = 1]
+lstat         [test $HAVE_LCHMOD = 0 || test $REPLACE_LCHMOD = 1]
+sys_stat
+unistd        [test $HAVE_LCHMOD = 0 || test $REPLACE_LCHMOD = 1]
 
 configure.ac:
 gl_FUNC_LCHMOD
+if test $HAVE_LCHMOD = 0 || test $REPLACE_LCHMOD = 1; then
+  AC_LIBOBJ([lchmod])
+fi
 gl_SYS_STAT_MODULE_INDICATOR([lchmod])
 
 Makefile.am:
+lib_SOURCES += lchmod.c
 
 Include:
 <sys/stat.h>
index 867cc8549818cd72ed6031fae2865b75d2e3ef69..93e0cf07ecfbce1c2c3eb40fd3d0e94ad83d45d0 100644 (file)
@@ -59,9 +59,11 @@ sys/stat.h: sys_stat.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNU
              -e 's|@''HAVE_MKNOD''@|$(HAVE_MKNOD)|g' \
              -e 's|@''HAVE_MKNODAT''@|$(HAVE_MKNODAT)|g' \
              -e 's|@''HAVE_UTIMENSAT''@|$(HAVE_UTIMENSAT)|g' \
+             -e 's|@''REPLACE_FCHMODAT''@|$(REPLACE_FCHMODAT)|g' \
              -e 's|@''REPLACE_FSTAT''@|$(REPLACE_FSTAT)|g' \
              -e 's|@''REPLACE_FSTATAT''@|$(REPLACE_FSTATAT)|g' \
              -e 's|@''REPLACE_FUTIMENS''@|$(REPLACE_FUTIMENS)|g' \
+             -e 's|@''REPLACE_LCHMOD''@|$(REPLACE_LCHMOD)|g' \
              -e 's|@''REPLACE_LSTAT''@|$(REPLACE_LSTAT)|g' \
              -e 's|@''REPLACE_MKDIR''@|$(REPLACE_MKDIR)|g' \
              -e 's|@''REPLACE_MKFIFO''@|$(REPLACE_MKFIFO)|g' \
index 395b2b78e03667ad27cc6c436170d4d453df5fb3..df0f604d83c13bf05bab57b55acb624d6b57e617 100644 (file)
@@ -22,6 +22,8 @@
 SIGNATURE_CHECK (fchmodat, int, (int, const char *, mode_t, int));
 
 #include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
 #include <unistd.h>
 
 #include "macros.h"
@@ -42,5 +44,13 @@ main (void)
     ASSERT (errno == EBADF);
   }
 
+  /* Test that fchmodat works on non-symlinks, when given
+     the AT_SYMLINK_NOFOLLOW flag.  */
+  {
+    ASSERT (close (creat ("file", 0600)) == 0);
+    ASSERT (fchmodat (AT_FDCWD, "file", 0700, AT_SYMLINK_NOFOLLOW) == 0);
+    ASSERT (unlink ("file") == 0);
+  }
+
   return 0;
 }