bf.c revision 90792
190792Sgshapiro/*
290792Sgshapiro * Copyright (c) 1999-2001 Sendmail, Inc. and its suppliers.
390792Sgshapiro *	All rights reserved.
490792Sgshapiro *
590792Sgshapiro * By using this file, you agree to the terms and conditions set
690792Sgshapiro * forth in the LICENSE file which can be found at the top level of
790792Sgshapiro * the sendmail distribution.
890792Sgshapiro *
990792Sgshapiro * Contributed by Exactis.com, Inc.
1090792Sgshapiro *
1190792Sgshapiro */
1290792Sgshapiro
1390792Sgshapiro/*
1490792Sgshapiro**  This is in transition. Changed from the original bf_torek.c code
1590792Sgshapiro**  to use sm_io function calls directly rather than through stdio
1690792Sgshapiro**  translation layer. Will be made a built-in file type of libsm
1790792Sgshapiro**  next (once safeopen() linkable from libsm).
1890792Sgshapiro*/
1990792Sgshapiro
2090792Sgshapiro#include <sm/gen.h>
2190792SgshapiroSM_RCSID("@(#)$Id: bf.c,v 8.48 2001/11/04 17:10:49 ca Exp $")
2290792Sgshapiro
2390792Sgshapiro#include <sys/types.h>
2490792Sgshapiro#include <sys/stat.h>
2590792Sgshapiro#include <sys/uio.h>
2690792Sgshapiro#include <fcntl.h>
2790792Sgshapiro#include <unistd.h>
2890792Sgshapiro#include <stdlib.h>
2990792Sgshapiro#include <string.h>
3090792Sgshapiro#include <errno.h>
3190792Sgshapiro#include "sendmail.h"
3290792Sgshapiro#include "bf.h"
3390792Sgshapiro
3490792Sgshapiro#include <syslog.h>
3590792Sgshapiro
3690792Sgshapiro/* bf io functions */
3790792Sgshapirostatic ssize_t	sm_bfread __P((SM_FILE_T *, char *, size_t));
3890792Sgshapirostatic ssize_t	sm_bfwrite __P((SM_FILE_T *, const char *, size_t));
3990792Sgshapirostatic off_t	sm_bfseek __P((SM_FILE_T *, off_t, int));
4090792Sgshapirostatic int	sm_bfclose __P((SM_FILE_T *));
4190792Sgshapiro
4290792Sgshapirostatic int	sm_bfopen __P((SM_FILE_T *, const void *, int, const void *));
4390792Sgshapirostatic int	sm_bfsetinfo __P((SM_FILE_T *, int , void *));
4490792Sgshapirostatic int	sm_bfgetinfo __P((SM_FILE_T *, int , void *));
4590792Sgshapiro
4690792Sgshapiro/*
4790792Sgshapiro**  Data structure for storing information about each buffered file
4890792Sgshapiro**  (Originally in sendmail/bf_torek.h for the curious.)
4990792Sgshapiro*/
5090792Sgshapiro
5190792Sgshapirostruct bf
5290792Sgshapiro{
5390792Sgshapiro	bool	bf_committed;	/* Has this buffered file been committed? */
5490792Sgshapiro	bool	bf_ondisk;	/* On disk: committed or buffer overflow */
5590792Sgshapiro	long	bf_flags;
5690792Sgshapiro	int	bf_disk_fd;	/* If on disk, associated file descriptor */
5790792Sgshapiro	char	*bf_buf;	/* Memory buffer */
5890792Sgshapiro	int	bf_bufsize;	/* Length of above buffer */
5990792Sgshapiro	int	bf_buffilled;	/* Bytes of buffer actually filled */
6090792Sgshapiro	char	*bf_filename;	/* Name of buffered file, if ever committed */
6190792Sgshapiro	MODE_T	bf_filemode;	/* Mode of buffered file, if ever committed */
6290792Sgshapiro	off_t	bf_offset;	/* Currect file offset */
6390792Sgshapiro	int	bf_size;	/* Total current size of file */
6490792Sgshapiro	int	bf_refcount;	/* Reference count */
6590792Sgshapiro};
6690792Sgshapiro
6790792Sgshapiro#ifdef BF_STANDALONE
6890792Sgshapiro# define OPEN(fn, omode, cmode, sff) open(fn, omode, cmode)
6990792Sgshapiro#else /* BF_STANDALONE */
7090792Sgshapiro# define OPEN(fn, omode, cmode, sff) safeopen(fn, omode, cmode, sff)
7190792Sgshapiro#endif /* BF_STANDALONE */
7290792Sgshapiro
7390792Sgshapirostruct bf_info
7490792Sgshapiro{
7590792Sgshapiro	char	*bi_filename;
7690792Sgshapiro	MODE_T	bi_fmode;
7790792Sgshapiro	size_t	bi_bsize;
7890792Sgshapiro	long	bi_flags;
7990792Sgshapiro};
8090792Sgshapiro
8190792Sgshapiro/*
8290792Sgshapiro**  SM_BFOPEN -- the "base" open function called by sm_io_open() for the
8390792Sgshapiro**		internal, file-type-specific info setup.
8490792Sgshapiro**
8590792Sgshapiro**	Parameters:
8690792Sgshapiro**		fp -- file pointer being filled-in for file being open'd
8790792Sgshapiro**		filename -- name of the file being open'd
8890792Sgshapiro**		flags -- ignored
8990792Sgshapiro**		fmode -- file mode (stored for use later)
9090792Sgshapiro**		sflags -- "safeopen" flags (stored for use later)
9190792Sgshapiro**		rpool -- ignored (currently)
9290792Sgshapiro**
9390792Sgshapiro**	Returns:
9490792Sgshapiro**		Failure: -1 and sets errno
9590792Sgshapiro**		Success: 0 (zero)
9690792Sgshapiro*/
9790792Sgshapiro
9890792Sgshapirostatic int
9990792Sgshapirosm_bfopen(fp, info, flags, rpool)
10090792Sgshapiro	SM_FILE_T *fp;
10190792Sgshapiro	const void *info;
10290792Sgshapiro	int flags;
10390792Sgshapiro	const void *rpool;
10490792Sgshapiro{
10590792Sgshapiro	char *filename;
10690792Sgshapiro	MODE_T fmode;
10790792Sgshapiro	size_t bsize;
10890792Sgshapiro	long sflags;
10990792Sgshapiro	struct bf *bfp;
11090792Sgshapiro	int l;
11190792Sgshapiro	struct stat st;
11290792Sgshapiro
11390792Sgshapiro	filename = ((struct bf_info *) info)->bi_filename;
11490792Sgshapiro	fmode = ((struct bf_info *) info)->bi_fmode;
11590792Sgshapiro	bsize = ((struct bf_info *) info)->bi_bsize;
11690792Sgshapiro	sflags = ((struct bf_info *) info)->bi_flags;
11790792Sgshapiro
11890792Sgshapiro	/* Sanity checks */
11990792Sgshapiro	if (*filename == '\0')
12090792Sgshapiro	{
12190792Sgshapiro		/* Empty filename string */
12290792Sgshapiro		errno = ENOENT;
12390792Sgshapiro		return -1;
12490792Sgshapiro	}
12590792Sgshapiro	if (stat(filename, &st) == 0)
12690792Sgshapiro	{
12790792Sgshapiro		/* File already exists on disk */
12890792Sgshapiro		errno = EEXIST;
12990792Sgshapiro		return -1;
13090792Sgshapiro	}
13190792Sgshapiro
13290792Sgshapiro	/* Allocate memory */
13390792Sgshapiro	bfp = (struct bf *) sm_malloc(sizeof(struct bf));
13490792Sgshapiro	if (bfp == NULL)
13590792Sgshapiro	{
13690792Sgshapiro		errno = ENOMEM;
13790792Sgshapiro		return -1;
13890792Sgshapiro	}
13990792Sgshapiro
14090792Sgshapiro	/* Assign data buffer */
14190792Sgshapiro	/* A zero bsize is valid, just don't allocate memory */
14290792Sgshapiro	if (bsize > 0)
14390792Sgshapiro	{
14490792Sgshapiro		bfp->bf_buf = (char *) sm_malloc(bsize);
14590792Sgshapiro		if (bfp->bf_buf == NULL)
14690792Sgshapiro		{
14790792Sgshapiro			bfp->bf_bufsize = 0;
14890792Sgshapiro			sm_free(bfp);
14990792Sgshapiro			errno = ENOMEM;
15090792Sgshapiro			return -1;
15190792Sgshapiro		}
15290792Sgshapiro	}
15390792Sgshapiro	else
15490792Sgshapiro		bfp->bf_buf = NULL;
15590792Sgshapiro
15690792Sgshapiro	/* Nearly home free, just set all the parameters now */
15790792Sgshapiro	bfp->bf_committed = false;
15890792Sgshapiro	bfp->bf_ondisk = false;
15990792Sgshapiro	bfp->bf_refcount = 1;
16090792Sgshapiro	bfp->bf_flags = sflags;
16190792Sgshapiro	bfp->bf_bufsize = bsize;
16290792Sgshapiro	bfp->bf_buffilled = 0;
16390792Sgshapiro	l = strlen(filename) + 1;
16490792Sgshapiro	bfp->bf_filename = (char *) sm_malloc(l);
16590792Sgshapiro	if (bfp->bf_filename == NULL)
16690792Sgshapiro	{
16790792Sgshapiro		if (bfp->bf_buf != NULL)
16890792Sgshapiro			sm_free(bfp->bf_buf);
16990792Sgshapiro		sm_free(bfp);
17090792Sgshapiro		errno = ENOMEM;
17190792Sgshapiro		return -1;
17290792Sgshapiro	}
17390792Sgshapiro	(void) sm_strlcpy(bfp->bf_filename, filename, l);
17490792Sgshapiro	bfp->bf_filemode = fmode;
17590792Sgshapiro	bfp->bf_offset = 0;
17690792Sgshapiro	bfp->bf_size = bsize;
17790792Sgshapiro	bfp->bf_disk_fd = -1;
17890792Sgshapiro	fp->f_cookie = bfp;
17990792Sgshapiro
18090792Sgshapiro	if (tTd(58, 8))
18190792Sgshapiro		sm_dprintf("sm_bfopen(%s)\n", filename);
18290792Sgshapiro
18390792Sgshapiro	return 0;
18490792Sgshapiro}
18590792Sgshapiro
18690792Sgshapiro/*
18790792Sgshapiro**  BFOPEN -- create a new buffered file
18890792Sgshapiro**
18990792Sgshapiro**	Parameters:
19090792Sgshapiro**		filename -- the file's name
19190792Sgshapiro**		fmode -- what mode the file should be created as
19290792Sgshapiro**		bsize -- amount of buffer space to allocate (may be 0)
19390792Sgshapiro**		flags -- if running under sendmail, passed directly to safeopen
19490792Sgshapiro**
19590792Sgshapiro**	Returns:
19690792Sgshapiro**		a SM_FILE_T * which may then be used with stdio functions,
19790792Sgshapiro**		or NULL	on failure. SM_FILE_T * is opened for writing
19890792Sgshapiro**		"SM_IO_WHAT_VECTORS").
19990792Sgshapiro**
20090792Sgshapiro**	Side Effects:
20190792Sgshapiro**		none.
20290792Sgshapiro**
20390792Sgshapiro**	Sets errno:
20490792Sgshapiro**		any value of errno specified by sm_io_setinfo_type()
20590792Sgshapiro**		any value of errno specified by sm_io_open()
20690792Sgshapiro**		any value of errno specified by sm_io_setinfo()
20790792Sgshapiro*/
20890792Sgshapiro
20990792SgshapiroSM_FILE_T *
21090792Sgshapirobfopen(filename, fmode, bsize, flags)
21190792Sgshapiro	char *filename;
21290792Sgshapiro	MODE_T fmode;
21390792Sgshapiro	size_t bsize;
21490792Sgshapiro	long flags;
21590792Sgshapiro{
21690792Sgshapiro	MODE_T omask;
21790792Sgshapiro	SM_FILE_T SM_IO_SET_TYPE(vector, BF_FILE_TYPE, sm_bfopen, sm_bfclose,
21890792Sgshapiro		sm_bfread, sm_bfwrite, sm_bfseek, sm_bfgetinfo, sm_bfsetinfo,
21990792Sgshapiro		SM_TIME_FOREVER);
22090792Sgshapiro	struct bf_info info;
22190792Sgshapiro
22290792Sgshapiro	/*
22390792Sgshapiro	**  Apply current umask to fmode as it may change by the time
22490792Sgshapiro	**  the file is actually created.  fmode becomes the true
22590792Sgshapiro	**  permissions of the file, which OPEN() must obey.
22690792Sgshapiro	*/
22790792Sgshapiro
22890792Sgshapiro	omask = umask(0);
22990792Sgshapiro	fmode &= ~omask;
23090792Sgshapiro	(void) umask(omask);
23190792Sgshapiro
23290792Sgshapiro	SM_IO_INIT_TYPE(vector, BF_FILE_TYPE, sm_bfopen, sm_bfclose,
23390792Sgshapiro		sm_bfread, sm_bfwrite, sm_bfseek, sm_bfgetinfo, sm_bfsetinfo,
23490792Sgshapiro		SM_TIME_FOREVER);
23590792Sgshapiro	info.bi_filename = filename;
23690792Sgshapiro	info.bi_fmode = fmode;
23790792Sgshapiro	info.bi_bsize = bsize;
23890792Sgshapiro	info.bi_flags = flags;
23990792Sgshapiro
24090792Sgshapiro	return sm_io_open(&vector, SM_TIME_DEFAULT, &info, SM_IO_RDWR, NULL);
24190792Sgshapiro}
24290792Sgshapiro
24390792Sgshapiro/*
24490792Sgshapiro**  SM_BFGETINFO -- returns info about an open file pointer
24590792Sgshapiro**
24690792Sgshapiro**	Parameters:
24790792Sgshapiro**		fp -- file pointer to get info about
24890792Sgshapiro**		what -- type of info to obtain
24990792Sgshapiro**		valp -- thing to return the info in
25090792Sgshapiro*/
25190792Sgshapiro
25290792Sgshapirostatic int
25390792Sgshapirosm_bfgetinfo(fp, what, valp)
25490792Sgshapiro	SM_FILE_T *fp;
25590792Sgshapiro	int what;
25690792Sgshapiro	void *valp;
25790792Sgshapiro{
25890792Sgshapiro	struct bf *bfp;
25990792Sgshapiro
26090792Sgshapiro	bfp = (struct bf *) fp->f_cookie;
26190792Sgshapiro	switch (what)
26290792Sgshapiro	{
26390792Sgshapiro	  case SM_IO_WHAT_FD:
26490792Sgshapiro		return bfp->bf_disk_fd;
26590792Sgshapiro	  default:
26690792Sgshapiro		return -1;
26790792Sgshapiro	}
26890792Sgshapiro}
26990792Sgshapiro
27090792Sgshapiro/*
27190792Sgshapiro**  SM_BFCLOSE -- close a buffered file
27290792Sgshapiro**
27390792Sgshapiro**	Parameters:
27490792Sgshapiro**		fp -- cookie of file to close
27590792Sgshapiro**
27690792Sgshapiro**	Returns:
27790792Sgshapiro**		0 to indicate success
27890792Sgshapiro**
27990792Sgshapiro**	Side Effects:
28090792Sgshapiro**		deletes backing file, sm_frees memory.
28190792Sgshapiro**
28290792Sgshapiro**	Sets errno:
28390792Sgshapiro**		never.
28490792Sgshapiro*/
28590792Sgshapiro
28690792Sgshapirostatic int
28790792Sgshapirosm_bfclose(fp)
28890792Sgshapiro	SM_FILE_T *fp;
28990792Sgshapiro{
29090792Sgshapiro	struct bf *bfp;
29190792Sgshapiro
29290792Sgshapiro	/* Cast cookie back to correct type */
29390792Sgshapiro	bfp = (struct bf *) fp->f_cookie;
29490792Sgshapiro
29590792Sgshapiro	/* Need to clean up the file */
29690792Sgshapiro	if (bfp->bf_ondisk && !bfp->bf_committed)
29790792Sgshapiro		unlink(bfp->bf_filename);
29890792Sgshapiro	sm_free(bfp->bf_filename);
29990792Sgshapiro
30090792Sgshapiro	if (bfp->bf_disk_fd != -1)
30190792Sgshapiro		close(bfp->bf_disk_fd);
30290792Sgshapiro
30390792Sgshapiro	/* Need to sm_free the buffer */
30490792Sgshapiro	if (bfp->bf_bufsize > 0)
30590792Sgshapiro		sm_free(bfp->bf_buf);
30690792Sgshapiro
30790792Sgshapiro	/* Finally, sm_free the structure */
30890792Sgshapiro	sm_free(bfp);
30990792Sgshapiro	return 0;
31090792Sgshapiro}
31190792Sgshapiro
31290792Sgshapiro/*
31390792Sgshapiro**  SM_BFREAD -- read a buffered file
31490792Sgshapiro**
31590792Sgshapiro**	Parameters:
31690792Sgshapiro**		cookie -- cookie of file to read
31790792Sgshapiro**		buf -- buffer to fill
31890792Sgshapiro**		nbytes -- how many bytes to read
31990792Sgshapiro**
32090792Sgshapiro**	Returns:
32190792Sgshapiro**		number of bytes read or -1 indicate failure
32290792Sgshapiro**
32390792Sgshapiro**	Side Effects:
32490792Sgshapiro**		none.
32590792Sgshapiro**
32690792Sgshapiro*/
32790792Sgshapiro
32890792Sgshapirostatic ssize_t
32990792Sgshapirosm_bfread(fp, buf, nbytes)
33090792Sgshapiro	SM_FILE_T *fp;
33190792Sgshapiro	char *buf;
33290792Sgshapiro	size_t nbytes;
33390792Sgshapiro{
33490792Sgshapiro	struct bf *bfp;
33590792Sgshapiro	ssize_t count = 0;	/* Number of bytes put in buf so far */
33690792Sgshapiro	int retval;
33790792Sgshapiro
33890792Sgshapiro	/* Cast cookie back to correct type */
33990792Sgshapiro	bfp = (struct bf *) fp->f_cookie;
34090792Sgshapiro
34190792Sgshapiro	if (bfp->bf_offset < bfp->bf_buffilled)
34290792Sgshapiro	{
34390792Sgshapiro		/* Need to grab some from buffer */
34490792Sgshapiro		count = nbytes;
34590792Sgshapiro		if ((bfp->bf_offset + count) > bfp->bf_buffilled)
34690792Sgshapiro			count = bfp->bf_buffilled - bfp->bf_offset;
34790792Sgshapiro
34890792Sgshapiro		memcpy(buf, bfp->bf_buf + bfp->bf_offset, count);
34990792Sgshapiro	}
35090792Sgshapiro
35190792Sgshapiro	if ((bfp->bf_offset + nbytes) > bfp->bf_buffilled)
35290792Sgshapiro	{
35390792Sgshapiro		/* Need to grab some from file */
35490792Sgshapiro		if (!bfp->bf_ondisk)
35590792Sgshapiro		{
35690792Sgshapiro			/* Oops, the file doesn't exist. EOF. */
35790792Sgshapiro			if (tTd(58, 8))
35890792Sgshapiro				sm_dprintf("sm_bfread(%s): to disk\n",
35990792Sgshapiro					   bfp->bf_filename);
36090792Sgshapiro			goto finished;
36190792Sgshapiro		}
36290792Sgshapiro
36390792Sgshapiro		/* Catch a read() on an earlier failed write to disk */
36490792Sgshapiro		if (bfp->bf_disk_fd < 0)
36590792Sgshapiro		{
36690792Sgshapiro			errno = EIO;
36790792Sgshapiro			return -1;
36890792Sgshapiro		}
36990792Sgshapiro
37090792Sgshapiro		if (lseek(bfp->bf_disk_fd,
37190792Sgshapiro			  bfp->bf_offset + count, SEEK_SET) < 0)
37290792Sgshapiro		{
37390792Sgshapiro			if ((errno == EINVAL) || (errno == ESPIPE))
37490792Sgshapiro			{
37590792Sgshapiro				/*
37690792Sgshapiro				**  stdio won't be expecting these
37790792Sgshapiro				**  errnos from read()! Change them
37890792Sgshapiro				**  into something it can understand.
37990792Sgshapiro				*/
38090792Sgshapiro
38190792Sgshapiro				errno = EIO;
38290792Sgshapiro			}
38390792Sgshapiro			return -1;
38490792Sgshapiro		}
38590792Sgshapiro
38690792Sgshapiro		while (count < nbytes)
38790792Sgshapiro		{
38890792Sgshapiro			retval = read(bfp->bf_disk_fd,
38990792Sgshapiro				      buf + count,
39090792Sgshapiro				      nbytes - count);
39190792Sgshapiro			if (retval < 0)
39290792Sgshapiro			{
39390792Sgshapiro				/* errno is set implicitly by read() */
39490792Sgshapiro				return -1;
39590792Sgshapiro			}
39690792Sgshapiro			else if (retval == 0)
39790792Sgshapiro				goto finished;
39890792Sgshapiro			else
39990792Sgshapiro				count += retval;
40090792Sgshapiro		}
40190792Sgshapiro	}
40290792Sgshapiro
40390792Sgshapirofinished:
40490792Sgshapiro	bfp->bf_offset += count;
40590792Sgshapiro	return count;
40690792Sgshapiro}
40790792Sgshapiro
40890792Sgshapiro/*
40990792Sgshapiro**  SM_BFSEEK -- seek to a position in a buffered file
41090792Sgshapiro**
41190792Sgshapiro**	Parameters:
41290792Sgshapiro**		fp     -- fp of file to seek
41390792Sgshapiro**		offset -- position to seek to
41490792Sgshapiro**		whence -- how to seek
41590792Sgshapiro**
41690792Sgshapiro**	Returns:
41790792Sgshapiro**		new file offset or -1 indicate failure
41890792Sgshapiro**
41990792Sgshapiro**	Side Effects:
42090792Sgshapiro**		none.
42190792Sgshapiro**
42290792Sgshapiro*/
42390792Sgshapiro
42490792Sgshapirostatic off_t
42590792Sgshapirosm_bfseek(fp, offset, whence)
42690792Sgshapiro	SM_FILE_T *fp;
42790792Sgshapiro	off_t offset;
42890792Sgshapiro	int whence;
42990792Sgshapiro
43090792Sgshapiro{
43190792Sgshapiro	struct bf *bfp;
43290792Sgshapiro
43390792Sgshapiro	/* Cast cookie back to correct type */
43490792Sgshapiro	bfp = (struct bf *) fp->f_cookie;
43590792Sgshapiro
43690792Sgshapiro	switch (whence)
43790792Sgshapiro	{
43890792Sgshapiro	  case SEEK_SET:
43990792Sgshapiro		bfp->bf_offset = offset;
44090792Sgshapiro		break;
44190792Sgshapiro
44290792Sgshapiro	  case SEEK_CUR:
44390792Sgshapiro		bfp->bf_offset += offset;
44490792Sgshapiro		break;
44590792Sgshapiro
44690792Sgshapiro	  case SEEK_END:
44790792Sgshapiro		bfp->bf_offset = bfp->bf_size + offset;
44890792Sgshapiro		break;
44990792Sgshapiro
45090792Sgshapiro	  default:
45190792Sgshapiro		errno = EINVAL;
45290792Sgshapiro		return -1;
45390792Sgshapiro	}
45490792Sgshapiro	return bfp->bf_offset;
45590792Sgshapiro}
45690792Sgshapiro
45790792Sgshapiro/*
45890792Sgshapiro**  SM_BFWRITE -- write to a buffered file
45990792Sgshapiro**
46090792Sgshapiro**	Parameters:
46190792Sgshapiro**		fp -- fp of file to write
46290792Sgshapiro**		buf -- data buffer
46390792Sgshapiro**		nbytes -- how many bytes to write
46490792Sgshapiro**
46590792Sgshapiro**	Returns:
46690792Sgshapiro**		number of bytes written or -1 indicate failure
46790792Sgshapiro**
46890792Sgshapiro**	Side Effects:
46990792Sgshapiro**		may create backing file if over memory limit for file.
47090792Sgshapiro**
47190792Sgshapiro*/
47290792Sgshapiro
47390792Sgshapirostatic ssize_t
47490792Sgshapirosm_bfwrite(fp, buf, nbytes)
47590792Sgshapiro	SM_FILE_T *fp;
47690792Sgshapiro	const char *buf;
47790792Sgshapiro	size_t nbytes;
47890792Sgshapiro{
47990792Sgshapiro	struct bf *bfp;
48090792Sgshapiro	ssize_t count = 0;	/* Number of bytes written so far */
48190792Sgshapiro	int retval;
48290792Sgshapiro
48390792Sgshapiro	/* Cast cookie back to correct type */
48490792Sgshapiro	bfp = (struct bf *) fp->f_cookie;
48590792Sgshapiro
48690792Sgshapiro	/* If committed, go straight to disk */
48790792Sgshapiro	if (bfp->bf_committed)
48890792Sgshapiro	{
48990792Sgshapiro		if (lseek(bfp->bf_disk_fd, bfp->bf_offset, SEEK_SET) < 0)
49090792Sgshapiro		{
49190792Sgshapiro			if ((errno == EINVAL) || (errno == ESPIPE))
49290792Sgshapiro			{
49390792Sgshapiro				/*
49490792Sgshapiro				**  stdio won't be expecting these
49590792Sgshapiro				**  errnos from write()! Change them
49690792Sgshapiro				**  into something it can understand.
49790792Sgshapiro				*/
49890792Sgshapiro
49990792Sgshapiro				errno = EIO;
50090792Sgshapiro			}
50190792Sgshapiro			return -1;
50290792Sgshapiro		}
50390792Sgshapiro
50490792Sgshapiro		count = write(bfp->bf_disk_fd, buf, nbytes);
50590792Sgshapiro		if (count < 0)
50690792Sgshapiro		{
50790792Sgshapiro			/* errno is set implicitly by write() */
50890792Sgshapiro			return -1;
50990792Sgshapiro		}
51090792Sgshapiro		goto finished;
51190792Sgshapiro	}
51290792Sgshapiro
51390792Sgshapiro	if (bfp->bf_offset < bfp->bf_bufsize)
51490792Sgshapiro	{
51590792Sgshapiro		/* Need to put some in buffer */
51690792Sgshapiro		count = nbytes;
51790792Sgshapiro		if ((bfp->bf_offset + count) > bfp->bf_bufsize)
51890792Sgshapiro			count = bfp->bf_bufsize - bfp->bf_offset;
51990792Sgshapiro
52090792Sgshapiro		memcpy(bfp->bf_buf + bfp->bf_offset, buf, count);
52190792Sgshapiro		if ((bfp->bf_offset + count) > bfp->bf_buffilled)
52290792Sgshapiro			bfp->bf_buffilled = bfp->bf_offset + count;
52390792Sgshapiro	}
52490792Sgshapiro
52590792Sgshapiro	if ((bfp->bf_offset + nbytes) > bfp->bf_bufsize)
52690792Sgshapiro	{
52790792Sgshapiro		/* Need to put some in file */
52890792Sgshapiro		if (!bfp->bf_ondisk)
52990792Sgshapiro		{
53090792Sgshapiro			MODE_T omask;
53190792Sgshapiro
53290792Sgshapiro			/* Clear umask as bf_filemode are the true perms */
53390792Sgshapiro			omask = umask(0);
53490792Sgshapiro			retval = OPEN(bfp->bf_filename,
53590792Sgshapiro				      O_RDWR | O_CREAT | O_TRUNC,
53690792Sgshapiro				      bfp->bf_filemode, bfp->bf_flags);
53790792Sgshapiro			(void) umask(omask);
53890792Sgshapiro
53990792Sgshapiro			/* Couldn't create file: failure */
54090792Sgshapiro			if (retval < 0)
54190792Sgshapiro			{
54290792Sgshapiro				/*
54390792Sgshapiro				**  stdio may not be expecting these
54490792Sgshapiro				**  errnos from write()! Change to
54590792Sgshapiro				**  something which it can understand.
54690792Sgshapiro				**  Note that ENOSPC and EDQUOT are saved
54790792Sgshapiro				**  because they are actually valid for
54890792Sgshapiro				**  write().
54990792Sgshapiro				*/
55090792Sgshapiro
55190792Sgshapiro				if (!(errno == ENOSPC
55290792Sgshapiro#ifdef EDQUOT
55390792Sgshapiro				      || errno == EDQUOT
55490792Sgshapiro#endif /* EDQUOT */
55590792Sgshapiro				     ))
55690792Sgshapiro					errno = EIO;
55790792Sgshapiro
55890792Sgshapiro				return -1;
55990792Sgshapiro			}
56090792Sgshapiro			bfp->bf_disk_fd = retval;
56190792Sgshapiro			bfp->bf_ondisk = true;
56290792Sgshapiro		}
56390792Sgshapiro
56490792Sgshapiro		/* Catch a write() on an earlier failed write to disk */
56590792Sgshapiro		if (bfp->bf_ondisk && bfp->bf_disk_fd < 0)
56690792Sgshapiro		{
56790792Sgshapiro			errno = EIO;
56890792Sgshapiro			return -1;
56990792Sgshapiro		}
57090792Sgshapiro
57190792Sgshapiro		if (lseek(bfp->bf_disk_fd,
57290792Sgshapiro			  bfp->bf_offset + count, SEEK_SET) < 0)
57390792Sgshapiro		{
57490792Sgshapiro			if ((errno == EINVAL) || (errno == ESPIPE))
57590792Sgshapiro			{
57690792Sgshapiro				/*
57790792Sgshapiro				**  stdio won't be expecting these
57890792Sgshapiro				**  errnos from write()! Change them into
57990792Sgshapiro				**  something which it can understand.
58090792Sgshapiro				*/
58190792Sgshapiro
58290792Sgshapiro				errno = EIO;
58390792Sgshapiro			}
58490792Sgshapiro			return -1;
58590792Sgshapiro		}
58690792Sgshapiro
58790792Sgshapiro		while (count < nbytes)
58890792Sgshapiro		{
58990792Sgshapiro			retval = write(bfp->bf_disk_fd, buf + count,
59090792Sgshapiro				       nbytes - count);
59190792Sgshapiro			if (retval < 0)
59290792Sgshapiro			{
59390792Sgshapiro				/* errno is set implicitly by write() */
59490792Sgshapiro				return -1;
59590792Sgshapiro			}
59690792Sgshapiro			else
59790792Sgshapiro				count += retval;
59890792Sgshapiro		}
59990792Sgshapiro	}
60090792Sgshapiro
60190792Sgshapirofinished:
60290792Sgshapiro	bfp->bf_offset += count;
60390792Sgshapiro	if (bfp->bf_offset > bfp->bf_size)
60490792Sgshapiro		bfp->bf_size = bfp->bf_offset;
60590792Sgshapiro	return count;
60690792Sgshapiro}
60790792Sgshapiro
60890792Sgshapiro/*
60990792Sgshapiro**  BFREWIND -- rewinds the SM_FILE_T *
61090792Sgshapiro**
61190792Sgshapiro**	Parameters:
61290792Sgshapiro**		fp -- SM_FILE_T * to rewind
61390792Sgshapiro**
61490792Sgshapiro**	Returns:
61590792Sgshapiro**		0 on success, -1 on error
61690792Sgshapiro**
61790792Sgshapiro**	Side Effects:
61890792Sgshapiro**		rewinds the SM_FILE_T * and puts it into read mode. Normally one
61990792Sgshapiro**		would bfopen() a file, write to it, then bfrewind() and
62090792Sgshapiro**		fread(). If fp is not a buffered file, this is equivalent to
62190792Sgshapiro**		rewind().
62290792Sgshapiro**
62390792Sgshapiro**	Sets errno:
62490792Sgshapiro**		any value of errno specified by sm_io_rewind()
62590792Sgshapiro*/
62690792Sgshapiro
62790792Sgshapiroint
62890792Sgshapirobfrewind(fp)
62990792Sgshapiro	SM_FILE_T *fp;
63090792Sgshapiro{
63190792Sgshapiro	(void) sm_io_flush(fp, SM_TIME_DEFAULT);
63290792Sgshapiro	sm_io_clearerr(fp); /* quicker just to do it */
63390792Sgshapiro	return sm_io_seek(fp, SM_TIME_DEFAULT, 0, SM_IO_SEEK_SET);
63490792Sgshapiro}
63590792Sgshapiro
63690792Sgshapiro/*
63790792Sgshapiro**  SM_BFCOMMIT -- "commits" the buffered file
63890792Sgshapiro**
63990792Sgshapiro**	Parameters:
64090792Sgshapiro**		fp -- SM_FILE_T * to commit to disk
64190792Sgshapiro**
64290792Sgshapiro**	Returns:
64390792Sgshapiro**		0 on success, -1 on error
64490792Sgshapiro**
64590792Sgshapiro**	Side Effects:
64690792Sgshapiro**		Forces the given SM_FILE_T * to be written to disk if it is not
64790792Sgshapiro**		already, and ensures that it will be kept after closing. If
64890792Sgshapiro**		fp is not a buffered file, this is a no-op.
64990792Sgshapiro**
65090792Sgshapiro**	Sets errno:
65190792Sgshapiro**		any value of errno specified by open()
65290792Sgshapiro**		any value of errno specified by write()
65390792Sgshapiro**		any value of errno specified by lseek()
65490792Sgshapiro*/
65590792Sgshapiro
65690792Sgshapirostatic int
65790792Sgshapirosm_bfcommit(fp)
65890792Sgshapiro	SM_FILE_T *fp;
65990792Sgshapiro{
66090792Sgshapiro	struct bf *bfp;
66190792Sgshapiro	int retval;
66290792Sgshapiro	int byteswritten;
66390792Sgshapiro
66490792Sgshapiro	/* Get associated bf structure */
66590792Sgshapiro	bfp = (struct bf *) fp->f_cookie;
66690792Sgshapiro
66790792Sgshapiro	/* If already committed, noop */
66890792Sgshapiro	if (bfp->bf_committed)
66990792Sgshapiro		return 0;
67090792Sgshapiro
67190792Sgshapiro	/* Do we need to open a file? */
67290792Sgshapiro	if (!bfp->bf_ondisk)
67390792Sgshapiro	{
67490792Sgshapiro		MODE_T omask;
67590792Sgshapiro		struct stat st;
67690792Sgshapiro
67790792Sgshapiro		if (tTd(58, 8))
67890792Sgshapiro		{
67990792Sgshapiro			sm_dprintf("bfcommit(%s): to disk\n", bfp->bf_filename);
68090792Sgshapiro			if (tTd(58, 32))
68190792Sgshapiro				sm_dprintf("bfcommit(): filemode %o flags %ld\n",
68290792Sgshapiro					   bfp->bf_filemode, bfp->bf_flags);
68390792Sgshapiro		}
68490792Sgshapiro
68590792Sgshapiro		if (stat(bfp->bf_filename, &st) == 0)
68690792Sgshapiro		{
68790792Sgshapiro			errno = EEXIST;
68890792Sgshapiro			return -1;
68990792Sgshapiro		}
69090792Sgshapiro
69190792Sgshapiro		/* Clear umask as bf_filemode are the true perms */
69290792Sgshapiro		omask = umask(0);
69390792Sgshapiro		retval = OPEN(bfp->bf_filename, O_RDWR | O_CREAT | O_TRUNC,
69490792Sgshapiro			      bfp->bf_filemode, bfp->bf_flags);
69590792Sgshapiro		(void) umask(omask);
69690792Sgshapiro
69790792Sgshapiro		/* Couldn't create file: failure */
69890792Sgshapiro		if (retval < 0)
69990792Sgshapiro		{
70090792Sgshapiro			/* errno is set implicitly by open() */
70190792Sgshapiro			return -1;
70290792Sgshapiro		}
70390792Sgshapiro
70490792Sgshapiro		bfp->bf_disk_fd = retval;
70590792Sgshapiro		bfp->bf_ondisk = true;
70690792Sgshapiro	}
70790792Sgshapiro
70890792Sgshapiro	/* Write out the contents of our buffer, if we have any */
70990792Sgshapiro	if (bfp->bf_buffilled > 0)
71090792Sgshapiro	{
71190792Sgshapiro		byteswritten = 0;
71290792Sgshapiro
71390792Sgshapiro		if (lseek(bfp->bf_disk_fd, 0, SEEK_SET) < 0)
71490792Sgshapiro		{
71590792Sgshapiro			/* errno is set implicitly by lseek() */
71690792Sgshapiro			return -1;
71790792Sgshapiro		}
71890792Sgshapiro
71990792Sgshapiro		while (byteswritten < bfp->bf_buffilled)
72090792Sgshapiro		{
72190792Sgshapiro			retval = write(bfp->bf_disk_fd,
72290792Sgshapiro				       bfp->bf_buf + byteswritten,
72390792Sgshapiro				       bfp->bf_buffilled - byteswritten);
72490792Sgshapiro			if (retval < 0)
72590792Sgshapiro			{
72690792Sgshapiro				/* errno is set implicitly by write() */
72790792Sgshapiro				return -1;
72890792Sgshapiro			}
72990792Sgshapiro			else
73090792Sgshapiro				byteswritten += retval;
73190792Sgshapiro		}
73290792Sgshapiro	}
73390792Sgshapiro	bfp->bf_committed = true;
73490792Sgshapiro
73590792Sgshapiro	/* Invalidate buf; all goes to file now */
73690792Sgshapiro	bfp->bf_buffilled = 0;
73790792Sgshapiro	if (bfp->bf_bufsize > 0)
73890792Sgshapiro	{
73990792Sgshapiro		/* Don't need buffer anymore; free it */
74090792Sgshapiro		bfp->bf_bufsize = 0;
74190792Sgshapiro		sm_free(bfp->bf_buf);
74290792Sgshapiro	}
74390792Sgshapiro	return 0;
74490792Sgshapiro}
74590792Sgshapiro
74690792Sgshapiro/*
74790792Sgshapiro**  SM_BFTRUNCATE -- rewinds and truncates the SM_FILE_T *
74890792Sgshapiro**
74990792Sgshapiro**	Parameters:
75090792Sgshapiro**		fp -- SM_FILE_T * to truncate
75190792Sgshapiro**
75290792Sgshapiro**	Returns:
75390792Sgshapiro**		0 on success, -1 on error
75490792Sgshapiro**
75590792Sgshapiro**	Side Effects:
75690792Sgshapiro**		rewinds the SM_FILE_T *, truncates it to zero length, and puts
75790792Sgshapiro**		it into write mode.
75890792Sgshapiro**
75990792Sgshapiro**	Sets errno:
76090792Sgshapiro**		any value of errno specified by fseek()
76190792Sgshapiro**		any value of errno specified by ftruncate()
76290792Sgshapiro*/
76390792Sgshapiro
76490792Sgshapirostatic int
76590792Sgshapirosm_bftruncate(fp)
76690792Sgshapiro	SM_FILE_T *fp;
76790792Sgshapiro{
76890792Sgshapiro	struct bf *bfp;
76990792Sgshapiro
77090792Sgshapiro	if (bfrewind(fp) < 0)
77190792Sgshapiro		return -1;
77290792Sgshapiro
77390792Sgshapiro	/* Get bf structure */
77490792Sgshapiro	bfp = (struct bf *) fp->f_cookie;
77590792Sgshapiro	bfp->bf_buffilled = 0;
77690792Sgshapiro	bfp->bf_size = 0;
77790792Sgshapiro
77890792Sgshapiro	/* Need to zero the buffer */
77990792Sgshapiro	if (bfp->bf_bufsize > 0)
78090792Sgshapiro		memset(bfp->bf_buf, '\0', bfp->bf_bufsize);
78190792Sgshapiro	if (bfp->bf_ondisk)
78290792Sgshapiro	{
78390792Sgshapiro#if NOFTRUNCATE
78490792Sgshapiro		/* XXX: Not much we can do except rewind it */
78590792Sgshapiro		errno = EINVAL;
78690792Sgshapiro		return -1;
78790792Sgshapiro#else /* NOFTRUNCATE */
78890792Sgshapiro		return ftruncate(bfp->bf_disk_fd, 0);
78990792Sgshapiro#endif /* NOFTRUNCATE */
79090792Sgshapiro	}
79190792Sgshapiro	else
79290792Sgshapiro		return 0;
79390792Sgshapiro}
79490792Sgshapiro
79590792Sgshapiro/*
79690792Sgshapiro**  SM_BFSETINFO -- set/change info for an open file pointer
79790792Sgshapiro**
79890792Sgshapiro**	Parameters:
79990792Sgshapiro**		fp -- file pointer to get info about
80090792Sgshapiro**		what -- type of info to set/change
80190792Sgshapiro**		valp -- thing to set/change the info to
80290792Sgshapiro**
80390792Sgshapiro*/
80490792Sgshapiro
80590792Sgshapirostatic int
80690792Sgshapirosm_bfsetinfo(fp, what, valp)
80790792Sgshapiro	SM_FILE_T *fp;
80890792Sgshapiro	int what;
80990792Sgshapiro	void *valp;
81090792Sgshapiro{
81190792Sgshapiro	struct bf *bfp;
81290792Sgshapiro	int bsize;
81390792Sgshapiro
81490792Sgshapiro	/* Get bf structure */
81590792Sgshapiro	bfp = (struct bf *) fp->f_cookie;
81690792Sgshapiro	switch (what)
81790792Sgshapiro	{
81890792Sgshapiro	  case SM_BF_SETBUFSIZE:
81990792Sgshapiro		bsize = *((int *) valp);
82090792Sgshapiro		bfp->bf_bufsize = bsize;
82190792Sgshapiro
82290792Sgshapiro		/* A zero bsize is valid, just don't allocate memory */
82390792Sgshapiro		if (bsize > 0)
82490792Sgshapiro		{
82590792Sgshapiro			bfp->bf_buf = (char *) sm_malloc(bsize);
82690792Sgshapiro			if (bfp->bf_buf == NULL)
82790792Sgshapiro			{
82890792Sgshapiro				bfp->bf_bufsize = 0;
82990792Sgshapiro				errno = ENOMEM;
83090792Sgshapiro				return -1;
83190792Sgshapiro			}
83290792Sgshapiro		}
83390792Sgshapiro		else
83490792Sgshapiro			bfp->bf_buf = NULL;
83590792Sgshapiro		return 0;
83690792Sgshapiro	  case SM_BF_COMMIT:
83790792Sgshapiro		return sm_bfcommit(fp);
83890792Sgshapiro	  case SM_BF_TRUNCATE:
83990792Sgshapiro		return sm_bftruncate(fp);
84090792Sgshapiro	  case SM_BF_TEST:
84190792Sgshapiro		return 1; /* always */
84290792Sgshapiro	  default:
84390792Sgshapiro		errno = EINVAL;
84490792Sgshapiro		return -1;
84590792Sgshapiro	}
84690792Sgshapiro}
847