summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2015-01-23 17:48:15 -0800
committerElliott Hughes <enh@google.com>2015-01-24 12:35:41 -0800
commite6bb5a27769cc974c4c6c1bfc96dcd07f0c0f5ef (patch)
tree67156569bf455a9eb3fd18e0151e8e67dfed309c
parent31005ca4c8562f3e6dfbed079eeaff8361ff8cdc (diff)
downloadbionic-e6bb5a27769cc974c4c6c1bfc96dcd07f0c0f5ef.zip
bionic-e6bb5a27769cc974c4c6c1bfc96dcd07f0c0f5ef.tar.gz
bionic-e6bb5a27769cc974c4c6c1bfc96dcd07f0c0f5ef.tar.bz2
Fix optimized fread.
gcov does writes after reads on the same stream, but the bulk read optimization was clobbering the FILE _flags, causing fwrite to fail. Bug: 19129055 Change-Id: I9650cb7de4bb173a706b502406266ed0d2b654d7
-rw-r--r--libc/stdio/fread.c2
-rw-r--r--tests/stdio_test.cpp56
2 files changed, 56 insertions, 2 deletions
diff --git a/libc/stdio/fread.c b/libc/stdio/fread.c
index baf62b9..bac8dad 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 0e24325..890e86e 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -27,6 +27,8 @@
#include <wchar.h>
#include <locale.h>
+#include <vector>
+
#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<char> 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<char> 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);
+}