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
sm_stdopen(fp,info,flags,rpool)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
sm_stdread(fp,buf,n)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
sm_stdwrite(fp,buf,n)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
sm_stdseek(fp,offset,whence)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
sm_stdclose(fp)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
sm_stdsetmode(fp,mode)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
sm_stdgetmode(fp,mode)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
sm_stdsetinfo(fp,what,valp)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
sm_stdgetinfo(fp,what,valp)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
sm_stdfdopen(fp,info,flags,rpool)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
sm_io_fopen(char * pathname,int flags,...)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