xref: /freebsd/contrib/sendmail/libsm/fvwrite.c (revision 94c01205)
140266059SGregory Neil Shapiro /*
240266059SGregory Neil Shapiro  * Copyright (c) 2000-2001 Proofpoint, Inc. and its suppliers.
340266059SGregory Neil Shapiro  *      All rights reserved.
440266059SGregory Neil Shapiro  * Copyright (c) 1990, 1993
540266059SGregory Neil Shapiro  *	The Regents of the University of California.  All rights reserved.
640266059SGregory Neil Shapiro  *
740266059SGregory Neil Shapiro  * This code is derived from software contributed to Berkeley by
840266059SGregory Neil Shapiro  * Chris Torek.
940266059SGregory Neil Shapiro  *
1040266059SGregory Neil Shapiro  * By using this file, you agree to the terms and conditions set
1140266059SGregory Neil Shapiro  * forth in the LICENSE file which can be found at the top level of
1240266059SGregory Neil Shapiro  * the sendmail distribution.
1340266059SGregory Neil Shapiro  */
1440266059SGregory Neil Shapiro 
1540266059SGregory Neil Shapiro #include <sm/gen.h>
1694c01205SGregory Neil Shapiro SM_RCSID("@(#)$Id: fvwrite.c,v 1.50 2013-11-22 20:51:42 ca Exp $")
1740266059SGregory Neil Shapiro #include <stdlib.h>
1840266059SGregory Neil Shapiro #include <unistd.h>
1940266059SGregory Neil Shapiro #include <string.h>
2040266059SGregory Neil Shapiro #include <errno.h>
2140266059SGregory Neil Shapiro #include <signal.h>
2240266059SGregory Neil Shapiro #include <fcntl.h>
2340266059SGregory Neil Shapiro #include <sm/io.h>
2440266059SGregory Neil Shapiro #include <sm/setjmp.h>
2540266059SGregory Neil Shapiro #include <sm/conf.h>
2640266059SGregory Neil Shapiro #include "local.h"
2740266059SGregory Neil Shapiro #include "fvwrite.h"
2840266059SGregory Neil Shapiro 
2940266059SGregory Neil Shapiro /*
3040266059SGregory Neil Shapiro **  SM_FVWRITE -- write memory regions and buffer for file pointer
3140266059SGregory Neil Shapiro **
3240266059SGregory Neil Shapiro **	Parameters:
3340266059SGregory Neil Shapiro **		fp -- the file pointer to write to
3440266059SGregory Neil Shapiro **		timeout -- time length for function to return by
3540266059SGregory Neil Shapiro **		uio -- the memory regions to write
3640266059SGregory Neil Shapiro **
3740266059SGregory Neil Shapiro **	Returns:
3840266059SGregory Neil Shapiro **		Failure: returns SM_IO_EOF and sets errno
3940266059SGregory Neil Shapiro **		Success: returns 0 (zero)
4040266059SGregory Neil Shapiro **
4140266059SGregory Neil Shapiro **	This routine is large and unsightly, but most of the ugliness due
4240266059SGregory Neil Shapiro **	to the different kinds of output buffering handled here.
4340266059SGregory Neil Shapiro */
4440266059SGregory Neil Shapiro 
4540266059SGregory Neil Shapiro #define COPY(n)	  (void)memcpy((void *)fp->f_p, (void *)p, (size_t)(n))
4640266059SGregory Neil Shapiro #define GETIOV(extra_work)		\
4740266059SGregory Neil Shapiro 	while (len == 0)		\
4840266059SGregory Neil Shapiro 	{				\
4940266059SGregory Neil Shapiro 		extra_work;		\
5040266059SGregory Neil Shapiro 		p = iov->iov_base;	\
5140266059SGregory Neil Shapiro 		len = iov->iov_len;	\
5240266059SGregory Neil Shapiro 		iov++;			\
5340266059SGregory Neil Shapiro 	}
5440266059SGregory Neil Shapiro 
5540266059SGregory Neil Shapiro int
5640266059SGregory Neil Shapiro sm_fvwrite(fp, timeout, uio)
5740266059SGregory Neil Shapiro 	register SM_FILE_T *fp;
5840266059SGregory Neil Shapiro 	int timeout;
5940266059SGregory Neil Shapiro 	register struct sm_uio *uio;
6040266059SGregory Neil Shapiro {
6140266059SGregory Neil Shapiro 	register size_t len;
6240266059SGregory Neil Shapiro 	register char *p;
6340266059SGregory Neil Shapiro 	register struct sm_iov *iov;
6440266059SGregory Neil Shapiro 	register int w, s;
6540266059SGregory Neil Shapiro 	char *nl;
6640266059SGregory Neil Shapiro 	int nlknown, nldist;
6740266059SGregory Neil Shapiro 	int fd;
6840266059SGregory Neil Shapiro 	struct timeval to;
6940266059SGregory Neil Shapiro 
7040266059SGregory Neil Shapiro 	if (uio->uio_resid == 0)
7140266059SGregory Neil Shapiro 		return 0;
7240266059SGregory Neil Shapiro 
7340266059SGregory Neil Shapiro 	/* make sure we can write */
7440266059SGregory Neil Shapiro 	if (cantwrite(fp))
7540266059SGregory Neil Shapiro 	{
7640266059SGregory Neil Shapiro 		errno = EBADF;
7740266059SGregory Neil Shapiro 		return SM_IO_EOF;
7840266059SGregory Neil Shapiro 	}
7940266059SGregory Neil Shapiro 
8040266059SGregory Neil Shapiro 	SM_CONVERT_TIME(fp, fd, timeout, &to);
8140266059SGregory Neil Shapiro 
8240266059SGregory Neil Shapiro 	iov = uio->uio_iov;
8340266059SGregory Neil Shapiro 	p = iov->iov_base;
8440266059SGregory Neil Shapiro 	len = iov->iov_len;
8540266059SGregory Neil Shapiro 	iov++;
8640266059SGregory Neil Shapiro 	if (fp->f_flags & SMNBF)
8740266059SGregory Neil Shapiro 	{
8840266059SGregory Neil Shapiro 		/* Unbuffered: write up to BUFSIZ bytes at a time. */
8940266059SGregory Neil Shapiro 		do
9040266059SGregory Neil Shapiro 		{
9140266059SGregory Neil Shapiro 			GETIOV(;);
9240266059SGregory Neil Shapiro 			errno = 0; /* needed to ensure EOF correctly found */
9340266059SGregory Neil Shapiro 			w = (*fp->f_write)(fp, p, SM_MIN(len, SM_IO_BUFSIZ));
9440266059SGregory Neil Shapiro 			if (w <= 0)
9540266059SGregory Neil Shapiro 			{
9640266059SGregory Neil Shapiro 				if (w == 0 && errno == 0)
9740266059SGregory Neil Shapiro 					break; /* EOF found */
9840266059SGregory Neil Shapiro 				if (IS_IO_ERROR(fd, w, timeout))
9940266059SGregory Neil Shapiro 					goto err; /* errno set */
10040266059SGregory Neil Shapiro 
10140266059SGregory Neil Shapiro 				/* write would block */
10240266059SGregory Neil Shapiro 				SM_IO_WR_TIMEOUT(fp, fd, timeout);
10340266059SGregory Neil Shapiro 				w = 0;
10440266059SGregory Neil Shapiro 			}
10540266059SGregory Neil Shapiro 			else
10640266059SGregory Neil Shapiro 			{
10740266059SGregory Neil Shapiro 				p += w;
10840266059SGregory Neil Shapiro 				len -= w;
10940266059SGregory Neil Shapiro 			}
11040266059SGregory Neil Shapiro 		} while ((uio->uio_resid -= w) != 0);
11140266059SGregory Neil Shapiro 	}
11240266059SGregory Neil Shapiro 	else if ((fp->f_flags & SMLBF) == 0)
11340266059SGregory Neil Shapiro 	{
11440266059SGregory Neil Shapiro 		/*
11540266059SGregory Neil Shapiro 		**  Not SMLBF (line-buffered). Either SMFBF or SMNOW
11640266059SGregory Neil Shapiro 		**  buffered: fill partially full buffer, if any,
11740266059SGregory Neil Shapiro 		**  and then flush.  If there is no partial buffer, write
11840266059SGregory Neil Shapiro 		**  one bf._size byte chunk directly (without copying).
11940266059SGregory Neil Shapiro 		**
12040266059SGregory Neil Shapiro 		**  String output is a special case: write as many bytes
12140266059SGregory Neil Shapiro 		**  as fit, but pretend we wrote everything.  This makes
12240266059SGregory Neil Shapiro 		**  snprintf() return the number of bytes needed, rather
12340266059SGregory Neil Shapiro 		**  than the number used, and avoids its write function
12440266059SGregory Neil Shapiro 		**  (so that the write function can be invalid).
12540266059SGregory Neil Shapiro 		*/
12640266059SGregory Neil Shapiro 
12740266059SGregory Neil Shapiro 		do
12840266059SGregory Neil Shapiro 		{
12940266059SGregory Neil Shapiro 			GETIOV(;);
13040266059SGregory Neil Shapiro 			if ((((fp->f_flags & (SMALC | SMSTR)) == (SMALC | SMSTR))
13140266059SGregory Neil Shapiro 			    || ((fp->f_flags & SMNOW) != 0))
13240266059SGregory Neil Shapiro 			    && (size_t) fp->f_w < len)
13340266059SGregory Neil Shapiro 			{
13440266059SGregory Neil Shapiro 				size_t blen = fp->f_p - fp->f_bf.smb_base;
13540266059SGregory Neil Shapiro 				unsigned char *tbase;
13640266059SGregory Neil Shapiro 				int tsize;
13740266059SGregory Neil Shapiro 
13840266059SGregory Neil Shapiro 				/* Allocate space exponentially. */
13940266059SGregory Neil Shapiro 				tsize = fp->f_bf.smb_size;
14040266059SGregory Neil Shapiro 				do
14140266059SGregory Neil Shapiro 				{
14240266059SGregory Neil Shapiro 					tsize = (tsize << 1) + 1;
14340266059SGregory Neil Shapiro 				} while ((size_t) tsize < blen + len);
14440266059SGregory Neil Shapiro 				tbase = (unsigned char *) sm_realloc(fp->f_bf.smb_base,
14540266059SGregory Neil Shapiro 								     tsize + 1);
14640266059SGregory Neil Shapiro 				if (tbase == NULL)
14740266059SGregory Neil Shapiro 				{
14840266059SGregory Neil Shapiro 					errno = ENOMEM;
14940266059SGregory Neil Shapiro 					goto err; /* errno set */
15040266059SGregory Neil Shapiro 				}
15140266059SGregory Neil Shapiro 				fp->f_w += tsize - fp->f_bf.smb_size;
15240266059SGregory Neil Shapiro 				fp->f_bf.smb_base = tbase;
15340266059SGregory Neil Shapiro 				fp->f_bf.smb_size = tsize;
15440266059SGregory Neil Shapiro 				fp->f_p = tbase + blen;
15540266059SGregory Neil Shapiro 			}
15640266059SGregory Neil Shapiro 			w = fp->f_w;
15740266059SGregory Neil Shapiro 			errno = 0; /* needed to ensure EOF correctly found */
15840266059SGregory Neil Shapiro 			if (fp->f_flags & SMSTR)
15940266059SGregory Neil Shapiro 			{
16040266059SGregory Neil Shapiro 				if (len < (size_t) w)
16140266059SGregory Neil Shapiro 					w = len;
16240266059SGregory Neil Shapiro 				COPY(w);	/* copy SM_MIN(fp->f_w,len), */
16340266059SGregory Neil Shapiro 				fp->f_w -= w;
16440266059SGregory Neil Shapiro 				fp->f_p += w;
16540266059SGregory Neil Shapiro 				w = len;	/* but pretend copied all */
16640266059SGregory Neil Shapiro 			}
16740266059SGregory Neil Shapiro 			else if (fp->f_p > fp->f_bf.smb_base
16840266059SGregory Neil Shapiro 				 && len > (size_t) w)
16940266059SGregory Neil Shapiro 			{
17040266059SGregory Neil Shapiro 				/* fill and flush */
17140266059SGregory Neil Shapiro 				COPY(w);
17240266059SGregory Neil Shapiro 				fp->f_p += w;
17340266059SGregory Neil Shapiro 				if (sm_flush(fp, &timeout))
17440266059SGregory Neil Shapiro 					goto err; /* errno set */
17540266059SGregory Neil Shapiro 			}
17640266059SGregory Neil Shapiro 			else if (len >= (size_t) (w = fp->f_bf.smb_size))
17740266059SGregory Neil Shapiro 			{
17840266059SGregory Neil Shapiro 				/* write directly */
17940266059SGregory Neil Shapiro 				w = (*fp->f_write)(fp, p, w);
18040266059SGregory Neil Shapiro 				if (w <= 0)
18140266059SGregory Neil Shapiro 				{
18240266059SGregory Neil Shapiro 					if (w == 0 && errno == 0)
18340266059SGregory Neil Shapiro 						break; /* EOF found */
18440266059SGregory Neil Shapiro 					if (IS_IO_ERROR(fd, w, timeout))
18540266059SGregory Neil Shapiro 						goto err; /* errno set */
18640266059SGregory Neil Shapiro 
18740266059SGregory Neil Shapiro 					/* write would block */
18840266059SGregory Neil Shapiro 					SM_IO_WR_TIMEOUT(fp, fd, timeout);
18940266059SGregory Neil Shapiro 					w = 0;
19040266059SGregory Neil Shapiro 				}
19140266059SGregory Neil Shapiro 			}
19240266059SGregory Neil Shapiro 			else
19340266059SGregory Neil Shapiro 			{
19440266059SGregory Neil Shapiro 				/* fill and done */
19540266059SGregory Neil Shapiro 				w = len;
19640266059SGregory Neil Shapiro 				COPY(w);
19740266059SGregory Neil Shapiro 				fp->f_w -= w;
19840266059SGregory Neil Shapiro 				fp->f_p += w;
19940266059SGregory Neil Shapiro 			}
20040266059SGregory Neil Shapiro 			p += w;
20140266059SGregory Neil Shapiro 			len -= w;
20240266059SGregory Neil Shapiro 		} while ((uio->uio_resid -= w) != 0);
20340266059SGregory Neil Shapiro 
20440266059SGregory Neil Shapiro 		if ((fp->f_flags & SMNOW) != 0 && sm_flush(fp, &timeout))
20540266059SGregory Neil Shapiro 			goto err; /* errno set */
20640266059SGregory Neil Shapiro 	}
20740266059SGregory Neil Shapiro 	else
20840266059SGregory Neil Shapiro 	{
20940266059SGregory Neil Shapiro 		/*
21040266059SGregory Neil Shapiro 		**  Line buffered: like fully buffered, but we
21140266059SGregory Neil Shapiro 		**  must check for newlines.  Compute the distance
21240266059SGregory Neil Shapiro 		**  to the first newline (including the newline),
21340266059SGregory Neil Shapiro 		**  or `infinity' if there is none, then pretend
21440266059SGregory Neil Shapiro 		**  that the amount to write is SM_MIN(len,nldist).
21540266059SGregory Neil Shapiro 		*/
21640266059SGregory Neil Shapiro 
21740266059SGregory Neil Shapiro 		nlknown = 0;
21840266059SGregory Neil Shapiro 		nldist = 0;	/* XXX just to keep gcc happy */
21940266059SGregory Neil Shapiro 		do
22040266059SGregory Neil Shapiro 		{
22140266059SGregory Neil Shapiro 			GETIOV(nlknown = 0);
22240266059SGregory Neil Shapiro 			if (!nlknown)
22340266059SGregory Neil Shapiro 			{
22440266059SGregory Neil Shapiro 				nl = memchr((void *)p, '\n', len);
22540266059SGregory Neil Shapiro 				nldist = nl != NULL ? nl + 1 - p : len + 1;
22640266059SGregory Neil Shapiro 				nlknown = 1;
22740266059SGregory Neil Shapiro 			}
22840266059SGregory Neil Shapiro 			s = SM_MIN(len, ((size_t) nldist));
22940266059SGregory Neil Shapiro 			w = fp->f_w + fp->f_bf.smb_size;
23040266059SGregory Neil Shapiro 			errno = 0; /* needed to ensure EOF correctly found */
23140266059SGregory Neil Shapiro 			if (fp->f_p > fp->f_bf.smb_base && s > w)
23240266059SGregory Neil Shapiro 			{
23340266059SGregory Neil Shapiro 				COPY(w);
23440266059SGregory Neil Shapiro 				/* fp->f_w -= w; */
23540266059SGregory Neil Shapiro 				fp->f_p += w;
23640266059SGregory Neil Shapiro 				if (sm_flush(fp, &timeout))
23740266059SGregory Neil Shapiro 					goto err; /* errno set */
23840266059SGregory Neil Shapiro 			}
23940266059SGregory Neil Shapiro 			else if (s >= (w = fp->f_bf.smb_size))
24040266059SGregory Neil Shapiro 			{
24140266059SGregory Neil Shapiro 				w = (*fp->f_write)(fp, p, w);
24240266059SGregory Neil Shapiro 				if (w <= 0)
24340266059SGregory Neil Shapiro 				{
24440266059SGregory Neil Shapiro 					if (w == 0 && errno == 0)
24540266059SGregory Neil Shapiro 						break; /* EOF found */
24640266059SGregory Neil Shapiro 					if (IS_IO_ERROR(fd, w, timeout))
24740266059SGregory Neil Shapiro 						goto err; /* errno set */
24840266059SGregory Neil Shapiro 
24940266059SGregory Neil Shapiro 					/* write would block */
25040266059SGregory Neil Shapiro 					SM_IO_WR_TIMEOUT(fp, fd, timeout);
25140266059SGregory Neil Shapiro 					w = 0;
25240266059SGregory Neil Shapiro 				}
25340266059SGregory Neil Shapiro 			}
25440266059SGregory Neil Shapiro 			else
25540266059SGregory Neil Shapiro 			{
25640266059SGregory Neil Shapiro 				w = s;
25740266059SGregory Neil Shapiro 				COPY(w);
25840266059SGregory Neil Shapiro 				fp->f_w -= w;
25940266059SGregory Neil Shapiro 				fp->f_p += w;
26040266059SGregory Neil Shapiro 			}
26140266059SGregory Neil Shapiro 			if ((nldist -= w) == 0)
26240266059SGregory Neil Shapiro 			{
26340266059SGregory Neil Shapiro 				/* copied the newline: flush and forget */
26440266059SGregory Neil Shapiro 				if (sm_flush(fp, &timeout))
26540266059SGregory Neil Shapiro 					goto err; /* errno set */
26640266059SGregory Neil Shapiro 				nlknown = 0;
26740266059SGregory Neil Shapiro 			}
26840266059SGregory Neil Shapiro 			p += w;
26940266059SGregory Neil Shapiro 			len -= w;
27040266059SGregory Neil Shapiro 		} while ((uio->uio_resid -= w) != 0);
27140266059SGregory Neil Shapiro 	}
27240266059SGregory Neil Shapiro 
27340266059SGregory Neil Shapiro 	return 0;
27440266059SGregory Neil Shapiro 
27540266059SGregory Neil Shapiro err:
27640266059SGregory Neil Shapiro 	/* errno set before goto places us here */
27740266059SGregory Neil Shapiro 	fp->f_flags |= SMERR;
27840266059SGregory Neil Shapiro 	return SM_IO_EOF;
27940266059SGregory Neil Shapiro }
280