190792Sgshapiro/*
2261363Sgshapiro * Copyright (c) 2000-2001 Proofpoint, Inc. and its suppliers.
390792Sgshapiro *      All rights reserved.
490792Sgshapiro * Copyright (c) 1990, 1993
590792Sgshapiro *	The Regents of the University of California.  All rights reserved.
690792Sgshapiro *
790792Sgshapiro * This code is derived from software contributed to Berkeley by
890792Sgshapiro * Chris Torek.
990792Sgshapiro *
1090792Sgshapiro * By using this file, you agree to the terms and conditions set
1190792Sgshapiro * forth in the LICENSE file which can be found at the top level of
1290792Sgshapiro * the sendmail distribution.
1390792Sgshapiro */
1490792Sgshapiro
1590792Sgshapiro#include <sm/gen.h>
16266692SgshapiroSM_RCSID("@(#)$Id: fvwrite.c,v 1.50 2013-11-22 20:51:42 ca Exp $")
1790792Sgshapiro#include <stdlib.h>
1890792Sgshapiro#include <unistd.h>
1990792Sgshapiro#include <string.h>
2090792Sgshapiro#include <errno.h>
2190792Sgshapiro#include <signal.h>
2290792Sgshapiro#include <fcntl.h>
2390792Sgshapiro#include <sm/io.h>
2490792Sgshapiro#include <sm/setjmp.h>
2590792Sgshapiro#include <sm/conf.h>
2690792Sgshapiro#include "local.h"
2790792Sgshapiro#include "fvwrite.h"
2890792Sgshapiro
2990792Sgshapiro/*
3090792Sgshapiro**  SM_FVWRITE -- write memory regions and buffer for file pointer
3190792Sgshapiro**
3290792Sgshapiro**	Parameters:
3390792Sgshapiro**		fp -- the file pointer to write to
3490792Sgshapiro**		timeout -- time length for function to return by
3590792Sgshapiro**		uio -- the memory regions to write
3690792Sgshapiro**
3790792Sgshapiro**	Returns:
3890792Sgshapiro**		Failure: returns SM_IO_EOF and sets errno
3990792Sgshapiro**		Success: returns 0 (zero)
4090792Sgshapiro**
4190792Sgshapiro**	This routine is large and unsightly, but most of the ugliness due
4290792Sgshapiro**	to the different kinds of output buffering handled here.
4390792Sgshapiro*/
4490792Sgshapiro
4590792Sgshapiro#define COPY(n)	  (void)memcpy((void *)fp->f_p, (void *)p, (size_t)(n))
4690792Sgshapiro#define GETIOV(extra_work)		\
4790792Sgshapiro	while (len == 0)		\
4890792Sgshapiro	{				\
4990792Sgshapiro		extra_work;		\
5090792Sgshapiro		p = iov->iov_base;	\
5190792Sgshapiro		len = iov->iov_len;	\
5290792Sgshapiro		iov++;			\
5390792Sgshapiro	}
5490792Sgshapiro
5590792Sgshapiroint
5690792Sgshapirosm_fvwrite(fp, timeout, uio)
5790792Sgshapiro	register SM_FILE_T *fp;
5890792Sgshapiro	int timeout;
5990792Sgshapiro	register struct sm_uio *uio;
6090792Sgshapiro{
6190792Sgshapiro	register size_t len;
6290792Sgshapiro	register char *p;
6390792Sgshapiro	register struct sm_iov *iov;
6490792Sgshapiro	register int w, s;
6590792Sgshapiro	char *nl;
6690792Sgshapiro	int nlknown, nldist;
6790792Sgshapiro	int fd;
6890792Sgshapiro	struct timeval to;
6990792Sgshapiro
7090792Sgshapiro	if (uio->uio_resid == 0)
7190792Sgshapiro		return 0;
7290792Sgshapiro
7390792Sgshapiro	/* make sure we can write */
7490792Sgshapiro	if (cantwrite(fp))
7590792Sgshapiro	{
7690792Sgshapiro		errno = EBADF;
7790792Sgshapiro		return SM_IO_EOF;
7890792Sgshapiro	}
7990792Sgshapiro
8090792Sgshapiro	SM_CONVERT_TIME(fp, fd, timeout, &to);
8190792Sgshapiro
8290792Sgshapiro	iov = uio->uio_iov;
8390792Sgshapiro	p = iov->iov_base;
8490792Sgshapiro	len = iov->iov_len;
8590792Sgshapiro	iov++;
8690792Sgshapiro	if (fp->f_flags & SMNBF)
8790792Sgshapiro	{
8890792Sgshapiro		/* Unbuffered: write up to BUFSIZ bytes at a time. */
8990792Sgshapiro		do
9090792Sgshapiro		{
9190792Sgshapiro			GETIOV(;);
9290792Sgshapiro			errno = 0; /* needed to ensure EOF correctly found */
9390792Sgshapiro			w = (*fp->f_write)(fp, p, SM_MIN(len, SM_IO_BUFSIZ));
9490792Sgshapiro			if (w <= 0)
9590792Sgshapiro			{
9690792Sgshapiro				if (w == 0 && errno == 0)
9790792Sgshapiro					break; /* EOF found */
9890792Sgshapiro				if (IS_IO_ERROR(fd, w, timeout))
9990792Sgshapiro					goto err; /* errno set */
10090792Sgshapiro
10190792Sgshapiro				/* write would block */
10290792Sgshapiro				SM_IO_WR_TIMEOUT(fp, fd, timeout);
10390792Sgshapiro				w = 0;
10490792Sgshapiro			}
10590792Sgshapiro			else
10690792Sgshapiro			{
10790792Sgshapiro				p += w;
10890792Sgshapiro				len -= w;
10990792Sgshapiro			}
11090792Sgshapiro		} while ((uio->uio_resid -= w) != 0);
11190792Sgshapiro	}
11290792Sgshapiro	else if ((fp->f_flags & SMLBF) == 0)
11390792Sgshapiro	{
11490792Sgshapiro		/*
11590792Sgshapiro		**  Not SMLBF (line-buffered). Either SMFBF or SMNOW
11690792Sgshapiro		**  buffered: fill partially full buffer, if any,
11790792Sgshapiro		**  and then flush.  If there is no partial buffer, write
11890792Sgshapiro		**  one bf._size byte chunk directly (without copying).
11990792Sgshapiro		**
12090792Sgshapiro		**  String output is a special case: write as many bytes
12190792Sgshapiro		**  as fit, but pretend we wrote everything.  This makes
12290792Sgshapiro		**  snprintf() return the number of bytes needed, rather
12390792Sgshapiro		**  than the number used, and avoids its write function
12490792Sgshapiro		**  (so that the write function can be invalid).
12590792Sgshapiro		*/
12690792Sgshapiro
12790792Sgshapiro		do
12890792Sgshapiro		{
12990792Sgshapiro			GETIOV(;);
13090792Sgshapiro			if ((((fp->f_flags & (SMALC | SMSTR)) == (SMALC | SMSTR))
13190792Sgshapiro			    || ((fp->f_flags & SMNOW) != 0))
13290792Sgshapiro			    && (size_t) fp->f_w < len)
13390792Sgshapiro			{
13490792Sgshapiro				size_t blen = fp->f_p - fp->f_bf.smb_base;
13590792Sgshapiro				unsigned char *tbase;
13690792Sgshapiro				int tsize;
13790792Sgshapiro
13890792Sgshapiro				/* Allocate space exponentially. */
13990792Sgshapiro				tsize = fp->f_bf.smb_size;
14090792Sgshapiro				do
14190792Sgshapiro				{
14290792Sgshapiro					tsize = (tsize << 1) + 1;
14390792Sgshapiro				} while ((size_t) tsize < blen + len);
14490792Sgshapiro				tbase = (unsigned char *) sm_realloc(fp->f_bf.smb_base,
14590792Sgshapiro								     tsize + 1);
14690792Sgshapiro				if (tbase == NULL)
14790792Sgshapiro				{
14890792Sgshapiro					errno = ENOMEM;
14990792Sgshapiro					goto err; /* errno set */
15090792Sgshapiro				}
15190792Sgshapiro				fp->f_w += tsize - fp->f_bf.smb_size;
15290792Sgshapiro				fp->f_bf.smb_base = tbase;
15390792Sgshapiro				fp->f_bf.smb_size = tsize;
15490792Sgshapiro				fp->f_p = tbase + blen;
15590792Sgshapiro			}
15690792Sgshapiro			w = fp->f_w;
15790792Sgshapiro			errno = 0; /* needed to ensure EOF correctly found */
15890792Sgshapiro			if (fp->f_flags & SMSTR)
15990792Sgshapiro			{
16090792Sgshapiro				if (len < (size_t) w)
16190792Sgshapiro					w = len;
16290792Sgshapiro				COPY(w);	/* copy SM_MIN(fp->f_w,len), */
16390792Sgshapiro				fp->f_w -= w;
16490792Sgshapiro				fp->f_p += w;
16590792Sgshapiro				w = len;	/* but pretend copied all */
16690792Sgshapiro			}
16790792Sgshapiro			else if (fp->f_p > fp->f_bf.smb_base
16890792Sgshapiro				 && len > (size_t) w)
16990792Sgshapiro			{
17090792Sgshapiro				/* fill and flush */
17190792Sgshapiro				COPY(w);
17290792Sgshapiro				fp->f_p += w;
17390792Sgshapiro				if (sm_flush(fp, &timeout))
17490792Sgshapiro					goto err; /* errno set */
17590792Sgshapiro			}
17690792Sgshapiro			else if (len >= (size_t) (w = fp->f_bf.smb_size))
17790792Sgshapiro			{
17890792Sgshapiro				/* write directly */
17990792Sgshapiro				w = (*fp->f_write)(fp, p, w);
18090792Sgshapiro				if (w <= 0)
18190792Sgshapiro				{
18290792Sgshapiro					if (w == 0 && errno == 0)
18390792Sgshapiro						break; /* EOF found */
18490792Sgshapiro					if (IS_IO_ERROR(fd, w, timeout))
18590792Sgshapiro						goto err; /* errno set */
18690792Sgshapiro
18790792Sgshapiro					/* write would block */
18890792Sgshapiro					SM_IO_WR_TIMEOUT(fp, fd, timeout);
18990792Sgshapiro					w = 0;
19090792Sgshapiro				}
19190792Sgshapiro			}
19290792Sgshapiro			else
19390792Sgshapiro			{
19490792Sgshapiro				/* fill and done */
19590792Sgshapiro				w = len;
19690792Sgshapiro				COPY(w);
19790792Sgshapiro				fp->f_w -= w;
19890792Sgshapiro				fp->f_p += w;
19990792Sgshapiro			}
20090792Sgshapiro			p += w;
20190792Sgshapiro			len -= w;
20290792Sgshapiro		} while ((uio->uio_resid -= w) != 0);
20390792Sgshapiro
20490792Sgshapiro		if ((fp->f_flags & SMNOW) != 0 && sm_flush(fp, &timeout))
20590792Sgshapiro			goto err; /* errno set */
20690792Sgshapiro	}
20790792Sgshapiro	else
20890792Sgshapiro	{
20990792Sgshapiro		/*
21090792Sgshapiro		**  Line buffered: like fully buffered, but we
21190792Sgshapiro		**  must check for newlines.  Compute the distance
21290792Sgshapiro		**  to the first newline (including the newline),
21390792Sgshapiro		**  or `infinity' if there is none, then pretend
21490792Sgshapiro		**  that the amount to write is SM_MIN(len,nldist).
21590792Sgshapiro		*/
21690792Sgshapiro
21790792Sgshapiro		nlknown = 0;
21890792Sgshapiro		nldist = 0;	/* XXX just to keep gcc happy */
21990792Sgshapiro		do
22090792Sgshapiro		{
22190792Sgshapiro			GETIOV(nlknown = 0);
22290792Sgshapiro			if (!nlknown)
22390792Sgshapiro			{
22490792Sgshapiro				nl = memchr((void *)p, '\n', len);
22590792Sgshapiro				nldist = nl != NULL ? nl + 1 - p : len + 1;
22690792Sgshapiro				nlknown = 1;
22790792Sgshapiro			}
22890792Sgshapiro			s = SM_MIN(len, ((size_t) nldist));
22990792Sgshapiro			w = fp->f_w + fp->f_bf.smb_size;
23090792Sgshapiro			errno = 0; /* needed to ensure EOF correctly found */
23190792Sgshapiro			if (fp->f_p > fp->f_bf.smb_base && s > w)
23290792Sgshapiro			{
23390792Sgshapiro				COPY(w);
23490792Sgshapiro				/* fp->f_w -= w; */
23590792Sgshapiro				fp->f_p += w;
23690792Sgshapiro				if (sm_flush(fp, &timeout))
23790792Sgshapiro					goto err; /* errno set */
23890792Sgshapiro			}
23990792Sgshapiro			else if (s >= (w = fp->f_bf.smb_size))
24090792Sgshapiro			{
24190792Sgshapiro				w = (*fp->f_write)(fp, p, w);
24290792Sgshapiro				if (w <= 0)
24390792Sgshapiro				{
24490792Sgshapiro					if (w == 0 && errno == 0)
24590792Sgshapiro						break; /* EOF found */
24690792Sgshapiro					if (IS_IO_ERROR(fd, w, timeout))
24790792Sgshapiro						goto err; /* errno set */
24890792Sgshapiro
24990792Sgshapiro					/* write would block */
25090792Sgshapiro					SM_IO_WR_TIMEOUT(fp, fd, timeout);
25190792Sgshapiro					w = 0;
25290792Sgshapiro				}
25390792Sgshapiro			}
25490792Sgshapiro			else
25590792Sgshapiro			{
25690792Sgshapiro				w = s;
25790792Sgshapiro				COPY(w);
25890792Sgshapiro				fp->f_w -= w;
25990792Sgshapiro				fp->f_p += w;
26090792Sgshapiro			}
26190792Sgshapiro			if ((nldist -= w) == 0)
26290792Sgshapiro			{
26390792Sgshapiro				/* copied the newline: flush and forget */
26490792Sgshapiro				if (sm_flush(fp, &timeout))
26590792Sgshapiro					goto err; /* errno set */
26690792Sgshapiro				nlknown = 0;
26790792Sgshapiro			}
26890792Sgshapiro			p += w;
26990792Sgshapiro			len -= w;
27090792Sgshapiro		} while ((uio->uio_resid -= w) != 0);
27190792Sgshapiro	}
27290792Sgshapiro
27390792Sgshapiro	return 0;
27490792Sgshapiro
27590792Sgshapiroerr:
27690792Sgshapiro	/* errno set before goto places us here */
27790792Sgshapiro	fp->f_flags |= SMERR;
27890792Sgshapiro	return SM_IO_EOF;
27990792Sgshapiro}
280