]> Savannah Git Hosting - gnulib.git/commitdiff
lchmod: Make more future-proof.
authorBruno Haible <bruno@clisp.org>
Sun, 16 Feb 2020 21:37:28 +0000 (22:37 +0100)
committerBruno Haible <bruno@clisp.org>
Sun, 16 Feb 2020 21:41:53 +0000 (22:41 +0100)
* 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
lib/lchmod.c
m4/lchmod.m4
modules/lchmod

index e0c9882c6bf0088b14a7126281368c5bc2d4c583..30cc425601305c935b4b556d027e0ae22f238f4e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,14 @@
 2020-02-16  Bruno Haible  <bruno@clisp.org>
 
+       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.
 
index c7191c07d89fc07dd88818621a7a7845b964d796..57f75da8cfa8cb54267afda263e35c02411e2216 100644 (file)
 
 #include <config.h>
 
+/* 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
+
+#if HAVE_LCHMOD
+static inline int
+orig_lchmod (char const *file, mode_t mode)
+{
+  return lchmod (file, mode);
+}
+#endif
 
 #include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <unistd.h>
 
+#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>
 
-/* 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. <https://sourceware.org/bugzilla/show_bug.cgi?id=14578>  */
   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
 }
index 1646bd7db345a2a31c046a6febffe3e876dc78c1..61e3f11228729733a551713e06c989227ac169dd 100644 (file)
@@ -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])
+  :
+])
index c82991008c1aadf7f585970a7b671b5771e24c75..c3e0a166de71eff53b397b187a73c016d0ad4b54 100644 (file)
@@ -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])