xref: /illumos-gate/usr/src/cmd/sendmail/libsm/stdio.c (revision bd97c7ce2344fa3252d8785c35895490916bc79b)
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 #include <sm/gen.h>
16 SM_RCSID("@(#)$Id: stdio.c,v 1.71 2005/06/14 23:07:20 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 
32 static int	sm_stdsetmode __P((SM_FILE_T *, const int *));
33 static 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 */
59 int
60 sm_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 
124 ssize_t
125 sm_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 
153 ssize_t
154 sm_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 
178 off_t
179 sm_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 
203 int
204 sm_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 
224 static int
225 sm_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 
266 static int
267 sm_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 
302 int
303 sm_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_GETINFO -- 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 
334 int
335 sm_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 */
403 int
404 sm_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 
470 SM_FILE_T *
471 #if SM_VA_STD
472 sm_io_fopen(char *pathname, int flags, ...)
473 #else /* SM_VA_STD */
474 sm_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