1 /*
2 Unix SMB/CIFS implementation.
3 stdio replacement
4 Copyright (C) Andrew Tridgell 2001
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 /**
22 * @file
23 * @brief scalable FILE replacement
24 */
25
26 /*
27 stdio is very convenient, but on some systems the file descriptor
28 in FILE* is 8 bits, so it fails when more than 255 files are open.
29
30 XFILE replaces stdio. It is less efficient, but at least it works
31 when you have lots of files open
32
33 The main restriction on XFILE is that it doesn't support seeking,
34 and doesn't support O_RDWR. That keeps the code simple.
35 */
36
37 #include "includes.h"
38 #include "system/filesys.h"
39
40 #define XBUFSIZE BUFSIZ
41
42 static XFILE _x_stdin = { 0, NULL, NULL, XBUFSIZE, 0, O_RDONLY, X_IOFBF, 0 };
43 static XFILE _x_stdout = { 1, NULL, NULL, XBUFSIZE, 0, O_WRONLY, X_IOLBF, 0 };
44 static XFILE _x_stderr = { 2, NULL, NULL, 0, 0, O_WRONLY, X_IONBF, 0 };
45
46 XFILE *x_stdin = &_x_stdin;
47 XFILE *x_stdout = &_x_stdout;
48 XFILE *x_stderr = &_x_stderr;
49
50 #define X_FLAG_EOF 1
51 #define X_FLAG_ERROR 2
52 #define X_FLAG_EINVAL 3
53
54 /** simulate setvbuf() */
x_setvbuf(XFILE * f,char * buf,int mode,size_t size)55 int x_setvbuf(XFILE *f, char *buf, int mode, size_t size)
56 {
57 x_fflush(f);
58 if (f->bufused) return -1;
59
60 /* on files being read full buffering is the only option */
61 if ((f->open_flags & O_ACCMODE) == O_RDONLY) {
62 mode = X_IOFBF;
63 }
64
65 /* destroy any earlier buffer */
66 SAFE_FREE(f->buf);
67 f->buf = 0;
68 f->bufsize = 0;
69 f->next = NULL;
70 f->bufused = 0;
71 f->buftype = mode;
72
73 if (f->buftype == X_IONBF) return 0;
74
75 /* if buffering then we need some size */
76 if (size == 0) size = XBUFSIZE;
77
78 f->bufsize = size;
79 f->bufused = 0;
80
81 return 0;
82 }
83
84 /* allocate the buffer */
x_allocate_buffer(XFILE * f)85 static int x_allocate_buffer(XFILE *f)
86 {
87 if (f->buf) return 1;
88 if (f->bufsize == 0) return 0;
89 f->buf = malloc(f->bufsize);
90 if (!f->buf) return 0;
91 f->next = f->buf;
92 return 1;
93 }
94
95
96 /** this looks more like open() than fopen(), but that is quite deliberate.
97 I want programmers to *think* about O_EXCL, O_CREAT etc not just
98 get them magically added
99 */
x_fopen(const char * fname,int flags,mode_t mode)100 XFILE *x_fopen(const char *fname, int flags, mode_t mode)
101 {
102 XFILE *ret;
103
104 ret = malloc_p(XFILE);
105 if (!ret) return NULL;
106
107 memset(ret, 0, sizeof(XFILE));
108
109 if ((flags & O_ACCMODE) == O_RDWR) {
110 /* we don't support RDWR in XFILE - use file
111 descriptors instead */
112 errno = EINVAL;
113 return NULL;
114 }
115
116 ret->open_flags = flags;
117
118 ret->fd = open(fname, flags, mode);
119 if (ret->fd == -1) {
120 SAFE_FREE(ret);
121 return NULL;
122 }
123
124 x_setvbuf(ret, NULL, X_IOFBF, XBUFSIZE);
125
126 return ret;
127 }
128
129 /** simulate fclose() */
x_fclose(XFILE * f)130 int x_fclose(XFILE *f)
131 {
132 int ret;
133
134 /* make sure we flush any buffered data */
135 x_fflush(f);
136
137 ret = close(f->fd);
138 f->fd = -1;
139 if (f->buf) {
140 /* make sure data can't leak into a later malloc */
141 memset(f->buf, 0, f->bufsize);
142 SAFE_FREE(f->buf);
143 }
144 /* check the file descriptor given to the function is NOT one of the static
145 * descriptor of this libreary or we will free unallocated memory
146 * --sss */
147 if (f != x_stdin && f != x_stdout && f != x_stderr) {
148 SAFE_FREE(f);
149 }
150 return ret;
151 }
152
153 /** simulate fwrite() */
x_fwrite(const void * p,size_t size,size_t nmemb,XFILE * f)154 size_t x_fwrite(const void *p, size_t size, size_t nmemb, XFILE *f)
155 {
156 ssize_t ret;
157 size_t total=0;
158
159 /* we might be writing unbuffered */
160 if (f->buftype == X_IONBF ||
161 (!f->buf && !x_allocate_buffer(f))) {
162 ret = write(f->fd, p, size*nmemb);
163 if (ret == -1) return -1;
164 return ret/size;
165 }
166
167
168 while (total < size*nmemb) {
169 size_t n = f->bufsize - f->bufused;
170 n = MIN(n, (size*nmemb)-total);
171
172 if (n == 0) {
173 /* it's full, flush it */
174 x_fflush(f);
175 continue;
176 }
177
178 memcpy(f->buf + f->bufused, total+(const char *)p, n);
179 f->bufused += n;
180 total += n;
181 }
182
183 /* when line buffered we need to flush at the last linefeed. This can
184 flush a bit more than necessary, but that is harmless */
185 if (f->buftype == X_IOLBF && f->bufused) {
186 int i;
187 for (i=(size*nmemb)-1; i>=0; i--) {
188 if (*(i+(const char *)p) == '\n') {
189 x_fflush(f);
190 break;
191 }
192 }
193 }
194
195 return total/size;
196 }
197
198 /** thank goodness for asprintf() */
x_vfprintf(XFILE * f,const char * format,va_list ap)199 int x_vfprintf(XFILE *f, const char *format, va_list ap)
200 {
201 char *p;
202 int len, ret;
203 va_list ap2;
204
205 va_copy(ap2, ap);
206
207 len = vasprintf(&p, format, ap2);
208 if (len <= 0) return len;
209 ret = x_fwrite(p, 1, len, f);
210 SAFE_FREE(p);
211 return ret;
212 }
213
x_fprintf(XFILE * f,const char * format,...)214 int x_fprintf(XFILE *f, const char *format, ...)
215 {
216 va_list ap;
217 int ret;
218
219 va_start(ap, format);
220 ret = x_vfprintf(f, format, ap);
221 va_end(ap);
222 return ret;
223 }
224
225 /* at least fileno() is simple! */
x_fileno(XFILE * f)226 int x_fileno(XFILE *f)
227 {
228 return f->fd;
229 }
230
231 /** simulate fflush() */
x_fflush(XFILE * f)232 int x_fflush(XFILE *f)
233 {
234 int ret;
235
236 if (f->flags & X_FLAG_ERROR) return -1;
237
238 if ((f->open_flags & O_ACCMODE) != O_WRONLY) {
239 errno = EINVAL;
240 return -1;
241 }
242
243 if (f->bufused == 0) return 0;
244
245 ret = write(f->fd, f->buf, f->bufused);
246 if (ret == -1) return -1;
247
248 f->bufused -= ret;
249 if (f->bufused > 0) {
250 f->flags |= X_FLAG_ERROR;
251 memmove(f->buf, ret + (char *)f->buf, f->bufused);
252 return -1;
253 }
254
255 return 0;
256 }
257
258 /** simulate setbuffer() */
x_setbuffer(XFILE * f,char * buf,size_t size)259 void x_setbuffer(XFILE *f, char *buf, size_t size)
260 {
261 x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, size);
262 }
263
264 /** simulate setbuf() */
x_setbuf(XFILE * f,char * buf)265 void x_setbuf(XFILE *f, char *buf)
266 {
267 x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, XBUFSIZE);
268 }
269
270 /** simulate setlinebuf() */
x_setlinebuf(XFILE * f)271 void x_setlinebuf(XFILE *f)
272 {
273 x_setvbuf(f, NULL, X_IOLBF, 0);
274 }
275
276
277 /** simulate feof() */
x_feof(XFILE * f)278 int x_feof(XFILE *f)
279 {
280 if (f->flags & X_FLAG_EOF) return 1;
281 return 0;
282 }
283
284 /** simulate ferror() */
x_ferror(XFILE * f)285 int x_ferror(XFILE *f)
286 {
287 if (f->flags & X_FLAG_ERROR) return 1;
288 return 0;
289 }
290
291 /* fill the read buffer */
x_fillbuf(XFILE * f)292 static void x_fillbuf(XFILE *f)
293 {
294 int n;
295
296 if (f->bufused) return;
297
298 if (!f->buf && !x_allocate_buffer(f)) return;
299
300 n = read(f->fd, f->buf, f->bufsize);
301 if (n <= 0) return;
302 f->bufused = n;
303 f->next = f->buf;
304 }
305
306 /** simulate fgetc() */
x_fgetc(XFILE * f)307 int x_fgetc(XFILE *f)
308 {
309 int ret;
310
311 if (f->flags & (X_FLAG_EOF | X_FLAG_ERROR)) return EOF;
312
313 if (f->bufused == 0) x_fillbuf(f);
314
315 if (f->bufused == 0) {
316 f->flags |= X_FLAG_EOF;
317 return EOF;
318 }
319
320 ret = *(uint8_t *)(f->next);
321 f->next++;
322 f->bufused--;
323 return ret;
324 }
325
326 /** simulate fread */
x_fread(void * p,size_t size,size_t nmemb,XFILE * f)327 size_t x_fread(void *p, size_t size, size_t nmemb, XFILE *f)
328 {
329 size_t total = 0;
330 while (total < size*nmemb) {
331 int c = x_fgetc(f);
332 if (c == EOF) break;
333 (total+(char *)p)[0] = (char)c;
334 total++;
335 }
336 return total/size;
337 }
338
339 /** simulate fgets() */
x_fgets(char * s,int size,XFILE * stream)340 char *x_fgets(char *s, int size, XFILE *stream)
341 {
342 char *s0 = s;
343 int l = size;
344 while (l>1) {
345 int c = x_fgetc(stream);
346 if (c == EOF) break;
347 *s++ = (char)c;
348 l--;
349 if (c == '\n') break;
350 }
351 if (l==size || x_ferror(stream)) {
352 return 0;
353 }
354 *s = 0;
355 return s0;
356 }
357
358 /**
359 * trivial seek, works only for SEEK_SET and SEEK_END if SEEK_CUR is
360 * set then an error is returned */
x_tseek(XFILE * f,off_t offset,int whence)361 off_t x_tseek(XFILE *f, off_t offset, int whence)
362 {
363 if (f->flags & X_FLAG_ERROR)
364 return -1;
365
366 /* only SEEK_SET and SEEK_END are supported */
367 /* SEEK_CUR needs internal offset counter */
368 if (whence != SEEK_SET && whence != SEEK_END) {
369 f->flags |= X_FLAG_EINVAL;
370 errno = EINVAL;
371 return -1;
372 }
373
374 /* empty the buffer */
375 switch (f->open_flags & O_ACCMODE) {
376 case O_RDONLY:
377 f->bufused = 0;
378 break;
379 case O_WRONLY:
380 if (x_fflush(f) != 0)
381 return -1;
382 break;
383 default:
384 errno = EINVAL;
385 return -1;
386 }
387
388 f->flags &= ~X_FLAG_EOF;
389 return lseek(f->fd, offset, whence);
390 }
391