libstdc++: Fix --enable-cstdio=stdio_pure [PR110574]

When configured with --enable-cstdio=stdio_pure we need to consistently
use fseek and not mix seeks on the file descriptor with reads and writes
on the FILE stream.

There are also a number of bugs related to error handling and return
values, because fread and fwrite return 0 on error, not -1, and fseek
returns 0 on success, not the file offset.

libstdc++-v3/ChangeLog:

	PR libstdc++/110574
	* acinclude.m4 (GLIBCXX_CHECK_LFS): Check for fseeko and ftello
	and define _GLIBCXX_USE_FSEEKO_FTELLO.
	* config.h.in: Regenerate.
	* configure: Regenerate.
	* config/io/basic_file_stdio.cc (xwrite) [_GLIBCXX_USE_STDIO_PURE]:
	Check for fwrite error correctly.
	(__basic_file<char>::xsgetn) [_GLIBCXX_USE_STDIO_PURE]: Check for
	fread error correctly.
	(get_file_offset): New function.
	(__basic_file<char>::seekoff) [_GLIBCXX_USE_STDIO_PURE]: Use
	fseeko if available. Use get_file_offset instead of return value
	of fseek.
	(__basic_file<char>::showmanyc): Use get_file_offset.

(cherry picked from commit 2f6bbc9a7d)
This commit is contained in:
Jonathan Wakely 2023-07-06 17:10:41 +01:00
parent c9da749155
commit 5342e3cc44
4 changed files with 133 additions and 18 deletions

View File

@ -497,6 +497,22 @@ AC_DEFUN([GLIBCXX_CHECK_LFS], [
if test $glibcxx_cv_LFS = yes; then if test $glibcxx_cv_LFS = yes; then
AC_DEFINE(_GLIBCXX_USE_LFS, 1, [Define if LFS support is available.]) AC_DEFINE(_GLIBCXX_USE_LFS, 1, [Define if LFS support is available.])
fi fi
AC_CACHE_CHECK([for fseeko and ftello], glibcxx_cv_posix_lfs, [
GCC_TRY_COMPILE_OR_LINK(
[#include <stdio.h>
],
[FILE* fp;
fseeko(fp, 0, SEEK_CUR);
ftello(fp);
],
[glibcxx_cv_posix_lfs=yes],
[glibcxx_cv_posix_lfs=no])
])
if test $glibcxx_cv_posix_lfs = yes; then
AC_DEFINE(_GLIBCXX_USE_FSEEKO_FTELLO, 1, [Define if fseeko and ftello are available.])
fi
CXXFLAGS="$ac_save_CXXFLAGS" CXXFLAGS="$ac_save_CXXFLAGS"
AC_LANG_RESTORE AC_LANG_RESTORE
]) ])

View File

@ -949,6 +949,9 @@
/* Define if fchmodat is available in <sys/stat.h>. */ /* Define if fchmodat is available in <sys/stat.h>. */
#undef _GLIBCXX_USE_FCHMODAT #undef _GLIBCXX_USE_FCHMODAT
/* Define if fseeko and ftello are available. */
#undef _GLIBCXX_USE_FSEEKO_FTELLO
/* Defined if gettimeofday is available. */ /* Defined if gettimeofday is available. */
#undef _GLIBCXX_USE_GETTIMEOFDAY #undef _GLIBCXX_USE_GETTIMEOFDAY

View File

