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