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