summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2022-06-12 13:46:52 -0700
committerPaul Eggert <eggert@cs.ucla.edu>2022-06-12 13:47:12 -0700
commitd682f8de7f9d384f4cfc482a3ba2960329a8db21 (patch)
tree65d04a8b5e462b2b4939b3addf0982de988a1bac
parent2cd86e5891af0cdb7d88d076e9613e5c8475e1ee (diff)
downloadgnulib-d682f8de7f9d384f4cfc482a3ba2960329a8db21.tar.gz
fchmodat: port better to MS-Windows etc.
MS-Windows problem reported by Bruno Haible in: https://lists.gnu.org/r/bug-gnulib/2022-06/msg00041.html Although I don’t use MS-Windows I see some related fstatat etc. problems and am trying to fix them with this further patch. * lib/fchmodat.c (fchmodat): * lib/lchmod.c (lchmod): * lib/lchown.c (lchown) [!HAVE_LCHOWN && HAVE_CHOWN && !CHOWN_MODIFIES_SYMLINK]: * lib/renameatu.c (renameatu) [HAVE_RENAME && RENAME_TRAILING_SLASH_SOURCE_BUG]: Use readlinkat/readlink instead of fstatat/lstat to test merely whether a string names a symlink, as this avoids problems with EOVERFLOW. Also, I hope it works around the MS-Windows issues that Bruno noted. * m4/fchmodat.m4 (gl_PREREQ_FCHMODAT): Check for readlinkat, not lchmod. * m4/lchmod.m4 (gl_FUNC_LCHMOD): Do not require AC_CANONICAL_HOST or check for lstat. (gl_PREREQ_LCHMOD): Check for readlink. * modules/lchown (Depends-on): Add readlink. Do not depend on lstat merely because !HAVE_LCHOWN. * modules/renameatu (Depends-on): Add fstatat, readlinkat.
-rw-r--r--ChangeLog26
-rw-r--r--lib/fchmodat.c16
-rw-r--r--lib/lchmod.c17
-rw-r--r--lib/lchown.c4
-rw-r--r--lib/renameatu.c7
-rw-r--r--m4/fchmodat.m44
-rw-r--r--m4/lchmod.m47
-rw-r--r--modules/lchown3
-rw-r--r--modules/renameatu2
9 files changed, 56 insertions, 30 deletions
diff --git a/ChangeLog b/ChangeLog
index 2d0340b933..2daa6d8c81 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,29 @@
+2022-06-12 Paul Eggert <eggert@cs.ucla.edu>
+
+ fchmodat: port better to MS-Windows etc.
+ MS-Windows problem reported by Bruno Haible in:
+ https://lists.gnu.org/r/bug-gnulib/2022-06/msg00041.html
+ Although I don’t use MS-Windows I see some related fstatat etc.
+ problems and am trying to fix them with this further patch.
+ * lib/fchmodat.c (fchmodat):
+ * lib/lchmod.c (lchmod):
+ * lib/lchown.c (lchown)
+ [!HAVE_LCHOWN && HAVE_CHOWN && !CHOWN_MODIFIES_SYMLINK]:
+ * lib/renameatu.c (renameatu)
+ [HAVE_RENAME && RENAME_TRAILING_SLASH_SOURCE_BUG]:
+ Use readlinkat/readlink instead of fstatat/lstat to test merely
+ whether a string names a symlink, as this avoids problems
+ with EOVERFLOW. Also, I hope it works around the MS-Windows
+ issues that Bruno noted.
+ * m4/fchmodat.m4 (gl_PREREQ_FCHMODAT):
+ Check for readlinkat, not lchmod.
+ * m4/lchmod.m4 (gl_FUNC_LCHMOD): Do not require AC_CANONICAL_HOST
+ or check for lstat.
+ (gl_PREREQ_LCHMOD): Check for readlink.
+ * modules/lchown (Depends-on): Add readlink. Do not depend on
+ lstat merely because !HAVE_LCHOWN.
+ * modules/renameatu (Depends-on): Add fstatat, readlinkat.
+
2022-06-12 Bruno Haible <bruno@clisp.org>
doc: Update O_PATH platforms list.
diff --git a/lib/fchmodat.c b/lib/fchmodat.c
index b233c366de..8ed4cb7398 100644
--- a/lib/fchmodat.c
+++ b/lib/fchmodat.c
@@ -83,9 +83,10 @@ 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 HAVE_READLINKAT
+ char readlink_buf[1];
-# ifdef O_PATH
+# ifdef O_PATH
/* 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.
@@ -96,7 +97,7 @@ fchmodat (int dir, char const *file, mode_t mode, int flags)
int err;
char buf[1];
- if (0 <= readlinkat (fd, "", buf, sizeof buf))
+ if (0 <= readlinkat (fd, "", readlink_buf, sizeof readlink_buf))
err = EOPNOTSUPP;
else if (errno == EINVAL)
{
@@ -113,17 +114,16 @@ fchmodat (int dir, char const *file, mode_t mode, int flags)
errno = err;
if (0 <= err)
return err == 0 ? 0 : -1;
-# endif
+# endif
/* O_PATH + /proc is not supported. */
- int fstatat_result = fstatat (dir, file, &st, AT_SYMLINK_NOFOLLOW);
- if (fstatat_result != 0)
- return fstatat_result;
- if (S_ISLNK (st.st_mode))
+
+ if (0 <= readlinkat (dir, file, readlink_buf, sizeof readlink_buf))
{
errno = EOPNOTSUPP;
return -1;
}
+# endif
/* Fall back on orig_fchmodat with no flags, despite a possible race. */
flags = 0;
diff --git a/lib/lchmod.c b/lib/lchmod.c
index b4cc0a8176..f21bf7f652 100644
--- a/lib/lchmod.c
+++ b/lib/lchmod.c
@@ -45,7 +45,10 @@
int
lchmod (char const *file, mode_t mode)
{
-#ifdef O_PATH
+#if HAVE_READLINK
+ char readlink_buf[1];
+
+# ifdef O_PATH
/* 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.
@@ -55,8 +58,7 @@ lchmod (char const *file, mode_t mode)
return fd;
int err;
- char buf[1];
- if (0 <= readlinkat (fd, "", buf, sizeof buf))
+ if (0 <= readlinkat (fd, "", readlink_buf, sizeof readlink_buf))
err = EOPNOTSUPP;
else if (errno == EINVAL)
{
@@ -73,16 +75,11 @@ lchmod (char const *file, mode_t mode)
errno = err;
if (0 <= err)
return err == 0 ? 0 : -1;
-#endif
+# endif
/* O_PATH + /proc is not supported. */
-#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))
+ if (0 <= readlink (file, readlink_buf, sizeof readlink_buf))
{
errno = EOPNOTSUPP;
return -1;
diff --git a/lib/lchown.c b/lib/lchown.c
index 105c2d9990..8b0d871a27 100644
--- a/lib/lchown.c
+++ b/lib/lchown.c
@@ -45,9 +45,9 @@ lchown (const char *file, uid_t uid, gid_t gid)
{
# if HAVE_CHOWN
# if ! CHOWN_MODIFIES_SYMLINK
- struct stat stats;
+ char readlink_buf[1];
- if (lstat (file, &stats) == 0 && S_ISLNK (stats.st_mode))
+ if (0 <= readlink (file, readlink_buf, sizeof readlink_buf))
{
errno = EOPNOTSUPP;
return -1;
diff --git a/lib/renameatu.c b/lib/renameatu.c
index b4e317d6e5..7ba186cae7 100644
--- a/lib/renameatu.c
+++ b/lib/renameatu.c
@@ -214,15 +214,16 @@ renameatu (int fd1, char const *src, int fd2, char const *dst,
goto out;
}
strip_trailing_slashes (dst_temp);
- if (fstatat (fd2, dst_temp, &dst_st, AT_SYMLINK_NOFOLLOW))
+ char readlink_buf[1];
+ if (readlinkat (fd2, dst_temp, readlink_buf, sizeof readlink_buf) < 0)
{
- if (errno != ENOENT)
+ if (errno != ENOENT && errno != EINVAL)
{
rename_errno = errno;
goto out;
}
}
- else if (S_ISLNK (dst_st.st_mode))
+ else
goto out;
}
# endif /* RENAME_TRAILING_SLASH_SOURCE_BUG */
diff --git a/m4/fchmodat.m4 b/m4/fchmodat.m4
index a5cf95a88b..f743ce1b02 100644
--- a/m4/fchmodat.m4
+++ b/m4/fchmodat.m4
@@ -1,4 +1,4 @@
-# fchmodat.m4 serial 6
+# fchmodat.m4 serial 7
dnl Copyright (C) 2004-2022 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
@@ -97,6 +97,6 @@ AC_DEFUN([gl_FUNC_FCHMODAT],
# Prerequisites of lib/fchmodat.c.
AC_DEFUN([gl_PREREQ_FCHMODAT],
[
- AC_CHECK_FUNCS_ONCE([lchmod])
+ AC_CHECK_FUNCS_ONCE([readlinkat])
:
])
diff --git a/m4/lchmod.m4 b/m4/lchmod.m4
index 5baee738ef..bfc925fbe4 100644
--- a/m4/lchmod.m4
+++ b/m4/lchmod.m4
@@ -1,4 +1,4 @@
-#serial 8
+#serial 9
dnl Copyright (C) 2005-2006, 2008-2022 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
@@ -15,9 +15,7 @@ AC_DEFUN([gl_FUNC_LCHMOD],
dnl Persuade glibc <sys/stat.h> to declare lchmod().
AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
- AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
-
- AC_CHECK_FUNCS_ONCE([lchmod lstat])
+ AC_CHECK_FUNCS_ONCE([lchmod])
if test "$ac_cv_func_lchmod" = no; then
HAVE_LCHMOD=0
fi
@@ -26,5 +24,6 @@ AC_DEFUN([gl_FUNC_LCHMOD],
# Prerequisites of lib/lchmod.c.
AC_DEFUN([gl_PREREQ_LCHMOD],
[
+ AC_CHECK_FUNCS_ONCE([readlink])
:
])
diff --git a/modules/lchown b/modules/lchown
index 335d191f9f..217dd75cce 100644
--- a/modules/lchown
+++ b/modules/lchown
@@ -7,11 +7,12 @@ m4/lchown.m4
Depends-on:
unistd
+readlink [test $HAVE_LCHOWN = 0]
chown [test $HAVE_LCHOWN = 0 || test $REPLACE_LCHOWN = 1]
errno [test $HAVE_LCHOWN = 0 || test $REPLACE_LCHOWN = 1]
-lstat [test $HAVE_LCHOWN = 0 || test $REPLACE_LCHOWN = 1]
stdbool [test $HAVE_LCHOWN = 0 || test $REPLACE_LCHOWN = 1]
sys_stat [test $HAVE_LCHOWN = 0 || test $REPLACE_LCHOWN = 1]
+lstat [test $REPLACE_LCHOWN = 1]
configure.ac:
gl_FUNC_LCHOWN
diff --git a/modules/renameatu b/modules/renameatu
index e0c1ce3176..3fc68a91dc 100644
--- a/modules/renameatu
+++ b/modules/renameatu
@@ -13,6 +13,8 @@ extensions
fcntl-h
filenamecat-lgpl [test $HAVE_RENAMEAT = 0 || test $REPLACE_RENAMEAT = 1]
openat-h [test $HAVE_RENAMEAT = 0 || test $REPLACE_RENAMEAT = 1]
+fstatat [test $REPLACE_RENAMEAT = 1]
+readlinkat [test $REPLACE_RENAMEAT = 1]
stdbool [test $REPLACE_RENAMEAT = 1]
at-internal [test $HAVE_RENAMEAT = 0]
filename [test $HAVE_RENAMEAT = 0]