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/libc/include/stdio.h b/libc/include/stdio.h
index 98f0fd7..a227904 100644
--- a/libc/include/stdio.h
+++ b/libc/include/stdio.h
@@ -49,7 +49,8 @@
__BEGIN_DECLS
-typedef off_t fpos_t; /* stdio file position type */
+typedef off_t fpos_t;
+typedef off64_t fpos64_t;
struct __sFILE;
typedef struct __sFILE FILE;
@@ -118,8 +119,6 @@
FILE * __restrict);
int fscanf(FILE * __restrict, const char * __restrict, ...)
__scanflike(2, 3);
-int fseek(FILE *, long, int);
-long ftell(FILE *);
size_t fwrite(const void * __restrict, size_t, size_t, FILE * __restrict);
int getc(FILE *);
int getchar(void);
@@ -166,25 +165,26 @@
#endif
#endif
-extern int rename(const char*, const char*);
-extern int renameat(int, const char*, int, const char*);
+int rename(const char*, const char*);
+int renameat(int, const char*, int, const char*);
+int fseek(FILE*, long, int);
+long ftell(FILE*);
#if defined(__USE_FILE_OFFSET64)
-/* Not possible. */
-int fgetpos(FILE * __restrict, fpos_t * __restrict)
- __attribute__((__error__("not available with _FILE_OFFSET_BITS=64")));
-int fsetpos(FILE *, const fpos_t *)
- __attribute__((__error__("not available with _FILE_OFFSET_BITS=64")));
-int fseeko(FILE *, off_t, int)
- __attribute__((__error__("not available with _FILE_OFFSET_BITS=64")));
-off_t ftello(FILE *)
- __attribute__((__error__("not available with _FILE_OFFSET_BITS=64")));
+int fgetpos(FILE*, fpos_t*) __RENAME(fgetpos64);
+int fsetpos(FILE*, const fpos_t*) __RENAME(fsetpos64);
+int fseeko(FILE*, off_t, int) __RENAME(fseeko64);
+off_t ftello(FILE*) __RENAME(ftello64);
#else
-int fgetpos(FILE * __restrict, fpos_t * __restrict);
-int fsetpos(FILE *, const fpos_t *);
-int fseeko(FILE *, off_t, int);
-off_t ftello(FILE *);
+int fgetpos(FILE*, fpos_t*);
+int fsetpos(FILE*, const fpos_t*);
+int fseeko(FILE*, off_t, int);
+off_t ftello(FILE*);
#endif
+int fgetpos64(FILE*, fpos64_t*);
+int fsetpos64(FILE*, const fpos64_t*);
+int fseeko64(FILE*, off64_t, int);
+off64_t ftello64(FILE*);
#if __ISO_C_VISIBLE >= 1999 || __BSD_VISIBLE
int snprintf(char * __restrict, size_t, const char * __restrict, ...)
@@ -256,6 +256,7 @@
/*
* Stdio function-access interface.
+ * TODO: __USE_FILE_OFFSET64
*/
FILE *funopen(const void *,
int (*)(void *, char *, int),
diff --git a/libc/libc.arm.brillo.map b/libc/libc.arm.brillo.map
index 95f7cba..9c31f8f 100644
--- a/libc/libc.arm.brillo.map
+++ b/libc/libc.arm.brillo.map
@@ -1228,8 +1228,11 @@
__pwrite_chk;
__pwrite64_chk;
__write_chk;
+ fgetpos64;
fileno_unlocked;
freeifaddrs;
+ fseeko64;
+ fsetpos64;
getgrgid_r;
getgrnam_r;
getifaddrs;
diff --git a/libc/libc.arm.map b/libc/libc.arm.map
index 90b1db3..adb7a65 100644
--- a/libc/libc.arm.map
+++ b/libc/libc.arm.map
@@ -1228,8 +1228,11 @@
__pwrite_chk;
__pwrite64_chk;
__write_chk;
+ fgetpos64;
fileno_unlocked;
freeifaddrs;
+ fseeko64;
+ fsetpos64;
getgrgid_r;
getgrnam_r;
getifaddrs;
diff --git a/libc/libc.arm64.map b/libc/libc.arm64.map
index e5933ae..a3308bd 100644
--- a/libc/libc.arm64.map
+++ b/libc/libc.arm64.map
@@ -1152,8 +1152,11 @@
__pwrite_chk;
__pwrite64_chk;
__write_chk;
+ fgetpos64;
fileno_unlocked;
freeifaddrs;
+ fseeko64;
+ fsetpos64;
getgrgid_r;
getgrnam_r;
getifaddrs;
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index 928f10d..3ebb4ab 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1255,8 +1255,11 @@
__pwrite_chk;
__pwrite64_chk;
__write_chk;
+ fgetpos64;
fileno_unlocked;
freeifaddrs;
+ fseeko64;
+ fsetpos64;
getgrgid_r;
getgrnam_r;
getifaddrs;
diff --git a/libc/libc.mips.brillo.map b/libc/libc.mips.brillo.map
index 691a22a..a5b5376 100644
--- a/libc/libc.mips.brillo.map
+++ b/libc/libc.mips.brillo.map
@@ -1213,8 +1213,11 @@
__pwrite_chk;
__pwrite64_chk;
__write_chk;
+ fgetpos64;
fileno_unlocked;
freeifaddrs;
+ fseeko64;
+ fsetpos64;
getgrgid_r;
getgrnam_r;
getifaddrs;
diff --git a/libc/libc.mips.map b/libc/libc.mips.map
index 04c76d4..5b2cc3d 100644
--- a/libc/libc.mips.map
+++ b/libc/libc.mips.map
@@ -1213,8 +1213,11 @@
__pwrite_chk;
__pwrite64_chk;
__write_chk;
+ fgetpos64;
fileno_unlocked;
freeifaddrs;
+ fseeko64;
+ fsetpos64;
getgrgid_r;
getgrnam_r;
getifaddrs;
diff --git a/libc/libc.mips64.map b/libc/libc.mips64.map
index e5933ae..a3308bd 100644
--- a/libc/libc.mips64.map
+++ b/libc/libc.mips64.map
@@ -1152,8 +1152,11 @@
__pwrite_chk;
__pwrite64_chk;
__write_chk;
+ fgetpos64;
fileno_unlocked;
freeifaddrs;
+ fseeko64;
+ fsetpos64;
getgrgid_r;
getgrnam_r;
getifaddrs;
diff --git a/libc/libc.x86.brillo.map b/libc/libc.x86.brillo.map
index fbd7085..c28d35a 100644
--- a/libc/libc.x86.brillo.map
+++ b/libc/libc.x86.brillo.map
@@ -1212,8 +1212,11 @@
__pwrite_chk;
__pwrite64_chk;
__write_chk;
+ fgetpos64;
fileno_unlocked;
freeifaddrs;
+ fseeko64;
+ fsetpos64;
getgrgid_r;
getgrnam_r;
getifaddrs;
diff --git a/libc/libc.x86.map b/libc/libc.x86.map
index bcabf71..37fbba1 100644
--- a/libc/libc.x86.map
+++ b/libc/libc.x86.map
@@ -1212,8 +1212,11 @@
__pwrite_chk;
__pwrite64_chk;
__write_chk;
+ fgetpos64;
fileno_unlocked;
freeifaddrs;
+ fseeko64;
+ fsetpos64;
getgrgid_r;
getgrnam_r;
getifaddrs;
diff --git a/libc/libc.x86_64.map b/libc/libc.x86_64.map
index e5933ae..a3308bd 100644
--- a/libc/libc.x86_64.map
+++ b/libc/libc.x86_64.map
@@ -1152,8 +1152,11 @@
__pwrite_chk;
__pwrite64_chk;
__write_chk;
+ fgetpos64;
fileno_unlocked;
freeifaddrs;
+ fseeko64;
+ fsetpos64;
getgrgid_r;
getgrnam_r;
getifaddrs;
diff --git a/libc/stdio/stdio.cpp b/libc/stdio/stdio.cpp
index 7749e0e..23b6971 100644
--- a/libc/stdio/stdio.cpp
+++ b/libc/stdio/stdio.cpp
@@ -433,11 +433,10 @@
}
}
-// TODO: _FILE_OFFSET_BITS=64.
-static off_t __ftello_unlocked(FILE* fp) {
+static off64_t __ftello64_unlocked(FILE* fp) {
// Find offset of underlying I/O object, then adjust for buffered bytes.
__sflush(fp); // May adjust seek offset on append stream.
- fpos_t result = __seek_unlocked(fp, 0, SEEK_CUR);
+ off64_t result = __seek_unlocked(fp, 0, SEEK_CUR);
if (result == -1) {
return -1;
}
@@ -457,29 +456,34 @@
return result;
}
-// TODO: _FILE_OFFSET_BITS=64.
-int fseeko(FILE* fp, off_t offset, int whence) {
+int __fseeko64(FILE* fp, off64_t offset, int whence, int off_t_bits) {
ScopedFileLock sfl(fp);
// Change any SEEK_CUR to SEEK_SET, and check `whence` argument.
// After this, whence is either SEEK_SET or SEEK_END.
if (whence == SEEK_CUR) {
- fpos_t current_offset = __ftello_unlocked(fp);
+ fpos64_t current_offset = __ftello64_unlocked(fp);
if (current_offset == -1) {
- return EOF;
+ return -1;
}
offset += current_offset;
whence = SEEK_SET;
} else if (whence != SEEK_SET && whence != SEEK_END) {
errno = EINVAL;
- return EOF;
+ return -1;
+ }
+
+ // If our caller has a 32-bit interface, refuse to go past a 32-bit file offset.
+ if (off_t_bits == 32 && offset > LONG_MAX) {
+ errno = EOVERFLOW;
+ return -1;
}
if (fp->_bf._base == NULL) __smakebuf(fp);
// Flush unwritten data and attempt the seek.
if (__sflush(fp) || __seek_unlocked(fp, offset, whence) == -1) {
- return EOF;
+ return -1;
}
// Success: clear EOF indicator and discard ungetc() data.
@@ -491,34 +495,46 @@
return 0;
}
-// TODO: _FILE_OFFSET_BITS=64.
-int fseek(FILE* fp, long offset, int whence) {
- return fseeko(fp, offset, whence);
+int fseeko(FILE* fp, off_t offset, int whence) {
+ static_assert(sizeof(off_t) == sizeof(long), "sizeof(off_t) != sizeof(long)");
+ return __fseeko64(fp, offset, whence, 8*sizeof(off_t));
+}
+__strong_alias(fseek, fseeko);
+
+int fseeko64(FILE* fp, off64_t offset, int whence) {
+ return __fseeko64(fp, offset, whence, 8*sizeof(off_t));
}
-// TODO: _FILE_OFFSET_BITS=64.
+int fsetpos(FILE* fp, const fpos_t* pos) {
+ return fseeko(fp, *pos, SEEK_SET);
+}
+
+int fsetpos64(FILE* fp, const fpos64_t* pos) {
+ return fseeko64(fp, *pos, SEEK_SET);
+}
+
off_t ftello(FILE* fp) {
- ScopedFileLock sfl(fp);
- return __ftello_unlocked(fp);
-}
-
-// TODO: _FILE_OFFSET_BITS=64
-long ftell(FILE* fp) {
- off_t offset = ftello(fp);
- if (offset > LONG_MAX) {
+ static_assert(sizeof(off_t) == sizeof(long), "sizeof(off_t) != sizeof(long)");
+ off64_t result = ftello64(fp);
+ if (result > LONG_MAX) {
errno = EOVERFLOW;
return -1;
}
- return offset;
+ return result;
+}
+__strong_alias(ftell, ftello);
+
+off64_t ftello64(FILE* fp) {
+ ScopedFileLock sfl(fp);
+ return __ftello64_unlocked(fp);
}
-// TODO: _FILE_OFFSET_BITS=64
int fgetpos(FILE* fp, fpos_t* pos) {
*pos = ftello(fp);
return (*pos == -1);
}
-// TODO: _FILE_OFFSET_BITS=64
-int fsetpos(FILE* fp, const fpos_t* pos) {
- return fseeko(fp, *pos, SEEK_SET);
+int fgetpos64(FILE* fp, fpos64_t* pos) {
+ *pos = ftello64(fp);
+ return (*pos == -1);
}
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);
+}