Support _FILE_OFFSET_BITS=64 for most of <stdio.h>.

This doesn't address funopen, but does add fgetpos/fsetpos/fseeko/ftello.

Bug: http://b/24807045
Change-Id: Ibff6f00df5fb699c8e8f13b91a75caf024540b73
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index a7df784..89bf04a 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -280,7 +280,7 @@
   EXPECT_EQ(1234, i);
   EXPECT_STREQ("a n b", buf);
 #else
-  GTEST_LOG_(INFO) << "This test does nothing.\n";
+  GTEST_LOG_(INFO) << "This test does nothing on glibc.\n";
 #endif
 }
 
@@ -831,7 +831,7 @@
   ASSERT_EQ(nullptr, open_memstream(&p, nullptr));
   ASSERT_EQ(EINVAL, errno);
 #else
-  GTEST_LOG_(INFO) << "This test does nothing.\n";
+  GTEST_LOG_(INFO) << "This test does nothing on glibc.\n";
 #endif
 }
 
@@ -962,7 +962,7 @@
 
   // But hitting EOF doesn't prevent us from writing...
   errno = 0;
-  ASSERT_EQ(1U, fwrite("2", 1, 1, fp)) << errno;
+  ASSERT_EQ(1U, fwrite("2", 1, 1, fp)) << strerror(errno);
 
   // And if we rewind, everything's there.
   rewind(fp);
@@ -1011,7 +1011,7 @@
   ASSERT_EQ(memcmp(file_data+cur_location, buffer, 8192), 0);
 
   // Small backwards seek to verify fseek does not reuse the internal buffer.
-  ASSERT_EQ(0, fseek(fp, -22, SEEK_CUR));
+  ASSERT_EQ(0, fseek(fp, -22, SEEK_CUR)) << strerror(errno);
   cur_location = static_cast<size_t>(ftell(fp));
   ASSERT_EQ(22U, fread(buffer, 1, 22, fp));
   ASSERT_EQ(memcmp(file_data+cur_location, buffer, 22), 0);
@@ -1121,3 +1121,90 @@
     delete tfs[i];
   }
 }
+
+static void AssertFileOffsetAt(FILE* fp, off64_t offset) {
+  EXPECT_EQ(offset, ftell(fp));
+  EXPECT_EQ(offset, ftello(fp));
+  fpos_t pos;
+  fpos64_t pos64;
+  EXPECT_EQ(0, fgetpos(fp, &pos));
+  EXPECT_EQ(0, fgetpos64(fp, &pos64));
+#if defined(__BIONIC__)
+  EXPECT_EQ(offset, static_cast<off64_t>(pos));
+  EXPECT_EQ(offset, static_cast<off64_t>(pos64));
+#else
+  GTEST_LOG_(INFO) << "glibc's fpos_t is opaque.\n";
+#endif
+}
+
+TEST(STDIO_TEST, seek_tell_family_smoke) {
+  TemporaryFile tf;
+  FILE* fp = fdopen(tf.fd, "w+");
+
+  // Initially we should be at 0.
+  AssertFileOffsetAt(fp, 0);
+
+  // Seek to offset 8192.
+  ASSERT_EQ(0, fseek(fp, 8192, SEEK_SET));
+  AssertFileOffsetAt(fp, 8192);
+  fpos_t eight_k_pos;
+  ASSERT_EQ(0, fgetpos(fp, &eight_k_pos));
+
+  // Seek forward another 8192...
+  ASSERT_EQ(0, fseek(fp, 8192, SEEK_CUR));
+  AssertFileOffsetAt(fp, 8192 + 8192);
+  fpos64_t sixteen_k_pos64;
+  ASSERT_EQ(0, fgetpos64(fp, &sixteen_k_pos64));
+
+  // Seek back 8192...
+  ASSERT_EQ(0, fseek(fp, -8192, SEEK_CUR));
+  AssertFileOffsetAt(fp, 8192);
+
+  // Since we haven't written anything, the end is also at 0.
+  ASSERT_EQ(0, fseek(fp, 0, SEEK_END));
+  AssertFileOffsetAt(fp, 0);
+
+  // Check that our fpos64_t from 16KiB works...
+  ASSERT_EQ(0, fsetpos64(fp, &sixteen_k_pos64));
+  AssertFileOffsetAt(fp, 8192 + 8192);
+  // ...as does our fpos_t from 8192.
+  ASSERT_EQ(0, fsetpos(fp, &eight_k_pos));
+  AssertFileOffsetAt(fp, 8192);
+
+  // Do fseeko and fseeko64 work too?
+  ASSERT_EQ(0, fseeko(fp, 1234, SEEK_SET));
+  AssertFileOffsetAt(fp, 1234);
+  ASSERT_EQ(0, fseeko64(fp, 5678, SEEK_SET));
+  AssertFileOffsetAt(fp, 5678);
+
+  fclose(fp);
+}
+
+TEST(STDIO_TEST, fseek_fseeko_EINVAL) {
+  TemporaryFile tf;
+  FILE* fp = fdopen(tf.fd, "w+");
+
+  // Bad whence.
+  errno = 0;
+  ASSERT_EQ(-1, fseek(fp, 0, 123));
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  ASSERT_EQ(-1, fseeko(fp, 0, 123));
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  ASSERT_EQ(-1, fseeko64(fp, 0, 123));
+  ASSERT_EQ(EINVAL, errno);
+
+  // Bad offset.
+  errno = 0;
+  ASSERT_EQ(-1, fseek(fp, -1, SEEK_SET));
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  ASSERT_EQ(-1, fseeko(fp, -1, SEEK_SET));
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  ASSERT_EQ(-1, fseeko64(fp, -1, SEEK_SET));
+  ASSERT_EQ(EINVAL, errno);
+
+  fclose(fp);
+}