Fix Boot Setup example:
[dragonfly/netmp.git] / contrib / sendmail-8.14 / sendmail / bf.c
blobb31ce7e8b3033d3bf95ace4eba005f0b72ecf1ae
1 /*
2 * Copyright (c) 1999-2002, 2004, 2006 Sendmail, Inc. and its suppliers.
3 * All rights reserved.
5 * By using this file, you agree to the terms and conditions set
6 * forth in the LICENSE file which can be found at the top level of
7 * the sendmail distribution.
9 * Contributed by Exactis.com, Inc.
14 ** This is in transition. Changed from the original bf_torek.c code
15 ** to use sm_io function calls directly rather than through stdio
16 ** translation layer. Will be made a built-in file type of libsm
17 ** next (once safeopen() linkable from libsm).
20 #include <sm/gen.h>
21 SM_RCSID("@(#)$Id: bf.c,v 8.62 2006/03/31 18:45:56 ca Exp $")
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <sys/uio.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <errno.h>
31 #include "sendmail.h"
32 #include "bf.h"
34 #include <syslog.h>
36 /* bf io functions */
37 static ssize_t sm_bfread __P((SM_FILE_T *, char *, size_t));
38 static ssize_t sm_bfwrite __P((SM_FILE_T *, const char *, size_t));
39 static off_t sm_bfseek __P((SM_FILE_T *, off_t, int));
40 static int sm_bfclose __P((SM_FILE_T *));
41 static int sm_bfcommit __P((SM_FILE_T *));
42 static int sm_bftruncate __P((SM_FILE_T *));
44 static int sm_bfopen __P((SM_FILE_T *, const void *, int, const void *));
45 static int sm_bfsetinfo __P((SM_FILE_T *, int , void *));
46 static int sm_bfgetinfo __P((SM_FILE_T *, int , void *));
49 ** Data structure for storing information about each buffered file
50 ** (Originally in sendmail/bf_torek.h for the curious.)
53 struct bf
55 bool bf_committed; /* Has this buffered file been committed? */
56 bool bf_ondisk; /* On disk: committed or buffer overflow */
57 long bf_flags;
58 int bf_disk_fd; /* If on disk, associated file descriptor */
59 char *bf_buf; /* Memory buffer */
60 int bf_bufsize; /* Length of above buffer */
61 int bf_buffilled; /* Bytes of buffer actually filled */
62 char *bf_filename; /* Name of buffered file, if ever committed */
63 MODE_T bf_filemode; /* Mode of buffered file, if ever committed */
64 off_t bf_offset; /* Currect file offset */
65 int bf_size; /* Total current size of file */
68 #ifdef BF_STANDALONE
69 # define OPEN(fn, omode, cmode, sff) open(fn, omode, cmode)
70 #else /* BF_STANDALONE */
71 # define OPEN(fn, omode, cmode, sff) safeopen(fn, omode, cmode, sff)
72 #endif /* BF_STANDALONE */
74 struct bf_info
76 char *bi_filename;
77 MODE_T bi_fmode;
78 size_t bi_bsize;
79 long bi_flags;
83 ** SM_BFOPEN -- the "base" open function called by sm_io_open() for the
84 ** internal, file-type-specific info setup.
86 ** Parameters:
87 ** fp -- file pointer being filled-in for file being open'd
88 ** info -- information about file being opened
89 ** flags -- ignored
90 ** rpool -- ignored (currently)
92 ** Returns:
93 ** Failure: -1 and sets errno
94 ** Success: 0 (zero)
97 static int
98 sm_bfopen(fp, info, flags, rpool)
99 SM_FILE_T *fp;
100 const void *info;
101 int flags;
102 const void *rpool;
104 char *filename;
105 MODE_T fmode;
106 size_t bsize;
107 long sflags;
108 struct bf *bfp;
109 int l;
110 struct stat st;
112 filename = ((struct bf_info *) info)->bi_filename;
113 fmode = ((struct bf_info *) info)->bi_fmode;
114 bsize = ((struct bf_info *) info)->bi_bsize;
115 sflags = ((struct bf_info *) info)->bi_flags;
117 /* Sanity checks */
118 if (*filename == '\0')
120 /* Empty filename string */
121 errno = ENOENT;
122 return -1;
124 if (stat(filename, &st) == 0)
126 /* File already exists on disk */
127 errno = EEXIST;
128 return -1;
131 /* Allocate memory */
132 bfp = (struct bf *) sm_malloc(sizeof(struct bf));
133 if (bfp == NULL)
135 errno = ENOMEM;
136 return -1;
139 /* Assign data buffer */
140 /* A zero bsize is valid, just don't allocate memory */
141 if (bsize > 0)
143 bfp->bf_buf = (char *) sm_malloc(bsize);
144 if (bfp->bf_buf == NULL)
146 bfp->bf_bufsize = 0;
147 sm_free(bfp);
148 errno = ENOMEM;
149 return -1;
152 else
153 bfp->bf_buf = NULL;
155 /* Nearly home free, just set all the parameters now */
156 bfp->bf_committed = false;
157 bfp->bf_ondisk = false;
158 bfp->bf_flags = sflags;
159 bfp->bf_bufsize = bsize;
160 bfp->bf_buffilled = 0;
161 l = strlen(filename) + 1;
162 bfp->bf_filename = (char *) sm_malloc(l);
163 if (bfp->bf_filename == NULL)
165 if (bfp->bf_buf != NULL)
166 sm_free(bfp->bf_buf);
167 sm_free(bfp);
168 errno = ENOMEM;
169 return -1;
171 (void) sm_strlcpy(bfp->bf_filename, filename, l);
172 bfp->bf_filemode = fmode;
173 bfp->bf_offset = 0;
174 bfp->bf_size = 0;
175 bfp->bf_disk_fd = -1;
176 fp->f_cookie = bfp;
178 if (tTd(58, 8))
179 sm_dprintf("sm_bfopen(%s)\n", filename);
181 return 0;
185 ** BFOPEN -- create a new buffered file
187 ** Parameters:
188 ** filename -- the file's name
189 ** fmode -- what mode the file should be created as
190 ** bsize -- amount of buffer space to allocate (may be 0)
191 ** flags -- if running under sendmail, passed directly to safeopen
193 ** Returns:
194 ** a SM_FILE_T * which may then be used with stdio functions,
195 ** or NULL on failure. SM_FILE_T * is opened for writing
196 ** "SM_IO_WHAT_VECTORS").
198 ** Side Effects:
199 ** none.
201 ** Sets errno:
202 ** any value of errno specified by sm_io_setinfo_type()
203 ** any value of errno specified by sm_io_open()
204 ** any value of errno specified by sm_io_setinfo()
207 #ifdef __STDC__
209 ** XXX This is a temporary hack since MODE_T on HP-UX 10.x is short.
210 ** If we use K&R here, the compiler will complain about
211 ** Inconsistent parameter list declaration
212 ** due to the change from short to int.
215 SM_FILE_T *
216 bfopen(char *filename, MODE_T fmode, size_t bsize, long flags)
217 #else /* __STDC__ */
218 SM_FILE_T *
219 bfopen(filename, fmode, bsize, flags)
220 char *filename;
221 MODE_T fmode;
222 size_t bsize;
223 long flags;
224 #endif /* __STDC__ */
226 MODE_T omask;
227 SM_FILE_T SM_IO_SET_TYPE(vector, BF_FILE_TYPE, sm_bfopen, sm_bfclose,
228 sm_bfread, sm_bfwrite, sm_bfseek, sm_bfgetinfo, sm_bfsetinfo,
229 SM_TIME_FOREVER);
230 struct bf_info info;
233 ** Apply current umask to fmode as it may change by the time
234 ** the file is actually created. fmode becomes the true
235 ** permissions of the file, which OPEN() must obey.
238 omask = umask(0);
239 fmode &= ~omask;
240 (void) umask(omask);
242 SM_IO_INIT_TYPE(vector, BF_FILE_TYPE, sm_bfopen, sm_bfclose,
243 sm_bfread, sm_bfwrite, sm_bfseek, sm_bfgetinfo, sm_bfsetinfo,
244 SM_TIME_FOREVER);
245 info.bi_filename = filename;
246 info.bi_fmode = fmode;
247 info.bi_bsize = bsize;
248 info.bi_flags = flags;
250 return sm_io_open(&vector, SM_TIME_DEFAULT, &info, SM_IO_RDWR, NULL);
254 ** SM_BFGETINFO -- returns info about an open file pointer
256 ** Parameters:
257 ** fp -- file pointer to get info about
258 ** what -- type of info to obtain
259 ** valp -- thing to return the info in
262 static int
263 sm_bfgetinfo(fp, what, valp)
264 SM_FILE_T *fp;
265 int what;
266 void *valp;
268 struct bf *bfp;
270 bfp = (struct bf *) fp->f_cookie;
271 switch (what)
273 case SM_IO_WHAT_FD:
274 return bfp->bf_disk_fd;
275 case SM_IO_WHAT_SIZE:
276 return bfp->bf_size;
277 default:
278 return -1;
283 ** SM_BFCLOSE -- close a buffered file
285 ** Parameters:
286 ** fp -- cookie of file to close
288 ** Returns:
289 ** 0 to indicate success
291 ** Side Effects:
292 ** deletes backing file, sm_frees memory.
294 ** Sets errno:
295 ** never.
298 static int
299 sm_bfclose(fp)
300 SM_FILE_T *fp;
302 struct bf *bfp;
304 /* Cast cookie back to correct type */
305 bfp = (struct bf *) fp->f_cookie;
307 /* Need to clean up the file */
308 if (bfp->bf_ondisk && !bfp->bf_committed)
309 unlink(bfp->bf_filename);
310 sm_free(bfp->bf_filename);
312 if (bfp->bf_disk_fd != -1)
313 close(bfp->bf_disk_fd);
315 /* Need to sm_free the buffer */
316 if (bfp->bf_bufsize > 0)
317 sm_free(bfp->bf_buf);
319 /* Finally, sm_free the structure */
320 sm_free(bfp);
321 return 0;
325 ** SM_BFREAD -- read a buffered file
327 ** Parameters:
328 ** cookie -- cookie of file to read
329 ** buf -- buffer to fill
330 ** nbytes -- how many bytes to read
332 ** Returns:
333 ** number of bytes read or -1 indicate failure
335 ** Side Effects:
336 ** none.
340 static ssize_t
341 sm_bfread(fp, buf, nbytes)
342 SM_FILE_T *fp;
343 char *buf;
344 size_t nbytes;
346 struct bf *bfp;
347 ssize_t count = 0; /* Number of bytes put in buf so far */
348 int retval;
350 /* Cast cookie back to correct type */
351 bfp = (struct bf *) fp->f_cookie;
353 if (bfp->bf_offset < bfp->bf_buffilled)
355 /* Need to grab some from buffer */
356 count = nbytes;
357 if ((bfp->bf_offset + count) > bfp->bf_buffilled)
358 count = bfp->bf_buffilled - bfp->bf_offset;
360 memcpy(buf, bfp->bf_buf + bfp->bf_offset, count);
363 if ((bfp->bf_offset + nbytes) > bfp->bf_buffilled)
365 /* Need to grab some from file */
366 if (!bfp->bf_ondisk)
368 /* Oops, the file doesn't exist. EOF. */
369 if (tTd(58, 8))
370 sm_dprintf("sm_bfread(%s): to disk\n",
371 bfp->bf_filename);
372 goto finished;
375 /* Catch a read() on an earlier failed write to disk */
376 if (bfp->bf_disk_fd < 0)
378 errno = EIO;
379 return -1;
382 if (lseek(bfp->bf_disk_fd,
383 bfp->bf_offset + count, SEEK_SET) < 0)
385 if ((errno == EINVAL) || (errno == ESPIPE))
388 ** stdio won't be expecting these
389 ** errnos from read()! Change them
390 ** into something it can understand.
393 errno = EIO;
395 return -1;
398 while (count < nbytes)
400 retval = read(bfp->bf_disk_fd,
401 buf + count,
402 nbytes - count);
403 if (retval < 0)
405 /* errno is set implicitly by read() */
406 return -1;
408 else if (retval == 0)
409 goto finished;
410 else
411 count += retval;
415 finished:
416 bfp->bf_offset += count;
417 return count;
421 ** SM_BFSEEK -- seek to a position in a buffered file
423 ** Parameters:
424 ** fp -- fp of file to seek
425 ** offset -- position to seek to
426 ** whence -- how to seek
428 ** Returns:
429 ** new file offset or -1 indicate failure
431 ** Side Effects:
432 ** none.
436 static off_t
437 sm_bfseek(fp, offset, whence)
438 SM_FILE_T *fp;
439 off_t offset;
440 int whence;
443 struct bf *bfp;
445 /* Cast cookie back to correct type */
446 bfp = (struct bf *) fp->f_cookie;
448 switch (whence)
450 case SEEK_SET:
451 bfp->bf_offset = offset;
452 break;
454 case SEEK_CUR:
455 bfp->bf_offset += offset;
456 break;
458 case SEEK_END:
459 bfp->bf_offset = bfp->bf_size + offset;
460 break;
462 default:
463 errno = EINVAL;
464 return -1;
466 return bfp->bf_offset;
470 ** SM_BFWRITE -- write to a buffered file
472 ** Parameters:
473 ** fp -- fp of file to write
474 ** buf -- data buffer
475 ** nbytes -- how many bytes to write
477 ** Returns:
478 ** number of bytes written or -1 indicate failure
480 ** Side Effects:
481 ** may create backing file if over memory limit for file.
485 static ssize_t
486 sm_bfwrite(fp, buf, nbytes)
487 SM_FILE_T *fp;
488 const char *buf;
489 size_t nbytes;
491 struct bf *bfp;
492 ssize_t count = 0; /* Number of bytes written so far */
493 int retval;
495 /* Cast cookie back to correct type */
496 bfp = (struct bf *) fp->f_cookie;
498 /* If committed, go straight to disk */
499 if (bfp->bf_committed)
501 if (lseek(bfp->bf_disk_fd, bfp->bf_offset, SEEK_SET) < 0)
503 if ((errno == EINVAL) || (errno == ESPIPE))
506 ** stdio won't be expecting these
507 ** errnos from write()! Change them
508 ** into something it can understand.
511 errno = EIO;
513 return -1;
516 count = write(bfp->bf_disk_fd, buf, nbytes);
517 if (count < 0)
519 /* errno is set implicitly by write() */
520 return -1;
522 goto finished;
525 if (bfp->bf_offset < bfp->bf_bufsize)
527 /* Need to put some in buffer */
528 count = nbytes;
529 if ((bfp->bf_offset + count) > bfp->bf_bufsize)
530 count = bfp->bf_bufsize - bfp->bf_offset;
532 memcpy(bfp->bf_buf + bfp->bf_offset, buf, count);
533 if ((bfp->bf_offset + count) > bfp->bf_buffilled)
534 bfp->bf_buffilled = bfp->bf_offset + count;
537 if ((bfp->bf_offset + nbytes) > bfp->bf_bufsize)
539 /* Need to put some in file */
540 if (!bfp->bf_ondisk)
542 MODE_T omask;
543 int save_errno;
545 /* Clear umask as bf_filemode are the true perms */
546 omask = umask(0);
547 retval = OPEN(bfp->bf_filename,
548 O_RDWR | O_CREAT | O_TRUNC | QF_O_EXTRA,
549 bfp->bf_filemode, bfp->bf_flags);
550 save_errno = errno;
551 (void) umask(omask);
552 errno = save_errno;
554 /* Couldn't create file: failure */
555 if (retval < 0)
558 ** stdio may not be expecting these
559 ** errnos from write()! Change to
560 ** something which it can understand.
561 ** Note that ENOSPC and EDQUOT are saved
562 ** because they are actually valid for
563 ** write().
566 if (!(errno == ENOSPC
567 #ifdef EDQUOT
568 || errno == EDQUOT
569 #endif /* EDQUOT */
571 errno = EIO;
573 return -1;
575 bfp->bf_disk_fd = retval;
576 bfp->bf_ondisk = true;
579 /* Catch a write() on an earlier failed write to disk */
580 if (bfp->bf_ondisk && bfp->bf_disk_fd < 0)
582 errno = EIO;
583 return -1;
586 if (lseek(bfp->bf_disk_fd,
587 bfp->bf_offset + count, SEEK_SET) < 0)
589 if ((errno == EINVAL) || (errno == ESPIPE))
592 ** stdio won't be expecting these
593 ** errnos from write()! Change them into
594 ** something which it can understand.
597 errno = EIO;
599 return -1;
602 while (count < nbytes)
604 retval = write(bfp->bf_disk_fd, buf + count,
605 nbytes - count);
606 if (retval < 0)
608 /* errno is set implicitly by write() */
609 return -1;
611 else
612 count += retval;
616 finished:
617 bfp->bf_offset += count;
618 if (bfp->bf_offset > bfp->bf_size)
619 bfp->bf_size = bfp->bf_offset;
620 return count;
624 ** BFREWIND -- rewinds the SM_FILE_T *
626 ** Parameters:
627 ** fp -- SM_FILE_T * to rewind
629 ** Returns:
630 ** 0 on success, -1 on error
632 ** Side Effects:
633 ** rewinds the SM_FILE_T * and puts it into read mode. Normally
634 ** one would bfopen() a file, write to it, then bfrewind() and
635 ** fread(). If fp is not a buffered file, this is equivalent to
636 ** rewind().
638 ** Sets errno:
639 ** any value of errno specified by sm_io_rewind()
643 bfrewind(fp)
644 SM_FILE_T *fp;
646 (void) sm_io_flush(fp, SM_TIME_DEFAULT);
647 sm_io_clearerr(fp); /* quicker just to do it */
648 return sm_io_seek(fp, SM_TIME_DEFAULT, 0, SM_IO_SEEK_SET);
652 ** SM_BFCOMMIT -- "commits" the buffered file
654 ** Parameters:
655 ** fp -- SM_FILE_T * to commit to disk
657 ** Returns:
658 ** 0 on success, -1 on error
660 ** Side Effects:
661 ** Forces the given SM_FILE_T * to be written to disk if it is not
662 ** already, and ensures that it will be kept after closing. If
663 ** fp is not a buffered file, this is a no-op.
665 ** Sets errno:
666 ** any value of errno specified by open()
667 ** any value of errno specified by write()
668 ** any value of errno specified by lseek()
671 static int
672 sm_bfcommit(fp)
673 SM_FILE_T *fp;
675 struct bf *bfp;
676 int retval;
677 int byteswritten;
679 /* Get associated bf structure */
680 bfp = (struct bf *) fp->f_cookie;
682 /* If already committed, noop */
683 if (bfp->bf_committed)
684 return 0;
686 /* Do we need to open a file? */
687 if (!bfp->bf_ondisk)
689 int save_errno;
690 MODE_T omask;
691 struct stat st;
693 if (tTd(58, 8))
695 sm_dprintf("bfcommit(%s): to disk\n", bfp->bf_filename);
696 if (tTd(58, 32))
697 sm_dprintf("bfcommit(): filemode %o flags %ld\n",
698 bfp->bf_filemode, bfp->bf_flags);
701 if (stat(bfp->bf_filename, &st) == 0)
703 errno = EEXIST;
704 return -1;
707 /* Clear umask as bf_filemode are the true perms */
708 omask = umask(0);
709 retval = OPEN(bfp->bf_filename,
710 O_RDWR | O_CREAT | O_EXCL | QF_O_EXTRA,
711 bfp->bf_filemode, bfp->bf_flags);
712 save_errno = errno;
713 (void) umask(omask);
715 /* Couldn't create file: failure */
716 if (retval < 0)
718 /* errno is set implicitly by open() */
719 errno = save_errno;
720 return -1;
723 bfp->bf_disk_fd = retval;
724 bfp->bf_ondisk = true;
727 /* Write out the contents of our buffer, if we have any */
728 if (bfp->bf_buffilled > 0)
730 byteswritten = 0;
732 if (lseek(bfp->bf_disk_fd, 0, SEEK_SET) < 0)
734 /* errno is set implicitly by lseek() */
735 return -1;
738 while (byteswritten < bfp->bf_buffilled)
740 retval = write(bfp->bf_disk_fd,
741 bfp->bf_buf + byteswritten,
742 bfp->bf_buffilled - byteswritten);
743 if (retval < 0)
745 /* errno is set implicitly by write() */
746 return -1;
748 else
749 byteswritten += retval;
752 bfp->bf_committed = true;
754 /* Invalidate buf; all goes to file now */
755 bfp->bf_buffilled = 0;
756 if (bfp->bf_bufsize > 0)
758 /* Don't need buffer anymore; free it */
759 bfp->bf_bufsize = 0;
760 sm_free(bfp->bf_buf);
762 return 0;
766 ** SM_BFTRUNCATE -- rewinds and truncates the SM_FILE_T *
768 ** Parameters:
769 ** fp -- SM_FILE_T * to truncate
771 ** Returns:
772 ** 0 on success, -1 on error
774 ** Side Effects:
775 ** rewinds the SM_FILE_T *, truncates it to zero length, and puts
776 ** it into write mode.
778 ** Sets errno:
779 ** any value of errno specified by fseek()
780 ** any value of errno specified by ftruncate()
783 static int
784 sm_bftruncate(fp)
785 SM_FILE_T *fp;
787 struct bf *bfp;
789 if (bfrewind(fp) < 0)
790 return -1;
792 /* Get bf structure */
793 bfp = (struct bf *) fp->f_cookie;
794 bfp->bf_buffilled = 0;
795 bfp->bf_size = 0;
797 /* Need to zero the buffer */
798 if (bfp->bf_bufsize > 0)
799 memset(bfp->bf_buf, '\0', bfp->bf_bufsize);
800 if (bfp->bf_ondisk)
802 #if NOFTRUNCATE
803 /* XXX: Not much we can do except rewind it */
804 errno = EINVAL;
805 return -1;
806 #else /* NOFTRUNCATE */
807 return ftruncate(bfp->bf_disk_fd, 0);
808 #endif /* NOFTRUNCATE */
810 return 0;
814 ** SM_BFSETINFO -- set/change info for an open file pointer
816 ** Parameters:
817 ** fp -- file pointer to get info about
818 ** what -- type of info to set/change
819 ** valp -- thing to set/change the info to
823 static int
824 sm_bfsetinfo(fp, what, valp)
825 SM_FILE_T *fp;
826 int what;
827 void *valp;
829 struct bf *bfp;
830 int bsize;
832 /* Get bf structure */
833 bfp = (struct bf *) fp->f_cookie;
834 switch (what)
836 case SM_BF_SETBUFSIZE:
837 bsize = *((int *) valp);
838 bfp->bf_bufsize = bsize;
840 /* A zero bsize is valid, just don't allocate memory */
841 if (bsize > 0)
843 bfp->bf_buf = (char *) sm_malloc(bsize);
844 if (bfp->bf_buf == NULL)
846 bfp->bf_bufsize = 0;
847 errno = ENOMEM;
848 return -1;
851 else
852 bfp->bf_buf = NULL;
853 return 0;
854 case SM_BF_COMMIT:
855 return sm_bfcommit(fp);
856 case SM_BF_TRUNCATE:
857 return sm_bftruncate(fp);
858 case SM_BF_TEST:
859 return 1; /* always */
860 default:
861 errno = EINVAL;
862 return -1;