xref: /freebsd/contrib/sendmail/libsm/stdio.c (revision ee2ea5ceafed78a5bd9810beb9e3ca927180c226)
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.1.1.2 2002/04/10 03:04:56 gshapiro 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/fdset.h>
28 #include <sm/setjmp.h>
29 #include <sm/conf.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 		  FD_ZERO(&readfds);
357 		  SM_FD_SET(fp->f_file, &readfds);
358 		  timeout.tv_sec = 0;
359 		  timeout.tv_usec = 0;
360 		  if (select(fp->f_file + 1, FDSET_CAST &readfds,
361 			     NULL, NULL, &timeout) > 0 &&
362 		      SM_FD_ISSET(fp->f_file, &readfds))
363 			  return 1;
364 		  return 0;
365 	  }
366 
367 	  default:
368 		errno = EINVAL;
369 		return -1;
370 	}
371 }
372 
373 /*
374 **  SM_STDFDOPEN -- open file by primitive 'fd' rather than pathname
375 **
376 **	I/O function to handle fdopen() stdio equivalence. The rest of
377 **	the functions are the same as the sm_stdopen() above.
378 **
379 **	Parameters:
380 **		fp -- the file pointer to be associated with the open
381 **		name -- the primitive file descriptor for association
382 **		flags -- indicates type of access methods
383 **		rpool -- ignored
384 **
385 **	Results:
386 **		Success: primitive file descriptor value
387 **		Failure: -1 and sets errno
388 */
389 
390 /* ARGSUSED3 */
391 int
392 sm_stdfdopen(fp, info, flags, rpool)
393 	SM_FILE_T *fp;
394 	const void *info;
395 	int flags;
396 	const void *rpool;
397 {
398 	int oflags, tmp, fdflags, fd = *((int *) info);
399 
400 	switch (flags)
401 	{
402 	  case SM_IO_RDWR:
403 		oflags = O_RDWR | O_CREAT;
404 		break;
405 	  case SM_IO_RDONLY:
406 		oflags = O_RDONLY;
407 		break;
408 	  case SM_IO_WRONLY:
409 		oflags = O_WRONLY | O_CREAT | O_TRUNC;
410 		break;
411 	  case SM_IO_APPEND:
412 		oflags = O_APPEND | O_WRONLY | O_CREAT;
413 		break;
414 	  case SM_IO_APPENDRW:
415 		oflags = O_APPEND | O_RDWR | O_CREAT;
416 		break;
417 	  default:
418 		errno = EINVAL;
419 		return -1;
420 	}
421 
422 	/* Make sure the mode the user wants is a subset of the actual mode. */
423 	if ((fdflags = fcntl(fd, F_GETFL, 0)) < 0)
424 		return -1;
425 
426 	tmp = fdflags & O_ACCMODE;
427 	if (tmp != O_RDWR && (tmp != (oflags & O_ACCMODE)))
428 	{
429 		errno = EINVAL;
430 		return -1;
431 	}
432 	fp->f_file = fd;
433 	if (oflags & O_APPEND)
434 		(void) (*fp->f_seek)(fp, (off_t)0, SEEK_END);
435 	return fp->f_file;
436 }
437 
438 /*
439 **  SM_IO_FOPEN -- open a file
440 **
441 **	Same interface and semantics as the open() system call,
442 **	except that it returns SM_FILE_T* instead of a file descriptor.
443 **
444 **	Parameters:
445 **		pathname -- path of file to open
446 **		flags -- flags controlling the open
447 **		...  -- option "mode" for opening the file
448 **
449 **	Returns:
450 **		Raises an exception on heap exhaustion.
451 **		Returns NULL and sets errno if open() fails.
452 **		Returns an SM_FILE_T pointer on success.
453 */
454 
455 SM_FILE_T *
456 #if SM_VA_STD
457 sm_io_fopen(char *pathname, int flags, ...)
458 #else /* SM_VA_STD */
459 sm_io_fopen(pathname, flags, va_alist)
460 	char *pathname;
461 	int flags;
462 	va_dcl
463 #endif /* SM_VA_STD */
464 {
465 	MODE_T mode;
466 	SM_FILE_T *fp;
467 	int ioflags;
468 
469 	if (flags & O_CREAT)
470 	{
471 		SM_VA_LOCAL_DECL
472 
473 		SM_VA_START(ap, flags);
474 		mode = (MODE_T) SM_VA_ARG(ap, int);
475 		SM_VA_END(ap);
476 	}
477 	else
478 		mode = 0;
479 
480 	switch (flags & O_ACCMODE)
481 	{
482 	  case O_RDONLY:
483 		ioflags = SMRD;
484 		break;
485 	  case O_WRONLY:
486 		ioflags = SMWR;
487 		break;
488 	  case O_RDWR:
489 		ioflags = SMRW;
490 		break;
491 	  default:
492 		sm_abort("sm_io_fopen: bad flags 0%o", flags);
493 	}
494 
495 	fp = sm_fp(SmFtStdio, ioflags, NULL);
496 	fp->f_file = open(pathname, flags, mode);
497 	if (fp->f_file == -1)
498 	{
499 		fp->f_flags = 0;
500 		fp->sm_magic = NULL;
501 		return NULL;
502 	}
503 	return fp;
504 }
505