@ -129,14 +129,15 @@ namespace
{ {
#ifdef _GLIBCXX_USE_STDIO_PURE #ifdef _GLIBCXX_USE_STDIO_PURE
const std::streamsize __ret = fwrite(__s, 1, __nleft, __file); const std::streamsize __ret = fwrite(__s, 1, __nleft, __file);
if (__ret == 0 && ferror(__file))
break;
#else #else
const std::streamsize __ret = write(__fd, __s, __nleft); const std::streamsize __ret = write(__fd, __s, __nleft);
#endif
if (__ret == -1L && errno == EINTR) if (__ret == -1L && errno == EINTR)
continue; continue;
if (__ret == -1L) if (__ret == -1L)
break; break;
#endif
__nleft -= __ret; __nleft -= __ret;
if (__nleft == 0) if (__nleft == 0)
break; break;
@ -330,13 +331,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__basic_file<char>::xsgetn(char* __s, streamsize __n) __basic_file<char>::xsgetn(char* __s, streamsize __n)
{ {
streamsize __ret; streamsize __ret;
do
#ifdef _GLIBCXX_USE_STDIO_PURE #ifdef _GLIBCXX_USE_STDIO_PURE
__ret = fread(__s, 1, __n, this->file()); __ret = fread(__s, 1, __n, this->file());
if (__ret == 0 && ferror(this->file()))
__ret = -1;
#else #else
do
__ret = read(this->fd(), __s, __n); __ret = read(this->fd(), __s, __n);
#endif
while (__ret == -1L && errno == EINTR); while (__ret == -1L && errno == EINTR);
#endif
return __ret; return __ret;
} }
@ -375,20 +378,52 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return __ret; return __ret;
} }
namespace
{
inline streamoff
get_file_offset(__basic_file<char>* __f)
{
#ifdef _GLIBCXX_USE_STDIO_PURE
# ifdef _GLIBCXX_USE_FSEEKO_FTELLO
return ftello(__f->file());
# else
return ftell(__f->file());
# endif
#elif defined(_GLIBCXX_USE_LFS)
return lseek64(__f->fd(), 0, (int)ios_base::cur);
#else
return lseek(__f->fd(), 0, (int)ios_base::cur);
#endif
}
}
streamoff streamoff
__basic_file<char>::seekoff(streamoff __off, ios_base::seekdir __way) throw () __basic_file<char>::seekoff(streamoff __off, ios_base::seekdir __way) throw ()
{ {
#ifdef _GLIBCXX_USE_LFS #ifdef _GLIBCXX_USE_STDIO_PURE
#ifdef _GLIBCXX_USE_FSEEKO_FTELLO
if _GLIBCXX17_CONSTEXPR (sizeof(off_t) > sizeof(long))
{
if (fseeko(this->file(), __off, __way))
return -1;
}
else
#endif
{
if (__off > numeric_limits<long>::max()
|| __off < numeric_limits<long>::min())
return -1;
if (fseek(this->file(), __off, __way))
return -1;
}
return __way == ios_base::beg ? __off : std::get_file_offset(this);
#elif defined(_GLIBCXX_USE_LFS)
return lseek64(this->fd(), __off, __way); return lseek64(this->fd(), __off, __way);
#else #else
if (__off > numeric_limits<off_t>::max() if (__off > numeric_limits<off_t>::max()
|| __off < numeric_limits<off_t>::min()) || __off < numeric_limits<off_t>::min())
return -1L; return -1L;
#ifdef _GLIBCXX_USE_STDIO_PURE
return fseek(this->file(), __off, __way);
#else
return lseek(this->fd(), __off, __way); return lseek(this->fd(), __off, __way);
#endif
#endif #endif
} }
@ -425,19 +460,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
const int __err = fstat64(this->fd(), &__buffer); const int __err = fstat64(this->fd(), &__buffer);
if (!__err && _GLIBCXX_ISREG(__buffer.st_mode)) if (!__err && _GLIBCXX_ISREG(__buffer.st_mode))
{ {
const streamoff __off = __buffer.st_size - lseek64(this->fd(), 0, const streamoff __off = __buffer.st_size - std::get_file_offset(this);
ios_base::cur);
return std::min(__off, streamoff(numeric_limits<streamsize>::max())); return std::min(__off, streamoff(numeric_limits<streamsize>::max()));
} }
#else #else
struct stat __buffer; struct stat __buffer;
const int __err = fstat(this->fd(), &__buffer); const int __err = fstat(this->fd(), &__buffer);
if (!__err && _GLIBCXX_ISREG(__buffer.st_mode)) if (!__err && _GLIBCXX_ISREG(__buffer.st_mode))
#ifdef _GLIBCXX_USE_STDIO_PURE return __buffer.st_size - std::get_file_offset(this);
return __buffer.st_size - fseek(this->file(), 0, ios_base::cur);
#else
return __buffer.st_size - lseek(this->fd(), 0, ios_base::cur);
#endif
#endif #endif
#endif #endif
return 0; return 0;

View File

@ -19363,6 +19363,72 @@ $as_echo "$glibcxx_cv_LFS" >&6; }
$as_echo "#define _GLIBCXX_USE_LFS 1" >>confdefs.h $as_echo "#define _GLIBCXX_USE_LFS 1" >>confdefs.h
fi fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fseeko and ftello" >&5
$as_echo_n "checking for fseeko and ftello... " >&6; }
if ${glibcxx_cv_posix_lfs+:} false; then :
$as_echo_n "(cached) " >&6
else
if test x$gcc_no_link = xyes; then
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdio.h>
int
main ()
{
FILE* fp;
fseeko(fp, 0, SEEK_CUR);
ftello(fp);
;
return 0;
}
_ACEOF
if ac_fn_cxx_try_compile "$LINENO"; then :
glibcxx_cv_posix_lfs=yes
else
glibcxx_cv_posix_lfs=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
else
if test x$gcc_no_link = xyes; then
as_fn_error $? "Link tests are not allowed after GCC_NO_EXECUTABLES." "$LINENO" 5
fi
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdio.h>
int
main ()
{
FILE* fp;
fseeko(fp, 0, SEEK_CUR);
ftello(fp);
;
return 0;
}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
glibcxx_cv_posix_lfs=yes
else
glibcxx_cv_posix_lfs=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $glibcxx_cv_posix_lfs" >&5
$as_echo "$glibcxx_cv_posix_lfs" >&6; }
if test $glibcxx_cv_posix_lfs = yes; then
$as_echo "#define _GLIBCXX_USE_FSEEKO_FTELLO 1" >>confdefs.h
fi
CXXFLAGS="$ac_save_CXXFLAGS" CXXFLAGS="$ac_save_CXXFLAGS"
ac_ext=c ac_ext=c
ac_cpp='$CPP $CPPFLAGS' ac_cpp='$CPP $CPPFLAGS'