diff --git a/libc/stdio/fread.c b/libc/stdio/fread.c index baf62b941..bac8dad66 100644 --- a/libc/stdio/fread.c +++ b/libc/stdio/fread.c @@ -120,7 +120,7 @@ fread(void *buf, size_t size, size_t count, FILE *fp) while (total > 0) { ssize_t bytes_read = (*fp->_read)(fp->_cookie, dst, total); if (bytes_read <= 0) { - fp->_flags = (fp->_r == 0) ? __SEOF : __SERR; + fp->_flags |= (bytes_read == 0) ? __SEOF : __SERR; break; } dst += bytes_read; diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp index 0e24325c4..890e86edc 100644 --- a/tests/stdio_test.cpp +++ b/tests/stdio_test.cpp @@ -27,6 +27,8 @@ #include #include +#include + #include "TemporaryFile.h" TEST(stdio, flockfile_18208568_stderr) { @@ -872,7 +874,7 @@ TEST(stdio, fread_unbuffered_pathological_performance) { time_t t0 = time(NULL); for (size_t i = 0; i < 1024; ++i) { - fread(buf, 64*1024, 1, fp); + ASSERT_EQ(1U, fread(buf, 64*1024, 1, fp)); } time_t t1 = time(NULL); @@ -911,3 +913,55 @@ TEST(stdio, fread_EOF) { fclose(fp); } + +static void test_fread_from_write_only_stream(size_t n) { + FILE* fp = fopen("/dev/null", "w"); + std::vector buf(n, 0); + errno = 0; + ASSERT_EQ(0U, fread(&buf[0], n, 1, fp)); + ASSERT_EQ(EBADF, errno); + ASSERT_TRUE(ferror(fp)); + ASSERT_FALSE(feof(fp)); + fclose(fp); +} + +TEST(stdio, fread_from_write_only_stream_slow_path) { + test_fread_from_write_only_stream(1); +} + +TEST(stdio, fread_from_write_only_stream_fast_path) { + test_fread_from_write_only_stream(64*1024); +} + +static void test_fwrite_after_fread(size_t n) { + TemporaryFile tf; + + FILE* fp = fdopen(tf.fd, "w+"); + ASSERT_EQ(1U, fwrite("1", 1, 1, fp)); + fflush(fp); + + // We've flushed but not rewound, so there's nothing to read. + std::vector buf(n, 0); + ASSERT_EQ(0U, fread(&buf[0], 1, buf.size(), fp)); + ASSERT_TRUE(feof(fp)); + + // But hitting EOF doesn't prevent us from writing... + errno = 0; + ASSERT_EQ(1U, fwrite("2", 1, 1, fp)) << errno; + + // And if we rewind, everything's there. + rewind(fp); + ASSERT_EQ(2U, fread(&buf[0], 1, buf.size(), fp)); + ASSERT_EQ('1', buf[0]); + ASSERT_EQ('2', buf[1]); + + fclose(fp); +} + +TEST(stdio, fwrite_after_fread_slow_path) { + test_fwrite_after_fread(16); +} + +TEST(stdio, fwrite_after_fread_fast_path) { + test_fwrite_after_fread(64*1024); +}