diff options
author | Jerry Jelinek <jerry.jelinek@joyent.com> | 2020-03-27 11:29:00 +0000 |
---|---|---|
committer | Jerry Jelinek <jerry.jelinek@joyent.com> | 2020-03-27 11:29:00 +0000 |
commit | 6dcfbbc68f881fbf5c20d25817a0221dfb135170 (patch) | |
tree | 40307666f6d7711499061d8c4be75029a6d851e9 /usr/src/lib/libc | |
parent | b0624b90ec9a0c04cca626925beee3ae403457ce (diff) | |
parent | cd62a92d4a964bfe61d35ba2301b69e65e22a509 (diff) | |
download | illumos-joyent-6dcfbbc68f881fbf5c20d25817a0221dfb135170.tar.gz |
[illumos-gate merge]
commit cd62a92d4a964bfe61d35ba2301b69e65e22a509
7092 Want support for stdio memory streams
12360 fwrite can loop forever on zero byte write
12392 ftello64 doesn't handle ungetc() correctly when unbuffered
commit 1470234269f4edea4cbf270cb2475e4988b788d5
12359 Want a means to set the umem mtbf at runtine
commit 0ac311bae7f6f50d9ba506b52bd8860f2d68d4ce
12358 Need mbrtowc variant that indicates consumed zero bytes
commit d726994754c938f91b6fd7e96b5cab3829615c58
12357 getc/putc_unlocked need to set orientation
Diffstat (limited to 'usr/src/lib/libc')
48 files changed, 1778 insertions, 319 deletions
diff --git a/usr/src/lib/libc/amd64/Makefile b/usr/src/lib/libc/amd64/Makefile index c0a065c9df..c21ef76ee2 100644 --- a/usr/src/lib/libc/amd64/Makefile +++ b/usr/src/lib/libc/amd64/Makefile @@ -639,6 +639,7 @@ PORTSTDIO= \ _filbuf.o \ _findbuf.o \ _flsbuf.o \ + _stdio_flags.o \ _wrtchk.o \ clearerr.o \ ctermid.o \ @@ -654,6 +655,7 @@ PORTSTDIO= \ fileno.o \ flockf.o \ flush.o \ + fmemopen.o \ fopen.o \ fpos.o \ fputc.o \ @@ -671,6 +673,8 @@ PORTSTDIO= \ gets.o \ getw.o \ mse.o \ + open_memstream.o \ + open_wmemstream.o \ popen.o \ putc.o \ putchar.o \ diff --git a/usr/src/lib/libc/i386/Makefile.com b/usr/src/lib/libc/i386/Makefile.com index 9987e2ee87..d9e56ee8ec 100644 --- a/usr/src/lib/libc/i386/Makefile.com +++ b/usr/src/lib/libc/i386/Makefile.com @@ -686,6 +686,7 @@ PORTSTDIO= \ _filbuf.o \ _findbuf.o \ _flsbuf.o \ + _stdio_flags.o \ _wrtchk.o \ clearerr.o \ ctermid.o \ @@ -701,6 +702,7 @@ PORTSTDIO= \ fileno.o \ flockf.o \ flush.o \ + fmemopen.o \ fopen.o \ fpos.o \ fputc.o \ @@ -718,6 +720,8 @@ PORTSTDIO= \ gets.o \ getw.o \ mse.o \ + open_memstream.o \ + open_wmemstream.o \ popen.o \ putc.o \ putchar.o \ diff --git a/usr/src/lib/libc/inc/file64.h b/usr/src/lib/libc/inc/file64.h index 40504d35ec..1bbc98e2bc 100644 --- a/usr/src/lib/libc/inc/file64.h +++ b/usr/src/lib/libc/inc/file64.h @@ -25,6 +25,10 @@ */ /* + * Copyright 2020 Robert Mustacchi + */ + +/* * This is the header where the internal to libc definition of the FILE * structure is defined. The exrernal defintion defines the FILE structure * as an array of longs. This prevents customers from writing code that @@ -41,8 +45,6 @@ #ifndef _FILE64_H #define _FILE64_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <synch.h> #include <stdio_tag.h> #include <wchar_impl.h> @@ -58,8 +60,25 @@ typedef __mbstate_t mbstate_t; #define rmutex_t mutex_t +typedef ssize_t (*fread_t)(__FILE *, char *, size_t); +typedef ssize_t (*fwrite_t)(__FILE *, const char *, size_t); +typedef off_t (*fseek_t)(__FILE *, off_t, int); +typedef int (*fclose_t)(__FILE *); + +typedef struct { + fread_t std_read; + fwrite_t std_write; + fseek_t std_seek; + fclose_t std_close; + void *std_data; +} stdio_ops_t; + #ifdef _LP64 +/* + * This structure cannot grow beyond its current size of 128 bytes. See the file + * lib/libc/port/stdio/README.design for more information. + */ struct __FILE_TAG { unsigned char *_ptr; /* next character from/to here in buffer */ unsigned char *_base; /* the buffer */ @@ -69,7 +88,8 @@ struct __FILE_TAG { unsigned int _flag; /* the state of the stream */ rmutex_t _lock; /* lock for this structure */ mbstate_t _state; /* mbstate_t */ - char __fill[32]; /* filler to bring size to 128 bytes */ + stdio_ops_t *_ops; /* Alternate impl ops */ + char __fill[24]; /* filler to bring size to 128 bytes */ }; #else @@ -83,6 +103,7 @@ struct xFILEdata { rmutex_t _lock; /* lock for this structure */ mbstate_t _state; /* mbstate_t */ int _altfd; /* alternate fd if > 255 */ + stdio_ops_t *_ops; /* Alternate impl ops */ }; #define XFILEINITIALIZER { 0, NULL, RECURSIVEMUTEX, DEFAULTMBSTATE } diff --git a/usr/src/lib/libc/inc/libc.h b/usr/src/lib/libc/inc/libc.h index 448523df01..90a2859b33 100644 --- a/usr/src/lib/libc/inc/libc.h +++ b/usr/src/lib/libc/inc/libc.h @@ -333,6 +333,14 @@ extern void __throw_constraint_handler_s(const char *_RESTRICT_KYWD, int); */ extern void common_panic(const char *, const char *); +/* + * defined in mbrtowc.c. + */ +extern size_t mbrtowc_nz_l(wchar_t *_RESTRICT_KYWD, const char *_RESTRICT_KYWD, + size_t, mbstate_t *_RESTRICT_KYWD, locale_t); +extern size_t mbrtowc_nz(wchar_t *_RESTRICT_KYWD, const char *_RESTRICT_KYWD, + size_t, mbstate_t *_RESTRICT_KYWD); + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/libc/inc/mtlib.h b/usr/src/lib/libc/inc/mtlib.h index aa5e9e0992..cad6c7ab8d 100644 --- a/usr/src/lib/libc/inc/mtlib.h +++ b/usr/src/lib/libc/inc/mtlib.h @@ -27,8 +27,6 @@ #ifndef _MTLIB_H #define _MTLIB_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <thread.h> #ifdef __cplusplus @@ -66,9 +64,9 @@ extern void assert_no_libc_locks_held(void); #define _FWRITE _fwrite_unlocked #define FILENO(s) _fileno(s) #define FERROR(s) ferror(s) -#define GETC(s) _getc_unlocked(s) +#define GETC(s) _getc_internal(s) #define UNGETC(c, s) _ungetc_unlocked(c, s) -#define PUTC(c, s) _putc_unlocked(c, s) +#define PUTC(c, s) _putc_internal(c, s) #define GETWC(s) getwc(s) #define PUTWC(c, s) putwc(c, s) diff --git a/usr/src/lib/libc/inc/stdiom.h b/usr/src/lib/libc/inc/stdiom.h index 9befb8a3c6..4769231968 100644 --- a/usr/src/lib/libc/inc/stdiom.h +++ b/usr/src/lib/libc/inc/stdiom.h @@ -25,7 +25,11 @@ */ /* Copyright (c) 1988 AT&T */ -/* All Rights Reserved */ +/* All Rights Reserved */ + +/* + * Copyright 2020 Robert Mustacchi + */ /* * stdiom.h - shared guts of stdio @@ -34,8 +38,6 @@ #ifndef _STDIOM_H #define _STDIOM_H -#pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.9 */ - #include <thread.h> #include <synch.h> #include <mtlib.h> @@ -48,11 +50,18 @@ /* * The following flags, and the macros that manipulate them, operate upon * the FILE structure used by stdio. If new flags are required, they should - * be created in this file. The values of the flags must be differnt from + * be created in this file. The values of the flags must be different from * the currently used values. New macros should be created to use the flags * so that the compilation mode dependencies can be isolated here. */ +/* + * The 32-bit version of the stdio FILE has 8 bits for its flags (see + * lib/libc/port/stdio/README.design). These 8 bits are used to determine if the + * FILE structure is allocated. We define '_DEF_FLAG_MASK' as a means to + * indicate this. + */ +#define _DEF_FLAG_MASK 0377 #ifdef _LP64 #define _BYTE_MODE_FLAG 0400 #define _WC_MODE_FLAG 01000 @@ -152,7 +161,7 @@ extern int __flsbuf(int, FILE *); */ #define _realbufend(iop) ((iop)->_end) #else -extern Uchar *_realbufend(FILE *iop); +extern Uchar *_realbufend(FILE *iop); extern rmutex_t *_reallock(FILE *iop); #endif /* _LP64 */ @@ -171,7 +180,7 @@ extern void close_pid(void); /* * Internal routines from flush.c */ -extern int _file_get(FILE *); +extern int _get_fd(FILE *); extern int _file_set(FILE *, int, const char *); /* @@ -180,11 +189,8 @@ extern int _file_set(FILE *, int, const char *); * since 64-bit Solaris is not affected by this. */ #ifdef _LP64 -#define GET_FD(iop) ((iop)->_file) #define SET_FILE(iop, fd) ((iop)->_file = (fd)) #else -#define GET_FD(iop) \ - (((iop)->__extendedfd) ? _file_get(iop) : (iop)->_magic) #define SET_FILE(iop, fd) (iop)->_magic = (fd); (iop)->__extendedfd = 0 #endif @@ -206,7 +212,7 @@ extern int _fileno(FILE *iop); /* * Internal routines from _findbuf.c */ -extern Uchar *_findbuf(FILE *iop); +extern Uchar *_findbuf(FILE *iop); /* * Internal routine used by fopen.c @@ -221,12 +227,12 @@ extern size_t _fwrite_unlocked(const void *, size_t, size_t, FILE *); /* * Internal routine from getc.c */ -int _getc_unlocked(FILE *); +int _getc_internal(FILE *); /* * Internal routine from put.c */ -int _putc_unlocked(int, FILE *); +int _putc_internal(int, FILE *); /* * Internal routine from ungetc.c @@ -260,4 +266,36 @@ extern struct xFILEdata _xftab[]; #endif /* _LP64 */ +/* + * A set of stdio routines to allow us to have alternate read, write, lseek, and + * close implementations. + */ +extern ssize_t _xread(FILE *iop, void *buf, size_t nbytes); +extern ssize_t _xwrite(FILE *iop, const void *buf, size_t nbytes); +extern off_t _xseek(FILE *iop, off_t off, int whence); +extern off64_t _xseek64(FILE *iop, off64_t off, int whence); +extern int _xclose(FILE *iop); +extern void *_xdata(FILE *iop); +extern int _xassoc(FILE *iop, fread_t readf, fwrite_t writef, + fseek_t seekf, fclose_t closef, void *data); +extern void _xunassoc(FILE *iop); + +/* + * Internal functions from _stdio_flags.c. + */ +extern int _stdio_flags(const char *type, int *oflags, int *fflags); + +/* + * Internal functions from open_memstream.c. + */ +extern boolean_t memstream_seek(size_t base, off_t off, size_t max, + size_t *nposp); +extern int memstream_newsize(size_t pos, size_t alloc, size_t nbytes, + size_t *nallocp); + +/* + * Internal function from ftell.o. + */ +extern off64_t ftell_common(FILE *iop); + #endif /* _STDIOM_H */ diff --git a/usr/src/lib/libc/port/locale/big5.c b/usr/src/lib/libc/port/locale/big5.c index 2729ab0289..889b182de5 100644 --- a/usr/src/lib/libc/port/locale/big5.c +++ b/usr/src/lib/libc/port/locale/big5.c @@ -44,7 +44,7 @@ static size_t _BIG5_mbrtowc(wchar_t *_RESTRICT_KYWD, const char *_RESTRICT_KYWD, - size_t, mbstate_t *RESTRICT_KYWD); + size_t, mbstate_t *RESTRICT_KYWD, boolean_t); static int _BIG5_mbsinit(const mbstate_t *); static size_t _BIG5_wcrtomb(char *_RESTRICT_KYWD, wchar_t, mbstate_t *_RESTRICT_KYWD); @@ -88,7 +88,7 @@ _big5_check(uint_t c) static size_t _BIG5_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, - size_t n, mbstate_t *_RESTRICT_KYWD ps) + size_t n, mbstate_t *_RESTRICT_KYWD ps, boolean_t zero) { _BIG5State *bs; wchar_t wc; @@ -143,7 +143,11 @@ _BIG5_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, } else { if (pwc != NULL) *pwc = wc; - return (wc == L'\0' ? 0 : 1); + if (zero || wc != L'\0') { + return (1); + } else { + return (0); + } } } diff --git a/usr/src/lib/libc/port/locale/euc.c b/usr/src/lib/libc/port/locale/euc.c index a99cdf8689..1d1d25b17b 100644 --- a/usr/src/lib/libc/port/locale/euc.c +++ b/usr/src/lib/libc/port/locale/euc.c @@ -46,22 +46,23 @@ static size_t _EUC_mbrtowc_impl(wchar_t *_RESTRICT_KYWD, const char *_RESTRICT_KYWD, - size_t, mbstate_t *_RESTRICT_KYWD, uint8_t, uint8_t, uint8_t, uint8_t); + size_t, mbstate_t *_RESTRICT_KYWD, uint8_t, uint8_t, uint8_t, uint8_t, + boolean_t); static size_t _EUC_wcrtomb_impl(char *_RESTRICT_KYWD, wchar_t, mbstate_t *_RESTRICT_KYWD, uint8_t, uint8_t, uint8_t, uint8_t); static size_t _EUC_CN_mbrtowc(wchar_t *_RESTRICT_KYWD, const char *_RESTRICT_KYWD, - size_t, mbstate_t *_RESTRICT_KYWD); + size_t, mbstate_t *_RESTRICT_KYWD, boolean_t); static size_t _EUC_JP_mbrtowc(wchar_t *_RESTRICT_KYWD, const char *_RESTRICT_KYWD, - size_t, mbstate_t *_RESTRICT_KYWD); + size_t, mbstate_t *_RESTRICT_KYWD, boolean_t); static size_t _EUC_KR_mbrtowc(wchar_t *_RESTRICT_KYWD, const char *_RESTRICT_KYWD, - size_t, mbstate_t *_RESTRICT_KYWD); + size_t, mbstate_t *_RESTRICT_KYWD, boolean_t); static size_t _EUC_TW_mbrtowc(wchar_t *_RESTRICT_KYWD, const char *_RESTRICT_KYWD, - size_t, mbstate_t *_RESTRICT_KYWD); + size_t, mbstate_t *_RESTRICT_KYWD, boolean_t); static size_t _EUC_CN_wcrtomb(char *_RESTRICT_KYWD, wchar_t, mbstate_t *_RESTRICT_KYWD); @@ -131,9 +132,9 @@ _EUC_CN_init(struct lc_ctype *lct) static size_t _EUC_CN_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, - size_t n, mbstate_t *_RESTRICT_KYWD ps) + size_t n, mbstate_t *_RESTRICT_KYWD ps, boolean_t zero) { - return (_EUC_mbrtowc_impl(pwc, s, n, ps, SS2, 4, 0, 0)); + return (_EUC_mbrtowc_impl(pwc, s, n, ps, SS2, 4, 0, 0, zero)); } static size_t @@ -176,9 +177,9 @@ _EUC_KR_init(struct lc_ctype *lct) static size_t _EUC_KR_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, - size_t n, mbstate_t *_RESTRICT_KYWD ps) + size_t n, mbstate_t *_RESTRICT_KYWD ps, boolean_t zero) { - return (_EUC_mbrtowc_impl(pwc, s, n, ps, 0, 0, 0, 0)); + return (_EUC_mbrtowc_impl(pwc, s, n, ps, 0, 0, 0, 0, zero)); } static size_t @@ -221,9 +222,9 @@ _EUC_JP_init(struct lc_ctype *lct) static size_t _EUC_JP_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, - size_t n, mbstate_t *_RESTRICT_KYWD ps) + size_t n, mbstate_t *_RESTRICT_KYWD ps, boolean_t zero) { - return (_EUC_mbrtowc_impl(pwc, s, n, ps, SS2, 2, SS3, 3)); + return (_EUC_mbrtowc_impl(pwc, s, n, ps, SS2, 2, SS3, 3, zero)); } static size_t @@ -266,9 +267,9 @@ _EUC_TW_init(struct lc_ctype *lct) static size_t _EUC_TW_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, - size_t n, mbstate_t *_RESTRICT_KYWD ps) + size_t n, mbstate_t *_RESTRICT_KYWD ps, boolean_t zero) { - return (_EUC_mbrtowc_impl(pwc, s, n, ps, SS2, 4, 0, 0)); + return (_EUC_mbrtowc_impl(pwc, s, n, ps, SS2, 4, 0, 0, zero)); } static size_t @@ -300,7 +301,8 @@ _EUC_TW_wcsnrtombs(char *_RESTRICT_KYWD dst, const wchar_t **_RESTRICT_KYWD src, static size_t _EUC_mbrtowc_impl(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, size_t n, mbstate_t *_RESTRICT_KYWD ps, - uint8_t cs2, uint8_t cs2width, uint8_t cs3, uint8_t cs3width) + uint8_t cs2, uint8_t cs2width, uint8_t cs3, uint8_t cs3width, + boolean_t zero) { _EucState *es; int i, want; @@ -329,7 +331,11 @@ _EUC_mbrtowc_impl(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, if (((ch = (unsigned char)*s) & 0x80) == 0) { if (pwc != NULL) *pwc = ch; - return (ch != '\0' ? 1 : 0); + if (zero || ch != '\0') { + return (1); + } else { + return (0); + } } if (ch >= 0xa1) { @@ -367,7 +373,11 @@ _EUC_mbrtowc_impl(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, if (pwc != NULL) *pwc = wc; es->want = 0; - return (wc == L'\0' ? 0 : want); + if (zero || wc != L'\0') { + return (want); + } else { + return (0); + } } static size_t diff --git a/usr/src/lib/libc/port/locale/fgetwc.c b/usr/src/lib/libc/port/locale/fgetwc.c index 282b65cebc..254a785891 100644 --- a/usr/src/lib/libc/port/locale/fgetwc.c +++ b/usr/src/lib/libc/port/locale/fgetwc.c @@ -66,7 +66,7 @@ _fgetwc_unlocked_l(FILE *fp, locale_t loc) } do { char x = (char)c; - nconv = lct->lc_mbrtowc(&wc, &x, 1, statep); + nconv = lct->lc_mbrtowc(&wc, &x, 1, statep, B_FALSE); if (nconv == (size_t)-1) { break; } else if (nconv == (size_t)-2) { diff --git a/usr/src/lib/libc/port/locale/gb18030.c b/usr/src/lib/libc/port/locale/gb18030.c index 232daade50..36c48c5cc5 100644 --- a/usr/src/lib/libc/port/locale/gb18030.c +++ b/usr/src/lib/libc/port/locale/gb18030.c @@ -44,7 +44,7 @@ static size_t _GB18030_mbrtowc(wchar_t *_RESTRICT_KYWD, const char *_RESTRICT_KYWD, - size_t, mbstate_t *_RESTRICT_KYWD); + size_t, mbstate_t *_RESTRICT_KYWD, boolean_t); static int _GB18030_mbsinit(const mbstate_t *); static size_t _GB18030_wcrtomb(char *_RESTRICT_KYWD, wchar_t, mbstate_t *_RESTRICT_KYWD); @@ -83,7 +83,7 @@ _GB18030_mbsinit(const mbstate_t *ps) static size_t _GB18030_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, - size_t n, mbstate_t *_RESTRICT_KYWD ps) + size_t n, mbstate_t *_RESTRICT_KYWD ps, boolean_t zero) { _GB18030State *gs; wchar_t wch; @@ -159,7 +159,11 @@ _GB18030_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, if (pwc != NULL) *pwc = wch; gs->count = 0; - return (wch == L'\0' ? 0 : len - ocount); + if (zero || wch != L'\0') { + return (len - ocount); + } else { + return (0); + } ilseq: errno = EILSEQ; return ((size_t)-1); diff --git a/usr/src/lib/libc/port/locale/gb2312.c b/usr/src/lib/libc/port/locale/gb2312.c index a25af781b4..bfb6c0177b 100644 --- a/usr/src/lib/libc/port/locale/gb2312.c +++ b/usr/src/lib/libc/port/locale/gb2312.c @@ -39,7 +39,7 @@ static size_t _GB2312_mbrtowc(wchar_t *_RESTRICT_KYWD, const char *_RESTRICT_KYWD, - size_t, mbstate_t *_RESTRICT_KYWD); + size_t, mbstate_t *_RESTRICT_KYWD, boolean_t); static int _GB2312_mbsinit(const mbstate_t *); static size_t _GB2312_wcrtomb(char *_RESTRICT_KYWD, wchar_t, mbstate_t *_RESTRICT_KYWD); @@ -101,7 +101,7 @@ _GB2312_check(const char *str, size_t n) static size_t _GB2312_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, - size_t n, mbstate_t *_RESTRICT_KYWD ps) + size_t n, mbstate_t *_RESTRICT_KYWD ps, boolean_t zero) { _GB2312State *gs; wchar_t wc; @@ -137,7 +137,11 @@ _GB2312_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, if (pwc != NULL) *pwc = wc; gs->count = 0; - return (wc == L'\0' ? 0 : len - ocount); + if (zero || wc != L'\0') { + return (len - ocount); + } else { + return (0); + } } static size_t diff --git a/usr/src/lib/libc/port/locale/gbk.c b/usr/src/lib/libc/port/locale/gbk.c index 5c94b9954b..f422ce8fb5 100644 --- a/usr/src/lib/libc/port/locale/gbk.c +++ b/usr/src/lib/libc/port/locale/gbk.c @@ -44,7 +44,7 @@ static size_t _GBK_mbrtowc(wchar_t *_RESTRICT_KYWD, const char *_RESTRICT_KYWD, - size_t, mbstate_t *_RESTRICT_KYWD); + size_t, mbstate_t *_RESTRICT_KYWD, boolean_t); static int _GBK_mbsinit(const mbstate_t *); static size_t _GBK_wcrtomb(char *_RESTRICT_KYWD, wchar_t, mbstate_t *_RESTRICT_KYWD); @@ -88,7 +88,7 @@ _gbk_check(uint_t c) static size_t _GBK_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, - size_t n, mbstate_t *_RESTRICT_KYWD ps) + size_t n, mbstate_t *_RESTRICT_KYWD ps, boolean_t zero) { _GBKState *gs; wchar_t wc; @@ -143,7 +143,11 @@ _GBK_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, } else { if (pwc != NULL) *pwc = wc; - return (wc == L'\0' ? 0 : 1); + if (zero || wc != L'\0') { + return (1); + } else { + return (0); + } } } diff --git a/usr/src/lib/libc/port/locale/lctype.h b/usr/src/lib/libc/port/locale/lctype.h index 25652da1d4..0a9e1335bf 100644 --- a/usr/src/lib/libc/port/locale/lctype.h +++ b/usr/src/lib/libc/port/locale/lctype.h @@ -17,6 +17,7 @@ #define _LCTYPE_H_ #include <wchar.h> +#include <sys/types.h> /* private LC_CTYPE related structures */ @@ -24,7 +25,8 @@ struct lc_ctype { size_t (*lc_mbrtowc)(wchar_t *_RESTRICT_KYWD, - const char *_RESTRICT_KYWD, size_t, mbstate_t *_RESTRICT_KYWD); + const char *_RESTRICT_KYWD, size_t, mbstate_t *_RESTRICT_KYWD, + boolean_t); int (*lc_mbsinit)(const mbstate_t *); @@ -51,7 +53,7 @@ struct lc_ctype { * Default implementation (C locale, i.e. ASCII). */ size_t __mbrtowc_ascii(wchar_t *_RESTRICT_KYWD, - const char *_RESTRICT_KYWD, size_t, mbstate_t *_RESTRICT_KYWD); + const char *_RESTRICT_KYWD, size_t, mbstate_t *_RESTRICT_KYWD, boolean_t); int __mbsinit_ascii(const mbstate_t *); size_t __mbsnrtowcs_ascii(wchar_t *_RESTRICT_KYWD dst, const char **_RESTRICT_KYWD src, size_t nms, size_t len, diff --git a/usr/src/lib/libc/port/locale/mblocal.h b/usr/src/lib/libc/port/locale/mblocal.h index 4412bec7cb..3d958e364e 100644 --- a/usr/src/lib/libc/port/locale/mblocal.h +++ b/usr/src/lib/libc/port/locale/mblocal.h @@ -48,7 +48,7 @@ void _BIG5_init(struct lc_ctype *); void _MSKanji_init(struct lc_ctype *); typedef size_t (*mbrtowc_pfn_t)(wchar_t *_RESTRICT_KYWD, - const char *_RESTRICT_KYWD, size_t, mbstate_t *_RESTRICT_KYWD); + const char *_RESTRICT_KYWD, size_t, mbstate_t *_RESTRICT_KYWD, boolean_t); typedef size_t (*wcrtomb_pfn_t)(char *_RESTRICT_KYWD, wchar_t, mbstate_t *_RESTRICT_KYWD); size_t __mbsnrtowcs_std(wchar_t *_RESTRICT_KYWD, const char **_RESTRICT_KYWD, diff --git a/usr/src/lib/libc/port/locale/mbrtowc.c b/usr/src/lib/libc/port/locale/mbrtowc.c index cd1e7b064f..c0934c88d1 100644 --- a/usr/src/lib/libc/port/locale/mbrtowc.c +++ b/usr/src/lib/libc/port/locale/mbrtowc.c @@ -40,7 +40,7 @@ mbrtowc_l(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, if (ps == NULL) ps = &mbs; - return (loc->ctype->lc_mbrtowc(pwc, s, n, ps)); + return (loc->ctype->lc_mbrtowc(pwc, s, n, ps, B_FALSE)); } size_t @@ -49,3 +49,21 @@ mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, { return (mbrtowc_l(pwc, s, n, ps, uselocale(NULL))); } + +size_t +mbrtowc_nz_l(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, + size_t n, mbstate_t *_RESTRICT_KYWD ps, locale_t loc) +{ + static mbstate_t mbs; + + if (ps == NULL) + ps = &mbs; + return (loc->ctype->lc_mbrtowc(pwc, s, n, ps, B_TRUE)); +} + +size_t +mbrtowc_nz(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, + size_t n, mbstate_t *_RESTRICT_KYWD ps) +{ + return (mbrtowc_nz_l(pwc, s, n, ps, uselocale(NULL))); +} diff --git a/usr/src/lib/libc/port/locale/mbsnrtowcs.c b/usr/src/lib/libc/port/locale/mbsnrtowcs.c index 533b7776e0..6411cfcb74 100644 --- a/usr/src/lib/libc/port/locale/mbsnrtowcs.c +++ b/usr/src/lib/libc/port/locale/mbsnrtowcs.c @@ -68,11 +68,13 @@ __mbsnrtowcs_std(wchar_t *_RESTRICT_KYWD dst, const char **_RESTRICT_KYWD src, if (dst == NULL) { for (;;) { - if ((nb = pmbrtowc(&wc, s, nms, ps)) == (size_t)-1) + if ((nb = pmbrtowc(&wc, s, nms, ps, B_FALSE)) == + (size_t)-1) { /* Invalid sequence - mbrtowc() sets errno. */ return ((size_t)-1); - else if (nb == 0 || nb == (size_t)-2) + } else if (nb == 0 || nb == (size_t)-2) { return (nchr); + } s += nb; nms -= nb; nchr++; @@ -81,7 +83,7 @@ __mbsnrtowcs_std(wchar_t *_RESTRICT_KYWD dst, const char **_RESTRICT_KYWD src, } while (len-- > 0) { - if ((nb = pmbrtowc(dst, s, nms, ps)) == (size_t)-1) { + if ((nb = pmbrtowc(dst, s, nms, ps, B_FALSE)) == (size_t)-1) { *src = s; return ((size_t)-1); } else if (nb == (size_t)-2) { diff --git a/usr/src/lib/libc/port/locale/mskanji.c b/usr/src/lib/libc/port/locale/mskanji.c index 5d9b899aae..69955e5afa 100644 --- a/usr/src/lib/libc/port/locale/mskanji.c +++ b/usr/src/lib/libc/port/locale/mskanji.c @@ -46,7 +46,7 @@ static size_t _MSKanji_mbrtowc(wchar_t *_RESTRICT_KYWD, const char *_RESTRICT_KYWD, - size_t, mbstate_t *_RESTRICT_KYWD); + size_t, mbstate_t *_RESTRICT_KYWD, boolean_t); static int _MSKanji_mbsinit(const mbstate_t *); static size_t _MSKanji_wcrtomb(char *_RESTRICT_KYWD, wchar_t, mbstate_t *_RESTRICT_KYWD); @@ -83,7 +83,7 @@ _MSKanji_mbsinit(const mbstate_t *ps) static size_t _MSKanji_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, - size_t n, mbstate_t *_RESTRICT_KYWD ps) + size_t n, mbstate_t *_RESTRICT_KYWD ps, boolean_t zero) { _MSKanjiState *ms; wchar_t wc; @@ -135,7 +135,11 @@ _MSKanji_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, } else { if (pwc != NULL) *pwc = wc; - return (wc == L'\0' ? 0 : 1); + if (zero || wc != L'\0') { + return (1); + } else { + return (0); + } } } diff --git a/usr/src/lib/libc/port/locale/none.c b/usr/src/lib/libc/port/locale/none.c index 0511563cb1..5eefc746aa 100644 --- a/usr/src/lib/libc/port/locale/none.c +++ b/usr/src/lib/libc/port/locale/none.c @@ -73,7 +73,7 @@ __mbsinit_ascii(const mbstate_t *unused) size_t __mbrtowc_ascii(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, - size_t n, mbstate_t *_RESTRICT_KYWD unused) + size_t n, mbstate_t *_RESTRICT_KYWD unused, boolean_t zero) { _NOTE(ARGUNUSED(unused)); @@ -85,7 +85,11 @@ __mbrtowc_ascii(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, return ((size_t)-2); if (pwc != NULL) *pwc = (unsigned char)*s; - return (*s == '\0' ? 0 : 1); + if (zero || *s != '\0') { + return (1); + } else { + return (0); + } } size_t diff --git a/usr/src/lib/libc/port/locale/utf8.c b/usr/src/lib/libc/port/locale/utf8.c index e919b9dd4a..a6e037d94e 100644 --- a/usr/src/lib/libc/port/locale/utf8.c +++ b/usr/src/lib/libc/port/locale/utf8.c @@ -37,7 +37,7 @@ static size_t _UTF8_mbrtowc(wchar_t *_RESTRICT_KYWD, const char *_RESTRICT_KYWD, - size_t, mbstate_t *_RESTRICT_KYWD); + size_t, mbstate_t *_RESTRICT_KYWD, boolean_t); static int _UTF8_mbsinit(const mbstate_t *); static size_t _UTF8_mbsnrtowcs(wchar_t *_RESTRICT_KYWD, const char **_RESTRICT_KYWD, size_t, size_t, @@ -75,7 +75,7 @@ _UTF8_mbsinit(const mbstate_t *ps) static size_t _UTF8_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, - size_t n, mbstate_t *_RESTRICT_KYWD ps) + size_t n, mbstate_t *_RESTRICT_KYWD ps, boolean_t zero) { _UTF8State *us; int ch, i, mask, want; @@ -116,7 +116,11 @@ _UTF8_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, /* Fast path for plain ASCII characters. */ if (pwc != NULL) *pwc = ch; - return (ch != '\0' ? 1 : 0); + if (zero || ch != '\0') { + return (1); + } else { + return (0); + } } if ((ch & 0xe0) == 0xc0) { mask = 0x1f; @@ -192,7 +196,11 @@ _UTF8_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, if (pwc != NULL) *pwc = wch; us->want = 0; - return (wch == L'\0' ? 0 : want); + if (zero || wch != L'\0') { + return (want); + } else { + return (0); + } } static size_t @@ -221,18 +229,19 @@ _UTF8_mbsnrtowcs(wchar_t *_RESTRICT_KYWD dst, const char **_RESTRICT_KYWD src, return ((size_t)-1); } for (;;) { - if (nms > 0 && (signed char)*s > 0) + if (nms > 0 && (signed char)*s > 0) { /* * Fast path for plain ASCII characters * excluding NUL. */ nb = 1; - else if ((nb = _UTF8_mbrtowc(&wc, s, nms, ps)) == - (size_t)-1) + } else if ((nb = _UTF8_mbrtowc(&wc, s, nms, ps, + B_FALSE)) == (size_t)-1) { /* Invalid sequence - mbrtowc() sets errno. */ return ((size_t)-1); - else if (nb == 0 || nb == (size_t)-2) + } else if (nb == 0 || nb == (size_t)-2) { return (nchr); + } s += nb; nms -= nb; nchr++; @@ -257,7 +266,7 @@ _UTF8_mbsnrtowcs(wchar_t *_RESTRICT_KYWD dst, const char **_RESTRICT_KYWD src, */ *dst = (wchar_t)*s; nb = 1; - } else if ((nb = _UTF8_mbrtowc(dst, s, nms, ps)) == + } else if ((nb = _UTF8_mbrtowc(dst, s, nms, ps, B_FALSE)) == (size_t)-1) { *src = s; return ((size_t)-1); diff --git a/usr/src/lib/libc/port/mapfile-vers b/usr/src/lib/libc/port/mapfile-vers index dbe33eb9d1..da8de86f25 100644 --- a/usr/src/lib/libc/port/mapfile-vers +++ b/usr/src/lib/libc/port/mapfile-vers @@ -78,6 +78,13 @@ $if _x86 && _ELF64 $add amd64 $endif +SYMBOL_VERSION ILLUMOS_0.32 { + protected: + fmemopen; + open_memstream; + open_wmemstream; +} ILLUMOS_0.31; + SYMBOL_VERSION ILLUMOS_0.31 { protected: __unlockpt_xpg4; diff --git a/usr/src/lib/libc/port/stdio/README.design b/usr/src/lib/libc/port/stdio/README.design new file mode 100644 index 0000000000..748f7f9913 --- /dev/null +++ b/usr/src/lib/libc/port/stdio/README.design @@ -0,0 +1,327 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Robert Mustacchi +# + _ _ _ + ___| |_ __| (_) ___ +/ __| __/ _` | |/ _ \ +\__ \ || (_| | | (_) | +|___/\__\__,_|_|\___/ + +Notes on the design of stdio. + +------------ +File Streams +------------ + +At the heart of the stdio is the 'FILE *'. The 'FILE *' represents a +stream that can be read, written, and seeked. The streams traditionally +refer to a file descriptor, when created by fopen(3C), or may refer to +memory, when created by open_memstream(3C) or fmopen(3C). This document +focuses on the implementation of streams. Other misc. functions in stdio +are not discussed. + +------------ +Organization +------------ + +Most functions exist in a file with the same name. When adding new +files to stdio the file name should match the primary function name. +There are a few exceptions. Almost all of the logic related to both +flushing and knowledge of how to handle the 32-bit ABI issues (described +in the next section) can be found in flush.c. + +----------------------------- +struct __FILE_TAG and the ABI +----------------------------- + +The definition of the 'FILE *' is a pointer to a 'struct __FILE_TAG'. +The 'struct __FILE_TAG' structure has a long history that dates back to +historical UNIX. For better or for worse, we have inherited some of the +design decisions of the past, it's important to understand what those +are as they have profound impact on the stdio design and serve as a good +cautionary tale for future ABI decisions. + +In the original UNIX designs, the 'struct __FILE_TAG' was exposed as a +non-opaque structure. This was also true on other platforms. This had a +couple of challenges: + +* It meant the size of the 'struct __FILE_TAG' was part of the ABI +* Consumers would access the members directly. You can find examples of + this in our public headers where things like getc() are inlined in + terms of the implementation. Various 3rd-party software that has + existed for quite some time knows the offset of members and directly + manipulates them. This is still true as of 2020. +* The 'struct __FILE_TAG' only used an unsigned char (uint8_t) for the + file descriptor in the 32-bit version. Other systems used a short, so + they were in better shape. This was changed in the 64-bit version to + use an int. +* The main C stdio symbols 'stdin', 'stdout', and 'stderr', were (and + still are) exposed as an array. This means that while the 64-bit + structure is opaque, its size is actually part of the ABI. + +All of these issues have been dealt with in different ways in the +system. The first thing that is a little confusing is where to find the +definitions of the actual implementation. The 32-bit 'struct __FILE_BUF' +is split into two different pieces, the part that is public and a +secondary, private part. + +The public definition of the 'struct __FILE_TAG' for 32-bit code and the +opaque definition for 64-bit code may be found in +'usr/src/head/stdio_impl.h.'. The actual definition of the 64-bit +structure and the 32-bit additions are all found in +'usr/src/lib/libc/inc/file64.h.' + +In file64.h, one will find the 'struct xFILEdata' (extended FILE * data). +This represents all of the data that has been added to stdio that is +missing from the public structure. Whenever a 'FILE *' is allocated, +32-bit code always ensures that there is a corresponding 'struct +xFILEdata' that exists. Currently, we still have plenty of padding left +in the 64-bit version of the structure for at least 3 pointers. + +To add a member to the structure, one has to add data to the structures +in 'lib/libc/inc/file64.h'. If for some reason, all the padding would be +used up, then you must stop. The size of the 64-bit structure _cannot_ +be extended, as noted earlier it is part of the ABI. If we hit this +case, then one must introduce the struct xFILEdata for the lp64 +environment. + +-------------------------- +Allocating FILE Structures +-------------------------- + +libc defines a number of 'FILE *' structures by default. These can all +be found in 'data.c'. The first _NFILE (20 or 60 depending on the +platform) are defined statically. In the 32-bit case, the corresponding +'struct _xFILEdata' is allocated along with it. + +To determine if a structure is free or not in the array, the `_flag` +member is consulted. If the flag has been set to zero, then the STREAM +is considered free and can be allocated. All of the allocated (whether +used or not) 'FILE *' structures are present on a linked list which is +found in 'flush.c' rooted at the symbol '__first_link'. This list is +always scanned to try and reuse an existing 'FILE *' structure before +allocating a new one. If all of the existing ones are in use, then one +will be allocated. + +An important thing to understand is that once allocated, a 'FILE *' will +never be freed by libc. It will always exist on the global list of +structures to be reused. + +--------- +Buffering +--------- + +Every stream in stdio starts out as buffered. Buffering can be changed +by calling either setbuf(3C) or setvbuf(3C). This buffer is stored in +the `_base` member of the 'struct __FILE_TAG'. The amount of valid data +in the buffer is maintained in the '_cnt' member of the structure. By +default, there is no associated buffer with a stream. When the stream is +first used, the buffer will be assigned by a call to _findbuf() in +_findbuf.c. + +There are pre-allocated buffers that exist. There are two specifically +for stdin and stdout (stderr is unbuffered). These include space for +both the buffer and the pushback buffer. The pushback buffer is used so +someone can call fungetc(3C) regardless of whether a buffering mode is +enabled or not. Characters that we 'unget' are placed on the pushback +buffer. + +For other buffering modes, we'll try and allocate an appropriate sized +buffer. The buffer size defaults to BUFSIZ, but if the stream is backed +by a file descriptor, we'll use fstat() to determine the appropriate +size to use and match the file system block size. If we cannot allocate +that, we'll fall back to trying to allocate a pushback buffer. + +libc defines static data for _NFILE worth of pushback buffers which are +indexed based on the underlying file descriptor. This and the stdin and +stdout buffers are all found in 'data.c' in _smbuf, _sibuf, and _sobuf +respectively. + +------------------------------ +Reading, Writing, and Flushing +------------------------------ + +By default, reads and writes on a stream, whether backed by a +file-descriptor or not, go through the buffer described in the previous +section. If a read or write can be satisfied by the buffer, then no +underlying I/O will occur, unless buffering has been disabled. + +The various function entry points that read such as fread(3C) or +fgetc(3C) will not call read() directly but will instead try to fill the +buffer, which will cause a read if required. This is centralized in +_filbuf(). When a read is required from the underlying file, it will +call _xread() in flush.c. For more on _xread() see the operations vector +section further along. + +Unlike reads, writes are much less centralized and each of the main +writing entry points has reimplemented the path of writing to the buffer +and flushing it. It would be good in the future to consolidate them. In +general, data will be written directly to the stdio buffer. When that +buffer needs to be flushed either the _flsbuf() or _xflsbuf() functions +will be called to actually flush out the buffer. + +When data needs to be flushed from a buffer to its underlying file +descriptor (or other backing store), all of the write family functions +ultimately call _xwrite(). + +Flushes can occur in a few different ways: + +1. A write has filled up the buffer. +2. A new line ('\n') is written and new-line buffering is used. +3. fflush(3C) or a similar function has been called. +4. A read occurs on a buffer that has unflushed writes. +5. The stream is being closed. + +Most of these methods are fairly similar; however, the fflush(3C) case +is a little different. fflush() may be asked to flush all of the streams +when it is passed a NULL stream. Even when that happens it will still +utilize the same underlying mechanism via _xflsbuf() or _flsbuf(). + +----------- +Orientation +----------- + +Streams handle both wide characters and narrow characters. There is an +internal multi-byte conversion state buffer that is included with every +stream. A stream may exist in one of three modes: + +1. It may have an explicit narrow orientation +2. It may have an explicit wide orientation +3. It may have no orientation + +When most streams are created, they have no orientation. The orientation +can then be explicitly set by calling fwide(3C). Some streams are also +created with an explicit orientation, for example, open_wmemstream(3C) +always sets the stream to be wide. + +The C standard dictates that certain operations will actually cause a +stream with no orientation to have an explicit orientation set. Calling +a narrow or wide related character function, such as 'fgetc(3C)' or +'fgetwc(3C)' respectively will then cause the orientation to be set if +it has not been. Once an orientation for a stream has been set, it +cannot be changed until the stream has been closed or it is reset by +calling freopen(3C). + +There are a few functions that don't change this today. One example is +ungetc(3C). Often this isn't indicative of whether it should or +shouldn't change the orientation, but is a side effect of the history of +the stdio implementation. + +------------------------------------- +Operations Vectors and Memory Streams +------------------------------------- + +Traditionally, stdio streams were always backed by a file descriptor of +some kind and therefore always called out into functions like read(2), +write(2), lseek(2), and close(2) directly. A series of new functions +were introduced in POSIX 2008 that add support for streams backed by +memory in the form of fmemopen(3C), open_memstream(3C), and +open_wmemstream(3C). + +To deal with this and other possible designs, an operations vector was +added to the stream represented by the 'stdio_ops_t' structure. This is +stored in the '_ops' member of the 'struct __FILE_BUF'. For a normal +stream backed by a file descriptor, this member will be NULL. + +In places where a normal system call would have been made there is now a +call to a corresponding function such as _xread(), _xwrite(), xseek(), +_xseek64(), and _xclose(). If an operations vector is defined, it will +call into the corresponding operation vector. If not, it will perform +the traditional system call. This design choice consolidates all of the +work required to implement non-file descriptor backed streams. + +When creating a non-file backed stream there are several expectations in +the system: + +* The stream code should obtain a stream normally through a call to + _findiop(). +* If one needs to translate the normal fopen(3C) arguments, they should + use the _stdio_flags() function. This will also construct the + appropriate internal stdio flags for the stream. +* The stream code must call _xassoc() to set the file operations vector + before return a 'FILE *' out of libc. +* All of the operations vectors must be implemented. +* If the stream is seekable, it must explicitly use the SET_SEEKABLE() + macro before return the stream. +* If the stream is supposed to have a default orientation, it must set + it by calling _setorientation(). Not all streams have a default + orientation. +* In the stream's close entry point it should call _xunassoc(). + +-------------------------- +Extended File and fileno() +-------------------------- + +The 32-bit libc has historically been limited to 255 open streams +because of the use of an unsigned char. This problem does not impact the +64-bit libc. To deal with this, libc uses a series of techniques which +are summarized for users in extendedFILE(5). The usage of extendedFILE +can also be enabled by passing the special 'F' character to fopen(3C). + +The '_magic' member in the 32-bit 'struct __FILE_TAG' contains what used +to be the file descriptor. When extended file is not in use, the +_magic member still does contain the file descriptor. However, when +extendedFILE is enabled, then the _magic member contains a sentinel +value and the actual value is stored in the 'struct xFILEdata' _magic +member. + +The act of getting the correct file descriptor has been centralized in a +function called _get_fd(). This function knows how to handle the special +32-bit case and the normal case. It also centralizes the logic of +checking for a non-file backed stream. There are many cases in libc +where we want to know the file descriptor to perform some operation; +however, non-file backed streams do not have a corresponding file +descriptor. When such a stream is detected, we will explicitly return +-1. This ensures that a bad file descriptor will be used if someone +mistakenly calls a system call. Functions like _fileno() call this +directly. + +------- +Testing +------- + +There is a burgeoning test suite for stdio in +usr/src/test/libc-tests/tests/stdio. If working in stdio (or libc more +generally) it is recommended that you run this test suite and add new +tests to it where appropriate. For most new functionality it is +encouraged that you both import test suites that may already exist and +that you also write your own test suites to properly cover a number of +error and corner cases. + +Tests should also be written against libumem(3LIB), and umem debugging +should be explicitly enabled in the program. Enabling umem debugging can +catch a number of common memory usage errors. It also makes it easier to +test for memory leaks by taking a core file and used the mdb +'::findleaks' dcmd. A good starting point is to place the following in +the program: + +const char * +_umem_debug_init(void) +{ + return ("default,verbose"); +} + +const char * +_umem_logging_init(void) +{ + return ("fail,contents"); +} + +For the definition of these flags, see umem_debug(3MALLOC). + +In addition, by leveraging umem debugging it becomes very easy to +simulate malloc failure when required. This can be enabled by calling +umem_setmtbf(1), which ensures that any subsequent memory requests +through malloc(), including those made indirectly by libc, will fail. To +restore the behavior after a test, one can simply call umem_setmtbf(0). diff --git a/usr/src/lib/libc/port/stdio/_endopen.c b/usr/src/lib/libc/port/stdio/_endopen.c index a7c4508cbb..fa548f6385 100644 --- a/usr/src/lib/libc/port/stdio/_endopen.c +++ b/usr/src/lib/libc/port/stdio/_endopen.c @@ -25,7 +25,7 @@ */ /* Copyright (c) 1988 AT&T */ -/* All Rights Reserved */ +/* All Rights Reserved */ /* Copyright (c) 2013 OmniTI Computer Consulting, Inc. All rights reserved. */ @@ -60,53 +60,9 @@ _endopen(const char *name, const char *type, FILE *iop, int largefile) if (iop == NULL) return (NULL); - switch (type[0]) { - default: - errno = EINVAL; - return (NULL); - case 'r': - oflag = O_RDONLY; - fflag = _IOREAD; - break; - case 'w': - oflag = O_WRONLY | O_TRUNC | O_CREAT; - fflag = _IOWRT; - break; - case 'a': - oflag = O_WRONLY | O_APPEND | O_CREAT; - fflag = _IOWRT; - break; - } - plusflag = 0; - eflag = 0; - xflag = 0; - for (echr = type + 1; *echr != '\0'; echr++) { - switch (*echr) { - /* UNIX ignores 'b' and treats text and binary the same */ - default: - break; - case '+': - plusflag = 1; - break; - case 'e': - eflag = 1; - break; - case 'x': - xflag = 1; - break; - } - } - if (eflag) { - /* Subsequent to a mode flag, 'e' indicates O_CLOEXEC */ - oflag = oflag | O_CLOEXEC; - } - if (plusflag) { - oflag = (oflag & ~(O_RDONLY | O_WRONLY)) | O_RDWR; - fflag = _IORW; - } - if (xflag) { - oflag |= O_EXCL; + if (_stdio_flags(type, &oflag, &fflag) != 0) { + return (NULL); } /* select small or large file open based on flag */ @@ -121,21 +77,23 @@ _endopen(const char *name, const char *type, FILE *iop, int largefile) /* As long as we make sure _flag stays != 0, we don't need to lock */ #ifdef _LP64 iop->_file = fd; - iop->_flag = (iop->_flag & ~0377) | fflag; + iop->_flag = (iop->_flag & ~_DEF_FLAG_MASK) | fflag; #else if (fd <= _FILE_FD_MAX) { SET_FILE(iop, fd); } else if (_file_set(iop, fd, type) != 0) { /* errno set in _file_set() */ + int err = errno; (void) close(fd); + errno = err; return (NULL); } iop->_flag = fflag; #endif /* _LP64 */ if (oflag == (O_WRONLY | O_APPEND | O_CREAT)) { /* type == "a" */ - if (lseek64(fd, (off64_t)0, SEEK_END) < (off64_t)0) { - (void) close(fd); + if (_xseek64(iop, (off64_t)0, SEEK_END) < (off64_t)0) { + (void) _xclose(iop); return (NULL); } } diff --git a/usr/src/lib/libc/port/stdio/_filbuf.c b/usr/src/lib/libc/port/stdio/_filbuf.c index 73150feb3a..2accbe1158 100644 --- a/usr/src/lib/libc/port/stdio/_filbuf.c +++ b/usr/src/lib/libc/port/stdio/_filbuf.c @@ -25,10 +25,7 @@ */ /* Copyright (c) 1988 AT&T */ -/* All Rights Reserved */ - -#pragma ident "%Z%%M% %I% %E% SMI" - +/* All Rights Reserved */ #pragma weak __filbuf = _filbuf @@ -62,24 +59,24 @@ _filbuf(FILE *iop) unsigned char flag; #endif - if (!(iop->_flag & _IOREAD)) /* check, correct permissions */ - { - if (iop->_flag & _IORW) - iop->_flag |= _IOREAD; /* change direction */ + if (!(iop->_flag & _IOREAD)) { /* check, correct permissions */ + if (iop->_flag & _IORW) { + iop->_flag |= _IOREAD; /* change direction */ /* to read - fseek */ - else { + } else { errno = EBADF; return (EOF); } } if (iop->_base == 0) { - if ((endbuf = _findbuf(iop)) == 0) /* get buffer and */ - /* end_of_buffer */ + /* Get the buffer and end of buffer */ + if ((endbuf = _findbuf(iop)) == 0) { return (EOF); - } - else + } + } else { endbuf = _bufend(iop); + } /* * Flush all line-buffered streams before we @@ -109,6 +106,7 @@ _filbuf(FILE *iop) return (EOF); } } + /* * Fill buffer or read 1 byte for unbuffered, handling any errors. */ @@ -117,7 +115,7 @@ _filbuf(FILE *iop) nbyte = 1; else nbyte = endbuf - iop->_base; - if ((res = read(GET_FD(iop), (char *)iop->_base, nbyte)) > 0) { + if ((res = _xread(iop, (char *)iop->_base, nbyte)) > 0) { iop->_cnt = res - 1; return (*iop->_ptr++); } diff --git a/usr/src/lib/libc/port/stdio/_findbuf.c b/usr/src/lib/libc/port/stdio/_findbuf.c index d0c1949e98..e0633c80c6 100644 --- a/usr/src/lib/libc/port/stdio/_findbuf.c +++ b/usr/src/lib/libc/port/stdio/_findbuf.c @@ -25,9 +25,11 @@ */ /* Copyright (c) 1988 AT&T */ -/* All Rights Reserved */ +/* All Rights Reserved */ -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright 2020 Robert Mustacchi + */ #define _LARGEFILE64_SOURCE 1 @@ -51,48 +53,47 @@ Uchar * _findbuf(FILE *iop) { - int fd = GET_FD(iop); + int fd = _get_fd(iop); Uchar *buf; int size = BUFSIZ; Uchar *endbuf; int tty = -1; - struct stat64 stbuf; /* used to get file system block size */ - - if (iop->_flag & _IONBF) /* need a small buffer, at least */ - { - trysmall:; + if (iop->_flag & _IONBF) { /* need a small buffer, at least */ + trysmall: size = _SMBFSZ - PUSHBACK; - if (fd < _NFILE) + if (fd >= 0 && fd < _NFILE) { buf = _smbuf[fd]; - else if ((buf = (Uchar *)malloc(_SMBFSZ * sizeof (Uchar))) != - NULL) + } else if ((buf = (Uchar *)malloc(_SMBFSZ * sizeof (Uchar))) != + NULL) { iop->_flag |= _IOMYBUF; - } -#ifndef _STDIO_ALLOCATE - else if (fd < 2 && (tty = isatty(fd))) { - buf = (fd == 0) ? _sibuf : _sobuf; /* special buffer */ - /* for std{in,out} */ - } -#endif - else { + } + } else if (fd >= 0 && fd < 2 && (tty = isatty(fd))) { + /* Use special buffers for standard in and standard out */ + buf = (fd == 0) ? _sibuf : _sobuf; + } else { + /* - * The operating system can tell us the - * right size for a buffer; - * avoid 0-size buffers as returned for some - * special files (doors) + * The operating system can tell us the right size for a buffer; + * avoid 0-size buffers as returned for some special files + * (doors). Use the default buffer size for memory streams. */ - if (fstat64(fd, &stbuf) == 0 && stbuf.st_blksize > 0) + struct stat64 stbuf; + + if (fd != -1 && fstat64(fd, &stbuf) == 0 && stbuf.st_blksize > + 0) { size = stbuf.st_blksize; + } if ((buf = (Uchar *)malloc(sizeof (Uchar)*(size+_SMBFSZ))) != - NULL) + NULL) { iop->_flag |= _IOMYBUF; - else + } else { goto trysmall; + } } if (buf == NULL) - return (NULL); /* malloc() failed */ + return (NULL); /* malloc() failed */ iop->_base = buf + PUSHBACK; /* bytes for pushback */ iop->_ptr = buf + PUSHBACK; endbuf = iop->_base + size; diff --git a/usr/src/lib/libc/port/stdio/_flsbuf.c b/usr/src/lib/libc/port/stdio/_flsbuf.c index 234d9fd2c4..81d3b0dc3a 100644 --- a/usr/src/lib/libc/port/stdio/_flsbuf.c +++ b/usr/src/lib/libc/port/stdio/_flsbuf.c @@ -25,9 +25,7 @@ */ /* Copyright (c) 1988 AT&T */ -/* All Rights Reserved */ - -#pragma ident "%Z%%M% %I% %E% SMI" +/* All Rights Reserved */ #pragma weak __flsbuf = _flsbuf @@ -41,17 +39,18 @@ #include <sys/types.h> #include "stdiom.h" +/* + * flush (write) buffer, save ch, + * return EOF on failure + */ int -_flsbuf(int ch, FILE *iop) /* flush (write) buffer, save ch, */ - /* return EOF on failure */ +_flsbuf(int ch, FILE *iop) { Uchar uch; - do /* only loop if need to use _wrtchk() on non-_IOFBF */ - { + do { /* only loop if need to use _wrtchk() on non-_IOFBF */ switch (iop->_flag & (_IOFBF | _IOLBF | _IONBF | - _IOWRT | _IOEOF)) - { + _IOWRT | _IOEOF)) { case _IOFBF | _IOWRT: /* okay to do full-buffered case */ if (iop->_base != 0 && iop->_ptr > iop->_base) goto flush_putc; /* skip _wrtchk() */ @@ -71,7 +70,7 @@ _flsbuf(int ch, FILE *iop) /* flush (write) buffer, save ch, */ case _IONBF | _IOWRT: /* okay to do no-buffered case */ iop->_cnt = 0; uch = (unsigned char)ch; - if (write(GET_FD(iop), (char *)&uch, 1) != 1) { + if (_xwrite(iop, (char *)&uch, 1) != 1) { if (!cancel_active()) iop->_flag |= _IOERR; return (EOF); diff --git a/usr/src/lib/libc/port/stdio/_stdio_flags.c b/usr/src/lib/libc/port/stdio/_stdio_flags.c new file mode 100644 index 0000000000..b54f9051e6 --- /dev/null +++ b/usr/src/lib/libc/port/stdio/_stdio_flags.c @@ -0,0 +1,109 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1988 AT&T */ +/* All Rights Reserved */ + +/* Copyright (c) 2013 OmniTI Computer Consulting, Inc. All rights reserved. */ +/* + * Copyright 2020 Robert Mustacchi + */ + +/* + * Commonized processing of the 'mode' string for stdio. + */ + +#include "mtlib.h" +#include "file64.h" +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <fcntl.h> + +int +_stdio_flags(const char *type, int *oflagsp, int *fflagsp) +{ + int oflag, fflag, plusflag, eflag, xflag; + const char *echr; + + oflag = fflag = 0; + switch (type[0]) { + default: + errno = EINVAL; + return (-1); + case 'r': + oflag = O_RDONLY; + fflag = _IOREAD; + break; + case 'w': + oflag = O_WRONLY | O_TRUNC | O_CREAT; + fflag = _IOWRT; + break; + case 'a': + oflag = O_WRONLY | O_APPEND | O_CREAT; + fflag = _IOWRT; + break; + } + + plusflag = 0; + eflag = 0; + xflag = 0; + for (echr = type + 1; *echr != '\0'; echr++) { + switch (*echr) { + /* UNIX ignores 'b' and treats text and binary the same */ + default: + break; + case '+': + plusflag = 1; + break; + case 'e': + eflag = 1; + break; + case 'x': + xflag = 1; + break; + } + } + + if (eflag) { + /* Subsequent to a mode flag, 'e' indicates O_CLOEXEC */ + oflag = oflag | O_CLOEXEC; + } + + if (plusflag) { + oflag = (oflag & ~(O_RDONLY | O_WRONLY)) | O_RDWR; + fflag = _IORW; + } + + if (xflag) { + oflag |= O_EXCL; + } + + *oflagsp = oflag; + *fflagsp = fflag; + + return (0); +} diff --git a/usr/src/lib/libc/port/stdio/doscan.c b/usr/src/lib/libc/port/stdio/doscan.c index e375d9824f..fda4d5c2ea 100644 --- a/usr/src/lib/libc/port/stdio/doscan.c +++ b/usr/src/lib/libc/port/stdio/doscan.c @@ -25,9 +25,7 @@ */ /* Copyright (c) 1988 AT&T */ -/* All Rights Reserved */ - -#pragma ident "%Z%%M% %I% %E% SMI" +/* All Rights Reserved */ #include "lint.h" #include <sys/types.h> @@ -402,7 +400,7 @@ charswitch: /* target of a goto 8-( */ if (size == 'l') { int c, len, i; int negflg = 0; - unsigned char *p; + unsigned char *p; p = (unsigned char *)(fmt - 1); len = 0; @@ -830,10 +828,10 @@ readchar(FILE *iop, int *chcount) int inchar; char buf[1]; - if ((iop->_flag & _IOWRT) || (iop->_cnt != 0)) + if ((iop->_flag & _IOWRT) || (iop->_cnt != 0)) { inchar = locgetc((*chcount)); - else { - if (read(FILENO(iop), buf, 1) != 1) + } else { + if (_xread(iop, buf, 1) != 1) return (EOF); inchar = (int)buf[0]; (*chcount) += 1; diff --git a/usr/src/lib/libc/port/stdio/fileno.c b/usr/src/lib/libc/port/stdio/fileno.c index 70f64faa89..4e99438dc9 100644 --- a/usr/src/lib/libc/port/stdio/fileno.c +++ b/usr/src/lib/libc/port/stdio/fileno.c @@ -25,9 +25,7 @@ */ /* Copyright (c) 1988 AT&T */ -/* All Rights Reserved */ - -#pragma ident "%Z%%M% %I% %E% SMI" +/* All Rights Reserved */ #pragma weak _fileno = fileno @@ -43,5 +41,5 @@ int fileno(FILE *iop) { - return (GET_FD(iop)); + return (_get_fd(iop)); } diff --git a/usr/src/lib/libc/port/stdio/flush.c b/usr/src/lib/libc/port/stdio/flush.c index 7d3549ca0d..be710ccb2a 100644 --- a/usr/src/lib/libc/port/stdio/flush.c +++ b/usr/src/lib/libc/port/stdio/flush.c @@ -21,10 +21,11 @@ /* * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2020 Robert Mustacchi */ /* Copyright (c) 1988 AT&T */ -/* All Rights Reserved */ +/* All Rights Reserved */ #include "lint.h" #include "mtlib.h" @@ -42,6 +43,8 @@ #include <stddef.h> #include <errno.h> #include <fcntl.h> +#include <sys/debug.h> +#include <limits.h> #define _iob __iob @@ -376,7 +379,8 @@ rescan: lastlink = *prev = hdr; fp->_ptr = 0; fp->_base = 0; - fp->_flag = 0377; /* claim the fp by setting low 8 bits */ + /* claim the fp by setting low 8 bits */ + fp->_flag = _DEF_FLAG_MASK; if (threaded) cancel_safe_mutex_unlock(&_first_link_lock); @@ -387,11 +391,19 @@ static void isseekable(FILE *iop) { struct stat64 fstatbuf; - int save_errno; + int fd, save_errno; save_errno = errno; - if (fstat64(GET_FD(iop), &fstatbuf) != 0) { + /* + * non-FILE based STREAMS are required to declare their own seekability + * and therefore we should not try and test them below. + */ + fd = _get_fd(iop); + if (fd == -1) { + return; + } + if (fstat64(fd, &fstatbuf) != 0) { /* * when we don't know what it is we'll * do the old behaviour and flush @@ -545,8 +557,7 @@ _xflsbuf(FILE *iop) _bufsync(iop, bufend); if (n > 0) { - int fd = GET_FD(iop); - while ((num_wrote = write(fd, base, (size_t)n)) != n) { + while ((num_wrote = _xwrite(iop, base, (size_t)n)) != n) { if (num_wrote <= 0) { if (!cancel_active()) iop->_flag |= _IOERR; @@ -600,8 +611,8 @@ _fflush_l_iops(void) /* flush all buffers */ * knowing that when it is 0, it isn't allocated and * cannot be allocated while we're holding the * _first_link_lock. And when _IONBF is set (also the - * case when _flag is 0377, or alloc in progress), we - * also ignore it. + * case when _flag is 0377 -- _DEF_FLAG_MASK, or alloc in + * progress), we also ignore it. * * Ignore locked streams; it will appear as if * concurrent updates happened after fflush(NULL). Note @@ -662,7 +673,7 @@ _fflush_u(FILE *iop) /* this portion is always assumed locked */ if (!(iop->_flag & _IOWRT)) { - (void) lseek64(GET_FD(iop), -iop->_cnt, SEEK_CUR); + (void) _xseek64(iop, -iop->_cnt, SEEK_CUR); iop->_cnt = 0; /* needed for ungetc & multibyte pushbacks */ iop->_ptr = iop->_base; @@ -700,7 +711,7 @@ fclose(FILE *iop) /* Is not unbuffered and opened for read and/or write ? */ if (!(iop->_flag & _IONBF) && (iop->_flag & (_IOWRT | _IOREAD | _IORW))) res = _fflush_u(iop); - if (close(GET_FD(iop)) < 0) + if (_xclose(iop) < 0) res = EOF; if (iop->_flag & _IOMYBUF) { (void) free((char *)iop->_base - PUSHBACK); @@ -751,7 +762,7 @@ fcloseall(void) if (!(iop->_flag & _IONBF) && (iop->_flag & (_IOWRT | _IOREAD | _IORW))) (void) _fflush_u(iop); - (void) close(GET_FD(iop)); + (void) _xclose(iop); if (iop->_flag & _IOMYBUF) free((char *)iop->_base - PUSHBACK); iop->_base = NULL; @@ -781,7 +792,7 @@ close_fd(FILE *iop) /* Is not unbuffered and opened for read and/or write ? */ if (!(iop->_flag & _IONBF) && (iop->_flag & (_IOWRT | _IOREAD | _IORW))) res = _fflush_u(iop); - if (close(GET_FD(iop)) < 0) + if (_xclose(iop) < 0) res = EOF; if (iop->_flag & _IOMYBUF) { (void) free((char *)iop->_base - PUSHBACK); @@ -809,7 +820,8 @@ getiop(FILE *fp, rmutex_t *lk, mbstate_t *mb) fp->_cnt = 0; fp->_ptr = NULL; fp->_base = NULL; - fp->_flag = 0377; /* claim the fp by setting low 8 bits */ + /* claim the fp by setting low 8 bits */ + fp->_flag = _DEF_FLAG_MASK; (void) memset(mb, 0, sizeof (mbstate_t)); FUNLOCKFILE(lk); return (fp); @@ -943,3 +955,164 @@ enable_extended_FILE_stdio(int fd, int action) return (0); } #endif + +/* + * Wrappers around the various system calls that stdio needs to make on a file + * descriptor. + */ +static stdio_ops_t * +get_stdops(FILE *iop) +{ +#ifdef _LP64 + return (iop->_ops); +#else + struct xFILEdata *dat = getxfdat(iop); + return (dat->_ops); +#endif +} + +static void +set_stdops(FILE *iop, stdio_ops_t *ops) +{ +#ifdef _LP64 + ASSERT3P(iop->_ops, ==, NULL); + iop->_ops = ops; +#else + struct xFILEdata *dat = getxfdat(iop); + ASSERT3P(dat->_ops, ==, NULL); + dat->_ops = ops; +#endif + +} + +ssize_t +_xread(FILE *iop, void *buf, size_t nbytes) +{ + stdio_ops_t *ops = get_stdops(iop); + if (ops != NULL) { + return (ops->std_read(iop, buf, nbytes)); + } + + return (read(_get_fd(iop), buf, nbytes)); +} + +ssize_t +_xwrite(FILE *iop, const void *buf, size_t nbytes) +{ + stdio_ops_t *ops = get_stdops(iop); + if (ops != NULL) { + return (ops->std_write(iop, buf, nbytes)); + } + return (write(_get_fd(iop), buf, nbytes)); +} + +off_t +_xseek(FILE *iop, off_t off, int whence) +{ + stdio_ops_t *ops = get_stdops(iop); + if (ops != NULL) { + return (ops->std_seek(iop, off, whence)); + } + + return (lseek(_get_fd(iop), off, whence)); +} + +off64_t +_xseek64(FILE *iop, off64_t off, int whence) +{ + stdio_ops_t *ops = get_stdops(iop); + if (ops != NULL) { + /* + * The internal APIs only operate with an off_t. An off64_t in + * an ILP32 environment may represent a value larger than they + * can accept. As such, we try and catch such cases and error + * about it before we get there. + */ + if (off > LONG_MAX || off < LONG_MIN) { + errno = EOVERFLOW; + return (-1); + } + return (ops->std_seek(iop, off, whence)); + } + + return (lseek64(_get_fd(iop), off, whence)); +} + +int +_xclose(FILE *iop) +{ + stdio_ops_t *ops = get_stdops(iop); + if (ops != NULL) { + return (ops->std_close(iop)); + } + + return (close(_get_fd(iop))); +} + +void * +_xdata(FILE *iop) +{ + stdio_ops_t *ops = get_stdops(iop); + if (ops != NULL) { + return (ops->std_data); + } + + return (NULL); +} + +int +_xassoc(FILE *iop, fread_t readf, fwrite_t writef, fseek_t seekf, + fclose_t closef, void *data) +{ + stdio_ops_t *ops = get_stdops(iop); + + if (ops == NULL) { + ops = malloc(sizeof (*ops)); + if (ops == NULL) { + return (-1); + } + set_stdops(iop, ops); + } + + ops->std_read = readf; + ops->std_write = writef; + ops->std_seek = seekf; + ops->std_close = closef; + ops->std_data = data; + + return (0); +} + +void +_xunassoc(FILE *iop) +{ + stdio_ops_t *ops = get_stdops(iop); + if (ops == NULL) { + return; + } + set_stdops(iop, NULL); + free(ops); +} + +int +_get_fd(FILE *iop) +{ + /* + * Streams with an ops vector (currently the memory stream family) do + * not have an underlying file descriptor that we can give back to the + * user. In such cases, return -1 to explicitly make sure that they'll + * get an ebadf from things. + */ + if (get_stdops(iop) != NULL) { + return (-1); + } +#ifdef _LP64 + return (iop->_file); +#else + if (iop->__extendedfd) { + return (_file_get(iop)); + } else { + return (iop->_magic); + } +#endif +} diff --git a/usr/src/lib/libc/port/stdio/fmemopen.c b/usr/src/lib/libc/port/stdio/fmemopen.c new file mode 100644 index 0000000000..e2321c9632 --- /dev/null +++ b/usr/src/lib/libc/port/stdio/fmemopen.c @@ -0,0 +1,270 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2020 Robert Mustacchi + */ + +/* + * Implements fmemopen(3C). + */ + +#include "mtlib.h" +#include "file64.h" +#include <stdio.h> +#include "stdiom.h" +#include <errno.h> +#include <stdlib.h> +#include <fcntl.h> +#include <sys/sysmacros.h> +#include <limits.h> + +typedef enum fmemopen_flags { + /* + * Indicates that the user gave us the buffer and so we shouldn't free + * it. + */ + FMO_F_USER_BUFFER = 1 << 0, + /* + * When the stream is open for update (a, a+) then we have to have + * slightly different behavior on write and zeroing the buffer. + */ + FMO_F_APPEND = 1 << 1 +} fmemopen_flags_t; + +typedef struct fmemopen { + /* + * Pointer to the underlying memory stream. + */ + char *fmo_buf; + /* + * Allocated length of the buffer. + */ + size_t fmo_alloc; + /* + * Current position of the buffer. + */ + size_t fmo_pos; + /* + * Current 'size' of the buffer. POSIX describes a size that the buffer + * has which is separate from the allocated size, but cannot exceed it. + */ + size_t fmo_lsize; + fmemopen_flags_t fmo_flags; +} fmemopen_t; + +static ssize_t +fmemopen_read(FILE *iop, char *buf, size_t nbytes) +{ + fmemopen_t *fmp = _xdata(iop); + + nbytes = MIN(nbytes, fmp->fmo_lsize - fmp->fmo_pos); + if (nbytes == 0) { + return (0); + } + + (void) memcpy(buf, fmp->fmo_buf, nbytes); + fmp->fmo_pos += nbytes; + + return (nbytes); +} + +static ssize_t +fmemopen_write(FILE *iop, const char *buf, size_t nbytes) +{ + size_t npos; + fmemopen_t *fmp = _xdata(iop); + + if ((fmp->fmo_flags & FMO_F_APPEND) != 0) { + /* + * POSIX says that if append mode is in effect, we must always + * seek to the logical size. This effectively is mimicking the + * O_APPEND behavior. + */ + fmp->fmo_pos = fmp->fmo_lsize; + } + + if (nbytes == 0) { + return (0); + } else if (nbytes >= SSIZE_MAX) { + errno = EINVAL; + return (-1); + } + + npos = fmp->fmo_pos + nbytes; + if (npos < nbytes) { + errno = EOVERFLOW; + return (-1); + } else if (npos > fmp->fmo_alloc) { + nbytes = fmp->fmo_alloc - fmp->fmo_pos; + } + + (void) memcpy(&fmp->fmo_buf[fmp->fmo_pos], buf, nbytes); + fmp->fmo_pos += nbytes; + + if (fmp->fmo_pos > fmp->fmo_lsize) { + fmp->fmo_lsize = fmp->fmo_pos; + + /* + * POSIX distinguishes behavior for writing a NUL in these + * streams. Basically if we are open for update and we are at + * the end of the buffer, we don't place a NUL. Otherwise, we + * always place one at the current position (or the end if we + * were over the edge). + */ + if (fmp->fmo_lsize < fmp->fmo_alloc) { + fmp->fmo_buf[fmp->fmo_lsize] = '\0'; + } else if ((fmp->fmo_flags & FMO_F_APPEND) == 0) { + fmp->fmo_buf[fmp->fmo_alloc - 1] = '\0'; + } + } + + return (nbytes); +} + +static off_t +fmemopen_seek(FILE *iop, off_t off, int whence) +{ + fmemopen_t *fmp = _xdata(iop); + size_t base, npos; + + switch (whence) { + case SEEK_SET: + base = 0; + break; + case SEEK_CUR: + base = fmp->fmo_pos; + break; + case SEEK_END: + base = fmp->fmo_lsize; + break; + default: + errno = EINVAL; + return (-1); + } + + if (!memstream_seek(base, off, fmp->fmo_alloc, &npos)) { + errno = EINVAL; + return (-1); + } + fmp->fmo_pos = npos; + + return ((off_t)fmp->fmo_pos); +} + +static void +fmemopen_free(fmemopen_t *fmp) +{ + if (fmp->fmo_buf != NULL && + (fmp->fmo_flags & FMO_F_USER_BUFFER) == 0) { + free(fmp->fmo_buf); + } + + free(fmp); +} + +static int +fmemopen_close(FILE *iop) +{ + fmemopen_t *fmp = _xdata(iop); + fmemopen_free(fmp); + _xunassoc(iop); + return (0); +} + +FILE * +fmemopen(void *_RESTRICT_KYWD buf, size_t size, + const char *_RESTRICT_KYWD mode) +{ + int oflags, fflags, err; + fmemopen_t *fmp; + FILE *iop; + + if (size == 0 || mode == NULL) { + errno = EINVAL; + return (NULL); + } + + if (_stdio_flags(mode, &oflags, &fflags) != 0) { + /* errno set for us */ + return (NULL); + } + + /* + * buf is only allowed to be NULL if the '+' is specified. If the '+' + * mode was specified, then we'll have fflags set to _IORW. + */ + if (buf == NULL && fflags != _IORW) { + errno = EINVAL; + return (NULL); + } + + if ((fmp = calloc(1, sizeof (fmemopen_t))) == NULL) { + errno = ENOMEM; + return (NULL); + } + + if (buf == NULL) { + fmp->fmo_buf = calloc(size, sizeof (uint8_t)); + if (fmp->fmo_buf == NULL) { + errno = ENOMEM; + goto cleanup; + } + } else { + fmp->fmo_buf = buf; + fmp->fmo_flags |= FMO_F_USER_BUFFER; + } + + fmp->fmo_alloc = size; + + /* + * Set the initial logical size and position depending on whether we're + * using r(+), w(+), and a(+). The latter two are identified by O_TRUNC + * and O_APPEND in oflags. + */ + if ((oflags & O_APPEND) != 0) { + fmp->fmo_pos = strnlen(fmp->fmo_buf, fmp->fmo_alloc); + fmp->fmo_lsize = fmp->fmo_pos; + fmp->fmo_flags |= FMO_F_APPEND; + } else if ((oflags & O_TRUNC) != 0) { + fmp->fmo_buf[0] = '\0'; + fmp->fmo_pos = 0; + fmp->fmo_lsize = 0; + } else { + fmp->fmo_pos = 0; + fmp->fmo_lsize = size; + } + + iop = _findiop(); + if (iop == NULL) { + goto cleanup; + } + +#ifdef _LP64 + iop->_flag = (iop->_flag & ~_DEF_FLAG_MASK) | fflags; +#else + iop->_flag = fflags; +#endif + if (_xassoc(iop, fmemopen_read, fmemopen_write, fmemopen_seek, + fmemopen_close, fmp) != 0) { + goto cleanup; + } + + SET_SEEKABLE(iop); + + return (iop); + +cleanup: + err = errno; + fmemopen_free(fmp); + errno = err; + return (NULL); +} diff --git a/usr/src/lib/libc/port/stdio/fopen.c b/usr/src/lib/libc/port/stdio/fopen.c index eea8093aca..728a0f4978 100644 --- a/usr/src/lib/libc/port/stdio/fopen.c +++ b/usr/src/lib/libc/port/stdio/fopen.c @@ -25,7 +25,7 @@ */ /* Copyright (c) 1988 AT&T */ -/* All Rights Reserved */ +/* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 @@ -37,7 +37,9 @@ * contributors. */ -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright 2020 Robert Mustacchi + */ #include "lint.h" #include "file64.h" @@ -88,6 +90,17 @@ _freopen_null(const char *type, FILE *iop) return (NULL); } + /* + * If this is not a file-based stream (as in we have no file + * descriptor), then we need to close this, but still actually return an + * error. + */ + if (_get_fd(iop) == -1) { + (void) close_fd(iop); + errno = EBADF; + return (NULL); + } + if (!(iop->_flag & _IONBF) && (iop->_flag & (_IOWRT | _IOREAD | _IORW))) (void) _fflush_u(iop); @@ -140,7 +153,7 @@ _freopen_null(const char *type, FILE *iop) } #ifdef _LP64 - iop->_flag &= ~0377; /* clear lower 8-bits */ + iop->_flag &= ~_DEF_FLAG_MASK; /* clear lower 8-bits */ if (mode == 'r') { iop->_flag |= _IOREAD; nflag = oflag & ~O_APPEND; diff --git a/usr/src/lib/libc/port/stdio/fputs.c b/usr/src/lib/libc/port/stdio/fputs.c index be0f5b4f8f..46b0d87107 100644 --- a/usr/src/lib/libc/port/stdio/fputs.c +++ b/usr/src/lib/libc/port/stdio/fputs.c @@ -25,10 +25,7 @@ */ /* Copyright (c) 1988 AT&T */ -/* All Rights Reserved */ - -#pragma ident "%Z%%M% %I% %E% SMI" - +/* All Rights Reserved */ /* * Ptr args aren't checked for NULL because the program would be a @@ -112,15 +109,13 @@ fputs(const char *ptr, FILE *iop) return (EOF); } } - } - else - { + } else { /* write out to an unbuffered file */ ssize_t num_wrote; ssize_t count = (ssize_t)ptrlen; - int fd = GET_FD(iop); - while ((num_wrote = write(fd, ptr, (size_t)count)) != count) { + while ((num_wrote = _xwrite(iop, ptr, (size_t)count)) != + count) { if (num_wrote <= 0) { if (!cancel_active()) iop->_flag |= _IOERR; diff --git a/usr/src/lib/libc/port/stdio/fseek.c b/usr/src/lib/libc/port/stdio/fseek.c index 55017a6584..01ef4c4c00 100644 --- a/usr/src/lib/libc/port/stdio/fseek.c +++ b/usr/src/lib/libc/port/stdio/fseek.c @@ -25,9 +25,7 @@ */ /* Copyright (c) 1988 AT&T */ -/* All Rights Reserved */ - -#pragma ident "%Z%%M% %I% %E% SMI" +/* All Rights Reserved */ /* * Seek for standard library. Coordinates with buffering. @@ -76,7 +74,7 @@ fseek(FILE *iop, long offset, int ptrname) if (iop->_flag & _IORW) { iop->_flag &= ~(_IOREAD | _IOWRT); } - p = lseek(FILENO(iop), (off_t)offset, ptrname); + p = _xseek(iop, (off_t)offset, ptrname); FUNLOCKFILE(lk); return ((p == (off_t)-1) ? -1: 0); } diff --git a/usr/src/lib/libc/port/stdio/fseeko.c b/usr/src/lib/libc/port/stdio/fseeko.c index b47c7a5313..2af3fdf532 100644 --- a/usr/src/lib/libc/port/stdio/fseeko.c +++ b/usr/src/lib/libc/port/stdio/fseeko.c @@ -25,9 +25,7 @@ */ /* Copyright (c) 1988 AT&T */ -/* All Rights Reserved */ - -#pragma ident "%Z%%M% %I% %E% SMI" +/* All Rights Reserved */ /* * Seek for standard library. Coordinates with buffering. @@ -83,7 +81,7 @@ fseeko64(FILE *iop, off64_t offset, int ptrname) if (iop->_flag & _IORW) { iop->_flag &= ~(_IOREAD | _IOWRT); } - p = lseek64(FILENO(iop), offset, ptrname); + p = _xseek64(iop, offset, ptrname); FUNLOCKFILE(lk); return ((p == -1)? -1: 0); } diff --git a/usr/src/lib/libc/port/stdio/ftell.c b/usr/src/lib/libc/port/stdio/ftell.c index 45c0b841d6..dae2abea03 100644 --- a/usr/src/lib/libc/port/stdio/ftell.c +++ b/usr/src/lib/libc/port/stdio/ftell.c @@ -25,9 +25,7 @@ */ /* Copyright (c) 1988 AT&T */ -/* All Rights Reserved */ - -#pragma ident "%Z%%M% %I% %E% SMI" +/* All Rights Reserved */ /* * Return file offset. @@ -49,41 +47,63 @@ #include <sys/types.h> #include "stdiom.h" -long -ftell(FILE *iop) +off64_t +ftell_common(FILE *iop) { ptrdiff_t adjust; off64_t tres; rmutex_t *lk; FLOCKFILE(lk, iop); + + /* + * If we're dealing with a memory stream, we need to flush the internal + * state before we try and determine the location. This is especially + * important for open_wmemstream() as it will have buffered bytes, but + * we need to convert that to wide characters before we proceed. If we + * have no file descriptor, then the units that the backing store are in + * can be arbitrary. + */ + if (_get_fd(iop) == -1) { + (void) _fflush_u(iop); + } + if (iop->_cnt < 0) iop->_cnt = 0; - if (iop->_flag & _IOREAD) + if (iop->_flag & _IOREAD) { adjust = (ptrdiff_t)-iop->_cnt; - else if (iop->_flag & (_IOWRT | _IORW)) { + } else if (iop->_flag & (_IOWRT | _IORW)) { adjust = 0; if (((iop->_flag & (_IOWRT | _IONBF)) == _IOWRT) && - (iop->_base != 0)) + (iop->_base != 0)) { adjust = iop->_ptr - iop->_base; - else if ((iop->_flag & _IORW) && (iop->_base != 0)) + } else if ((iop->_flag & _IORW) && (iop->_base != 0)) { adjust = (ptrdiff_t)-iop->_cnt; + } } else { errno = EBADF; /* file descriptor refers to no open file */ FUNLOCKFILE(lk); return (EOF); } - tres = lseek64(FILENO(iop), 0, SEEK_CUR); + tres = _xseek64(iop, 0, SEEK_CUR); if (tres >= 0) tres += adjust; + FUNLOCKFILE(lk); + return ((long)tres); +} + +long +ftell(FILE *iop) +{ + off64_t tres; + + tres = ftell_common(iop); if (tres > LONG_MAX) { errno = EOVERFLOW; - FUNLOCKFILE(lk); return (EOF); } - FUNLOCKFILE(lk); return ((long)tres); } diff --git a/usr/src/lib/libc/port/stdio/ftello.c b/usr/src/lib/libc/port/stdio/ftello.c index 2eafdf1fef..cd43037ab1 100644 --- a/usr/src/lib/libc/port/stdio/ftello.c +++ b/usr/src/lib/libc/port/stdio/ftello.c @@ -25,9 +25,7 @@ */ /* Copyright (c) 1988 AT&T */ -/* All Rights Reserved */ - -#pragma ident "%Z%%M% %I% %E% SMI" +/* All Rights Reserved */ /* * Return file offset. @@ -60,30 +58,7 @@ off64_t ftello64(FILE *iop) { - ptrdiff_t adjust; - off64_t tres; - rmutex_t *lk; - - FLOCKFILE(lk, iop); - if (iop->_cnt < 0) - iop->_cnt = 0; - if (iop->_flag & _IOREAD) - adjust = (ptrdiff_t)-iop->_cnt; - else if (iop->_flag & (_IOWRT | _IORW)) { - adjust = 0; - if (((iop->_flag & (_IOWRT | _IONBF)) == _IOWRT) && - (iop->_base != 0)) - adjust = iop->_ptr - iop->_base; - } else { - errno = EBADF; /* file descriptor refers to no open file */ - FUNLOCKFILE(lk); - return ((off64_t)EOF); - } - tres = lseek64(FILENO(iop), 0, SEEK_CUR); - if (tres >= 0) - tres += (off64_t)adjust; - FUNLOCKFILE(lk); - return (tres); + return (ftell_common(iop)); } #endif /* _LP64 */ diff --git a/usr/src/lib/libc/port/stdio/fwrite.c b/usr/src/lib/libc/port/stdio/fwrite.c index db5928f9f4..070354f24b 100644 --- a/usr/src/lib/libc/port/stdio/fwrite.c +++ b/usr/src/lib/libc/port/stdio/fwrite.c @@ -25,9 +25,7 @@ */ /* Copyright (c) 1988 AT&T */ -/* All Rights Reserved */ - -#pragma ident "%Z%%M% %I% %E% SMI" +/* All Rights Reserved */ #include "lint.h" #include "file64.h" @@ -108,9 +106,9 @@ _fwrite_unlocked(const void *ptr, size_t size, size_t count, FILE *iop) bytes = iop->_ptr - iop->_base; data = (char *)iop->_base; - while ((n = write(fileno(iop), data, (size_t)bytes)) - != bytes) { - if (n == -1) { + while ((n = _xwrite(iop, data, (size_t)bytes)) != + bytes) { + if (n <= 0) { if (!cancel_active()) iop->_flag |= _IOERR; return (0); @@ -126,8 +124,8 @@ _fwrite_unlocked(const void *ptr, size_t size, size_t count, FILE *iop) * written is in bytes until the return. * Then it is divided by size to produce items. */ - while ((n = write(fileno(iop), dptr, s)) != s) { - if (n == -1) { + while ((n = _xwrite(iop, dptr, s)) != s) { + if (n <= 0) { if (!cancel_active()) iop->_flag |= _IOERR; return (written / size); diff --git a/usr/src/lib/libc/port/stdio/getc.c b/usr/src/lib/libc/port/stdio/getc.c index cfcf53f347..b7a48b7f3a 100644 --- a/usr/src/lib/libc/port/stdio/getc.c +++ b/usr/src/lib/libc/port/stdio/getc.c @@ -25,9 +25,7 @@ */ /* Copyright (c) 1988 AT&T */ -/* All Rights Reserved */ - -#pragma ident "%Z%%M% %I% %E% SMI" +/* All Rights Reserved */ #pragma weak _getc_unlocked = getc_unlocked @@ -51,10 +49,7 @@ getc(FILE *iop) int c; FLOCKFILE(lk, iop); - - _SET_ORIENTATION_BYTE(iop); - - c = (--iop->_cnt < 0) ? __filbuf(iop) : *iop->_ptr++; + c = getc_unlocked(iop); FUNLOCKFILE(lk); return (c); } @@ -63,5 +58,12 @@ getc(FILE *iop) int getc_unlocked(FILE *iop) { + _SET_ORIENTATION_BYTE(iop); + return ((--iop->_cnt < 0) ? __filbuf(iop) : *iop->_ptr++); +} + +int +_getc_internal(FILE *iop) +{ return ((--iop->_cnt < 0) ? __filbuf(iop) : *iop->_ptr++); } diff --git a/usr/src/lib/libc/port/stdio/getw.c b/usr/src/lib/libc/port/stdio/getw.c index cca7732a00..9c81e8cea9 100644 --- a/usr/src/lib/libc/port/stdio/getw.c +++ b/usr/src/lib/libc/port/stdio/getw.c @@ -25,9 +25,7 @@ */ /* Copyright (c) 1988 AT&T */ -/* All Rights Reserved */ - -#pragma ident "%Z%%M% %I% %E% SMI" +/* All Rights Reserved */ /* * The intent here is to provide a means to make the order of @@ -57,7 +55,7 @@ getw(FILE *stream) FLOCKFILE(lk, stream); while (--i >= 0 && !(stream->_flag & (_IOERR | _IOEOF))) - *s++ = GETC(stream); + *s++ = getc_unlocked(stream); ret = ((stream->_flag & (_IOERR | _IOEOF)) ? EOF : w); FUNLOCKFILE(lk); return (ret); diff --git a/usr/src/lib/libc/port/stdio/open_memstream.c b/usr/src/lib/libc/port/stdio/open_memstream.c new file mode 100644 index 0000000000..a841f44fb5 --- /dev/null +++ b/usr/src/lib/libc/port/stdio/open_memstream.c @@ -0,0 +1,263 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2020 Robert Mustacchi + */ + +/* + * Implements open_memstream(3C). + */ + +#include "mtlib.h" +#include "file64.h" +#include <stdio.h> +#include "stdiom.h" +#include <errno.h> +#include <stdlib.h> +#include <fcntl.h> +#include <sys/sysmacros.h> +#include <limits.h> + +typedef struct memstream { + char *mstr_buf; + size_t mstr_alloc; + size_t mstr_pos; + size_t mstr_lsize; + char **mstr_ubufp; + size_t *mstr_usizep; +} memstream_t; + +/* + * Common seek and overflow detection logic for the memory stream family of + * functions (open_memstream, open_wmemstream, etc.). We need to validate + * several things: + * + * - That the offset when applied to base doesn't cause an over or underflow. + * - That the resulting offset is positive (done implicitly with the above) + * - That the resulting offset does not exceed an off_t's maximum size. + * Unfortunately the kernel doesn't export an OFF_MAX value to userland, so + * we have to know that it will always be equivalent to the environment's + * long. This is designed with the assumption that in an ILP32 environment we + * care about an off_t and not an off64_t. In cases where an off64_t is + * valid, we still have to fit inside of the size_t constraints. + * + * We check for each of the cases and only perform unsigned arithmetic to verify + * that we have defined behavior. + */ +boolean_t +memstream_seek(size_t base, off_t off, size_t max, size_t *nposp) +{ + size_t npos; + + npos = base + (size_t)off; + if (off >= 0 && npos < base) { + return (B_FALSE); + } + + if (off >= 0 && npos > LONG_MAX) { + return (B_FALSE); + } + + if (off < 0 && npos >= base) { + return (B_FALSE); + } + + if (npos > max) { + return (B_FALSE); + } + + *nposp = npos; + return (B_TRUE); +} + +int +memstream_newsize(size_t pos, size_t alloc, size_t nbytes, size_t *nallocp) +{ + size_t npos = pos + nbytes + 1; + if (npos < pos) { + /* + * We've been asked to write a number of bytes that would result + * in an overflow in the position. This means the stream would + * need to allocate all of memory, that's impractical. + */ + errno = EOVERFLOW; + return (-1); + } + + /* + * If the new position is beyond the allocated amount, grow the array to + * a practical amount. + */ + if (npos > alloc) { + size_t newalloc = P2ROUNDUP(npos, BUFSIZ); + if (newalloc < npos) { + errno = EOVERFLOW; + return (-1); + } + *nallocp = newalloc; + return (1); + } + + return (0); +} + +/* + * The SUSv4 spec says that this should not support reads. + */ +static ssize_t +open_memstream_read(FILE *iop, char *buf, size_t nbytes) +{ + errno = EBADF; + return (-1); +} + +static ssize_t +open_memstream_write(FILE *iop, const char *buf, size_t nbytes) +{ + memstream_t *memp = _xdata(iop); + size_t newsize; + int ret; + + /* + * We need to fit inside of an ssize_t, so we need to first constrain + * nbytes to a reasonable value. + */ + nbytes = MIN(nbytes, SSIZE_MAX); + ret = memstream_newsize(memp->mstr_pos, memp->mstr_alloc, nbytes, + &newsize); + if (ret < 0) { + return (-1); + } else if (ret > 0) { + void *temp; + temp = recallocarray(memp->mstr_buf, memp->mstr_alloc, + newsize, sizeof (char)); + if (temp == NULL) { + return (-1); + } + memp->mstr_buf = temp; + memp->mstr_alloc = newsize; + *memp->mstr_ubufp = temp; + } + + (void) memcpy(&memp->mstr_buf[memp->mstr_pos], buf, nbytes); + memp->mstr_pos += nbytes; + + if (memp->mstr_pos > memp->mstr_lsize) { + memp->mstr_lsize = memp->mstr_pos; + memp->mstr_buf[memp->mstr_pos] = '\0'; + } + *memp->mstr_usizep = MIN(memp->mstr_pos, memp->mstr_lsize); + + return (nbytes); +} + +static off_t +open_memstream_seek(FILE *iop, off_t off, int whence) +{ + memstream_t *memp = _xdata(iop); + size_t base, npos; + + switch (whence) { + case SEEK_SET: + base = 0; + break; + case SEEK_CUR: + base = memp->mstr_pos; + break; + case SEEK_END: + base = memp->mstr_lsize; + break; + default: + errno = EINVAL; + return (-1); + } + + if (!memstream_seek(base, off, SSIZE_MAX, &npos)) { + errno = EINVAL; + return (-1); + } + memp->mstr_pos = npos; + *memp->mstr_usizep = MIN(memp->mstr_pos, memp->mstr_lsize); + + return ((off_t)memp->mstr_pos); +} + +static int +open_memstream_close(FILE *iop) +{ + memstream_t *memp = _xdata(iop); + free(memp); + _xunassoc(iop); + return (0); +} + +FILE * +open_memstream(char **bufp, size_t *sizep) +{ + int err; + FILE *iop; + memstream_t *memp; + + if (bufp == NULL || sizep == NULL) { + errno = EINVAL; + return (NULL); + } + + memp = calloc(1, sizeof (memstream_t)); + if (memp == NULL) { + return (NULL); + } + + memp->mstr_alloc = BUFSIZ; + memp->mstr_buf = calloc(memp->mstr_alloc, sizeof (char)); + if (memp->mstr_buf == NULL) { + goto cleanup; + } + memp->mstr_buf[0] = '\0'; + memp->mstr_pos = 0; + memp->mstr_lsize = 0; + memp->mstr_ubufp = bufp; + memp->mstr_usizep = sizep; + + iop = _findiop(); + if (iop == NULL) { + goto cleanup; + } + +#ifdef _LP64 + iop->_flag = (iop->_flag & ~_DEF_FLAG_MASK) | _IOWRT; +#else + iop->_flag = _IOWRT; +#endif + + /* + * Update the user pointers now, in case a call to fflush() happens + * immediately. + */ + + if (_xassoc(iop, open_memstream_read, open_memstream_write, + open_memstream_seek, open_memstream_close, memp) != 0) { + goto cleanup; + } + _setorientation(iop, _BYTE_MODE); + SET_SEEKABLE(iop); + + *memp->mstr_ubufp = memp->mstr_buf; + *memp->mstr_usizep = MIN(memp->mstr_pos, memp->mstr_lsize); + + return (iop); + +cleanup: + free(memp->mstr_buf); + free(memp); + return (NULL); +} diff --git a/usr/src/lib/libc/port/stdio/open_wmemstream.c b/usr/src/lib/libc/port/stdio/open_wmemstream.c new file mode 100644 index 0000000000..d46c5e34ce --- /dev/null +++ b/usr/src/lib/libc/port/stdio/open_wmemstream.c @@ -0,0 +1,225 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2020 Robert Mustacchi + */ + +/* + * Implements open_wmemstream(3C). + */ + +#include "mtlib.h" +#include "file64.h" +#include <stdio.h> +#include "stdiom.h" +#include <errno.h> +#include <stdlib.h> +#include <fcntl.h> +#include <sys/sysmacros.h> +#include <limits.h> +#include "libc.h" + +typedef struct wmemstream { + wchar_t *wmstr_buf; + size_t wmstr_alloc; + size_t wmstr_pos; + size_t wmstr_lsize; + mbstate_t wmstr_mbs; + wchar_t **wmstr_ubufp; + size_t *wmstr_usizep; +} wmemstream_t; + +#define WMEMSTREAM_MAX (SSIZE_MAX / sizeof (wchar_t)) + +/* + * The SUSv4 spec says that this should not support reads. + */ +static ssize_t +open_wmemstream_read(FILE *iop, char *buf, size_t nbytes) +{ + errno = EBADF; + return (-1); +} + +static ssize_t +open_wmemstream_write(FILE *iop, const char *buf, size_t nbytes) +{ + wmemstream_t *wmemp = _xdata(iop); + size_t newsize, mbscount; + ssize_t nwritten = 0; + int ret; + + /* + * nbytes is in bytes not wide characters. However, the most + * pathological case from a writing perspective is using ASCII + * characters. Thus if we size things assuming that nbytes will all + * possibly be valid wchar_t values on their own, then we'll always have + * enough buffer space. + */ + nbytes = MIN(nbytes, WMEMSTREAM_MAX); + ret = memstream_newsize(wmemp->wmstr_pos, wmemp->wmstr_alloc, nbytes, + &newsize); + if (ret < 0) { + return (-1); + } else if (ret > 0) { + void *temp; + temp = recallocarray(wmemp->wmstr_buf, wmemp->wmstr_alloc, + newsize, sizeof (wchar_t)); + if (temp == NULL) { + return (-1); + } + wmemp->wmstr_buf = temp; + wmemp->wmstr_alloc = newsize; + *wmemp->wmstr_ubufp = temp; + + } + + while (nbytes > 0) { + size_t nchars; + + nchars = mbrtowc_nz(&wmemp->wmstr_buf[wmemp->wmstr_pos], + &buf[nwritten], nbytes, &wmemp->wmstr_mbs); + if (nchars == (size_t)-1) { + if (nwritten > 0) { + errno = 0; + break; + } else { + /* + * Overwrite errno in this case to be EIO. Most + * callers of stdio routines don't expect + * EILSEQ and it's not documented in POSIX, so + * we use this instead. + */ + errno = EIO; + return (-1); + } + } else if (nchars == (size_t)-2) { + nwritten += nbytes; + nbytes = 0; + } else { + nwritten += nchars; + nbytes -= nchars; + wmemp->wmstr_pos++; + } + } + + if (wmemp->wmstr_pos > wmemp->wmstr_lsize) { + wmemp->wmstr_lsize = wmemp->wmstr_pos; + wmemp->wmstr_buf[wmemp->wmstr_pos] = L'\0'; + } + *wmemp->wmstr_usizep = MIN(wmemp->wmstr_pos, wmemp->wmstr_lsize); + return (nwritten); +} + +static off_t +open_wmemstream_seek(FILE *iop, off_t off, int whence) +{ + wmemstream_t *wmemp = _xdata(iop); + size_t base, npos; + + switch (whence) { + case SEEK_SET: + base = 0; + break; + case SEEK_CUR: + base = wmemp->wmstr_pos; + break; + case SEEK_END: + base = wmemp->wmstr_lsize; + break; + default: + errno = EINVAL; + return (-1); + } + + if (!memstream_seek(base, off, WMEMSTREAM_MAX, &npos)) { + errno = EINVAL; + return (-1); + } + + wmemp->wmstr_pos = npos; + *wmemp->wmstr_usizep = MIN(wmemp->wmstr_pos, wmemp->wmstr_lsize); + + return ((off_t)wmemp->wmstr_pos); +} + +static int +open_wmemstream_close(FILE *iop) +{ + wmemstream_t *wmemp = _xdata(iop); + free(wmemp); + _xunassoc(iop); + return (0); +} + + +FILE * +open_wmemstream(wchar_t **bufp, size_t *sizep) +{ + int err; + FILE *iop; + wmemstream_t *wmemp; + + if (bufp == NULL || sizep == NULL) { + errno = EINVAL; + return (NULL); + } + + wmemp = calloc(1, sizeof (wmemstream_t)); + if (wmemp == NULL) { + return (NULL); + } + + wmemp->wmstr_alloc = BUFSIZ; + wmemp->wmstr_buf = calloc(wmemp->wmstr_alloc, sizeof (wchar_t)); + if (wmemp->wmstr_buf == NULL) { + goto cleanup; + } + wmemp->wmstr_buf[0] = L'\0'; + wmemp->wmstr_pos = 0; + wmemp->wmstr_lsize = 0; + wmemp->wmstr_ubufp = bufp; + wmemp->wmstr_usizep = sizep; + + iop = _findiop(); + if (iop == NULL) { + goto cleanup; + } + +#ifdef _LP64 + iop->_flag = (iop->_flag & ~_DEF_FLAG_MASK) | _IOWRT; +#else + iop->_flag = _IOWRT; +#endif + + /* + * Update the user pointers now, in case a call to fflush() happens + * immediately. + */ + + if (_xassoc(iop, open_wmemstream_read, open_wmemstream_write, + open_wmemstream_seek, open_wmemstream_close, wmemp) != 0) { + goto cleanup; + } + _setorientation(iop, _WC_MODE); + SET_SEEKABLE(iop); + + *wmemp->wmstr_ubufp = wmemp->wmstr_buf; + *wmemp->wmstr_usizep = MIN(wmemp->wmstr_pos, wmemp->wmstr_lsize); + + return (iop); + +cleanup: + free(wmemp->wmstr_buf); + free(wmemp); + return (NULL); +} diff --git a/usr/src/lib/libc/port/stdio/putc.c b/usr/src/lib/libc/port/stdio/putc.c index 05383201a3..df74a0cf89 100644 --- a/usr/src/lib/libc/port/stdio/putc.c +++ b/usr/src/lib/libc/port/stdio/putc.c @@ -25,9 +25,7 @@ */ /* Copyright (c) 1988 AT&T */ -/* All Rights Reserved */ - -#pragma ident "%Z%%M% %I% %E% SMI" +/* All Rights Reserved */ #pragma weak _putc_unlocked = putc_unlocked @@ -51,22 +49,24 @@ putc(int ch, FILE *iop) int ret; FLOCKFILE(lk, iop); + ret = putc_unlocked(ch, iop); + FUNLOCKFILE(lk); + return (ret); +} +int +putc_unlocked(int ch, FILE *iop) +{ _SET_ORIENTATION_BYTE(iop); if (--iop->_cnt < 0) - ret = __flsbuf((unsigned char) ch, iop); - else { - (*iop->_ptr++) = (unsigned char)ch; - ret = (unsigned char)ch; - } - FUNLOCKFILE(lk); - return (ret); + return (__flsbuf((unsigned char) ch, iop)); + else + return (*iop->_ptr++ = (unsigned char)ch); } - int -putc_unlocked(int ch, FILE *iop) +_putc_internal(int ch, FILE *iop) { if (--iop->_cnt < 0) return (__flsbuf((unsigned char) ch, iop)); diff --git a/usr/src/lib/libc/port/stdio/putw.c b/usr/src/lib/libc/port/stdio/putw.c index 043125e113..70848e6a06 100644 --- a/usr/src/lib/libc/port/stdio/putw.c +++ b/usr/src/lib/libc/port/stdio/putw.c @@ -25,9 +25,7 @@ */ /* Copyright (c) 1988 AT&T */ -/* All Rights Reserved */ - -#pragma ident "%Z%%M% %I% %E% SMI" +/* All Rights Reserved */ /* * The intent here is to provide a means to make the order of @@ -54,7 +52,7 @@ putw(int w, FILE *stream) rmutex_t *lk; FLOCKFILE(lk, stream); - while (--i >= 0 && PUTC(*s++, stream) != EOF) + while (--i >= 0 && putc_unlocked(*s++, stream) != EOF) ; ret = stream->_flag & _IOERR; FUNLOCKFILE(lk); diff --git a/usr/src/lib/libc/port/stdio/rewind.c b/usr/src/lib/libc/port/stdio/rewind.c index 57e4cc2540..c7da173ec3 100644 --- a/usr/src/lib/libc/port/stdio/rewind.c +++ b/usr/src/lib/libc/port/stdio/rewind.c @@ -25,9 +25,7 @@ */ /* Copyright (c) 1988 AT&T */ -/* All Rights Reserved */ - -#pragma ident "%Z%%M% %I% %E% SMI" +/* All Rights Reserved */ #include "lint.h" #include "file64.h" @@ -55,7 +53,7 @@ void _rewind_unlocked(FILE *iop) { (void) _fflush_u(iop); - (void) lseek64(FILENO(iop), 0, SEEK_SET); + (void) _xseek64(iop, 0, SEEK_SET); iop->_cnt = 0; iop->_ptr = iop->_base; iop->_flag &= ~(_IOERR | _IOEOF); diff --git a/usr/src/lib/libc/port/stdio/setbuf.c b/usr/src/lib/libc/port/stdio/setbuf.c index 78c4c8cc76..10eb21d5b5 100644 --- a/usr/src/lib/libc/port/stdio/setbuf.c +++ b/usr/src/lib/libc/port/stdio/setbuf.c @@ -25,9 +25,11 @@ */ /* Copyright (c) 1988 AT&T */ -/* All Rights Reserved */ +/* All Rights Reserved */ -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright 2020 Robert Mustacchi + */ #include "lint.h" #include "file64.h" @@ -39,41 +41,37 @@ #include <synch.h> #include "stdiom.h" - void setbuf(FILE *iop, char *abuf) { Uchar *buf = (Uchar *)abuf; - int fno = GET_FD(iop); /* file number */ + int fno = _get_fd(iop); /* file number */ int size = BUFSIZ - _SMBFSZ; Uchar *temp; rmutex_t *lk; FLOCKFILE(lk, iop); - if ((iop->_base != 0) && (iop->_flag & _IOMYBUF)) + if ((iop->_base != NULL) && (iop->_flag & _IOMYBUF)) free((char *)iop->_base - PUSHBACK); iop->_flag &= ~(_IOMYBUF | _IONBF | _IOLBF); - if (buf == 0) { + if (buf == NULL) { iop->_flag |= _IONBF; -#ifndef _STDIO_ALLOCATE - if (fno < 2) { + if (fno == 0 || fno == 1) { /* use special buffer for std{in,out} */ buf = (fno == 0) ? _sibuf : _sobuf; - } else /* needed for ifndef */ -#endif - if (fno < _NFILE) { + } else if (fno >= 2 && fno < _NFILE) { buf = _smbuf[fno]; size = _SMBFSZ - PUSHBACK; - } else - if ((buf = (Uchar *)malloc(_SMBFSZ * sizeof (Uchar))) != 0) { + } else if ((buf = (Uchar *)malloc(_SMBFSZ * + sizeof (Uchar))) != NULL) { iop->_flag |= _IOMYBUF; size = _SMBFSZ - PUSHBACK; } } else { /* regular buffered I/O, standard buffer size */ - if (isatty(fno)) + if (fno != -1 && isatty(fno)) iop->_flag |= _IOLBF; } - if (buf == 0) { + if (buf == NULL) { FUNLOCKFILE(lk); return; /* malloc() failed */ } diff --git a/usr/src/lib/libc/port/stdio/setvbuf.c b/usr/src/lib/libc/port/stdio/setvbuf.c index a6e63e09f7..936466f893 100644 --- a/usr/src/lib/libc/port/stdio/setvbuf.c +++ b/usr/src/lib/libc/port/stdio/setvbuf.c @@ -25,9 +25,7 @@ */ /* Copyright (c) 1988 AT&T */ -/* All Rights Reserved */ - -#pragma ident "%Z%%M% %I% %E% SMI" +/* All Rights Reserved */ #include "lint.h" #include "file64.h" @@ -47,7 +45,7 @@ setvbuf(FILE *iop, char *abuf, int type, size_t size) Uchar *temp; int sflag = iop->_flag & _IOMYBUF; rmutex_t *lk; - int fd = GET_FD(iop); + int fd = _get_fd(iop); FLOCKFILE(lk, iop); iop->_flag &= ~(_IOMYBUF | _IONBF | _IOLBF); @@ -55,17 +53,14 @@ setvbuf(FILE *iop, char *abuf, int type, size_t size) /* note that the flags are the same as the possible values for type */ case _IONBF: iop->_flag |= _IONBF; /* file is unbuffered */ -#ifndef _STDIO_ALLOCATE - if (fd < 2) { + if (fd == 0 || fd == 1) { /* use special buffer for std{in,out} */ buf = (fd == 0) ? _sibuf : _sobuf; size = BUFSIZ; - } else /* needed for ifdef */ -#endif - if (fd < _NFILE) { + } else if (fd >= 2 && fd < _NFILE) { buf = _smbuf[fd]; size = _SMBFSZ - PUSHBACK; - } else + } else { if ((buf = malloc(_SMBFSZ * sizeof (Uchar))) != NULL) { iop->_flag |= _IOMYBUF; size = _SMBFSZ - PUSHBACK; @@ -73,6 +68,7 @@ setvbuf(FILE *iop, char *abuf, int type, size_t size) FUNLOCKFILE(lk); return (EOF); } + } break; case _IOLBF: case _IOFBF: diff --git a/usr/src/lib/libc/sparc/Makefile.com b/usr/src/lib/libc/sparc/Makefile.com index e48562f400..ee580e8513 100644 --- a/usr/src/lib/libc/sparc/Makefile.com +++ b/usr/src/lib/libc/sparc/Makefile.com @@ -715,6 +715,7 @@ PORTSTDIO= \ _filbuf.o \ _findbuf.o \ _flsbuf.o \ + _stdio_flags.o \ _wrtchk.o \ clearerr.o \ ctermid.o \ @@ -730,6 +731,7 @@ PORTSTDIO= \ fileno.o \ flockf.o \ flush.o \ + fmemopen.o \ fopen.o \ fpos.o \ fputc.o \ @@ -746,6 +748,9 @@ PORTSTDIO= \ getpass.o \ gets.o \ getw.o \ + mse.o \ + open_memstream.o \ + open_wmemstream.o \ popen.o \ putc.o \ putchar.o \ @@ -761,7 +766,6 @@ PORTSTDIO= \ tmpfile.o \ tmpnam_r.o \ ungetc.o \ - mse.o \ vscanf.o \ vwscanf.o \ wscanf.o diff --git a/usr/src/lib/libc/sparcv9/Makefile.com b/usr/src/lib/libc/sparcv9/Makefile.com index d9bfef117f..3aacce8f61 100644 --- a/usr/src/lib/libc/sparcv9/Makefile.com +++ b/usr/src/lib/libc/sparcv9/Makefile.com @@ -658,6 +658,7 @@ PORTSTDIO= \ _filbuf.o \ _findbuf.o \ _flsbuf.o \ + _stdio_flags.o \ _wrtchk.o \ clearerr.o \ ctermid.o \ @@ -673,6 +674,7 @@ PORTSTDIO= \ fileno.o \ flockf.o \ flush.o \ + fmemopen.o \ fopen.o \ fpos.o \ fputc.o \ @@ -689,6 +691,9 @@ PORTSTDIO= \ getpass.o \ gets.o \ getw.o \ + mse.o \ + open_memstream.o \ + open_wmemstream.o \ popen.o \ putc.o \ putchar.o \ @@ -704,7 +709,6 @@ PORTSTDIO= \ tmpfile.o \ tmpnam_r.o \ ungetc.o \ - mse.o \ vscanf.o \ vwscanf.o \ wscanf.o |