diff options
Diffstat (limited to 'contrib/sendmail/src/bf_torek.c')
-rw-r--r-- | contrib/sendmail/src/bf_torek.c | 784 |
1 files changed, 784 insertions, 0 deletions
diff --git a/contrib/sendmail/src/bf_torek.c b/contrib/sendmail/src/bf_torek.c new file mode 100644 index 0000000..fb05368 --- /dev/null +++ b/contrib/sendmail/src/bf_torek.c @@ -0,0 +1,784 @@ +/* + * Copyright (c) 1999 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + * Contributed by Exactis.com, Inc. + * + */ + +#ifndef lint +static char id[] = "@(#)$Id: bf_torek.c,v 8.19.18.1 2000/07/18 16:52:26 gshapiro Exp $"; +#endif /* ! lint */ + +#if SFIO + ERROR README: Can not use bf_torek.c with SFIO. +#endif /* SFIO */ + +#include <sys/types.h> +#include <sys/uio.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> +#ifndef BF_STANDALONE +# include "sendmail.h" +#endif /* ! BF_STANDALONE */ +#include "bf_torek.h" +#include "bf.h" + +/* +** BFOPEN -- create a new buffered file +** +** Parameters: +** filename -- the file's name +** fmode -- what mode the file should be created as +** bsize -- amount of buffer space to allocate (may be 0) +** flags -- if running under sendmail, passed directly to safeopen +** +** Returns: +** a FILE * which may then be used with stdio functions, or NULL +** on failure. FILE * is opened for writing (mode "w+"). +** +** Side Effects: +** none. +** +** Sets errno: +** ENOMEM -- out of memory +** ENOENT -- illegal empty filename specified +** any value of errno specified by open() +** any value of errno specified by fdopen() +** any value of errno specified by funopen() +*/ + +#ifdef BF_STANDALONE +# define OPEN(fn, omode, cmode, sff) open(fn, omode, cmode) +#else /* BF_STANDALONE */ +# define OPEN(fn, omode, cmode, sff) safeopen(fn, omode, cmode, sff) +#endif /* BF_STANDALONE */ + +FILE * +bfopen(filename, fmode, bsize, flags) + char *filename; + int fmode; + size_t bsize; + long flags; +{ + struct bf *bfp; + FILE *retval; + int save_errno, l; + struct stat st; + + /* Sanity checks */ + /* Empty filename string */ + if (*filename == '\0') + { + errno = ENOENT; + return NULL; + } + + if (stat(filename, &st) == 0) + { + /* file already exists on disk */ + errno = EEXIST; + return NULL; + } + + /* Allocate memory */ + bfp = (struct bf *)malloc(sizeof(struct bf)); + if (bfp == NULL) + { + errno = ENOMEM; + return NULL; + } + + /* A zero bsize is valid, just don't allocate memory */ + if (bsize > 0) + { + bfp->bf_buf = (char *)malloc(bsize); + if (bfp->bf_buf == NULL) + { + free(bfp); + errno = ENOMEM; + return NULL; + } + } + else + bfp->bf_buf = NULL; + + /* Nearly home free, just set all the parameters now */ + bfp->bf_committed = FALSE; + bfp->bf_ondisk = FALSE; + bfp->bf_refcount = 1; + bfp->bf_flags = flags; + bfp->bf_bufsize = bsize; + bfp->bf_buffilled = 0; + l = strlen(filename) + 1; + bfp->bf_filename = (char *)malloc(l); + if (bfp->bf_filename == NULL) + { + free(bfp); + if (bfp->bf_buf != NULL) + free(bfp->bf_buf); + errno = ENOMEM; + return NULL; + } + (void) strlcpy(bfp->bf_filename, filename, l); + bfp->bf_filemode = fmode; + bfp->bf_offset = 0; + bfp->bf_size = 0; + + if (tTd(58, 8)) + dprintf("bfopen(%s, %d)\n", filename, bsize); + + /* The big test: will funopen accept it? */ + retval = funopen((void *)bfp, _bfread, _bfwrite, _bfseek, _bfclose); + if (retval == NULL) + { + /* Just in case free() sets errno */ + save_errno = errno; + free(bfp); + free(bfp->bf_filename); + if (bfp->bf_buf != NULL) + free(bfp->bf_buf); + errno = save_errno; + return NULL; + } + else + { + /* Success */ + return retval; + } +} +/* +** BFDUP -- increase refcount on buffered file +** +** Parameters: +** fp -- FILE * to "duplicate" +** +** Returns: +** If file is memory buffered, fp with increased refcount +** If file is on disk, NULL (need to use link()) +*/ + +FILE * +bfdup(fp) + FILE *fp; +{ + struct bf *bfp; + + /* If called on a normal FILE *, noop */ + if (!bftest(fp)) + return NULL; + + /* Get associated bf structure */ + bfp = (struct bf *)fp->_cookie; + + /* Increase ref count */ + bfp->bf_refcount++; + + return fp; +} + +/* +** BFCOMMIT -- "commits" the buffered file +** +** Parameters: +** fp -- FILE * to commit to disk +** +** Returns: +** 0 on success, -1 on error +** +** Side Effects: +** Forces the given FILE * to be written to disk if it is not +** already, and ensures that it will be kept after closing. If +** fp is not a buffered file, this is a no-op. +** +** Sets errno: +** any value of errno specified by open() +** any value of errno specified by write() +** any value of errno specified by lseek() +*/ + +int +bfcommit(fp) + FILE *fp; +{ + struct bf *bfp; + int retval; + int byteswritten; + + /* If called on a normal FILE *, noop */ + if (!bftest(fp)) + return 0; + + /* Get associated bf structure */ + bfp = (struct bf *)fp->_cookie; + + /* If already committed, noop */ + if (bfp->bf_committed) + return 0; + + /* Do we need to open a file? */ + if (!bfp->bf_ondisk) + { + struct stat st; + + if (tTd(58, 8)) + dprintf("bfcommit(%s): to disk\n", bfp->bf_filename); + + if (stat(bfp->bf_filename, &st) == 0) + { + errno = EEXIST; + return -1; + } + + retval = OPEN(bfp->bf_filename, O_RDWR | O_CREAT | O_TRUNC, + bfp->bf_filemode, bfp->bf_flags); + + /* Couldn't create file: failure */ + if (retval < 0) + { + /* errno is set implicitly by open() */ + return -1; + } + + bfp->bf_disk_fd = retval; + bfp->bf_ondisk = TRUE; + } + + /* Write out the contents of our buffer, if we have any */ + if (bfp->bf_buffilled > 0) + { + byteswritten = 0; + + if (lseek(bfp->bf_disk_fd, 0, SEEK_SET) < 0) + { + /* errno is set implicitly by lseek() */ + return -1; + } + + while (byteswritten < bfp->bf_buffilled) + { + retval = write(bfp->bf_disk_fd, + bfp->bf_buf + byteswritten, + bfp->bf_buffilled - byteswritten); + if (retval < 0) + { + /* errno is set implicitly by write() */ + return -1; + } + else + byteswritten += retval; + } + } + bfp->bf_committed = TRUE; + + /* Invalidate buf; all goes to file now */ + bfp->bf_buffilled = 0; + if (bfp->bf_bufsize > 0) + { + /* Don't need buffer anymore; free it */ + bfp->bf_bufsize = 0; + free(bfp->bf_buf); + } + return 0; +} + +/* +** BFREWIND -- rewinds the FILE * +** +** Parameters: +** fp -- FILE * to rewind +** +** Returns: +** 0 on success, -1 on error +** +** Side Effects: +** rewinds the FILE * and puts it into read mode. Normally one +** would bfopen() a file, write to it, then bfrewind() and +** fread(). If fp is not a buffered file, this is equivalent to +** rewind(). +** +** Sets errno: +** any value of errno specified by fseek() +*/ + +int +bfrewind(fp) + FILE *fp; +{ + int err; + + /* check to see if there is an error on the stream */ + err = ferror(fp); + + (void) fflush(fp); + + /* + ** Clear error if tried to fflush() + ** a read-only file pointer and + ** there wasn't a previous error. + */ + + if (err == 0) + clearerr(fp); + + /* errno is set implicitly by fseek() before return */ + return fseek(fp, 0, SEEK_SET); +} + +/* +** BFTRUNCATE -- rewinds and truncates the FILE * +** +** Parameters: +** fp -- FILE * to truncate +** +** Returns: +** 0 on success, -1 on error +** +** Side Effects: +** rewinds the FILE *, truncates it to zero length, and puts it +** into write mode. If fp is not a buffered file, this is +** equivalent to a rewind() and then an ftruncate(fileno(fp), 0). +** +** Sets errno: +** any value of errno specified by fseek() +** any value of errno specified by ftruncate() +*/ + +int +bftruncate(fp) + FILE *fp; +{ + struct bf *bfp; + + if (bfrewind(fp) < 0) + return -1; + + if (bftest(fp)) + { + /* Get bf structure */ + bfp = (struct bf *)fp->_cookie; + bfp->bf_buffilled = 0; + bfp->bf_size = 0; + + /* Need to zero the buffer */ + if (bfp->bf_bufsize > 0) + memset(bfp->bf_buf, '\0', bfp->bf_bufsize); + if (bfp->bf_ondisk) + return ftruncate(bfp->bf_disk_fd, 0); + else + return 0; + } + else + return ftruncate(fileno(fp), 0); +} + +/* +** BFCLOSE -- close a buffered file +** +** Parameters: +** fp -- FILE * to close +** +** Returns: +** 0 on success, EOF on failure +** +** Side Effects: +** Closes fp. If fp is a buffered file, unlink it if it has not +** already been committed. If fp is not a buffered file, this is +** equivalent to fclose(). +** +** Sets errno: +** any value of errno specified by fclose() +*/ + +int +bfclose(fp) + FILE *fp; +{ + struct bf *bfp; + + /* If called on a normal FILE *, call fclose() on it */ + if (!bftest(fp)) + return fclose(fp); + + /* Cast cookie back to correct type */ + bfp = (struct bf *)fp->_cookie; + + /* Check reference count to see if we actually want to close */ + if (bfp != NULL && --bfp->bf_refcount > 0) + return 0; + + /* + ** In this implementation, just call fclose--the _bfclose + ** routine will be called by that + */ + + return fclose(fp); +} + +/* +** BFTEST -- test if a FILE * is a buffered file +** +** Parameters: +** fp -- FILE * to test +** +** Returns: +** TRUE if fp is a buffered file, FALSE otherwise. +** +** Side Effects: +** none. +** +** Sets errno: +** never. +*/ + +bool +bftest(fp) + FILE *fp; +{ + /* + ** Check to see if our special I/O routines are installed + ** in this file structure + */ + + return ((fp->_close == _bfclose) && + (fp->_read == _bfread) && + (fp->_seek == _bfseek) && + (fp->_write == _bfwrite)); +} + +/* +** _BFCLOSE -- close a buffered file +** +** Parameters: +** cookie -- cookie of file to close +** +** Returns: +** 0 to indicate success +** +** Side Effects: +** deletes backing file, frees memory. +** +** Sets errno: +** never. +*/ + +int +_bfclose(cookie) + void *cookie; +{ + struct bf *bfp; + + /* Cast cookie back to correct type */ + bfp = (struct bf *)cookie; + + /* Need to clean up the file */ + if (bfp->bf_ondisk && !bfp->bf_committed) + unlink(bfp->bf_filename); + + /* Need to free the buffer */ + if (bfp->bf_bufsize > 0) + free(bfp->bf_buf); + + /* Finally, free the structure */ + free(bfp); + + return 0; +} + +/* +** _BFREAD -- read a buffered file +** +** Parameters: +** cookie -- cookie of file to read +** buf -- buffer to fill +** nbytes -- how many bytes to read +** +** Returns: +** number of bytes read or -1 indicate failure +** +** Side Effects: +** none. +** +*/ + +int +_bfread(cookie, buf, nbytes) + void *cookie; + char *buf; + int nbytes; +{ + struct bf *bfp; + int count = 0; /* Number of bytes put in buf so far */ + int retval; + + /* Cast cookie back to correct type */ + bfp = (struct bf *)cookie; + + if (bfp->bf_offset < bfp->bf_buffilled) + { + /* Need to grab some from buffer */ + count = nbytes; + if ((bfp->bf_offset + count) > bfp->bf_buffilled) + count = bfp->bf_buffilled - bfp->bf_offset; + + memcpy(buf, bfp->bf_buf + bfp->bf_offset, count); + } + + if ((bfp->bf_offset + nbytes) > bfp->bf_buffilled) + { + /* Need to grab some from file */ + + if (!bfp->bf_ondisk) + { + /* Oops, the file doesn't exist. EOF. */ + goto finished; + } + + /* Catch a read() on an earlier failed write to disk */ + if (bfp->bf_disk_fd < 0) + { + errno = EIO; + return -1; + } + + if (lseek(bfp->bf_disk_fd, + bfp->bf_offset + count, SEEK_SET) < 0) + { + if ((errno == EINVAL) || (errno == ESPIPE)) + { + /* + ** stdio won't be expecting these + ** errnos from read()! Change them + ** into something it can understand. + */ + + errno = EIO; + } + return -1; + } + + while (count < nbytes) + { + retval = read(bfp->bf_disk_fd, + buf + count, + nbytes - count); + if (retval < 0) + { + /* errno is set implicitly by read() */ + return -1; + } + else if (retval == 0) + goto finished; + else + count += retval; + } + } + +finished: + bfp->bf_offset += count; + return count; +} + +/* +** _BFSEEK -- seek to a position in a buffered file +** +** Parameters: +** cookie -- cookie of file to seek +** offset -- position to seek to +** whence -- how to seek +** +** Returns: +** new file offset or -1 indicate failure +** +** Side Effects: +** none. +** +*/ + +fpos_t +_bfseek(cookie, offset, whence) + void *cookie; + fpos_t offset; + int whence; + +{ + struct bf *bfp; + + /* Cast cookie back to correct type */ + bfp = (struct bf *)cookie; + + switch (whence) + { + case SEEK_SET: + bfp->bf_offset = offset; + break; + + case SEEK_CUR: + bfp->bf_offset += offset; + break; + + case SEEK_END: + bfp->bf_offset = bfp->bf_size + offset; + break; + + default: + errno = EINVAL; + return -1; + } + return bfp->bf_offset; +} + +/* +** _BFWRITE -- write to a buffered file +** +** Parameters: +** cookie -- cookie of file to write +** buf -- data buffer +** nbytes -- how many bytes to write +** +** Returns: +** number of bytes written or -1 indicate failure +** +** Side Effects: +** may create backing file if over memory limit for file. +** +*/ + +int +_bfwrite(cookie, buf, nbytes) + void *cookie; + const char *buf; + int nbytes; +{ + struct bf *bfp; + int count = 0; /* Number of bytes written so far */ + int retval; + + /* Cast cookie back to correct type */ + bfp = (struct bf *)cookie; + + /* If committed, go straight to disk */ + if (bfp->bf_committed) + { + if (lseek(bfp->bf_disk_fd, bfp->bf_offset, SEEK_SET) < 0) + { + if ((errno == EINVAL) || (errno == ESPIPE)) + { + /* + ** stdio won't be expecting these + ** errnos from write()! Change them + ** into something it can understand. + */ + + errno = EIO; + } + return -1; + } + + count = write(bfp->bf_disk_fd, buf, nbytes); + if (count < 0) + { + /* errno is set implicitly by write() */ + return -1; + } + goto finished; + } + + if (bfp->bf_offset < bfp->bf_bufsize) + { + /* Need to put some in buffer */ + count = nbytes; + if ((bfp->bf_offset + count) > bfp->bf_bufsize) + count = bfp->bf_bufsize - bfp->bf_offset; + + memcpy(bfp->bf_buf + bfp->bf_offset, buf, count); + if ((bfp->bf_offset + count) > bfp->bf_buffilled) + bfp->bf_buffilled = bfp->bf_offset + count; + } + + if ((bfp->bf_offset + nbytes) > bfp->bf_bufsize) + { + /* Need to put some in file */ + if (!bfp->bf_ondisk) + { + /* Oops, the file doesn't exist. */ + if (tTd(58, 8)) + dprintf("_bfwrite(%s): to disk\n", + bfp->bf_filename); + + retval = OPEN(bfp->bf_filename, + O_RDWR | O_CREAT | O_TRUNC, + bfp->bf_filemode, bfp->bf_flags); + + /* Couldn't create file: failure */ + if (retval < 0) + { + /* + ** stdio may not be expecting these + ** errnos from write()! Change to + ** something which it can understand. + ** Note that ENOSPC and EDQUOT are saved + ** because they are actually valid for + ** write(). + */ + + if (!((errno == ENOSPC) || (errno == EDQUOT))) + errno = EIO; + + return -1; + } + bfp->bf_disk_fd = retval; + bfp->bf_ondisk = TRUE; + } + + /* Catch a write() on an earlier failed write to disk */ + if (bfp->bf_ondisk && bfp->bf_disk_fd < 0) + { + errno = EIO; + return -1; + } + + if (lseek(bfp->bf_disk_fd, + bfp->bf_offset + count, SEEK_SET) < 0) + { + if ((errno == EINVAL) || (errno == ESPIPE)) + { + /* + ** stdio won't be expecting these + ** errnos from write()! Change them into + ** something which it can understand. + */ + + errno = EIO; + } + return -1; + } + + while (count < nbytes) + { + retval = write(bfp->bf_disk_fd, buf + count, + nbytes - count); + if (retval < 0) + { + /* errno is set implicitly by write() */ + return -1; + } + else + count += retval; + } + } + +finished: + bfp->bf_offset += count; + if (bfp->bf_offset > bfp->bf_size) + bfp->bf_size = bfp->bf_offset; + return count; +} |