author | Ian Blanes
<ian@kewao.com> 2018-09-12 10:12:13 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2018-09-26 18:32:03 UTC |
parent | 0b0ab9a723d86ee17c1922709267d6f86c18df6f |
preload/posix/codegen.h | +23 | -2 |
preload/posix/generate | +19 | -2 |
preload/posix/modules/posix.custom.c | +727 | -1 |
preload/posix/modules/posix.stdio.mod | +232 | -0 |
diff --git a/preload/posix/codegen.h b/preload/posix/codegen.h index 9e8cb3a..8cbabaa 100644 --- a/preload/posix/codegen.h +++ b/preload/posix/codegen.h @@ -11,6 +11,9 @@ extern int __thread _fiu_called; /* Get a symbol from libc */ void *libc_symbol(const char *symbol); +/* Record internally an error for a stream */ +void set_ferror(void * stream); + /* Some compilers support constructor priorities. Since we don't rely on them, * but use them for clarity purposes, use a macro so libfiu builds on systems * where they're not supported. @@ -75,9 +78,9 @@ void *libc_symbol(const char *symbol); /* Generates the init part of the wrapped function */ #define mkwrap_init(RTYPE, NAME, PARAMS, PARAMST) \ - static RTYPE (*_fiu_orig_##NAME) PARAMS = NULL; \ + static __thread RTYPE (*_fiu_orig_##NAME) PARAMS = NULL; \ \ - static int _fiu_in_init_##NAME = 0; \ + static __thread int _fiu_in_init_##NAME = 0; \ \ static void constructor_attr(201) _fiu_init_##NAME(void) \ { \ @@ -166,6 +169,24 @@ void *libc_symbol(const char *symbol); goto exit; \ } +/* As mkwrap_body_errno, but calls set_ferror for the given stream. */ +#define mkwrap_body_errno_ferror(FIU_NAME, FAIL_RET, STREAM) \ + \ + fstatus = fiu_fail(FIU_NAME); \ + if (fstatus != 0) { \ + void *finfo = fiu_failinfo(); \ + if (finfo == NULL) { \ + errno = valid_errnos[random() % \ + sizeof(valid_errnos) / sizeof(int)]; \ + } else { \ + errno = (long) finfo; \ + } \ + r = FAIL_RET; \ + printd("failing\n"); \ + set_ferror(STREAM); \ + goto exit; \ + } + /* Generates a body part that will reduce the CNT parameter in a random * amount when the given point of failure is enabled. Can be combined with the * other body generators. */ diff --git a/preload/posix/generate b/preload/posix/generate index 3c862f3..0b073e4 100755 --- a/preload/posix/generate +++ b/preload/posix/generate @@ -51,6 +51,9 @@ class Function: self.use_errno = False self.valid_errnos = [] + # the FILE * for which a future ferror() shall fail too. + self.ferror = None + # if the given parameter should be reduced by a random amount self.reduce = None @@ -78,6 +81,8 @@ class Function: elif k == 'valid errnos': self.use_errno = True self.valid_errnos = v.split() + elif k == 'ferror': + self.ferror = v elif k == 'reduce': self.reduce = v elif k == 'variants': @@ -125,8 +130,12 @@ class Function: # be explicit self.write_valid_errnos(f) - f.write('mkwrap_body_errno("%s", %s)\n' % \ - (self.fiu_name, self.on_error) ) + if self.ferror is not None: + f.write('mkwrap_body_errno_ferror("%s", %s, %s)\n' % \ + (self.fiu_name, self.on_error, self.ferror) ) + else: + f.write('mkwrap_body_errno("%s", %s)\n' % \ + (self.fiu_name, self.on_error) ) elif self.on_error is not None: f.write('mkwrap_body_hardcoded("%s", %s)\n' % \ (self.fiu_name, self.on_error) ) @@ -163,9 +172,17 @@ class Function: # enabling just <func>. f.name = f.name + "64" f.params = f.params.replace("off_t", "off64_t") + f.params = f.params.replace("fpos_t *", "fpos64_t *") + f.params = f.params.replace("const fpos_t *", "const fpos64_t *") f.params_info = [ (x, y) if x != "off_t " else ("off64_t ", y) for (x, y) in f.params_info] + f.params_info = [ + (x, y) if x != "fpos_t *" else ("fpos64_t *", y) + for (x, y) in f.params_info] + f.params_info = [ + (x, y) if x != "const fpos_t *" else ("const fpos64_t *", y) + for (x, y) in f.params_info] # This is glibc-specific, so surround it with #ifdefs. return [Verbatim("#ifdef __GLIBC__"), f, Verbatim("#endif")] diff --git a/preload/posix/modules/posix.custom.c b/preload/posix/modules/posix.custom.c index 2672728..e099be7 100644 --- a/preload/posix/modules/posix.custom.c +++ b/preload/posix/modules/posix.custom.c @@ -12,6 +12,9 @@ #include <fcntl.h> #include <errno.h> #include <stdarg.h> +#include <stdio.h> +#include <stdint.h> +#include <pthread.h> /* Wrapper for open(), we can't generate it because it has a variable number @@ -36,7 +39,7 @@ int open(const char *pathname, int flags, ...) * sometimes is smaller than an int, so we should always pass * int to it, and not mode_t. Not doing so would may result in * a compile-time warning and run-time error. We asume that it - * is never bigger than an int, which holds in practise. */ + * is never bigger than an int, which holds in practice. */ mode = va_arg(l, int); va_end(l); @@ -180,3 +183,726 @@ mkwrap_body_errno("posix/io/oc/open", -1) mkwrap_bottom(open64, (pathname, flags, mode)) #endif + +/* + * Here we keep track of when a FILE* I/O operation fails and fix ferror + * accordingly. + */ + +#define MAX_FERROR_TRACKED_FILES 16384 + +static void * ferror_hash_table[MAX_FERROR_TRACKED_FILES] = {NULL}; + +static int ferror_hash_table_usage = 0; + +static pthread_mutex_t ferror_hash_table_usage_mutex + = PTHREAD_MUTEX_INITIALIZER; + +void set_ferror(void * stream) +{ + if (stream == NULL) + return; + + /* Hash table has to have at least one empty position. */ + if (ferror_hash_table_usage + 1 == MAX_FERROR_TRACKED_FILES) { + /* Original call is already failing, so we cannot report this + * otherwise. */ + fprintf(stderr, "libfiu: ferror() hash table is full, ferror() will" + " not be faked for this file (too many open files)\n"); + return; + } + + pthread_mutex_lock(&ferror_hash_table_usage_mutex); + + /* Our hash function is taking the least significant bits of a FILE *. */ + uintptr_t ptr = (uintptr_t) stream; + + int index = (int) (ptr % MAX_FERROR_TRACKED_FILES); + + for (;;) { + if (ferror_hash_table[index] == stream) { + // found => do nothing + break; + } + + if (ferror_hash_table[index] == NULL) { + // not found => insert + ferror_hash_table[index] = stream; + ferror_hash_table_usage++; + break; + } + + index = (index + 1) % MAX_FERROR_TRACKED_FILES; + } + + pthread_mutex_unlock(&ferror_hash_table_usage_mutex); +} + +static int get_ferror(void * stream) +{ + if (stream == NULL) + return 1; + + pthread_mutex_lock(&ferror_hash_table_usage_mutex); + + uintptr_t ptr = (uintptr_t) stream; + + int index = (int) (ptr % MAX_FERROR_TRACKED_FILES); + + int r; + + for (;;) { + if (ferror_hash_table[index] == stream) { + // found + r = 1; + break; + } + + if (ferror_hash_table[index] == NULL) { + // not found + r = 0; + break; + } + + index = (index + 1) % MAX_FERROR_TRACKED_FILES; + } + + pthread_mutex_unlock(&ferror_hash_table_usage_mutex); + + return r; +} + +static void clear_ferror(void * stream) +{ + if (stream == NULL) + return; + + pthread_mutex_lock(&ferror_hash_table_usage_mutex); + + uintptr_t ptr = (uintptr_t) stream; + + int index = (int) (ptr % MAX_FERROR_TRACKED_FILES); + + for (;;) { + if (ferror_hash_table[index] == stream) { + /* found => clear and move back colliding entries. */ + + for (;;) { + int next_index = (index + 1) % MAX_FERROR_TRACKED_FILES; + ferror_hash_table[index] = ferror_hash_table[next_index]; + + if (ferror_hash_table[index] == NULL) + break; + + index = next_index; + } + + ferror_hash_table_usage--; + + break; + } + + if (ferror_hash_table[index] == NULL) { + /* not found => do nothing */ + break; + } + + index = (index + 1) % MAX_FERROR_TRACKED_FILES; + } + + pthread_mutex_unlock(&ferror_hash_table_usage_mutex); +} + + +/* Wrapper for ferror() */ +static __thread int (*_fiu_orig_ferror) (FILE *stream) = NULL; + +static __thread int _fiu_in_init_ferror = 0; + +static void __attribute__((constructor(201))) _fiu_init_ferror(void) { + rec_inc(); + _fiu_in_init_ferror++; + _fiu_orig_ferror = (int (*) (FILE *)) libc_symbol("ferror"); + _fiu_in_init_ferror--; + rec_dec(); +} + +int ferror (FILE *stream) +{ + int r; + int fstatus; + if (_fiu_called) { + if (_fiu_orig_ferror == NULL) { + if (_fiu_in_init_ferror) { + printd("fail on init\n"); + return 1; + } else { + printd("get orig\n"); + _fiu_init_ferror(); + } + } + + printd("orig\n"); + return (*_fiu_orig_ferror) (stream); + } + printd("fiu\n"); + + /* fiu_fail() may call anything */ + rec_inc(); + fstatus = fiu_fail("posix/stdio/error/ferror"); + if (fstatus != 0) { + r = 1; + printd("failing\n"); + goto exit; + } + + if (_fiu_orig_ferror == NULL) + _fiu_init_ferror(); + + printd("calling orig\n"); + r = (*_fiu_orig_ferror) (stream); + + if (r == 0 && get_ferror(stream)) { + printd("ferror fixed\n"); + return 1; + } + +exit: + rec_dec(); + return r; +} + + + +/* Wrapper for clearerr() */ +static __thread void (*_fiu_orig_clearerr) (FILE *stream) = NULL; + +static __thread int _fiu_in_init_clearerr = 0; + +static void __attribute__((constructor(201))) _fiu_init_clearerr(void) +{ + rec_inc(); + _fiu_in_init_clearerr++; + _fiu_orig_clearerr = (void (*) (FILE *)) libc_symbol("clearerr"); + _fiu_in_init_clearerr--; + rec_dec(); +} + +void clearerr (FILE *stream) +{ + if (_fiu_called) { + if (_fiu_orig_clearerr == NULL) { + if (_fiu_in_init_clearerr) { + printd("fail on init\n"); + return; + } else { + printd("get orig\n"); + _fiu_init_clearerr(); + } + } + printd("orig\n"); + (*_fiu_orig_clearerr) (stream); + return; + } + + printd("fiu\n"); + + rec_inc(); + + if (_fiu_orig_clearerr == NULL) + _fiu_init_clearerr(); + + printd("calling orig\n"); + (*_fiu_orig_clearerr) (stream); + + printd("fixing internal state\n"); + clear_ferror(stream); + + rec_dec(); +} + + + +/* Wrapper for fprintf(), we can't generate it because it has a variable + * number of arguments */ + +mkwrap_init(int, vfprintf, + (FILE *restrict stream, const char *restrict format, va_list ap), + (FILE *restrict, const char *restrict, va_list)); + +int fprintf(FILE *restrict stream, const char *restrict format, ...) +{ + int r; + va_list arguments; + + if (_fiu_called) { + if (_fiu_orig_vfprintf == NULL) { + if (_fiu_in_init_vfprintf) { + printd("fail on init\n"); + return -1; + } else { + printd("get orig\n"); + _fiu_init_vfprintf(); + } + } + printd("orig\n"); + va_start(arguments, format); + r = (*_fiu_orig_vfprintf) (stream, format, arguments); + va_end(arguments); + + return r; + } + + printd("fiu\n"); + + /* fiu_fail() may call anything */ + rec_inc(); + + static const int valid_errnos[] = { + #ifdef EAGAIN + EAGAIN, + #endif + #ifdef EBADF + EBADF, + #endif + #ifdef EFBIG + EFBIG, + #endif + #ifdef EINTR + EINTR, + #endif + #ifdef EIO + EIO, + #endif + #ifdef ENOMEM + ENOMEM, + #endif + #ifdef ENOSPC + ENOSPC, + #endif + #ifdef ENXIO + ENXIO, + #endif + #ifdef EPIPE + EPIPE, + #endif + #ifdef EILSEQ + EILSEQ, + #endif + #ifdef EOVERFLOW + EOVERFLOW, + #endif + }; + + const int fstatus = fiu_fail("posix/stdio/sp/fprintf"); + + if (fstatus != 0) { + void *finfo = fiu_failinfo(); + if (finfo == NULL) { + errno = valid_errnos[random() % + sizeof(valid_errnos) / sizeof(int)]; + } else { + errno = (long) finfo; + } + r = -1; + printd("failing\n"); + set_ferror(stream); + goto exit; + } + + if (_fiu_orig_vfprintf == NULL) + _fiu_init_vfprintf(); + + printd("calling orig\n"); + va_start(arguments, format); + r = (*_fiu_orig_vfprintf) (stream, format, arguments); + va_end(arguments); + +exit: + rec_dec(); + return r; +} + + +/* Wrapper for printf(), we can't generate it because it has a variable + * number of arguments */ + +mkwrap_init(int, vprintf, + (const char *restrict format, va_list ap), + (const char *restrict, va_list)); + +int printf(const char *restrict format, ...) +{ + int r; + va_list arguments; + + if (_fiu_called) { + if (_fiu_orig_vprintf == NULL) { + if (_fiu_in_init_vprintf) { + printd("fail on init\n"); + return -1; + } else { + printd("get orig\n"); + _fiu_init_vprintf(); + } + } + printd("orig\n"); + va_start(arguments, format); + r = (*_fiu_orig_vprintf) (format, arguments); + va_end(arguments); + + return r; + } + + printd("fiu\n"); + + /* fiu_fail() may call anything */ + rec_inc(); + + static const int valid_errnos[] = { + #ifdef EAGAIN + EAGAIN, + #endif + #ifdef EBADF + EBADF, + #endif + #ifdef EFBIG + EFBIG, + #endif + #ifdef EINTR + EINTR, + #endif + #ifdef EIO + EIO, + #endif + #ifdef ENOMEM + ENOMEM, + #endif + #ifdef ENOSPC + ENOSPC, + #endif + #ifdef ENXIO + ENXIO, + #endif + #ifdef EPIPE + EPIPE, + #endif + #ifdef EILSEQ + EILSEQ, + #endif + #ifdef EOVERFLOW + EOVERFLOW, + #endif + }; + + const int fstatus = fiu_fail("posix/stdio/sp/printf"); + if (fstatus != 0) { + void *finfo = fiu_failinfo(); + if (finfo == NULL) { + errno = valid_errnos[random() % + sizeof(valid_errnos) / sizeof(int)]; + } else { + errno = (long) finfo; + } + r = -1; + printd("failing\n"); + set_ferror(stdout); + goto exit; + } + + if (_fiu_orig_vprintf == NULL) + _fiu_init_vprintf(); + + printd("calling orig\n"); + va_start(arguments, format); + r = (*_fiu_orig_vprintf) (format, arguments); + va_end(arguments); + +exit: + rec_dec(); + return r; +} + + +/* Wrapper for dprintf(), we can't generate it because it has a variable + * number of arguments */ + +mkwrap_init(int, vdprintf, + (int fildes, const char *restrict format, va_list ap), + (int, const char *restrict, va_list)); + +int dprintf(int fildes, const char *restrict format, ...) +{ + int r; + va_list arguments; + + if (_fiu_called) { + if (_fiu_orig_vdprintf == NULL) { + if (_fiu_in_init_vdprintf) { + printd("fail on init\n"); + return -1; + } else { + printd("get orig\n"); + _fiu_init_vdprintf(); + } + } + printd("orig\n"); + va_start(arguments, format); + r = (*_fiu_orig_vdprintf) (fildes, format, arguments); + va_end(arguments); + + return r; + } + + printd("fiu\n"); + + /* fiu_fail() may call anything */ + rec_inc(); + + static const int valid_errnos[] = { + #ifdef EAGAIN + EAGAIN, + #endif + #ifdef EBADF + EBADF, + #endif + #ifdef EFBIG + EFBIG, + #endif + #ifdef EINTR + EINTR, + #endif + #ifdef EIO + EIO, + #endif + #ifdef ENOMEM + ENOMEM, + #endif + #ifdef ENOSPC + ENOSPC, + #endif + #ifdef ENXIO + ENXIO, + #endif + #ifdef EPIPE + EPIPE, + #endif + #ifdef EILSEQ + EILSEQ, + #endif + #ifdef EOVERFLOW + EOVERFLOW, + #endif + }; + + const int fstatus = fiu_fail("posix/stdio/sp/dprintf"); + if (fstatus != 0) { + void *finfo = fiu_failinfo(); + if (finfo == NULL) { + errno = valid_errnos[random() % + sizeof(valid_errnos) / sizeof(int)]; + } else { + errno = (long) finfo; + } + r = -1; + printd("failing\n"); + goto exit; + } + + if (_fiu_orig_vdprintf == NULL) + _fiu_init_vdprintf(); + + printd("calling orig\n"); + va_start(arguments, format); + r = (*_fiu_orig_vdprintf) (fildes, format, arguments); + va_end(arguments); + +exit: + rec_dec(); + return r; +} + + +/* Wrapper for fscanf(), we can't generate it because it has a variable + * number of arguments */ + +mkwrap_init(int, vfscanf, + (FILE *restrict stream, const char *restrict format, va_list ap), + (FILE *restrict, const char *restrict, va_list)); + +int fscanf(FILE *restrict stream, const char *restrict format, ...) +{ + int r; + va_list arguments; + + if (_fiu_called) { + if (_fiu_orig_vfscanf == NULL) { + if (_fiu_in_init_vfscanf) { + printd("fail on init\n"); + return EOF; + } else { + printd("get orig\n"); + _fiu_init_vfscanf(); + } + } + printd("orig\n"); + va_start(arguments, format); + r = (*_fiu_orig_vfscanf) (stream, format, arguments); + va_end(arguments); + + return r; + } + + printd("fiu\n"); + + /* fiu_fail() may call anything */ + rec_inc(); + + static const int valid_errnos[] = { + #ifdef EAGAIN + EAGAIN, + #endif + #ifdef EBADF + EBADF, + #endif + #ifdef EINTR + EINTR, + #endif + #ifdef EIO + EIO, + #endif + #ifdef ENOMEM + ENOMEM, + #endif + #ifdef ENXIO + ENXIO, + #endif + #ifdef EOVERFLOW + EOVERFLOW, + #endif + #ifdef EILSEQ + EILSEQ, + #endif + #ifdef EINVAL + EINVAL, + #endif + }; + + const int fstatus = fiu_fail("posix/stdio/sp/fscanf"); + if (fstatus != 0) { + void *finfo = fiu_failinfo(); + if (finfo == NULL) { + errno = valid_errnos[random() % + sizeof(valid_errnos) / sizeof(int)]; + } else { + errno = (long) finfo; + } + r = EOF; + printd("failing\n"); + set_ferror(stream); + goto exit; + } + + if (_fiu_orig_vfscanf == NULL) + _fiu_init_vfscanf(); + + printd("calling orig\n"); + va_start(arguments, format); + r = (*_fiu_orig_vfscanf) (stream, format, arguments); + va_end(arguments); + +exit: + rec_dec(); + return r; +} + + +/* Wrapper for scanf(), we can't generate it because it has a variable + * number of arguments */ + +mkwrap_init(int, vscanf, + (const char *restrict format, va_list ap), + (const char *restrict, va_list)); + +int scanf(const char *restrict format, ...) +{ + int r; + va_list arguments; + + if (_fiu_called) { + if (_fiu_orig_vscanf == NULL) { + if (_fiu_in_init_vscanf) { + printd("fail on init\n"); + return EOF; + } else { + printd("get orig\n"); + _fiu_init_vscanf(); + } + } + printd("orig\n"); + va_start(arguments, format); + r = (*_fiu_orig_vscanf) (format, arguments); + va_end(arguments); + + return r; + } + + printd("fiu\n"); + + /* fiu_fail() may call anything */ + rec_inc(); + + static const int valid_errnos[] = { + #ifdef EAGAIN + EAGAIN, + #endif + #ifdef EBADF + EBADF, + #endif + #ifdef EINTR + EINTR, + #endif + #ifdef EIO + EIO, + #endif + #ifdef ENOMEM + ENOMEM, + #endif + #ifdef ENXIO + ENXIO, + #endif + #ifdef EOVERFLOW + EOVERFLOW, + #endif + #ifdef EILSEQ + EILSEQ, + #endif + }; + + const int fstatus = fiu_fail("posix/stdio/sp/scanf"); + if (fstatus != 0) { + void *finfo = fiu_failinfo(); + if (finfo == NULL) { + errno = valid_errnos[random() % + sizeof(valid_errnos) / sizeof(int)]; + } else { + errno = (long) finfo; + } + r = EOF; + printd("failing\n"); + set_ferror(stdin); + goto exit; + } + + if (_fiu_orig_vscanf == NULL) + _fiu_init_vscanf(); + + printd("calling orig\n"); + va_start(arguments, format); + r = (*_fiu_orig_vscanf) (format, arguments); + va_end(arguments); + +exit: + rec_dec(); + return r; +} diff --git a/preload/posix/modules/posix.stdio.mod b/preload/posix/modules/posix.stdio.mod new file mode 100644 index 0000000..61e9567 --- /dev/null +++ b/preload/posix/modules/posix.stdio.mod @@ -0,0 +1,232 @@ + +# Posix stdio.h I/O + +include: <stdio.h> +include: <errno.h> +include: <stdarg.h> + +fiu name base: posix/stdio/oc/ + +FILE *fopen(const char *pathname, const char *mode); + on error: NULL + valid errnos: EACCES EINTR EISDIR ELOOP EMFILE ENAMETOOLONG ENFILE ENOENT ENOTDIR ENOSPC ENXIO EOVERFLOW EROFS EINVAL ENOMEM ETXTBSY + variants: off64_t + +FILE *freopen(const char *pathname, const char *mode, FILE *stream); + on error: NULL + valid errnos: EACCES EBADF EINTR EISDIR ELOOP EMFILE ENAMETOOLONG ENFILE ENOENT ENOTDIR ENOSPC ENXIO EOVERFLOW EROFS EBADF EINVAL ENOMEM ENXIO ETXTBSY + variants: off64_t + +int fclose(FILE *stream); + on error: EOF + valid errnos: EAGAIN EBADF EFBIG EFBIG EINTR EIO ENOMEM ENOSPC EPIPE ENXIO + +FILE *fdopen(int fd, const char *mode); + on error: NULL + valid errnos: EMFILE EBADF EINVAL EMFILE ENOMEM + +FILE *fmemopen(void *restrict buf, size_t size, const char *restrict mode); + on error: NULL + valid errnos: EMFILE EINVAL ENOMEM + +FILE *open_memstream(char **bufp, size_t *sizep); + on error: NULL + valid errnos: EMFILE EINVAL ENOMEM + +FILE *popen(const char *command, const char *mode); + on error: NULL + valid errnos: EMFILE EINVAL ENOMEM EAGAIN ENFILE + +int pclose(FILE *stream); + on error: -1 + valid errnos: ECHILD + + +fiu name base: posix/stdio/tmp/ + +FILE *tmpfile(void); + on error: NULL + valid errnos: EINTR EMFILE ENFILE ENOSPC EOVERFLOW ENOMEM + variants: off64_t + +char *tmpnam(char *s); + on error: NULL + +char *tempnam(const char *dir, const char *pfx); + on error: NULL + valid errnos: ENOMEM + +fiu name base: posix/stdio/rw/ + +size_t fread(void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream); + on error: 0 + valid errnos: EAGAIN EBADF EINTR EIO EOVERFLOW ENOMEM ENXIO + ferror: stream + +size_t fwrite(const void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream); + on error: 0 + valid errnos: EAGAIN EBADF EFBIG EINTR EIO ENOSPC EPIPE ENOMEM ENXIO + ferror: stream + +fiu name base: posix/stdio/seek/ + +int fgetpos(FILE *restrict stream, fpos_t *restrict pos); + on error: -1 + valid errnos: EBADF EOVERFLOW ESPIPE + +long ftell(FILE *stream); + on error: -1 + valid errnos: EBADF EOVERFLOW ESPIPE + +off_t ftello(FILE *stream); + on error: -1 + valid errnos: EBADF EOVERFLOW ESPIPE + variants: off64_t + +int fseek(FILE *stream, long int offset, int whence); + on error: -1 + valid errnos: EAGAIN EBADF EFBIG EINTR EINVAL EIO ENOSPC EOVERFLOW EPIPE ENXIO + ferror: stream + +int fseeko(FILE *stream, off_t offset, int whence); + on error: -1 + valid errnos: EAGAIN EBADF EFBIG EINTR EINVAL EIO ENOSPC EOVERFLOW EPIPE ENXIO + ferror: stream + variants: off64_t + +int fsetpos(FILE *stream, const fpos_t *pos); + on error: -1 + valid errnos: EAGAIN EBADF EFBIG EINTR EIO ENOSPC EOVERFLOW EPIPE ENXIO + ferror: stream + variants: off64_t + +# void rewind(FILE *stream); +# valid errnos: EAGAIN EBADF EFBIG EINTR EINVAL EIO ENOSPC EOVERFLOW EPIPE ENXIO +# ferror: stream + + +fiu name base: posix/stdio/gp/ + +int fgetc(FILE *stream); + on error: EOF + valid errnos: EAGAIN EBADF EINTR EIO ENOMEM ENXIO EOVERFLOW + ferror: stream + +char *fgets(char *restrict s, int n, FILE *restrict stream); + on error: NULL + valid errnos: EAGAIN EBADF EINTR EIO ENOMEM ENXIO EOVERFLOW + ferror: stream + +int getc(FILE *stream); + on error: EOF + valid errnos: EAGAIN EBADF EINTR EIO ENOMEM ENXIO EOVERFLOW + ferror: stream + +int getchar(void); + on error: EOF + valid errnos: EAGAIN EBADF EINTR EIO ENOMEM ENXIO EOVERFLOW + ferror: stdin + +char *gets(char *s); + on error: NULL + valid errnos: EAGAIN EBADF EINTR EIO ENOMEM ENXIO EOVERFLOW + ferror: stdin + +int fputc(int c, FILE *stream); + on error: EOF + valid errnos: EAGAIN EBADF EFBIG EINTR EIO ENOMEM ENOSPC ENXIO EPIPE + ferror: stream + +int fputs(const char *restrict s, FILE *restrict stream); + on error: EOF + valid errnos: EAGAIN EBADF EFBIG EINTR EIO ENOMEM ENOSPC ENXIO EPIPE + ferror: stream + +#ifndef putc +int putc(int c, FILE *stream); + on error: EOF + valid errnos: EAGAIN EBADF EFBIG EINTR EIO ENOMEM ENOSPC ENXIO EPIPE + ferror: stream +#endif + +int putchar(int c); + on error: EOF + valid errnos: EAGAIN EBADF EFBIG EINTR EIO ENOMEM ENOSPC ENXIO EPIPE + ferror: stdout + +int puts(const char *s); + on error: EOF + valid errnos: EAGAIN EBADF EFBIG EINTR EIO ENOMEM ENOSPC ENXIO EPIPE + ferror: stdout + +int ungetc(int c, FILE *stream); + on error: EOF + ferror: stream + +ssize_t getdelim(char **restrict lineptr, size_t *restrict n, int delimiter, FILE *restrict stream); + on error: -1 + valid errnos: EAGAIN EBADF EINTR EIO ENOMEM ENXIO EOVERFLOW INVAL + ferror: stream + +ssize_t getline(char **restrict lineptr, size_t *restrict n, FILE *restrict stream); + on error: -1 + valid errnos: EAGAIN EBADF EINTR EIO ENOMEM ENXIO EOVERFLOW INVAL + ferror: stream + + +fiu name base: posix/stdio/sp/ + +# Variants with a variable number of arguments need a custom definition. + +# int fprintf(FILE *restrict stream, const char *restrict format, ...); +# int printf(const char *restrict format, ...); +# int dprintf(int fildes, const char *restrict format, ...); + +int vfprintf(FILE *restrict stream, const char *restrict format, va_list ap); + on error: -1 + valid errnos: EAGAIN EBADF EFBIG EINTR EIO ENOMEM ENOSPC ENXIO EPIPE EILSEQ EOVERFLOW + ferror: stream + +int vprintf(const char *restrict format, va_list ap); + on error: -1 + valid errnos: EAGAIN EBADF EFBIG EINTR EIO ENOMEM ENOSPC ENXIO EPIPE EILSEQ EOVERFLOW + ferror: stdout + +int vdprintf(int fildes, const char *restrict format, va_list ap); + on error: -1 + valid errnos: EAGAIN EBADF EFBIG EINTR EIO ENOMEM ENOSPC ENXIO EPIPE EILSEQ EOVERFLOW + +# int fscanf(FILE *restrict stream, const char *restrict format, ...); +# int scanf(const char *restrict format, ...); + +int vfscanf(FILE *restrict stream, const char *restrict format, va_list arg); + on error: EOF + valid errnos: EAGAIN EBADF EINTR EIO ENOMEM ENXIO EOVERFLOW EILSEQ EINVAL + ferror: stream + +int vscanf(const char *restrict format, va_list arg); + on error: EOF + valid errnos: EAGAIN EBADF EINTR EIO ENOMEM ENXIO EOVERFLOW EILSEQ + ferror: stdin + +fiu name base: posix/stdio/ + +# Other functions not worth categorizing + +int remove(const char *filename); + on error: -1 + valid errnos: EACCES EBUSY EEXIST ENOTEMPTY EINVAL EIO ELOOP ENAMETOOLONG ENOENT ENOTDIR EPERM EROFS ETXTBSY + +int setvbuf(FILE *restrict stream, char *restrict buf, int type, size_t size); + on error: EOF + valid errnos: EBADF + +int ftrylockfile(FILE *file); + on error: 1 + +int fflush(FILE *stream); + on error: EOF + valid errnos: EAGAIN EBADF EFBIG EINTR EIO ENOMEM ENOSPC EPIPE ENXIO + ferror: stream + +