yuezonghe | 824eb0c | 2024-06-27 02:32:26 -0700 | [diff] [blame^] | 1 | /* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> |
| 2 | * |
| 3 | * GNU Library General Public License (LGPL) version 2 or later. |
| 4 | * |
| 5 | * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. |
| 6 | */ |
| 7 | |
| 8 | #include "_stdio.h" |
| 9 | |
| 10 | |
| 11 | /* |
| 12 | * Cases: |
| 13 | * fopen64 : filename != NULL, stream == NULL, filedes == -2 |
| 14 | * fopen : filename != NULL, stream == NULL, filedes == -1 |
| 15 | * freopen : filename != NULL, stream != NULL, filedes == -1 |
| 16 | * fdopen : filename == NULL, stream == NULL, filedes valid |
| 17 | * |
| 18 | * fsfopen : filename != NULL, stream != NULL, filedes == -1 |
| 19 | */ |
| 20 | |
| 21 | #if (O_ACCMODE != 3) || (O_RDONLY != 0) || (O_WRONLY != 1) || (O_RDWR != 2) |
| 22 | #error Assumption violated - mode constants |
| 23 | #endif |
| 24 | |
| 25 | #ifndef O_LARGEFILE |
| 26 | #define O_LARGEFILE 0 |
| 27 | #endif |
| 28 | |
| 29 | /* Internal function -- reentrant (locks open file list) */ |
| 30 | |
| 31 | FILE attribute_hidden *_stdio_fopen(intptr_t fname_or_mode, |
| 32 | register const char * __restrict mode, |
| 33 | register FILE * __restrict stream, int filedes) |
| 34 | { |
| 35 | __mode_t open_mode; |
| 36 | int i; |
| 37 | |
| 38 | /* Parse the specified mode. */ |
| 39 | open_mode = O_RDONLY; |
| 40 | if (*mode != 'r') { /* Not read... */ |
| 41 | open_mode = (O_WRONLY | O_CREAT | O_TRUNC); |
| 42 | if (*mode != 'w') { /* Not write (create or truncate)... */ |
| 43 | open_mode = (O_WRONLY | O_CREAT | O_APPEND); |
| 44 | if (*mode != 'a') { /* Not write (create or append)... */ |
| 45 | DO_EINVAL: |
| 46 | __set_errno(EINVAL); /* So illegal mode. */ |
| 47 | if (stream) { |
| 48 | FREE_STREAM: |
| 49 | assert(!(stream->__modeflags & __FLAG_FREEBUF)); |
| 50 | __STDIO_STREAM_FREE_FILE(stream); |
| 51 | } |
| 52 | return NULL; |
| 53 | } |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | if ((mode[1] == 'b')) { /* Binary mode (NO-OP currently). */ |
| 58 | ++mode; |
| 59 | } |
| 60 | |
| 61 | if (mode[1] == '+') { /* Read and Write. */ |
| 62 | ++mode; |
| 63 | open_mode |= (O_RDONLY | O_WRONLY); |
| 64 | open_mode += (O_RDWR - (O_RDONLY | O_WRONLY)); |
| 65 | } |
| 66 | |
| 67 | #ifdef __UCLIBC_MJN3_ONLY__ |
| 68 | #warning CONSIDER: Implement glibc ccs option to bind a codeset? |
| 69 | #warning CONSIDER: Implement glibc mmap option for readonly files? |
| 70 | #warning CONSIDER: Implement a text mode using custom read/write funcs? |
| 71 | #endif |
| 72 | #if defined(__UCLIBC_HAS_FOPEN_EXCLUSIVE_MODE__) || defined(__UCLIBC_HAS_FOPEN_LARGEFILE_MODE__) || \ |
| 73 | defined(__UCLIBC_HAS_FOPEN_CLOSEEXEC_MODE__) |
| 74 | |
| 75 | while (*++mode) { |
| 76 | # ifdef __UCLIBC_HAS_FOPEN_EXCLUSIVE_MODE__ |
| 77 | if (*mode == 'x') { /* Open exclusive (a glibc extension). */ |
| 78 | open_mode |= O_EXCL; |
| 79 | continue; |
| 80 | } |
| 81 | # endif |
| 82 | # ifdef __UCLIBC_HAS_FOPEN_LARGEFILE_MODE__ |
| 83 | if (*mode == 'F') { /* Open as large file (uClibc extension). */ |
| 84 | open_mode |= O_LARGEFILE; |
| 85 | continue; |
| 86 | } |
| 87 | # endif |
| 88 | # ifdef __UCLIBC_HAS_FOPEN_CLOSEEXEC_MODE__ |
| 89 | if (*mode == 'e') { /* Close on exec (a glibc extension). */ |
| 90 | open_mode |= O_CLOEXEC; |
| 91 | continue; |
| 92 | } |
| 93 | # endif |
| 94 | } |
| 95 | |
| 96 | #endif |
| 97 | |
| 98 | if (!stream) { /* Need to allocate a FILE (not freopen). */ |
| 99 | if ((stream = malloc(sizeof(FILE))) == NULL) { |
| 100 | return stream; |
| 101 | } |
| 102 | stream->__modeflags = __FLAG_FREEFILE; |
| 103 | #ifdef __STDIO_BUFFERS |
| 104 | stream->__bufstart = NULL; /* We allocate a buffer below. */ |
| 105 | #endif |
| 106 | #ifdef __UCLIBC_HAS_THREADS__ |
| 107 | /* We only initialize the mutex in the non-freopen case. */ |
| 108 | /* stream->__user_locking = _stdio_user_locking; */ |
| 109 | STDIO_INIT_MUTEX(stream->__lock); |
| 110 | #endif |
| 111 | } |
| 112 | |
| 113 | #ifdef __UCLIBC_MJN3_ONLY__ |
| 114 | #warning TODO: Verify fdopen append behavior of glibc. |
| 115 | #endif |
| 116 | |
| 117 | if (filedes >= 0) { /* Handle fdopen trickery. */ |
| 118 | stream->__filedes = filedes; |
| 119 | /* NOTE: it is insufficient to just check R/W/RW agreement. |
| 120 | * We must also check largefile compatibility if applicable. |
| 121 | * Also, if append mode is desired for fdopen but O_APPEND isn't |
| 122 | * currently set, then set it as recommended by SUSv3. However, |
| 123 | * if append mode is not specified for fdopen but O_APPEND is set, |
| 124 | * leave it set (glibc compat). */ |
| 125 | i = (open_mode & (O_ACCMODE|O_LARGEFILE)) + 1; |
| 126 | |
| 127 | /* NOTE: fopencookie needs changing if the basic check changes! */ |
| 128 | if ((i & ((int)fname_or_mode + 1)) != i) /* Basic agreement? */ |
| 129 | goto DO_EINVAL; |
| 130 | if ((open_mode & ~(__mode_t)fname_or_mode) & O_APPEND) { |
| 131 | if (fcntl(filedes, F_SETFL, O_APPEND)) /* Need O_APPEND. */ |
| 132 | goto DO_EINVAL; |
| 133 | } |
| 134 | /* For later... to reflect largefile setting in stream flags. */ |
| 135 | __STDIO_WHEN_LFS( open_mode |= (((__mode_t) fname_or_mode) |
| 136 | & O_LARGEFILE) ); |
| 137 | } else { |
| 138 | __STDIO_WHEN_LFS( if (filedes < -1) open_mode |= O_LARGEFILE ); |
| 139 | if ((stream->__filedes = open(((const char *) fname_or_mode), |
| 140 | open_mode, 0666)) < 0) { |
| 141 | goto FREE_STREAM; |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | stream->__modeflags &= __FLAG_FREEFILE; |
| 146 | /* stream->__modeflags &= ~(__FLAG_READONLY|__FLAG_WRITEONLY); */ |
| 147 | |
| 148 | stream->__modeflags |= /* Note: Makes assumptions about flag vals! */ |
| 149 | #if (O_APPEND != __FLAG_APPEND) || ((O_LARGEFILE != __FLAG_LARGEFILE) && (O_LARGEFILE != 0)) |
| 150 | # if (O_APPEND != __FLAG_APPEND) |
| 151 | ((open_mode & O_APPEND) ? __FLAG_APPEND : 0) | |
| 152 | # else |
| 153 | (open_mode & O_APPEND) | |
| 154 | # endif |
| 155 | # if (O_LARGEFILE != __FLAG_LARGEFILE) && (O_LARGEFILE != 0) |
| 156 | ((open_mode & O_LARGEFILE) ? __FLAG_LARGEFILE : 0) | |
| 157 | # else |
| 158 | (open_mode & O_LARGEFILE) | |
| 159 | # endif |
| 160 | #else |
| 161 | (open_mode & (O_APPEND|O_LARGEFILE)) | /* i386 linux and elks */ |
| 162 | #endif |
| 163 | ((((open_mode & O_ACCMODE) + 1) ^ 0x03) * __FLAG_WRITEONLY); |
| 164 | |
| 165 | #ifdef __STDIO_BUFFERS |
| 166 | if (stream->__filedes != INT_MAX) { |
| 167 | /* NB: fopencookie uses bogus filedes == INT_MAX, |
| 168 | * avoiding isatty() in that case. |
| 169 | */ |
| 170 | i = errno; /* preserve errno against isatty call. */ |
| 171 | if (isatty(stream->__filedes)) |
| 172 | stream->__modeflags |= __FLAG_LBF; |
| 173 | __set_errno(i); |
| 174 | } |
| 175 | |
| 176 | if (!stream->__bufstart) { |
| 177 | if ((stream->__bufstart = malloc(BUFSIZ)) != NULL) { |
| 178 | stream->__bufend = stream->__bufstart + BUFSIZ; |
| 179 | stream->__modeflags |= __FLAG_FREEBUF; |
| 180 | } else { |
| 181 | # if __STDIO_BUILTIN_BUF_SIZE > 0 |
| 182 | stream->__bufstart = stream->__builtinbuf; |
| 183 | stream->__bufend = stream->__builtinbuf + sizeof(stream->__builtinbuf); |
| 184 | # else /* __STDIO_BUILTIN_BUF_SIZE > 0 */ |
| 185 | stream->__bufend = stream->__bufstart; |
| 186 | # endif /* __STDIO_BUILTIN_BUF_SIZE > 0 */ |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | __STDIO_STREAM_DISABLE_GETC(stream); |
| 191 | __STDIO_STREAM_DISABLE_PUTC(stream); |
| 192 | __STDIO_STREAM_INIT_BUFREAD_BUFPOS(stream); |
| 193 | #endif |
| 194 | |
| 195 | __STDIO_STREAM_RESET_GCS(stream); |
| 196 | |
| 197 | #ifdef __UCLIBC_HAS_WCHAR__ |
| 198 | stream->__ungot_width[0] = 0; |
| 199 | #endif |
| 200 | #ifdef __STDIO_MBSTATE |
| 201 | __INIT_MBSTATE(&(stream->__state)); |
| 202 | #endif |
| 203 | |
| 204 | #ifdef __UCLIBC_HAS_THREADS__ |
| 205 | /* Even in the freopen case, we reset the user locking flag. */ |
| 206 | stream->__user_locking = _stdio_user_locking; |
| 207 | /* STDIO_INIT_MUTEX(stream->__lock); */ |
| 208 | #endif |
| 209 | |
| 210 | #ifdef __STDIO_HAS_OPENLIST |
| 211 | #if defined(__UCLIBC_HAS_THREADS__) && defined(__STDIO_BUFFERS) |
| 212 | if (!(stream->__modeflags & __FLAG_FREEFILE)) |
| 213 | { |
| 214 | /* An freopen call so the file was never removed from the list. */ |
| 215 | } |
| 216 | else |
| 217 | #endif |
| 218 | { |
| 219 | /* We have to lock the del mutex in case another thread wants to fclose() |
| 220 | * the last file. */ |
| 221 | __STDIO_THREADLOCK_OPENLIST_DEL; |
| 222 | __STDIO_THREADLOCK_OPENLIST_ADD; |
| 223 | stream->__nextopen = _stdio_openlist; /* New files are inserted at */ |
| 224 | _stdio_openlist = stream; /* the head of the list. */ |
| 225 | __STDIO_THREADUNLOCK_OPENLIST_ADD; |
| 226 | __STDIO_THREADUNLOCK_OPENLIST_DEL; |
| 227 | } |
| 228 | #endif |
| 229 | |
| 230 | __STDIO_STREAM_VALIDATE(stream); |
| 231 | |
| 232 | return stream; |
| 233 | } |