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