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