>From ea91b32b0719d46354c1d995c1d1b1c36842a2eb Mon Sep 17 00:00:00 2001
From: Bruno Haible
Date: Sun, 16 Feb 2020 22:37:28 +0100
Subject: [PATCH 2/2] lchmod: Make more future-proof.
* m4/lchmod.m4 (gl_FUNC_LCHMOD): Define NEED_LCHMOD_NONSYMLINK_FIX.
(gl_PREREQ_LCHMOD): New macro.
* lib/lchmod.c (orig_lchmod): New function.
(lchmod): Test NEED_LCHMOD_NONSYMLINK_FIX. Access /proc only on Linux.
Return EOPNOTSUPP only on Linux and on platforms without lchmod
function.
* modules/lchmod (configure.ac): Invoke gl_PREREQ_LCHMOD.
---
ChangeLog | 9 +++++++++
lib/lchmod.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++-------
m4/lchmod.m4 | 15 +++++++++++++--
modules/lchmod | 1 +
4 files changed, 77 insertions(+), 9 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index f7a3959..0705107 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,14 @@
2020-02-16 Bruno Haible
+ lchmod: Make more future-proof.
+ * m4/lchmod.m4 (gl_FUNC_LCHMOD): Define NEED_LCHMOD_NONSYMLINK_FIX.
+ (gl_PREREQ_LCHMOD): New macro.
+ * lib/lchmod.c (orig_lchmod): New function.
+ (lchmod): Test NEED_LCHMOD_NONSYMLINK_FIX. Access /proc only on Linux.
+ Return EOPNOTSUPP only on Linux and on platforms without lchmod
+ function.
+ * modules/lchmod (configure.ac): Invoke gl_PREREQ_LCHMOD.
+
lchmod: Fix buggy override on macOS, HP-UX (regression from 2020-02-08).
* modules/lchmod (Makefile.am): Don't add lchmod.c to lib_SOURCES.
diff --git a/lib/lchmod.c b/lib/lchmod.c
index c7191c0..57f75da 100644
--- a/lib/lchmod.c
+++ b/lib/lchmod.c
@@ -19,30 +19,68 @@
#include
+/* If the user's config.h happens to include , let it include only
+ the system's here, so that orig_fchmodat doesn't recurse to
+ rpl_fchmodat. */
+#define __need_system_sys_stat_h
+#include
+
+/* Specification. */
#include
+#undef __need_system_sys_stat_h
+
+#if HAVE_LCHMOD
+static inline int
+orig_lchmod (char const *file, mode_t mode)
+{
+ return lchmod (file, mode);
+}
+#endif
#include
#include
#include
#include
+#ifdef __osf__
+/* Write "sys/stat.h" here, not , otherwise OSF/1 5.1 DTK cc
+ eliminates this include because of the preliminary #include
+ above. */
+# include "sys/stat.h"
+#else
+# include
+#endif
+
#include
-/* Work like lchmod, except when FILE is a symbolic link.
- In that case, set errno to EOPNOTSUPP and return -1. */
+/* Work like chmod, except when FILE is a symbolic link.
+ In that case, on systems where permissions on symbolic links are unsupported
+ (such as Linux), set errno to EOPNOTSUPP and return -1. */
int
lchmod (char const *file, mode_t mode)
{
#if HAVE_FCHMODAT
+ /* Gnulib's fchmodat contains the workaround. No need to duplicate it
+ here. */
return fchmodat (AT_FDCWD, file, mode, AT_SYMLINK_NOFOLLOW);
-#else
-# if defined AT_FDCWD && defined O_PATH && defined AT_EMPTY_PATH
+#elif NEED_LCHMOD_NONSYMLINK_FIX
+# if defined AT_FDCWD && defined O_PATH && defined AT_EMPTY_PATH \
+ && (defined __linux__ || defined __ANDROID__)
+ /* Open a file descriptor with O_NOFOLLOW, to make sure we don't
+ follow symbolic links, if /proc is mounted. O_PATH is used to
+ avoid a failure if the file is not readable.
+ Cf. */
int fd = openat (AT_FDCWD, file, O_PATH | O_NOFOLLOW | O_CLOEXEC);
if (fd < 0)
return fd;
- /* Use fstatat because fstat does not work on O_PATH descriptors
+ /* Up to Linux 5.3 at least, when FILE refers to a symbolic link, the
+ chmod call below will change the permissions of the symbolic link
+ - which is undersired - and on many file systems (ext4, btrfs, jfs,
+ xfs, ..., but not reiserfs) fail with error EOPNOTSUPP - which is
+ misleading. Therefore test for a symbolic link explicitly.
+ Use fstatat because fstat does not work on O_PATH descriptors
before Linux 3.6. */
struct stat st;
if (fstatat (fd, "", &st, AT_EMPTY_PATH) != 0)
@@ -73,7 +111,10 @@ lchmod (char const *file, mode_t mode)
return chmod_result;
}
/* /proc is not mounted. */
+ /* Fall back on chmod, despite the race. */
+ return chmod (file, mode);
# elif HAVE_LSTAT
+# if (defined __linux__ || defined __ANDROID__) || !HAVE_LCHMOD
struct stat st;
int lstat_result = lstat (file, &st);
if (lstat_result != 0)
@@ -83,9 +124,15 @@ lchmod (char const *file, mode_t mode)
errno = EOPNOTSUPP;
return -1;
}
-# endif
-
/* Fall back on chmod, despite the race. */
return chmod (file, mode);
+# else /* GNU/kFreeBSD, GNU/Hurd, macOS, FreeBSD, NetBSD, HP-UX */
+ return orig_lchmod (file, mode);
+# endif
+# else /* native Windows */
+ return chmod (file, mode);
+# endif
+#else
+ return orig_lchmod (file, mode);
#endif
}
diff --git a/m4/lchmod.m4 b/m4/lchmod.m4
index 1646bd7..61e3f11 100644
--- a/m4/lchmod.m4
+++ b/m4/lchmod.m4
@@ -1,4 +1,4 @@
-#serial 5
+#serial 6
dnl Copyright (C) 2005-2006, 2008-2020 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
@@ -67,7 +67,18 @@ AC_DEFUN([gl_FUNC_LCHMOD],
rm -f conftest.lchmod])
case $gl_cv_func_lchmod_works in
*yes) ;;
- *) REPLACE_LCHMOD=1;;
+ *)
+ AC_DEFINE([NEED_LCHMOD_NONSYMLINK_FIX], [1],
+ [Define to 1 if lchmod does not work right on non-symlinks.])
+ REPLACE_LCHMOD=1
+ ;;
esac
fi
])
+
+# Prerequisites of lib/lchmod.c.
+AC_DEFUN([gl_PREREQ_LCHMOD],
+[
+ AC_REQUIRE([AC_C_INLINE])
+ :
+])
diff --git a/modules/lchmod b/modules/lchmod
index c829910..c3e0a16 100644
--- a/modules/lchmod
+++ b/modules/lchmod
@@ -19,6 +19,7 @@ configure.ac:
gl_FUNC_LCHMOD
if test $HAVE_LCHMOD = 0 || test $REPLACE_LCHMOD = 1; then
AC_LIBOBJ([lchmod])
+ gl_PREREQ_LCHMOD
fi
gl_SYS_STAT_MODULE_INDICATOR([lchmod])
--
2.7.4