summaryrefslogtreecommitdiff
path: root/libc/stdio/stdio.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libc/stdio/stdio.cpp')
-rw-r--r--libc/stdio/stdio.cpp93
1 files changed, 44 insertions, 49 deletions
diff --git a/libc/stdio/stdio.cpp b/libc/stdio/stdio.cpp
index a0b421923..c429ff2c3 100644
--- a/libc/stdio/stdio.cpp
+++ b/libc/stdio/stdio.cpp
@@ -50,15 +50,15 @@
#include <async_safe/log.h>
-#include "local.h"
#include "glue.h"
+#include "local.h"
+#include "private/ErrnoRestorer.h"
+#include "private/FdPath.h"
#include "private/__bionic_get_shell_path.h"
#include "private/bionic_fortify.h"
-#include "private/ErrnoRestorer.h"
#include "private/thread_private.h"
-#define ALIGNBYTES (sizeof(uintptr_t) - 1)
-#define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) &~ ALIGNBYTES)
+#include "private/bsd_sys_param.h" // For ALIGN/ALIGNBYTES.
#define NDYNAMIC 10 /* add ten more whenever necessary */
@@ -226,25 +226,21 @@ extern "C" __LIBC_HIDDEN__ void __libc_stdio_cleanup(void) {
_fwalk(__sflush);
}
-static FILE* __fopen(int fd, int flags) {
+static FILE* __FILE_init(FILE* fp, int fd, int flags) {
+ if (fp == nullptr) return nullptr;
+
#if !defined(__LP64__)
- if (fd > SHRT_MAX) {
- errno = EMFILE;
- return nullptr;
- }
+ if (fd > SHRT_MAX) __fortify_fatal("stdio: fd %d > SHRT_MAX", fd);
#endif
- FILE* fp = __sfp();
- if (fp != nullptr) {
- fp->_file = fd;
- android_fdsan_exchange_owner_tag(fd, 0, __get_file_tag(fp));
- fp->_flags = flags;
- fp->_cookie = fp;
- fp->_read = __sread;
- fp->_write = __swrite;
- fp->_close = __sclose;
- _EXT(fp)->_seek64 = __sseek64;
- }
+ fp->_file = fd;
+ android_fdsan_exchange_owner_tag(fd, 0, __get_file_tag(fp));
+ fp->_flags = flags;
+ fp->_cookie = fp;
+ fp->_read = __sread;
+ fp->_write = __swrite;
+ fp->_close = __sclose;
+ _EXT(fp)->_seek64 = __sseek64;
return fp;
}
@@ -258,14 +254,15 @@ FILE* fopen(const char* file, const char* mode) {
return nullptr;
}
- FILE* fp = __fopen(fd, flags);
+ FILE* fp = __FILE_init(__sfp(), fd, flags);
if (fp == nullptr) {
ErrnoRestorer errno_restorer;
close(fd);
return nullptr;
}
- // For append mode, even though we use O_APPEND, we need to seek to the end now.
+ // For append mode, O_APPEND sets the write position for free, but we need to
+ // set the read position manually.
if ((mode_flags & O_APPEND) != 0) __sseek64(fp, 0, SEEK_END);
return fp;
}
@@ -296,15 +293,26 @@ FILE* fdopen(int fd, const char* mode) {
fcntl(fd, F_SETFD, tmp | FD_CLOEXEC);
}
- return __fopen(fd, flags);
+ return __FILE_init(__sfp(), fd, flags);
}
-// Re-direct an existing, open (probably) file to some other file.
-// ANSI is written such that the original file gets closed if at
-// all possible, no matter what.
-// TODO: rewrite this mess completely.
FILE* freopen(const char* file, const char* mode, FILE* fp) {
CHECK_FP(fp);
+
+ // POSIX says: "If pathname is a null pointer, the freopen() function shall
+ // attempt to change the mode of the stream to that specified by mode, as if
+ // the name of the file currently associated with the stream had been used. In
+ // this case, the file descriptor associated with the stream need not be
+ // closed if the call to freopen() succeeds. It is implementation-defined
+ // which changes of mode are permitted (if any), and under what
+ // circumstances."
+ //
+ // Linux is quite restrictive about what changes you can make with F_SETFL,
+ // and in particular won't let you touch the access bits. It's easiest and
+ // most effective to just rely on /proc/self/fd/...
+ FdPath fd_path(fp->_file);
+ if (file == nullptr) file = fd_path.c_str();
+
int mode_flags;
int flags = __sflags(mode, &mode_flags);
if (flags == 0) {
@@ -314,6 +322,8 @@ FILE* freopen(const char* file, const char* mode, FILE* fp) {
ScopedFileLock sfl(fp);
+ // TODO: rewrite this mess completely.
+
// There are actually programs that depend on being able to "freopen"
// descriptors that weren't originally open. Keep this from breaking.
// Remember whether the stream was open to begin with, and which file
@@ -383,24 +393,12 @@ FILE* freopen(const char* file, const char* mode, FILE* fp) {
}
}
- // _file is only a short.
- if (fd > SHRT_MAX) {
- fp->_flags = 0; // Release.
- errno = EMFILE;
- return nullptr;
- }
-
- fp->_flags = flags;
- fp->_file = fd;
- android_fdsan_exchange_owner_tag(fd, 0, __get_file_tag(fp));
- fp->_cookie = fp;
- fp->_read = __sread;
- fp->_write = __swrite;
- fp->_close = __sclose;
- _EXT(fp)->_seek64 = __sseek64;
+ __FILE_init(fp, fd, flags);
- // For append mode, even though we use O_APPEND, we need to seek to the end now.
+ // For append mode, O_APPEND sets the write position for free, but we need to
+ // set the read position manually.
if ((mode_flags & O_APPEND) != 0) __sseek64(fp, 0, SEEK_END);
+
return fp;
}
__strong_alias(freopen64, freopen);
@@ -773,10 +771,7 @@ char* fgets(char* buf, int n, FILE* fp) {
// Returns first argument, or nullptr if no characters were read.
// Does not return nullptr if n == 1.
char* fgets_unlocked(char* buf, int n, FILE* fp) {
- if (n <= 0) {
- errno = EINVAL;
- return nullptr;
- }
+ if (n <= 0) __fortify_fatal("fgets: buffer size %d <= 0", n);
_SET_ORIENTATION(fp, -1);
@@ -1026,9 +1021,9 @@ int vsnprintf(char* s, size_t n, const char* fmt, va_list ap) {
__check_count("vsnprintf", "size", n);
// Stdio internals do not deal correctly with zero length buffer.
- char dummy;
+ char one_byte_buffer[1];
if (n == 0) {
- s = &dummy;
+ s = one_byte_buffer;
n = 1;
}