xref: /freebsd/contrib/sendmail/libsm/stdio.c (revision 2357939bc239bd5334a169b62313806178dd8f30)
1 /*
2  * Copyright (c) 2000-2003 Sendmail, Inc. and its suppliers.
3  *      All rights reserved.
4  * Copyright (c) 1990, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Chris Torek.
9  *
10  * By using this file, you agree to the terms and conditions set
11  * forth in the LICENSE file which can be found at the top level of
12  * the sendmail distribution.
13  */
14 
15 #include <sm/gen.h>
16 SM_RCSID("@(#)$Id: stdio.c,v 1.56.2.13 2003/09/04 01:18:08 ca Exp $")
17 #include <unistd.h>
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <string.h>	/* FreeBSD: FD_ZERO needs <string.h> */
21 #include <sys/stat.h>
22 #include <sys/time.h>
23 #include <sm/heap.h>
24 #include <sm/assert.h>
25 #include <sm/varargs.h>
26 #include <sm/io.h>
27 #include <sm/setjmp.h>
28 #include <sm/conf.h>
29 #include <sm/fdset.h>
30 #include "local.h"
31 
32 /*
33 **  Overall:
34 **  Small standard I/O/seek/close functions.
35 **  These maintain the `known seek offset' for seek optimization.
36 */
37 
38 /*
39 **  SM_STDOPEN -- open a file with stdio behavior
40 **
41 **  Not associated with the system's stdio in libc.
42 **
43 **	Parameters:
44 **		fp -- file pointer to be associated with the open
45 **		info -- pathname of the file to be opened
46 **		flags -- indicates type of access methods
47 **		rpool -- ignored
48 **
49 **	Returns:
50 **		Failure: -1 and set errno
51 **		Success: 0 or greater (fd of file from open(2)).
52 **
53 */
54 
55 /* ARGSUSED3 */
56 int
57 sm_stdopen(fp, info, flags, rpool)
58 	SM_FILE_T *fp;
59 	const void *info;
60 	int flags;
61 	const void *rpool;
62 {
63 	char *path = (char *) info;
64 	int oflags;
65 
66 	switch (SM_IO_MODE(flags))
67 	{
68 	  case SM_IO_RDWR:
69 		oflags = O_RDWR;
70 		break;
71 	  case SM_IO_RDWRTR:
72 		oflags = O_RDWR | O_CREAT | O_TRUNC;
73 		break;
74 	  case SM_IO_RDONLY:
75 		oflags = O_RDONLY;
76 		break;
77 	  case SM_IO_WRONLY:
78 		oflags = O_WRONLY | O_CREAT | O_TRUNC;
79 		break;
80 	  case SM_IO_APPEND:
81 		oflags = O_APPEND | O_WRONLY | O_CREAT;
82 		break;
83 	  case SM_IO_APPENDRW:
84 		oflags = O_APPEND | O_RDWR | O_CREAT;
85 		break;
86 	  default:
87 		errno = EINVAL;
88 		return -1;
89 	}
90 #ifdef O_BINARY
91 	if (SM_IS_BINARY(flags))
92 		oflags |= O_BINARY;
93 #endif /* O_BINARY */
94 	fp->f_file = open(path, oflags,
95 			  S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
96 	if (fp->f_file < 0)
97 		return -1; /* errno set by open() */
98 
99 	if (oflags & O_APPEND)
100 		(void) (*fp->f_seek)((void *)fp, (off_t)0, SEEK_END);
101 
102 	return fp->f_file;
103 }
104 
105 /*
106 **  SM_STDREAD -- read from the file
107 **
108 **	Parameters:
109 **		fp -- file pointer to read from
110 **		buf -- location to place read data
111 **		n -- number of bytes to read
112 **
113 **	Returns:
114 **		Failure: -1 and sets errno
115 **		Success: number of bytes read
116 **
117 **	Side Effects:
118 **		Updates internal offset into file.
119 */
120 
121 ssize_t
122 sm_stdread(fp, buf, n)
123 	SM_FILE_T *fp;
124 	char *buf;
125 	size_t n;
126 {
127 	register int ret;
128 
129 	ret = read(fp->f_file, buf, n);
130 
131 	/* if the read succeeded, update the current offset */
132 	if (ret > 0)
133 		fp->f_lseekoff += ret;
134 	return ret;
135 }
136 
137 /*
138 **  SM_STDWRITE -- write to the file
139 **
140 **	Parameters:
141 **		fp -- file pointer ro write to
142 **		buf -- location of data to be written
143 **		n - number of bytes to write
144 **
145 **	Returns:
146 **		Failure: -1 and sets errno
147 **		Success: number of bytes written
148 */
149 
150 ssize_t
151 sm_stdwrite(fp, buf, n)
152 	SM_FILE_T *fp;
153 	char const *buf;
154 	size_t n;
155 {
156 	return write(fp->f_file, buf, n);
157 }
158 
159 /*
160 **  SM_STDSEEK -- set the file offset position
161 **
162 **	Parmeters:
163 **		fp -- file pointer to position
164 **		offset -- how far to position from "base" (set by 'whence')
165 **		whence -- indicates where the "base" of the 'offset' to start
166 **
167 **	Results:
168 **		Failure: -1 and sets errno
169 **		Success: the current offset
170 **
171 **	Side Effects:
172 **		Updates the internal value of the offset.
173 */
174 
175 off_t
176 sm_stdseek(fp, offset, whence)
177 	SM_FILE_T *fp;
178 	off_t offset;
179 	int whence;
180 {
181 	register off_t ret;
182 
183 	ret = lseek(fp->f_file, (off_t) offset, whence);
184 	if (ret != (off_t) -1)
185 		fp->f_lseekoff = ret;
186 	return ret;
187 }
188 
189 /*
190 **  SM_STDCLOSE -- close the file
191 **
192 **	Parameters:
193 **		fp -- the file pointer to close
194 **
195 **	Returns:
196 **		Success: 0 (zero)
197 **		Failure: -1 and sets errno
198 */
199 
200 int
201 sm_stdclose(fp)
202 	SM_FILE_T *fp;
203 {
204 	return close(fp->f_file);
205 }
206 
207 /*
208 **  SM_STDSETMODE -- set the access mode for the file
209 **
210 **  Called by sm_stdsetinfo().
211 **
212 **	Parameters:
213 **		fp -- file pointer
214 **		mode -- new mode to set the file access to
215 **
216 **	Results:
217 **		Success: 0 (zero);
218 **		Failure: -1 and sets errno
219 */
220 
221 int
222 sm_stdsetmode(fp, mode)
223 	SM_FILE_T *fp;
224 	const int *mode;
225 {
226 	int flags = 0;
227 
228 	switch (SM_IO_MODE(*mode))
229 	{
230 	  case SM_IO_RDWR:
231 		flags |= SMRW;
232 		break;
233 	  case SM_IO_RDONLY:
234 		flags |= SMRD;
235 		break;
236 	  case SM_IO_WRONLY:
237 		flags |= SMWR;
238 		break;
239 	  case SM_IO_APPEND:
240 	  default:
241 		errno = EINVAL;
242 		return -1;
243 	}
244 	fp->f_flags = fp->f_flags & ~SMMODEMASK;
245 	fp->f_flags |= flags;
246 	return 0;
247 }
248 
249 /*
250 **  SM_STDGETMODE -- for getinfo determine open mode
251 **
252 **  Called by sm_stdgetinfo().
253 **
254 **	Parameters:
255 **		fp -- the file mode being determined
256 **		mode -- internal mode to map to external value
257 **
258 **	Results:
259 **		Failure: -1 and sets errno
260 **		Success: external mode value
261 */
262 
263 int
264 sm_stdgetmode(fp, mode)
265 	SM_FILE_T *fp;
266 	int *mode;
267 {
268 	switch (fp->f_flags & SMMODEMASK)
269 	{
270 	  case SMRW:
271 		*mode = SM_IO_RDWR;
272 		break;
273 	  case SMRD:
274 		*mode = SM_IO_RDONLY;
275 		break;
276 	  case SMWR:
277 		*mode = SM_IO_WRONLY;
278 		break;
279 	  default:
280 		errno = EINVAL;
281 		return -1;
282 	}
283 	return 0;
284 }
285 
286 /*
287 **  SM_STDSETINFO -- set/modify information for a file
288 **
289 **	Parameters:
290 **		fp -- file to set info for
291 **		what -- type of info to set
292 **		valp -- location of data used for setting
293 **
294 **	Returns:
295 **		Failure: -1 and sets errno
296 **		Success: >=0
297 */
298 
299 int
300 sm_stdsetinfo(fp, what, valp)
301 	SM_FILE_T *fp;
302 	int what;
303 	void *valp;
304 {
305 	switch (what)
306 	{
307 	  case SM_IO_WHAT_MODE:
308 		return sm_stdsetmode(fp, (const int *)valp);
309 
310 	  default:
311 		errno = EINVAL;
312 		return -1;
313 	}
314 }
315 
316 /*
317 **  SM_GETINFO -- get information about the open file
318 **
319 **	Parameters:
320 **		fp -- file to get info for
321 **		what -- type of info to get
322 **		valp -- location to place found info
323 **
324 **	Returns:
325 **		Success: may or may not place info in 'valp' depending
326 **			on 'what' value, and returns values >=0. Return
327 **			value may be the obtained info
328 **		Failure: -1 and sets errno
329 */
330 
331 int
332 sm_stdgetinfo(fp, what, valp)
333 	SM_FILE_T *fp;
334 	int what;
335 	void *valp;
336 {
337 	switch (what)
338 	{
339 	  case SM_IO_WHAT_MODE:
340 		return sm_stdgetmode(fp, (int *)valp);
341 
342 	  case SM_IO_WHAT_FD:
343 		return fp->f_file;
344 
345 	  case SM_IO_WHAT_SIZE:
346 	  {
347 		  struct stat st;
348 
349 		  if (fstat(fp->f_file, &st) == 0)
350 			  return st.st_size;
351 		  else
352 			  return -1;
353 	  }
354 
355 	  case SM_IO_IS_READABLE:
356 	  {
357 		  fd_set readfds;
358 		  struct timeval timeout;
359 
360 		  if (SM_FD_SETSIZE > 0 && fp->f_file >= SM_FD_SETSIZE)
361 		  {
362 			  errno = EINVAL;
363 			  return -1;
364 		  }
365 		  FD_ZERO(&readfds);
366 		  SM_FD_SET(fp->f_file, &readfds);
367 		  timeout.tv_sec = 0;
368 		  timeout.tv_usec = 0;
369 		  if (select(fp->f_file + 1, FDSET_CAST &readfds,
370 			     NULL, NULL, &timeout) > 0 &&
371 		      SM_FD_ISSET(fp->f_file, &readfds))
372 			  return 1;
373 		  return 0;
374 	  }
375 
376 	  default:
377 		errno = EINVAL;
378 		return -1;
379 	}
380 }
381 
382 /*
383 **  SM_STDFDOPEN -- open file by primitive 'fd' rather than pathname
384 **
385 **	I/O function to handle fdopen() stdio equivalence. The rest of
386 **	the functions are the same as the sm_stdopen() above.
387 **
388 **	Parameters:
389 **		fp -- the file pointer to be associated with the open
390 **		name -- the primitive file descriptor for association
391 **		flags -- indicates type of access methods
392 **		rpool -- ignored
393 **
394 **	Results:
395 **		Success: primitive file descriptor value
396 **		Failure: -1 and sets errno
397 */
398 
399 /* ARGSUSED3 */
400 int
401 sm_stdfdopen(fp, info, flags, rpool)
402 	SM_FILE_T *fp;
403 	const void *info;
404 	int flags;
405 	const void *rpool;
406 {
407 	int oflags, tmp, fdflags, fd = *((int *) info);
408 
409 	switch (SM_IO_MODE(flags))
410 	{
411 	  case SM_IO_RDWR:
412 		oflags = O_RDWR | O_CREAT;
413 		break;
414 	  case SM_IO_RDONLY:
415 		oflags = O_RDONLY;
416 		break;
417 	  case SM_IO_WRONLY:
418 		oflags = O_WRONLY | O_CREAT | O_TRUNC;
419 		break;
420 	  case SM_IO_APPEND:
421 		oflags = O_APPEND | O_WRONLY | O_CREAT;
422 		break;
423 	  case SM_IO_APPENDRW:
424 		oflags = O_APPEND | O_RDWR | O_CREAT;
425 		break;
426 	  default:
427 		errno = EINVAL;
428 		return -1;
429 	}
430 #ifdef O_BINARY
431 	if (SM_IS_BINARY(flags))
432 		oflags |= O_BINARY;
433 #endif /* O_BINARY */
434 
435 	/* Make sure the mode the user wants is a subset of the actual mode. */
436 	if ((fdflags = fcntl(fd, F_GETFL, 0)) < 0)
437 		return -1;
438 	tmp = fdflags & O_ACCMODE;
439 	if (tmp != O_RDWR && (tmp != (oflags & O_ACCMODE)))
440 	{
441 		errno = EINVAL;
442 		return -1;
443 	}
444 	fp->f_file = fd;
445 	if (oflags & O_APPEND)
446 		(void) (*fp->f_seek)(fp, (off_t)0, SEEK_END);
447 	return fp->f_file;
448 }
449 
450 /*
451 **  SM_IO_FOPEN -- open a file
452 **
453 **	Same interface and semantics as the open() system call,
454 **	except that it returns SM_FILE_T* instead of a file descriptor.
455 **
456 **	Parameters:
457 **		pathname -- path of file to open
458 **		flags -- flags controlling the open
459 **		...  -- option "mode" for opening the file
460 **
461 **	Returns:
462 **		Raises an exception on heap exhaustion.
463 **		Returns NULL and sets errno if open() fails.
464 **		Returns an SM_FILE_T pointer on success.
465 */
466 
467 SM_FILE_T *
468 #if SM_VA_STD
469 sm_io_fopen(char *pathname, int flags, ...)
470 #else /* SM_VA_STD */
471 sm_io_fopen(pathname, flags, va_alist)
472 	char *pathname;
473 	int flags;
474 	va_dcl
475 #endif /* SM_VA_STD */
476 {
477 	MODE_T mode;
478 	SM_FILE_T *fp;
479 	int ioflags;
480 
481 	if (flags & O_CREAT)
482 	{
483 		SM_VA_LOCAL_DECL
484 
485 		SM_VA_START(ap, flags);
486 		mode = (MODE_T) SM_VA_ARG(ap, int);
487 		SM_VA_END(ap);
488 	}
489 	else
490 		mode = 0;
491 
492 	switch (flags & O_ACCMODE)
493 	{
494 	  case O_RDONLY:
495 		ioflags = SMRD;
496 		break;
497 	  case O_WRONLY:
498 		ioflags = SMWR;
499 		break;
500 	  case O_RDWR:
501 		ioflags = SMRW;
502 		break;
503 	  default:
504 		sm_abort("sm_io_fopen: bad flags 0%o", flags);
505 	}
506 
507 	fp = sm_fp(SmFtStdio, ioflags, NULL);
508 	fp->f_file = open(pathname, flags, mode);
509 	if (fp->f_file == -1)
510 	{
511 		fp->f_flags = 0;
512 		fp->sm_magic = NULL;
513 		return NULL;
514 	}
515 	return fp;
516 }
517