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