1N/A/*
1N/A * Copyright (c) 1999-2002, 2004, 2006 Sendmail, Inc. and its suppliers.
1N/A * All rights reserved.
1N/A *
1N/A * By using this file, you agree to the terms and conditions set
1N/A * forth in the LICENSE file which can be found at the top level of
1N/A * the sendmail distribution.
1N/A *
1N/A * Contributed by Exactis.com, Inc.
1N/A *
1N/A */
1N/A
1N/A#pragma ident "%Z%%M% %I% %E% SMI"
1N/A
1N/A/*
1N/A** This is in transition. Changed from the original bf_torek.c code
1N/A** to use sm_io function calls directly rather than through stdio
1N/A** translation layer. Will be made a built-in file type of libsm
1N/A** next (once safeopen() linkable from libsm).
1N/A*/
1N/A
1N/A#include <sm/gen.h>
1N/ASM_RCSID("@(#)$Id: bf.c,v 8.62 2006/03/31 18:45:56 ca Exp $")
1N/A
1N/A#include <sys/types.h>
1N/A#include <sys/stat.h>
1N/A#include <sys/uio.h>
1N/A#include <fcntl.h>
1N/A#include <unistd.h>
1N/A#include <stdlib.h>
1N/A#include <string.h>
1N/A#include <errno.h>
1N/A#include "sendmail.h"
1N/A#include "bf.h"
1N/A
1N/A#include <syslog.h>
1N/A
1N/A/* bf io functions */
1N/Astatic ssize_t sm_bfread __P((SM_FILE_T *, char *, size_t));
1N/Astatic ssize_t sm_bfwrite __P((SM_FILE_T *, const char *, size_t));
1N/Astatic off_t sm_bfseek __P((SM_FILE_T *, off_t, int));
1N/Astatic int sm_bfclose __P((SM_FILE_T *));
1N/Astatic int sm_bfcommit __P((SM_FILE_T *));
1N/Astatic int sm_bftruncate __P((SM_FILE_T *));
1N/A
1N/Astatic int sm_bfopen __P((SM_FILE_T *, const void *, int, const void *));
1N/Astatic int sm_bfsetinfo __P((SM_FILE_T *, int , void *));
1N/Astatic int sm_bfgetinfo __P((SM_FILE_T *, int , void *));
1N/A
1N/A/*
1N/A** Data structure for storing information about each buffered file
1N/A** (Originally in sendmail/bf_torek.h for the curious.)
1N/A*/
1N/A
1N/Astruct bf
1N/A{
1N/A bool bf_committed; /* Has this buffered file been committed? */
1N/A bool bf_ondisk; /* On disk: committed or buffer overflow */
1N/A long bf_flags;
1N/A int bf_disk_fd; /* If on disk, associated file descriptor */
1N/A char *bf_buf; /* Memory buffer */
1N/A int bf_bufsize; /* Length of above buffer */
1N/A int bf_buffilled; /* Bytes of buffer actually filled */
1N/A char *bf_filename; /* Name of buffered file, if ever committed */
1N/A MODE_T bf_filemode; /* Mode of buffered file, if ever committed */
1N/A off_t bf_offset; /* Currect file offset */
1N/A int bf_size; /* Total current size of file */
1N/A};
1N/A
1N/A#ifdef BF_STANDALONE
1N/A# define OPEN(fn, omode, cmode, sff) open(fn, omode, cmode)
1N/A#else /* BF_STANDALONE */
1N/A# define OPEN(fn, omode, cmode, sff) safeopen(fn, omode, cmode, sff)
1N/A#endif /* BF_STANDALONE */
1N/A
1N/Astruct bf_info
1N/A{
1N/A char *bi_filename;
1N/A MODE_T bi_fmode;
1N/A size_t bi_bsize;
1N/A long bi_flags;
1N/A};
1N/A
1N/A/*
1N/A** SM_BFOPEN -- the "base" open function called by sm_io_open() for the
1N/A** internal, file-type-specific info setup.
1N/A**
1N/A** Parameters:
1N/A** fp -- file pointer being filled-in for file being open'd
1N/A** info -- information about file being opened
1N/A** flags -- ignored
1N/A** rpool -- ignored (currently)
1N/A**
1N/A** Returns:
1N/A** Failure: -1 and sets errno
1N/A** Success: 0 (zero)
1N/A*/
1N/A
1N/Astatic int
1N/Asm_bfopen(fp, info, flags, rpool)
1N/A SM_FILE_T *fp;
1N/A const void *info;
1N/A int flags;
1N/A const void *rpool;
1N/A{
1N/A char *filename;
1N/A MODE_T fmode;
1N/A size_t bsize;
1N/A long sflags;
1N/A struct bf *bfp;
1N/A int l;
1N/A struct stat st;
1N/A
1N/A filename = ((struct bf_info *) info)->bi_filename;
1N/A fmode = ((struct bf_info *) info)->bi_fmode;
1N/A bsize = ((struct bf_info *) info)->bi_bsize;
1N/A sflags = ((struct bf_info *) info)->bi_flags;
1N/A
1N/A /* Sanity checks */
1N/A if (*filename == '\0')
1N/A {
1N/A /* Empty filename string */
1N/A errno = ENOENT;
1N/A return -1;
1N/A }
1N/A if (stat(filename, &st) == 0)
1N/A {
1N/A /* File already exists on disk */
1N/A errno = EEXIST;
1N/A return -1;
1N/A }
1N/A
1N/A /* Allocate memory */
1N/A bfp = (struct bf *) sm_malloc(sizeof(struct bf));
1N/A if (bfp == NULL)
1N/A {
1N/A errno = ENOMEM;
1N/A return -1;
1N/A }
1N/A
1N/A /* Assign data buffer */
1N/A /* A zero bsize is valid, just don't allocate memory */
1N/A if (bsize > 0)
1N/A {
1N/A bfp->bf_buf = (char *) sm_malloc(bsize);
1N/A if (bfp->bf_buf == NULL)
1N/A {
1N/A bfp->bf_bufsize = 0;
1N/A sm_free(bfp);
1N/A errno = ENOMEM;
1N/A return -1;
1N/A }
1N/A }
1N/A else
1N/A bfp->bf_buf = NULL;
1N/A
1N/A /* Nearly home free, just set all the parameters now */
1N/A bfp->bf_committed = false;
1N/A bfp->bf_ondisk = false;
1N/A bfp->bf_flags = sflags;
1N/A bfp->bf_bufsize = bsize;
1N/A bfp->bf_buffilled = 0;
1N/A l = strlen(filename) + 1;
1N/A bfp->bf_filename = (char *) sm_malloc(l);
1N/A if (bfp->bf_filename == NULL)
1N/A {
1N/A if (bfp->bf_buf != NULL)
1N/A sm_free(bfp->bf_buf);
1N/A sm_free(bfp);
1N/A errno = ENOMEM;
1N/A return -1;
1N/A }
1N/A (void) sm_strlcpy(bfp->bf_filename, filename, l);
1N/A bfp->bf_filemode = fmode;
1N/A bfp->bf_offset = 0;
1N/A bfp->bf_size = 0;
1N/A bfp->bf_disk_fd = -1;
1N/A fp->f_cookie = bfp;
1N/A
1N/A if (tTd(58, 8))
1N/A sm_dprintf("sm_bfopen(%s)\n", filename);
1N/A
1N/A return 0;
1N/A}
1N/A
1N/A/*
1N/A** BFOPEN -- create a new buffered file
1N/A**
1N/A** Parameters:
1N/A** filename -- the file's name
1N/A** fmode -- what mode the file should be created as
1N/A** bsize -- amount of buffer space to allocate (may be 0)
1N/A** flags -- if running under sendmail, passed directly to safeopen
1N/A**
1N/A** Returns:
1N/A** a SM_FILE_T * which may then be used with stdio functions,
1N/A** or NULL on failure. SM_FILE_T * is opened for writing
1N/A** "SM_IO_WHAT_VECTORS").
1N/A**
1N/A** Side Effects:
1N/A** none.
1N/A**
1N/A** Sets errno:
1N/A** any value of errno specified by sm_io_setinfo_type()
1N/A** any value of errno specified by sm_io_open()
1N/A** any value of errno specified by sm_io_setinfo()
1N/A*/
1N/A
1N/A#ifdef __STDC__
1N/A/*
1N/A** XXX This is a temporary hack since MODE_T on HP-UX 10.x is short.
1N/A** If we use K&R here, the compiler will complain about
1N/A** Inconsistent parameter list declaration
1N/A** due to the change from short to int.
1N/A*/
1N/A
1N/ASM_FILE_T *
1N/Abfopen(char *filename, MODE_T fmode, size_t bsize, long flags)
1N/A#else /* __STDC__ */
1N/ASM_FILE_T *
1N/Abfopen(filename, fmode, bsize, flags)
1N/A char *filename;
1N/A MODE_T fmode;
1N/A size_t bsize;
1N/A long flags;
1N/A#endif /* __STDC__ */
1N/A{
1N/A MODE_T omask;
1N/A SM_FILE_T SM_IO_SET_TYPE(vector, BF_FILE_TYPE, sm_bfopen, sm_bfclose,
1N/A sm_bfread, sm_bfwrite, sm_bfseek, sm_bfgetinfo, sm_bfsetinfo,
1N/A SM_TIME_FOREVER);
1N/A struct bf_info info;
1N/A
1N/A /*
1N/A ** Apply current umask to fmode as it may change by the time
1N/A ** the file is actually created. fmode becomes the true
1N/A ** permissions of the file, which OPEN() must obey.
1N/A */
1N/A
1N/A omask = umask(0);
1N/A fmode &= ~omask;
1N/A (void) umask(omask);
1N/A
1N/A SM_IO_INIT_TYPE(vector, BF_FILE_TYPE, sm_bfopen, sm_bfclose,
1N/A sm_bfread, sm_bfwrite, sm_bfseek, sm_bfgetinfo, sm_bfsetinfo,
1N/A SM_TIME_FOREVER);
1N/A info.bi_filename = filename;
1N/A info.bi_fmode = fmode;
1N/A info.bi_bsize = bsize;
1N/A info.bi_flags = flags;
1N/A
1N/A return sm_io_open(&vector, SM_TIME_DEFAULT, &info, SM_IO_RDWR, NULL);
1N/A}
1N/A
1N/A/*
1N/A** SM_BFGETINFO -- returns info about an open file pointer
1N/A**
1N/A** Parameters:
1N/A** fp -- file pointer to get info about
1N/A** what -- type of info to obtain
1N/A** valp -- thing to return the info in
1N/A*/
1N/A
1N/Astatic int
1N/Asm_bfgetinfo(fp, what, valp)
1N/A SM_FILE_T *fp;
1N/A int what;
1N/A void *valp;
1N/A{
1N/A struct bf *bfp;
1N/A
1N/A bfp = (struct bf *) fp->f_cookie;
1N/A switch (what)
1N/A {
1N/A case SM_IO_WHAT_FD:
1N/A return bfp->bf_disk_fd;
1N/A case SM_IO_WHAT_SIZE:
1N/A return bfp->bf_size;
1N/A default:
1N/A return -1;
1N/A }
1N/A}
1N/A
1N/A/*
1N/A** SM_BFCLOSE -- close a buffered file
1N/A**
1N/A** Parameters:
1N/A** fp -- cookie of file to close
1N/A**
1N/A** Returns:
1N/A** 0 to indicate success
1N/A**
1N/A** Side Effects:
1N/A** deletes backing file, sm_frees memory.
1N/A**
1N/A** Sets errno:
1N/A** never.
1N/A*/
1N/A
1N/Astatic int
1N/Asm_bfclose(fp)
1N/A SM_FILE_T *fp;
1N/A{
1N/A struct bf *bfp;
1N/A
1N/A /* Cast cookie back to correct type */
1N/A bfp = (struct bf *) fp->f_cookie;
1N/A
1N/A /* Need to clean up the file */
1N/A if (bfp->bf_ondisk && !bfp->bf_committed)
1N/A unlink(bfp->bf_filename);
1N/A sm_free(bfp->bf_filename);
1N/A
1N/A if (bfp->bf_disk_fd != -1)
1N/A close(bfp->bf_disk_fd);
1N/A
1N/A /* Need to sm_free the buffer */
1N/A if (bfp->bf_bufsize > 0)
1N/A sm_free(bfp->bf_buf);
1N/A
1N/A /* Finally, sm_free the structure */
1N/A sm_free(bfp);
1N/A return 0;
1N/A}
1N/A
1N/A/*
1N/A** SM_BFREAD -- read a buffered file
1N/A**
1N/A** Parameters:
1N/A** cookie -- cookie of file to read
1N/A** buf -- buffer to fill
1N/A** nbytes -- how many bytes to read
1N/A**
1N/A** Returns:
1N/A** number of bytes read or -1 indicate failure
1N/A**
1N/A** Side Effects:
1N/A** none.
1N/A**
1N/A*/
1N/A
1N/Astatic ssize_t
1N/Asm_bfread(fp, buf, nbytes)
1N/A SM_FILE_T *fp;
1N/A char *buf;
1N/A size_t nbytes;
1N/A{
1N/A struct bf *bfp;
1N/A ssize_t count = 0; /* Number of bytes put in buf so far */
1N/A int retval;
1N/A
1N/A /* Cast cookie back to correct type */
1N/A bfp = (struct bf *) fp->f_cookie;
1N/A
1N/A if (bfp->bf_offset < bfp->bf_buffilled)
1N/A {
1N/A /* Need to grab some from buffer */
1N/A count = nbytes;
1N/A if ((bfp->bf_offset + count) > bfp->bf_buffilled)
1N/A count = bfp->bf_buffilled - bfp->bf_offset;
1N/A
1N/A memcpy(buf, bfp->bf_buf + bfp->bf_offset, count);
1N/A }
1N/A
1N/A if ((bfp->bf_offset + nbytes) > bfp->bf_buffilled)
1N/A {
1N/A /* Need to grab some from file */
1N/A if (!bfp->bf_ondisk)
1N/A {
1N/A /* Oops, the file doesn't exist. EOF. */
1N/A if (tTd(58, 8))
1N/A sm_dprintf("sm_bfread(%s): to disk\n",
1N/A bfp->bf_filename);
1N/A goto finished;
1N/A }
1N/A
1N/A /* Catch a read() on an earlier failed write to disk */
1N/A if (bfp->bf_disk_fd < 0)
1N/A {
1N/A errno = EIO;
1N/A return -1;
1N/A }
1N/A
1N/A if (lseek(bfp->bf_disk_fd,
1N/A bfp->bf_offset + count, SEEK_SET) < 0)
1N/A {
1N/A if ((errno == EINVAL) || (errno == ESPIPE))
1N/A {
1N/A /*
1N/A ** stdio won't be expecting these
1N/A ** errnos from read()! Change them
1N/A ** into something it can understand.
1N/A */
1N/A
1N/A errno = EIO;
1N/A }
1N/A return -1;
1N/A }
1N/A
1N/A while (count < nbytes)
1N/A {
1N/A retval = read(bfp->bf_disk_fd,
1N/A buf + count,
1N/A nbytes - count);
1N/A if (retval < 0)
1N/A {
1N/A /* errno is set implicitly by read() */
1N/A return -1;
1N/A }
1N/A else if (retval == 0)
1N/A goto finished;
1N/A else
1N/A count += retval;
1N/A }
1N/A }
1N/A
1N/Afinished:
1N/A bfp->bf_offset += count;
1N/A return count;
1N/A}
1N/A
1N/A/*
1N/A** SM_BFSEEK -- seek to a position in a buffered file
1N/A**
1N/A** Parameters:
1N/A** fp -- fp of file to seek
1N/A** offset -- position to seek to
1N/A** whence -- how to seek
1N/A**
1N/A** Returns:
1N/A** new file offset or -1 indicate failure
1N/A**
1N/A** Side Effects:
1N/A** none.
1N/A**
1N/A*/
1N/A
1N/Astatic off_t
1N/Asm_bfseek(fp, offset, whence)
1N/A SM_FILE_T *fp;
1N/A off_t offset;
1N/A int whence;
1N/A
1N/A{
1N/A struct bf *bfp;
1N/A
1N/A /* Cast cookie back to correct type */
1N/A bfp = (struct bf *) fp->f_cookie;
1N/A
1N/A switch (whence)
1N/A {
1N/A case SEEK_SET:
1N/A bfp->bf_offset = offset;
1N/A break;
1N/A
1N/A case SEEK_CUR:
1N/A bfp->bf_offset += offset;
1N/A break;
1N/A
1N/A case SEEK_END:
1N/A bfp->bf_offset = bfp->bf_size + offset;
1N/A break;
1N/A
1N/A default:
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A return bfp->bf_offset;
1N/A}
1N/A
1N/A/*
1N/A** SM_BFWRITE -- write to a buffered file
1N/A**
1N/A** Parameters:
1N/A** fp -- fp of file to write
1N/A** buf -- data buffer
1N/A** nbytes -- how many bytes to write
1N/A**
1N/A** Returns:
1N/A** number of bytes written or -1 indicate failure
1N/A**
1N/A** Side Effects:
1N/A** may create backing file if over memory limit for file.
1N/A**
1N/A*/
1N/A
1N/Astatic ssize_t
1N/Asm_bfwrite(fp, buf, nbytes)
1N/A SM_FILE_T *fp;
1N/A const char *buf;
1N/A size_t nbytes;
1N/A{
1N/A struct bf *bfp;
1N/A ssize_t count = 0; /* Number of bytes written so far */
1N/A int retval;
1N/A
1N/A /* Cast cookie back to correct type */
1N/A bfp = (struct bf *) fp->f_cookie;
1N/A
1N/A /* If committed, go straight to disk */
1N/A if (bfp->bf_committed)
1N/A {
1N/A if (lseek(bfp->bf_disk_fd, bfp->bf_offset, SEEK_SET) < 0)
1N/A {
1N/A if ((errno == EINVAL) || (errno == ESPIPE))
1N/A {
1N/A /*
1N/A ** stdio won't be expecting these
1N/A ** errnos from write()! Change them
1N/A ** into something it can understand.
1N/A */
1N/A
1N/A errno = EIO;
1N/A }
1N/A return -1;
1N/A }
1N/A
1N/A count = write(bfp->bf_disk_fd, buf, nbytes);
1N/A if (count < 0)
1N/A {
1N/A /* errno is set implicitly by write() */
1N/A return -1;
1N/A }
1N/A goto finished;
1N/A }
1N/A
1N/A if (bfp->bf_offset < bfp->bf_bufsize)
1N/A {
1N/A /* Need to put some in buffer */
1N/A count = nbytes;
1N/A if ((bfp->bf_offset + count) > bfp->bf_bufsize)
1N/A count = bfp->bf_bufsize - bfp->bf_offset;
1N/A
1N/A memcpy(bfp->bf_buf + bfp->bf_offset, buf, count);
1N/A if ((bfp->bf_offset + count) > bfp->bf_buffilled)
1N/A bfp->bf_buffilled = bfp->bf_offset + count;
1N/A }
1N/A
1N/A if ((bfp->bf_offset + nbytes) > bfp->bf_bufsize)
1N/A {
1N/A /* Need to put some in file */
1N/A if (!bfp->bf_ondisk)
1N/A {
1N/A MODE_T omask;
1N/A int save_errno;
1N/A
1N/A /* Clear umask as bf_filemode are the true perms */
1N/A omask = umask(0);
1N/A retval = OPEN(bfp->bf_filename,
1N/A O_RDWR | O_CREAT | O_TRUNC | QF_O_EXTRA,
1N/A bfp->bf_filemode, bfp->bf_flags);
1N/A save_errno = errno;
1N/A (void) umask(omask);
1N/A errno = save_errno;
1N/A
1N/A /* Couldn't create file: failure */
1N/A if (retval < 0)
1N/A {
1N/A /*
1N/A ** stdio may not be expecting these
1N/A ** errnos from write()! Change to
1N/A ** something which it can understand.
1N/A ** Note that ENOSPC and EDQUOT are saved
1N/A ** because they are actually valid for
1N/A ** write().
1N/A */
1N/A
1N/A if (!(errno == ENOSPC
1N/A#ifdef EDQUOT
1N/A || errno == EDQUOT
1N/A#endif /* EDQUOT */
1N/A ))
1N/A errno = EIO;
1N/A
1N/A return -1;
1N/A }
1N/A bfp->bf_disk_fd = retval;
1N/A bfp->bf_ondisk = true;
1N/A }
1N/A
1N/A /* Catch a write() on an earlier failed write to disk */
1N/A if (bfp->bf_ondisk && bfp->bf_disk_fd < 0)
1N/A {
1N/A errno = EIO;
1N/A return -1;
1N/A }
1N/A
1N/A if (lseek(bfp->bf_disk_fd,
1N/A bfp->bf_offset + count, SEEK_SET) < 0)
1N/A {
1N/A if ((errno == EINVAL) || (errno == ESPIPE))
1N/A {
1N/A /*
1N/A ** stdio won't be expecting these
1N/A ** errnos from write()! Change them into
1N/A ** something which it can understand.
1N/A */
1N/A
1N/A errno = EIO;
1N/A }
1N/A return -1;
1N/A }
1N/A
1N/A while (count < nbytes)
1N/A {
1N/A retval = write(bfp->bf_disk_fd, buf + count,
1N/A nbytes - count);
1N/A if (retval < 0)
1N/A {
1N/A /* errno is set implicitly by write() */
1N/A return -1;
1N/A }
1N/A else
1N/A count += retval;
1N/A }
1N/A }
1N/A
1N/Afinished:
1N/A bfp->bf_offset += count;
1N/A if (bfp->bf_offset > bfp->bf_size)
1N/A bfp->bf_size = bfp->bf_offset;
1N/A return count;
1N/A}
1N/A
1N/A/*
1N/A** BFREWIND -- rewinds the SM_FILE_T *
1N/A**
1N/A** Parameters:
1N/A** fp -- SM_FILE_T * to rewind
1N/A**
1N/A** Returns:
1N/A** 0 on success, -1 on error
1N/A**
1N/A** Side Effects:
1N/A** rewinds the SM_FILE_T * and puts it into read mode. Normally
1N/A** one would bfopen() a file, write to it, then bfrewind() and
1N/A** fread(). If fp is not a buffered file, this is equivalent to
1N/A** rewind().
1N/A**
1N/A** Sets errno:
1N/A** any value of errno specified by sm_io_rewind()
1N/A*/
1N/A
1N/Aint
1N/Abfrewind(fp)
1N/A SM_FILE_T *fp;
1N/A{
1N/A (void) sm_io_flush(fp, SM_TIME_DEFAULT);
1N/A sm_io_clearerr(fp); /* quicker just to do it */
1N/A return sm_io_seek(fp, SM_TIME_DEFAULT, 0, SM_IO_SEEK_SET);
1N/A}
1N/A
1N/A/*
1N/A** SM_BFCOMMIT -- "commits" the buffered file
1N/A**
1N/A** Parameters:
1N/A** fp -- SM_FILE_T * to commit to disk
1N/A**
1N/A** Returns:
1N/A** 0 on success, -1 on error
1N/A**
1N/A** Side Effects:
1N/A** Forces the given SM_FILE_T * to be written to disk if it is not
1N/A** already, and ensures that it will be kept after closing. If
1N/A** fp is not a buffered file, this is a no-op.
1N/A**
1N/A** Sets errno:
1N/A** any value of errno specified by open()
1N/A** any value of errno specified by write()
1N/A** any value of errno specified by lseek()
1N/A*/
1N/A
1N/Astatic int
1N/Asm_bfcommit(fp)
1N/A SM_FILE_T *fp;
1N/A{
1N/A struct bf *bfp;
1N/A int retval;
1N/A int byteswritten;
1N/A
1N/A /* Get associated bf structure */
1N/A bfp = (struct bf *) fp->f_cookie;
1N/A
1N/A /* If already committed, noop */
1N/A if (bfp->bf_committed)
1N/A return 0;
1N/A
1N/A /* Do we need to open a file? */
1N/A if (!bfp->bf_ondisk)
1N/A {
1N/A int save_errno;
1N/A MODE_T omask;
1N/A struct stat st;
1N/A
1N/A if (tTd(58, 8))
1N/A {
1N/A sm_dprintf("bfcommit(%s): to disk\n", bfp->bf_filename);
1N/A if (tTd(58, 32))
1N/A sm_dprintf("bfcommit(): filemode %o flags %ld\n",
1N/A bfp->bf_filemode, bfp->bf_flags);
1N/A }
1N/A
1N/A if (stat(bfp->bf_filename, &st) == 0)
1N/A {
1N/A errno = EEXIST;
1N/A return -1;
1N/A }
1N/A
1N/A /* Clear umask as bf_filemode are the true perms */
1N/A omask = umask(0);
1N/A retval = OPEN(bfp->bf_filename,
1N/A O_RDWR | O_CREAT | O_EXCL | QF_O_EXTRA,
1N/A bfp->bf_filemode, bfp->bf_flags);
1N/A save_errno = errno;
1N/A (void) umask(omask);
1N/A
1N/A /* Couldn't create file: failure */
1N/A if (retval < 0)
1N/A {
1N/A /* errno is set implicitly by open() */
1N/A errno = save_errno;
1N/A return -1;
1N/A }
1N/A
1N/A bfp->bf_disk_fd = retval;
1N/A bfp->bf_ondisk = true;
1N/A }
1N/A
1N/A /* Write out the contents of our buffer, if we have any */
1N/A if (bfp->bf_buffilled > 0)
1N/A {
1N/A byteswritten = 0;
1N/A
1N/A if (lseek(bfp->bf_disk_fd, 0, SEEK_SET) < 0)
1N/A {
1N/A /* errno is set implicitly by lseek() */
1N/A return -1;
1N/A }
1N/A
1N/A while (byteswritten < bfp->bf_buffilled)
1N/A {
1N/A retval = write(bfp->bf_disk_fd,
1N/A bfp->bf_buf + byteswritten,
1N/A bfp->bf_buffilled - byteswritten);
1N/A if (retval < 0)
1N/A {
1N/A /* errno is set implicitly by write() */
1N/A return -1;
1N/A }
1N/A else
1N/A byteswritten += retval;
1N/A }
1N/A }
1N/A bfp->bf_committed = true;
1N/A
1N/A /* Invalidate buf; all goes to file now */
1N/A bfp->bf_buffilled = 0;
1N/A if (bfp->bf_bufsize > 0)
1N/A {
1N/A /* Don't need buffer anymore; free it */
1N/A bfp->bf_bufsize = 0;
1N/A sm_free(bfp->bf_buf);
1N/A }
1N/A return 0;
1N/A}
1N/A
1N/A/*
1N/A** SM_BFTRUNCATE -- rewinds and truncates the SM_FILE_T *
1N/A**
1N/A** Parameters:
1N/A** fp -- SM_FILE_T * to truncate
1N/A**
1N/A** Returns:
1N/A** 0 on success, -1 on error
1N/A**
1N/A** Side Effects:
1N/A** rewinds the SM_FILE_T *, truncates it to zero length, and puts
1N/A** it into write mode.
1N/A**
1N/A** Sets errno:
1N/A** any value of errno specified by fseek()
1N/A** any value of errno specified by ftruncate()
1N/A*/
1N/A
1N/Astatic int
1N/Asm_bftruncate(fp)
1N/A SM_FILE_T *fp;
1N/A{
1N/A struct bf *bfp;
1N/A
1N/A if (bfrewind(fp) < 0)
1N/A return -1;
1N/A
1N/A /* Get bf structure */
1N/A bfp = (struct bf *) fp->f_cookie;
1N/A bfp->bf_buffilled = 0;
1N/A bfp->bf_size = 0;
1N/A
1N/A /* Need to zero the buffer */
1N/A if (bfp->bf_bufsize > 0)
1N/A memset(bfp->bf_buf, '\0', bfp->bf_bufsize);
1N/A if (bfp->bf_ondisk)
1N/A {
1N/A#if NOFTRUNCATE
1N/A /* XXX: Not much we can do except rewind it */
1N/A errno = EINVAL;
1N/A return -1;
1N/A#else /* NOFTRUNCATE */
1N/A return ftruncate(bfp->bf_disk_fd, 0);
1N/A#endif /* NOFTRUNCATE */
1N/A }
1N/A return 0;
1N/A}
1N/A
1N/A/*
1N/A** SM_BFSETINFO -- set/change info for an open file pointer
1N/A**
1N/A** Parameters:
1N/A** fp -- file pointer to get info about
1N/A** what -- type of info to set/change
1N/A** valp -- thing to set/change the info to
1N/A**
1N/A*/
1N/A
1N/Astatic int
1N/Asm_bfsetinfo(fp, what, valp)
1N/A SM_FILE_T *fp;
1N/A int what;
1N/A void *valp;
1N/A{
1N/A struct bf *bfp;
1N/A int bsize;
1N/A
1N/A /* Get bf structure */
1N/A bfp = (struct bf *) fp->f_cookie;
1N/A switch (what)
1N/A {
1N/A case SM_BF_SETBUFSIZE:
1N/A bsize = *((int *) valp);
1N/A bfp->bf_bufsize = bsize;
1N/A
1N/A /* A zero bsize is valid, just don't allocate memory */
1N/A if (bsize > 0)
1N/A {
1N/A bfp->bf_buf = (char *) sm_malloc(bsize);
1N/A if (bfp->bf_buf == NULL)
1N/A {
1N/A bfp->bf_bufsize = 0;
1N/A errno = ENOMEM;
1N/A return -1;
1N/A }
1N/A }
1N/A else
1N/A bfp->bf_buf = NULL;
1N/A return 0;
1N/A case SM_BF_COMMIT:
1N/A return sm_bfcommit(fp);
1N/A case SM_BF_TRUNCATE:
1N/A return sm_bftruncate(fp);
1N/A case SM_BF_TEST:
1N/A return 1; /* always */
1N/A default:
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A}