summaryrefslogtreecommitdiffstats
path: root/contrib/sendmail/src/bf.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/sendmail/src/bf.c')
-rw-r--r--contrib/sendmail/src/bf.c846
1 files changed, 846 insertions, 0 deletions
diff --git a/contrib/sendmail/src/bf.c b/contrib/sendmail/src/bf.c
new file mode 100644
index 0000000..031b1f7
--- /dev/null
+++ b/contrib/sendmail/src/bf.c
@@ -0,0 +1,846 @@
+/*
+ * Copyright (c) 1999-2001 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.
+ *
+ */
+
+/*
+** This is in transition. Changed from the original bf_torek.c code
+** to use sm_io function calls directly rather than through stdio
+** translation layer. Will be made a built-in file type of libsm
+** next (once safeopen() linkable from libsm).
+*/
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: bf.c,v 8.48 2001/11/04 17:10:49 ca Exp $")
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "sendmail.h"
+#include "bf.h"
+
+#include <syslog.h>
+
+/* bf io functions */
+static ssize_t sm_bfread __P((SM_FILE_T *, char *, size_t));
+static ssize_t sm_bfwrite __P((SM_FILE_T *, const char *, size_t));
+static off_t sm_bfseek __P((SM_FILE_T *, off_t, int));
+static int sm_bfclose __P((SM_FILE_T *));
+
+static int sm_bfopen __P((SM_FILE_T *, const void *, int, const void *));
+static int sm_bfsetinfo __P((SM_FILE_T *, int , void *));
+static int sm_bfgetinfo __P((SM_FILE_T *, int , void *));
+
+/*
+** Data structure for storing information about each buffered file
+** (Originally in sendmail/bf_torek.h for the curious.)
+*/
+
+struct bf
+{
+ bool bf_committed; /* Has this buffered file been committed? */
+ bool bf_ondisk; /* On disk: committed or buffer overflow */
+ long bf_flags;
+ int bf_disk_fd; /* If on disk, associated file descriptor */
+ char *bf_buf; /* Memory buffer */
+ int bf_bufsize; /* Length of above buffer */
+ int bf_buffilled; /* Bytes of buffer actually filled */
+ char *bf_filename; /* Name of buffered file, if ever committed */
+ MODE_T bf_filemode; /* Mode of buffered file, if ever committed */
+ off_t bf_offset; /* Currect file offset */
+ int bf_size; /* Total current size of file */
+ int bf_refcount; /* Reference count */
+};
+
+#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 */
+
+struct bf_info
+{
+ char *bi_filename;
+ MODE_T bi_fmode;
+ size_t bi_bsize;
+ long bi_flags;
+};
+
+/*
+** SM_BFOPEN -- the "base" open function called by sm_io_open() for the
+** internal, file-type-specific info setup.
+**
+** Parameters:
+** fp -- file pointer being filled-in for file being open'd
+** filename -- name of the file being open'd
+** flags -- ignored
+** fmode -- file mode (stored for use later)
+** sflags -- "safeopen" flags (stored for use later)
+** rpool -- ignored (currently)
+**
+** Returns:
+** Failure: -1 and sets errno
+** Success: 0 (zero)
+*/
+
+static int
+sm_bfopen(fp, info, flags, rpool)
+ SM_FILE_T *fp;
+ const void *info;
+ int flags;
+ const void *rpool;
+{
+ char *filename;
+ MODE_T fmode;
+ size_t bsize;
+ long sflags;
+ struct bf *bfp;
+ int l;
+ struct stat st;
+
+ filename = ((struct bf_info *) info)->bi_filename;
+ fmode = ((struct bf_info *) info)->bi_fmode;
+ bsize = ((struct bf_info *) info)->bi_bsize;
+ sflags = ((struct bf_info *) info)->bi_flags;
+
+ /* Sanity checks */
+ if (*filename == '\0')
+ {
+ /* Empty filename string */
+ errno = ENOENT;
+ return -1;
+ }
+ if (stat(filename, &st) == 0)
+ {
+ /* File already exists on disk */
+ errno = EEXIST;
+ return -1;
+ }
+
+ /* Allocate memory */
+ bfp = (struct bf *) sm_malloc(sizeof(struct bf));
+ if (bfp == NULL)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /* Assign data buffer */
+ /* A zero bsize is valid, just don't allocate memory */
+ if (bsize > 0)
+ {
+ bfp->bf_buf = (char *) sm_malloc(bsize);
+ if (bfp->bf_buf == NULL)
+ {
+ bfp->bf_bufsize = 0;
+ sm_free(bfp);
+ errno = ENOMEM;
+ return -1;
+ }
+ }
+ 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 = sflags;
+ bfp->bf_bufsize = bsize;
+ bfp->bf_buffilled = 0;
+ l = strlen(filename) + 1;
+ bfp->bf_filename = (char *) sm_malloc(l);
+ if (bfp->bf_filename == NULL)
+ {
+ if (bfp->bf_buf != NULL)
+ sm_free(bfp->bf_buf);
+ sm_free(bfp);
+ errno = ENOMEM;
+ return -1;
+ }
+ (void) sm_strlcpy(bfp->bf_filename, filename, l);
+ bfp->bf_filemode = fmode;
+ bfp->bf_offset = 0;
+ bfp->bf_size = bsize;
+ bfp->bf_disk_fd = -1;
+ fp->f_cookie = bfp;
+
+ if (tTd(58, 8))
+ sm_dprintf("sm_bfopen(%s)\n", filename);
+
+ return 0;
+}
+
+/*
+** 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 SM_FILE_T * which may then be used with stdio functions,
+** or NULL on failure. SM_FILE_T * is opened for writing
+** "SM_IO_WHAT_VECTORS").
+**
+** Side Effects:
+** none.
+**
+** Sets errno:
+** any value of errno specified by sm_io_setinfo_type()
+** any value of errno specified by sm_io_open()
+** any value of errno specified by sm_io_setinfo()
+*/
+
+SM_FILE_T *
+bfopen(filename, fmode, bsize, flags)
+ char *filename;
+ MODE_T fmode;
+ size_t bsize;
+ long flags;
+{
+ MODE_T omask;
+ SM_FILE_T SM_IO_SET_TYPE(vector, BF_FILE_TYPE, sm_bfopen, sm_bfclose,
+ sm_bfread, sm_bfwrite, sm_bfseek, sm_bfgetinfo, sm_bfsetinfo,
+ SM_TIME_FOREVER);
+ struct bf_info info;
+
+ /*
+ ** Apply current umask to fmode as it may change by the time
+ ** the file is actually created. fmode becomes the true
+ ** permissions of the file, which OPEN() must obey.
+ */
+
+ omask = umask(0);
+ fmode &= ~omask;
+ (void) umask(omask);
+
+ SM_IO_INIT_TYPE(vector, BF_FILE_TYPE, sm_bfopen, sm_bfclose,
+ sm_bfread, sm_bfwrite, sm_bfseek, sm_bfgetinfo, sm_bfsetinfo,
+ SM_TIME_FOREVER);
+ info.bi_filename = filename;
+ info.bi_fmode = fmode;
+ info.bi_bsize = bsize;
+ info.bi_flags = flags;
+
+ return sm_io_open(&vector, SM_TIME_DEFAULT, &info, SM_IO_RDWR, NULL);
+}
+
+/*
+** SM_BFGETINFO -- returns info about an open file pointer
+**
+** Parameters:
+** fp -- file pointer to get info about
+** what -- type of info to obtain
+** valp -- thing to return the info in
+*/
+
+static int
+sm_bfgetinfo(fp, what, valp)
+ SM_FILE_T *fp;
+ int what;
+ void *valp;
+{
+ struct bf *bfp;
+
+ bfp = (struct bf *) fp->f_cookie;
+ switch (what)
+ {
+ case SM_IO_WHAT_FD:
+ return bfp->bf_disk_fd;
+ default:
+ return -1;
+ }
+}
+
+/*
+** SM_BFCLOSE -- close a buffered file
+**
+** Parameters:
+** fp -- cookie of file to close
+**
+** Returns:
+** 0 to indicate success
+**
+** Side Effects:
+** deletes backing file, sm_frees memory.
+**
+** Sets errno:
+** never.
+*/
+
+static int
+sm_bfclose(fp)
+ SM_FILE_T *fp;
+{
+ struct bf *bfp;
+
+ /* Cast cookie back to correct type */
+ bfp = (struct bf *) fp->f_cookie;
+
+ /* Need to clean up the file */
+ if (bfp->bf_ondisk && !bfp->bf_committed)
+ unlink(bfp->bf_filename);
+ sm_free(bfp->bf_filename);
+
+ if (bfp->bf_disk_fd != -1)
+ close(bfp->bf_disk_fd);
+
+ /* Need to sm_free the buffer */
+ if (bfp->bf_bufsize > 0)
+ sm_free(bfp->bf_buf);
+
+ /* Finally, sm_free the structure */
+ sm_free(bfp);
+ return 0;
+}
+
+/*
+** SM_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.
+**
+*/
+
+static ssize_t
+sm_bfread(fp, buf, nbytes)
+ SM_FILE_T *fp;
+ char *buf;
+ size_t nbytes;
+{
+ struct bf *bfp;
+ ssize_t count = 0; /* Number of bytes put in buf so far */
+ int retval;
+
+ /* Cast cookie back to correct type */
+ bfp = (struct bf *) fp->f_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. */
+ if (tTd(58, 8))
+ sm_dprintf("sm_bfread(%s): to disk\n",
+ bfp->bf_filename);
+ 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;
+}
+
+/*
+** SM_BFSEEK -- seek to a position in a buffered file
+**
+** Parameters:
+** fp -- fp of file to seek
+** offset -- position to seek to
+** whence -- how to seek
+**
+** Returns:
+** new file offset or -1 indicate failure
+**
+** Side Effects:
+** none.
+**
+*/
+
+static off_t
+sm_bfseek(fp, offset, whence)
+ SM_FILE_T *fp;
+ off_t offset;
+ int whence;
+
+{
+ struct bf *bfp;
+
+ /* Cast cookie back to correct type */
+ bfp = (struct bf *) fp->f_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;
+}
+
+/*
+** SM_BFWRITE -- write to a buffered file
+**
+** Parameters:
+** fp -- fp 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.
+**
+*/
+
+static ssize_t
+sm_bfwrite(fp, buf, nbytes)
+ SM_FILE_T *fp;
+ const char *buf;
+ size_t nbytes;
+{
+ struct bf *bfp;
+ ssize_t count = 0; /* Number of bytes written so far */
+ int retval;
+
+ /* Cast cookie back to correct type */
+ bfp = (struct bf *) fp->f_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)
+ {
+ MODE_T omask;
+
+ /* Clear umask as bf_filemode are the true perms */
+ omask = umask(0);
+ retval = OPEN(bfp->bf_filename,
+ O_RDWR | O_CREAT | O_TRUNC,
+ bfp->bf_filemode, bfp->bf_flags);
+ (void) umask(omask);
+
+ /* 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
+#ifdef EDQUOT
+ || errno == EDQUOT
+#endif /* 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;
+}
+
+/*
+** BFREWIND -- rewinds the SM_FILE_T *
+**
+** Parameters:
+** fp -- SM_FILE_T * to rewind
+**
+** Returns:
+** 0 on success, -1 on error
+**
+** Side Effects:
+** rewinds the SM_FILE_T * 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 sm_io_rewind()
+*/
+
+int
+bfrewind(fp)
+ SM_FILE_T *fp;
+{
+ (void) sm_io_flush(fp, SM_TIME_DEFAULT);
+ sm_io_clearerr(fp); /* quicker just to do it */
+ return sm_io_seek(fp, SM_TIME_DEFAULT, 0, SM_IO_SEEK_SET);
+}
+
+/*
+** SM_BFCOMMIT -- "commits" the buffered file
+**
+** Parameters:
+** fp -- SM_FILE_T * to commit to disk
+**
+** Returns:
+** 0 on success, -1 on error
+**
+** Side Effects:
+** Forces the given SM_FILE_T * 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()
+*/
+
+static int
+sm_bfcommit(fp)
+ SM_FILE_T *fp;
+{
+ struct bf *bfp;
+ int retval;
+ int byteswritten;
+
+ /* Get associated bf structure */
+ bfp = (struct bf *) fp->f_cookie;
+
+ /* If already committed, noop */
+ if (bfp->bf_committed)
+ return 0;
+
+ /* Do we need to open a file? */
+ if (!bfp->bf_ondisk)
+ {
+ MODE_T omask;
+ struct stat st;
+
+ if (tTd(58, 8))
+ {
+ sm_dprintf("bfcommit(%s): to disk\n", bfp->bf_filename);
+ if (tTd(58, 32))
+ sm_dprintf("bfcommit(): filemode %o flags %ld\n",
+ bfp->bf_filemode, bfp->bf_flags);
+ }
+
+ if (stat(bfp->bf_filename, &st) == 0)
+ {
+ errno = EEXIST;
+ return -1;
+ }
+
+ /* Clear umask as bf_filemode are the true perms */
+ omask = umask(0);
+ retval = OPEN(bfp->bf_filename, O_RDWR | O_CREAT | O_TRUNC,
+ bfp->bf_filemode, bfp->bf_flags);
+ (void) umask(omask);
+
+ /* 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;
+ sm_free(bfp->bf_buf);
+ }
+ return 0;
+}
+
+/*
+** SM_BFTRUNCATE -- rewinds and truncates the SM_FILE_T *
+**
+** Parameters:
+** fp -- SM_FILE_T * to truncate
+**
+** Returns:
+** 0 on success, -1 on error
+**
+** Side Effects:
+** rewinds the SM_FILE_T *, truncates it to zero length, and puts
+** it into write mode.
+**
+** Sets errno:
+** any value of errno specified by fseek()
+** any value of errno specified by ftruncate()
+*/
+
+static int
+sm_bftruncate(fp)
+ SM_FILE_T *fp;
+{
+ struct bf *bfp;
+
+ if (bfrewind(fp) < 0)
+ return -1;
+
+ /* Get bf structure */
+ bfp = (struct bf *) fp->f_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)
+ {
+#if NOFTRUNCATE
+ /* XXX: Not much we can do except rewind it */
+ errno = EINVAL;
+ return -1;
+#else /* NOFTRUNCATE */
+ return ftruncate(bfp->bf_disk_fd, 0);
+#endif /* NOFTRUNCATE */
+ }
+ else
+ return 0;
+}
+
+/*
+** SM_BFSETINFO -- set/change info for an open file pointer
+**
+** Parameters:
+** fp -- file pointer to get info about
+** what -- type of info to set/change
+** valp -- thing to set/change the info to
+**
+*/
+
+static int
+sm_bfsetinfo(fp, what, valp)
+ SM_FILE_T *fp;
+ int what;
+ void *valp;
+{
+ struct bf *bfp;
+ int bsize;
+
+ /* Get bf structure */
+ bfp = (struct bf *) fp->f_cookie;
+ switch (what)
+ {
+ case SM_BF_SETBUFSIZE:
+ bsize = *((int *) valp);
+ bfp->bf_bufsize = bsize;
+
+ /* A zero bsize is valid, just don't allocate memory */
+ if (bsize > 0)
+ {
+ bfp->bf_buf = (char *) sm_malloc(bsize);
+ if (bfp->bf_buf == NULL)
+ {
+ bfp->bf_bufsize = 0;
+ errno = ENOMEM;
+ return -1;
+ }
+ }
+ else
+ bfp->bf_buf = NULL;
+ return 0;
+ case SM_BF_COMMIT:
+ return sm_bfcommit(fp);
+ case SM_BF_TRUNCATE:
+ return sm_bftruncate(fp);
+ case SM_BF_TEST:
+ return 1; /* always */
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+}
OpenPOWER on IntegriCloud