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