blob: 1066d7fc937610db924163cc0d74fcddcc6048a5 [file] [log] [blame]
Dmitriy Ivanov623b0d02014-05-14 23:11:05 -07001/* $OpenBSD: findfp.c,v 1.15 2013/12/17 16:33:27 deraadt Exp $ */
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08002/*-
3 * Copyright (c) 1990, 1993
4 * The Regents of the University of California. All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * Chris Torek.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080034#include <stdio.h>
Elliott Hughes021335e2016-01-19 16:28:15 -080035
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080036#include <errno.h>
Elliott Hughes021335e2016-01-19 16:28:15 -080037#include <fcntl.h>
Elliott Hughes2704bd12016-01-20 17:14:53 -080038#include <limits.h>
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080039#include <stdlib.h>
40#include <string.h>
Elliott Hughes021335e2016-01-19 16:28:15 -080041#include <sys/param.h>
42#include <unistd.h>
43
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080044#include "local.h"
45#include "glue.h"
Elliott Hughes6a03abc2014-11-03 12:32:17 -080046#include "private/thread_private.h"
47
48#define ALIGNBYTES (sizeof(uintptr_t) - 1)
49#define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) &~ ALIGNBYTES)
Calin Juravlec20de902014-03-20 15:21:32 +000050
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080051#define NDYNAMIC 10 /* add ten more whenever necessary */
52
53#define std(flags, file) \
Chih-Hung Hsiehdc6599e2014-11-04 12:09:35 -080054 {0,0,0,flags,file,{0,0},0,__sF+file,__sclose,__sread,__sseek,__swrite, \
Elliott Hughes2704bd12016-01-20 17:14:53 -080055 {(unsigned char *)(__sFext+file), 0},NULL,0,{0},{0},{0,0},0}
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080056
Kenny Rootf5823402011-02-12 07:13:44 -080057_THREAD_PRIVATE_MUTEX(__sfp_mutex);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080058
Elliott Hughes29ee6392015-12-07 11:07:15 -080059// TODO: when we no longer have to support both clang and GCC, we can simplify all this.
60#define SBUF_INIT {0,0}
61#if defined(__LP64__)
62#define MBSTATE_T_INIT {{0},{0}}
63#else
64#define MBSTATE_T_INIT {{0}}
65#endif
66#define WCHAR_IO_DATA_INIT {MBSTATE_T_INIT,MBSTATE_T_INIT,{0},0,0}
67
Elliott Hughesbb46afd2015-12-04 18:03:12 -080068static struct __sfileext __sFext[3] = {
Elliott Hughes29ee6392015-12-07 11:07:15 -080069 { SBUF_INIT, WCHAR_IO_DATA_INIT, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, false },
70 { SBUF_INIT, WCHAR_IO_DATA_INIT, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, false },
71 { SBUF_INIT, WCHAR_IO_DATA_INIT, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, false },
Elliott Hughesbb46afd2015-12-04 18:03:12 -080072};
Elliott Hughesf0141df2015-10-12 12:44:23 -070073
74// __sF is exported for backwards compatibility. Until M, we didn't have symbols
75// for stdin/stdout/stderr; they were macros accessing __sF.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080076FILE __sF[3] = {
Elliott Hughesbb46afd2015-12-04 18:03:12 -080077 std(__SRD, STDIN_FILENO),
78 std(__SWR, STDOUT_FILENO),
79 std(__SWR|__SNBF, STDERR_FILENO),
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080080};
Elliott Hughesf0141df2015-10-12 12:44:23 -070081
Elliott Hughes168667c2014-11-14 14:42:59 -080082FILE* stdin = &__sF[0];
83FILE* stdout = &__sF[1];
84FILE* stderr = &__sF[2];
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080085
Elliott Hughesbb46afd2015-12-04 18:03:12 -080086struct glue __sglue = { NULL, 3, __sF };
87static struct glue* lastglue = &__sglue;
88
Elliott Hughes2704bd12016-01-20 17:14:53 -080089class ScopedFileLock {
90 public:
91 ScopedFileLock(FILE* fp) : fp_(fp) {
92 FLOCKFILE(fp_);
93 }
94 ~ScopedFileLock() {
95 FUNLOCKFILE(fp_);
96 }
97
98 private:
99 FILE* fp_;
100};
101
Elliott Hughes021335e2016-01-19 16:28:15 -0800102static glue* moreglue(int n) {
Elliott Hughes2704bd12016-01-20 17:14:53 -0800103 static FILE empty;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800104
Elliott Hughes2704bd12016-01-20 17:14:53 -0800105 char* data = new char[sizeof(glue) + ALIGNBYTES + n * sizeof(FILE) + n * sizeof(__sfileext)];
106 if (data == nullptr) return nullptr;
Elliott Hughes021335e2016-01-19 16:28:15 -0800107
Elliott Hughes2704bd12016-01-20 17:14:53 -0800108 glue* g = reinterpret_cast<glue*>(data);
109 FILE* p = reinterpret_cast<FILE*>(ALIGN(data + sizeof(*g)));
110 __sfileext* pext = reinterpret_cast<__sfileext*>(ALIGN(data + sizeof(*g)) + n * sizeof(FILE));
111 g->next = NULL;
112 g->niobs = n;
113 g->iobs = p;
114 while (--n >= 0) {
115 *p = empty;
116 _FILEEXT_SETUP(p, pext);
117 p++;
118 pext++;
119 }
120 return g;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800121}
122
123/*
124 * Find a free FILE for fopen et al.
125 */
Elliott Hughes021335e2016-01-19 16:28:15 -0800126FILE* __sfp(void) {
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800127 FILE *fp;
128 int n;
129 struct glue *g;
130
Kenny Rootf5823402011-02-12 07:13:44 -0800131 _THREAD_PRIVATE_MUTEX_LOCK(__sfp_mutex);
132 for (g = &__sglue; g != NULL; g = g->next) {
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800133 for (fp = g->iobs, n = g->niobs; --n >= 0; fp++)
134 if (fp->_flags == 0)
135 goto found;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800136 }
Kenny Rootf5823402011-02-12 07:13:44 -0800137
138 /* release lock while mallocing */
139 _THREAD_PRIVATE_MUTEX_UNLOCK(__sfp_mutex);
140 if ((g = moreglue(NDYNAMIC)) == NULL)
141 return (NULL);
142 _THREAD_PRIVATE_MUTEX_LOCK(__sfp_mutex);
143 lastglue->next = g;
144 lastglue = g;
145 fp = g->iobs;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800146found:
147 fp->_flags = 1; /* reserve this slot; caller sets real flags */
Kenny Rootf5823402011-02-12 07:13:44 -0800148 _THREAD_PRIVATE_MUTEX_UNLOCK(__sfp_mutex);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800149 fp->_p = NULL; /* no current pointer */
150 fp->_w = 0; /* nothing to read or write */
151 fp->_r = 0;
152 fp->_bf._base = NULL; /* no buffer */
153 fp->_bf._size = 0;
154 fp->_lbfsize = 0; /* not line buffered */
155 fp->_file = -1; /* no file */
156/* fp->_cookie = <any>; */ /* caller sets cookie, _read/_write etc */
157 fp->_lb._base = NULL; /* no line buffer */
158 fp->_lb._size = 0;
159 _FILEEXT_INIT(fp);
160 return (fp);
161}
162
Elliott Hughes021335e2016-01-19 16:28:15 -0800163extern "C" __LIBC_HIDDEN__ void __libc_stdio_cleanup(void) {
Elliott Hughes2704bd12016-01-20 17:14:53 -0800164 // Equivalent to fflush(nullptr), but without all the locking since we're shutting down anyway.
165 _fwalk(__sflush);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800166}
Elliott Hughes923f1652016-01-19 15:46:05 -0800167
168int fclose(FILE* fp) {
Elliott Hughes2704bd12016-01-20 17:14:53 -0800169 if (fp->_flags == 0) {
170 // Already freed!
171 errno = EBADF;
172 return EOF;
173 }
Elliott Hughes923f1652016-01-19 15:46:05 -0800174
Elliott Hughes2704bd12016-01-20 17:14:53 -0800175 ScopedFileLock sfl(fp);
176 WCIO_FREE(fp);
177 int r = fp->_flags & __SWR ? __sflush(fp) : 0;
178 if (fp->_close != NULL && (*fp->_close)(fp->_cookie) < 0) {
179 r = EOF;
180 }
181 if (fp->_flags & __SMBF) free(fp->_bf._base);
182 if (HASUB(fp)) FREEUB(fp);
183 if (HASLB(fp)) FREELB(fp);
Elliott Hughes923f1652016-01-19 15:46:05 -0800184
Elliott Hughes2704bd12016-01-20 17:14:53 -0800185 // Poison this FILE so accesses after fclose will be obvious.
186 fp->_file = -1;
187 fp->_r = fp->_w = 0;
Elliott Hughes923f1652016-01-19 15:46:05 -0800188
Elliott Hughes2704bd12016-01-20 17:14:53 -0800189 // Release this FILE for reuse.
190 fp->_flags = 0;
191 return r;
Elliott Hughes923f1652016-01-19 15:46:05 -0800192}
193
194int fileno(FILE* fp) {
Elliott Hughes2704bd12016-01-20 17:14:53 -0800195 ScopedFileLock sfl(fp);
196 return fileno_unlocked(fp);
Elliott Hughes923f1652016-01-19 15:46:05 -0800197}
Elliott Hughes021335e2016-01-19 16:28:15 -0800198
Elliott Hughes021335e2016-01-19 16:28:15 -0800199int __sread(void* cookie, char* buf, int n) {
Elliott Hughes2704bd12016-01-20 17:14:53 -0800200 FILE* fp = reinterpret_cast<FILE*>(cookie);
201 return TEMP_FAILURE_RETRY(read(fp->_file, buf, n));
Elliott Hughes021335e2016-01-19 16:28:15 -0800202}
203
204int __swrite(void* cookie, const char* buf, int n) {
Elliott Hughes2704bd12016-01-20 17:14:53 -0800205 FILE* fp = reinterpret_cast<FILE*>(cookie);
206 if (fp->_flags & __SAPP) {
207 // The FILE* is in append mode, but the underlying fd doesn't have O_APPEND set.
208 // We need to seek manually.
209 // TODO: can we use fcntl(2) to set O_APPEND in fdopen(3) instead?
210 TEMP_FAILURE_RETRY(lseek64(fp->_file, 0, SEEK_END));
211 }
212 return TEMP_FAILURE_RETRY(write(fp->_file, buf, n));
Elliott Hughes021335e2016-01-19 16:28:15 -0800213}
214
215// TODO: _FILE_OFFSET_BITS=64.
216fpos_t __sseek(void* cookie, fpos_t offset, int whence) {
Elliott Hughes2704bd12016-01-20 17:14:53 -0800217 FILE* fp = reinterpret_cast<FILE*>(cookie);
218 return TEMP_FAILURE_RETRY(lseek(fp->_file, offset, whence));
Elliott Hughes021335e2016-01-19 16:28:15 -0800219}
220
221int __sclose(void* cookie) {
Elliott Hughes2704bd12016-01-20 17:14:53 -0800222 FILE* fp = reinterpret_cast<FILE*>(cookie);
223 return close(fp->_file);
224}
225
226static bool __file_is_seekable(FILE* fp) {
227 if (fp->_seek == nullptr) {
228 errno = ESPIPE; // Historic practice.
229 return false;
230 }
231 return true;
232}
233
234// TODO: _FILE_OFFSET_BITS=64.
235static off_t __ftello_unlocked(FILE* fp) {
236 if (!__file_is_seekable(fp)) return -1;
237
238 // Find offset of underlying I/O object, then
239 // adjust for buffered bytes.
240 __sflush(fp); // May adjust seek offset on append stream.
241 fpos_t result = (*fp->_seek)(fp->_cookie, 0, SEEK_CUR);
242 if (result == -1) {
243 return -1;
244 }
245
246 if (fp->_flags & __SRD) {
247 // Reading. Any unread characters (including
248 // those from ungetc) cause the position to be
249 // smaller than that in the underlying object.
250 result -= fp->_r;
251 if (HASUB(fp)) result -= fp->_ur;
252 } else if (fp->_flags & __SWR && fp->_p != NULL) {
253 // Writing. Any buffered characters cause the
254 // position to be greater than that in the
255 // underlying object.
256 result += fp->_p - fp->_bf._base;
257 }
258 return result;
259}
260
261int fseeko(FILE* fp, off_t offset, int whence) {
262 ScopedFileLock sfl(fp);
263
264 if (!__file_is_seekable(fp)) return -1;
265
266 // Change any SEEK_CUR to SEEK_SET, and check `whence' argument.
267 // After this, whence is either SEEK_SET or SEEK_END.
268 if (whence == SEEK_CUR) {
269 fpos_t current_offset = __ftello_unlocked(fp);
270 if (current_offset == -1) {
271 return EOF;
272 }
273 offset += current_offset;
274 whence = SEEK_SET;
275 } else if (whence != SEEK_SET && whence != SEEK_END) {
276 errno = EINVAL;
277 return EOF;
278 }
279
280 if (fp->_bf._base == NULL) __smakebuf(fp);
281
282 // Flush unwritten data and attempt the seek.
283 if (__sflush(fp) || (*fp->_seek)(fp->_cookie, offset, whence) == -1) {
284 return EOF;
285 }
286
287 // Success: clear EOF indicator and discard ungetc() data.
288 if (HASUB(fp)) FREEUB(fp);
289 fp->_p = fp->_bf._base;
290 fp->_r = 0;
291 /* fp->_w = 0; */ /* unnecessary (I think...) */
292 fp->_flags &= ~__SEOF;
293 return 0;
294}
295
296// TODO: _FILE_OFFSET_BITS=64.
297int fseek(FILE* fp, long offset, int whence) {
298 return fseeko(fp, offset, whence);
299}
300
301// TODO: _FILE_OFFSET_BITS=64.
302off_t ftello(FILE* fp) {
303 ScopedFileLock sfl(fp);
304 return __ftello_unlocked(fp);
305}
306
307// TODO: _FILE_OFFSET_BITS=64
308long ftell(FILE* fp) {
309 off_t offset = ftello(fp);
310 if (offset > LONG_MAX) {
311 errno = EOVERFLOW;
312 return -1;
313 }
314 return offset;
Elliott Hughes021335e2016-01-19 16:28:15 -0800315}