xref: /illumos-gate/usr/src/cmd/sendmail/libsm/stdio.c (revision 1566bc3431383e39ac9bc7fb7f00feff9c06acda)
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>
18 SM_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 
34 static int	sm_stdsetmode __P((SM_FILE_T *, const int *));
35 static 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 */
61 int
62 sm_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 
126 ssize_t
127 sm_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 
155 ssize_t
156 sm_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 
180 off_t
181 sm_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 
205 int
206 sm_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 
226 static int
227 sm_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 
268 static int
269 sm_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 
304 int
305 sm_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 
336 int
337 sm_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 */
405 int
406 sm_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 
472 SM_FILE_T *
473 #if SM_VA_STD
474 sm_io_fopen(char *pathname, int flags, ...)
475 #else /* SM_VA_STD */
476 sm_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