From f4693b0166bab83ab232dcd3cfd95906411d1110 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Sun, 16 Feb 2020 23:01:08 +0100 Subject: [PATCH] fchmodat: Make more future-proof. * m4/fchmodat.m4 (gl_FUNC_FCHMODAT): Define NEED_FCHMODAT_NONSYMLINK_FIX. (gl_PREREQ_FCHMODAT): New macro. * lib/fchmodat.c (fchmodat): Test NEED_FCHMODAT_NONSYMLINK_FIX. Access /proc only on Linux. Return EOPNOTSUPP only on Linux and on platforms without lchmod function. * modules/fchmodat (configure.ac): Invoke gl_PREREQ_FCHMODAT. --- ChangeLog | 11 +++++++++++ lib/fchmodat.c | 30 +++++++++++++++++++++++------- m4/fchmodat.m4 | 15 +++++++++++++-- modules/fchmodat | 1 + 4 files changed, 48 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index 30cc425..8215147 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,16 @@ 2020-02-16 Bruno Haible + fchmodat: Make more future-proof. + * m4/fchmodat.m4 (gl_FUNC_FCHMODAT): Define + NEED_FCHMODAT_NONSYMLINK_FIX. + (gl_PREREQ_FCHMODAT): New macro. + * lib/fchmodat.c (fchmodat): Test NEED_FCHMODAT_NONSYMLINK_FIX. Access + /proc only on Linux. Return EOPNOTSUPP only on Linux and on platforms + without lchmod function. + * modules/fchmodat (configure.ac): Invoke gl_PREREQ_FCHMODAT. + +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. diff --git a/lib/fchmodat.c b/lib/fchmodat.c index 02e2da9..bb48b44 100644 --- a/lib/fchmodat.c +++ b/lib/fchmodat.c @@ -14,7 +14,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -/* written by Jim Meyering */ +/* written by Jim Meyering and Paul Eggert */ /* 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 @@ -63,16 +63,27 @@ orig_fchmodat (int dir, char const *file, mode_t mode, int flags) int fchmodat (int dir, char const *file, mode_t mode, int flags) { +# if NEED_FCHMODAT_NONSYMLINK_FIX if (flags == AT_SYMLINK_NOFOLLOW) { struct stat st; -# if defined O_PATH && defined AT_EMPTY_PATH +# if 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 (dir, 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. */ if (fstatat (fd, "", &st, AT_EMPTY_PATH) != 0) { @@ -102,7 +113,9 @@ fchmodat (int dir, char const *file, mode_t mode, int flags) return chmod_result; } /* /proc is not mounted. */ -# else + /* Fall back on orig_fchmodat, despite the race. */ + return orig_fchmodat (dir, file, mode, 0); +# elif (defined __linux__ || defined __ANDROID__) || !HAVE_LCHMOD int fstatat_result = fstatat (dir, file, &st, AT_SYMLINK_NOFOLLOW); if (fstatat_result != 0) return fstatat_result; @@ -111,10 +124,13 @@ fchmodat (int dir, char const *file, mode_t mode, int flags) errno = EOPNOTSUPP; return -1; } -# endif - /* Fall back on chmod, despite the race. */ - flags = 0; + /* Fall back on orig_fchmodat, despite the race. */ + return orig_fchmodat (dir, file, mode, 0); +# else + return orig_fchmodat (dir, file, mode, 0); +# endif } +# endif return orig_fchmodat (dir, file, mode, flags); } diff --git a/m4/fchmodat.m4 b/m4/fchmodat.m4 index f284485..e3f2f04 100644 --- a/m4/fchmodat.m4 +++ b/m4/fchmodat.m4 @@ -1,4 +1,4 @@ -# fchmodat.m4 serial 3 +# fchmodat.m4 serial 4 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, @@ -65,7 +65,18 @@ AC_DEFUN([gl_FUNC_FCHMODAT], rm -f conftest.fchmodat]) case $gl_cv_func_fchmodat_works in *yes) ;; - *) REPLACE_FCHMODAT=1;; + *) + AC_DEFINE([NEED_FCHMODAT_NONSYMLINK_FIX], [1], + [Define to 1 if fchmodat+AT_SYMLINK_NOFOLLOW does not work right on non-symlinks.]) + REPLACE_FCHMODAT=1 + ;; esac fi ]) + +# Prerequisites of lib/fchmodat.c. +AC_DEFUN([gl_PREREQ_FCHMODAT], +[ + AC_CHECK_FUNCS_ONCE([lchmod]) + : +]) diff --git a/modules/fchmodat b/modules/fchmodat index b0f06e8..f0dac9d 100644 --- a/modules/fchmodat +++ b/modules/fchmodat @@ -28,6 +28,7 @@ configure.ac: gl_FUNC_FCHMODAT if test $HAVE_FCHMODAT = 0 || test $REPLACE_FCHMODAT = 1; then AC_LIBOBJ([fchmodat]) + gl_PREREQ_FCHMODAT fi gl_MODULE_INDICATOR([fchmodat]) dnl for lib/openat.h gl_SYS_STAT_MODULE_INDICATOR([fchmodat]) -- 2.7.4