stdio.c revision 120256
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>
16SM_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 */
56int
57sm_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
121ssize_t
122sm_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
150ssize_t
151sm_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
175off_t
176sm_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
200int
201sm_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
221int
222sm_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
263int
264sm_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
299int
300sm_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
331int
332sm_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 */
400int
401sm_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
467SM_FILE_T *
468#if SM_VA_STD
469sm_io_fopen(char *pathname, int flags, ...)
470#else /* SM_VA_STD */
471sm_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