xref: /illumos-gate/usr/src/cmd/sendmail/libsm/fvwrite.c (revision 2a8bcb4efb45d99ac41c94a75c396b362c414f7f)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
3*7c478bd9Sstevel@tonic-gate  *      All rights reserved.
4*7c478bd9Sstevel@tonic-gate  * Copyright (c) 1990, 1993
5*7c478bd9Sstevel@tonic-gate  *	The Regents of the University of California.  All rights reserved.
6*7c478bd9Sstevel@tonic-gate  *
7*7c478bd9Sstevel@tonic-gate  * This code is derived from software contributed to Berkeley by
8*7c478bd9Sstevel@tonic-gate  * Chris Torek.
9*7c478bd9Sstevel@tonic-gate  *
10*7c478bd9Sstevel@tonic-gate  * By using this file, you agree to the terms and conditions set
11*7c478bd9Sstevel@tonic-gate  * forth in the LICENSE file which can be found at the top level of
12*7c478bd9Sstevel@tonic-gate  * the sendmail distribution.
13*7c478bd9Sstevel@tonic-gate  */
14*7c478bd9Sstevel@tonic-gate 
15*7c478bd9Sstevel@tonic-gate #include <sm/gen.h>
16*7c478bd9Sstevel@tonic-gate SM_RCSID("@(#)$Id: fvwrite.c,v 1.47 2001/08/27 13:02:20 ca Exp $")
17*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
18*7c478bd9Sstevel@tonic-gate #include <unistd.h>
19*7c478bd9Sstevel@tonic-gate #include <string.h>
20*7c478bd9Sstevel@tonic-gate #include <errno.h>
21*7c478bd9Sstevel@tonic-gate #include <signal.h>
22*7c478bd9Sstevel@tonic-gate #include <fcntl.h>
23*7c478bd9Sstevel@tonic-gate #include <sm/io.h>
24*7c478bd9Sstevel@tonic-gate #include <sm/setjmp.h>
25*7c478bd9Sstevel@tonic-gate #include <sm/conf.h>
26*7c478bd9Sstevel@tonic-gate #include "local.h"
27*7c478bd9Sstevel@tonic-gate #include "fvwrite.h"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate /*
30*7c478bd9Sstevel@tonic-gate **  SM_FVWRITE -- write memory regions and buffer for file pointer
31*7c478bd9Sstevel@tonic-gate **
32*7c478bd9Sstevel@tonic-gate **	Parameters:
33*7c478bd9Sstevel@tonic-gate **		fp -- the file pointer to write to
34*7c478bd9Sstevel@tonic-gate **		timeout -- time length for function to return by
35*7c478bd9Sstevel@tonic-gate **		uio -- the memory regions to write
36*7c478bd9Sstevel@tonic-gate **
37*7c478bd9Sstevel@tonic-gate **	Returns:
38*7c478bd9Sstevel@tonic-gate **		Failure: returns SM_IO_EOF and sets errno
39*7c478bd9Sstevel@tonic-gate **		Success: returns 0 (zero)
40*7c478bd9Sstevel@tonic-gate **
41*7c478bd9Sstevel@tonic-gate **	This routine is large and unsightly, but most of the ugliness due
42*7c478bd9Sstevel@tonic-gate **	to the different kinds of output buffering handled here.
43*7c478bd9Sstevel@tonic-gate */
44*7c478bd9Sstevel@tonic-gate 
45*7c478bd9Sstevel@tonic-gate #define COPY(n)	  (void)memcpy((void *)fp->f_p, (void *)p, (size_t)(n))
46*7c478bd9Sstevel@tonic-gate #define GETIOV(extra_work)		\
47*7c478bd9Sstevel@tonic-gate 	while (len == 0)		\
48*7c478bd9Sstevel@tonic-gate 	{				\
49*7c478bd9Sstevel@tonic-gate 		extra_work;		\
50*7c478bd9Sstevel@tonic-gate 		p = iov->iov_base;	\
51*7c478bd9Sstevel@tonic-gate 		len = iov->iov_len;	\
52*7c478bd9Sstevel@tonic-gate 		iov++;			\
53*7c478bd9Sstevel@tonic-gate 	}
54*7c478bd9Sstevel@tonic-gate 
55*7c478bd9Sstevel@tonic-gate int
56*7c478bd9Sstevel@tonic-gate sm_fvwrite(fp, timeout, uio)
57*7c478bd9Sstevel@tonic-gate 	register SM_FILE_T *fp;
58*7c478bd9Sstevel@tonic-gate 	int timeout;
59*7c478bd9Sstevel@tonic-gate 	register struct sm_uio *uio;
60*7c478bd9Sstevel@tonic-gate {
61*7c478bd9Sstevel@tonic-gate 	register size_t len;
62*7c478bd9Sstevel@tonic-gate 	register char *p;
63*7c478bd9Sstevel@tonic-gate 	register struct sm_iov *iov;
64*7c478bd9Sstevel@tonic-gate 	register int w, s;
65*7c478bd9Sstevel@tonic-gate 	char *nl;
66*7c478bd9Sstevel@tonic-gate 	int nlknown, nldist;
67*7c478bd9Sstevel@tonic-gate 	int fd;
68*7c478bd9Sstevel@tonic-gate 	struct timeval to;
69*7c478bd9Sstevel@tonic-gate 
70*7c478bd9Sstevel@tonic-gate 	if (uio->uio_resid == 0)
71*7c478bd9Sstevel@tonic-gate 		return 0;
72*7c478bd9Sstevel@tonic-gate 
73*7c478bd9Sstevel@tonic-gate 	/* make sure we can write */
74*7c478bd9Sstevel@tonic-gate 	if (cantwrite(fp))
75*7c478bd9Sstevel@tonic-gate 	{
76*7c478bd9Sstevel@tonic-gate 		errno = EBADF;
77*7c478bd9Sstevel@tonic-gate 		return SM_IO_EOF;
78*7c478bd9Sstevel@tonic-gate 	}
79*7c478bd9Sstevel@tonic-gate 
80*7c478bd9Sstevel@tonic-gate 	SM_CONVERT_TIME(fp, fd, timeout, &to);
81*7c478bd9Sstevel@tonic-gate 
82*7c478bd9Sstevel@tonic-gate 	iov = uio->uio_iov;
83*7c478bd9Sstevel@tonic-gate 	p = iov->iov_base;
84*7c478bd9Sstevel@tonic-gate 	len = iov->iov_len;
85*7c478bd9Sstevel@tonic-gate 	iov++;
86*7c478bd9Sstevel@tonic-gate 	if (fp->f_flags & SMNBF)
87*7c478bd9Sstevel@tonic-gate 	{
88*7c478bd9Sstevel@tonic-gate 		/* Unbuffered: write up to BUFSIZ bytes at a time. */
89*7c478bd9Sstevel@tonic-gate 		do
90*7c478bd9Sstevel@tonic-gate 		{
91*7c478bd9Sstevel@tonic-gate 			GETIOV(;);
92*7c478bd9Sstevel@tonic-gate 			errno = 0; /* needed to ensure EOF correctly found */
93*7c478bd9Sstevel@tonic-gate 			w = (*fp->f_write)(fp, p, SM_MIN(len, SM_IO_BUFSIZ));
94*7c478bd9Sstevel@tonic-gate 			if (w <= 0)
95*7c478bd9Sstevel@tonic-gate 			{
96*7c478bd9Sstevel@tonic-gate 				if (w == 0 && errno == 0)
97*7c478bd9Sstevel@tonic-gate 					break; /* EOF found */
98*7c478bd9Sstevel@tonic-gate 				if (IS_IO_ERROR(fd, w, timeout))
99*7c478bd9Sstevel@tonic-gate 					goto err; /* errno set */
100*7c478bd9Sstevel@tonic-gate 
101*7c478bd9Sstevel@tonic-gate 				/* write would block */
102*7c478bd9Sstevel@tonic-gate 				SM_IO_WR_TIMEOUT(fp, fd, timeout);
103*7c478bd9Sstevel@tonic-gate 				w = 0;
104*7c478bd9Sstevel@tonic-gate 			}
105*7c478bd9Sstevel@tonic-gate 			else
106*7c478bd9Sstevel@tonic-gate 			{
107*7c478bd9Sstevel@tonic-gate 				p += w;
108*7c478bd9Sstevel@tonic-gate 				len -= w;
109*7c478bd9Sstevel@tonic-gate 			}
110*7c478bd9Sstevel@tonic-gate 		} while ((uio->uio_resid -= w) != 0);
111*7c478bd9Sstevel@tonic-gate 	}
112*7c478bd9Sstevel@tonic-gate 	else if ((fp->f_flags & SMLBF) == 0)
113*7c478bd9Sstevel@tonic-gate 	{
114*7c478bd9Sstevel@tonic-gate 		/*
115*7c478bd9Sstevel@tonic-gate 		**  Not SMLBF (line-buffered). Either SMFBF or SMNOW
116*7c478bd9Sstevel@tonic-gate 		**  buffered: fill partially full buffer, if any,
117*7c478bd9Sstevel@tonic-gate 		**  and then flush.  If there is no partial buffer, write
118*7c478bd9Sstevel@tonic-gate 		**  one bf._size byte chunk directly (without copying).
119*7c478bd9Sstevel@tonic-gate 		**
120*7c478bd9Sstevel@tonic-gate 		**  String output is a special case: write as many bytes
121*7c478bd9Sstevel@tonic-gate 		**  as fit, but pretend we wrote everything.  This makes
122*7c478bd9Sstevel@tonic-gate 		**  snprintf() return the number of bytes needed, rather
123*7c478bd9Sstevel@tonic-gate 		**  than the number used, and avoids its write function
124*7c478bd9Sstevel@tonic-gate 		**  (so that the write function can be invalid).
125*7c478bd9Sstevel@tonic-gate 		*/
126*7c478bd9Sstevel@tonic-gate 
127*7c478bd9Sstevel@tonic-gate 		do
128*7c478bd9Sstevel@tonic-gate 		{
129*7c478bd9Sstevel@tonic-gate 			GETIOV(;);
130*7c478bd9Sstevel@tonic-gate 			if ((((fp->f_flags & (SMALC | SMSTR)) == (SMALC | SMSTR))
131*7c478bd9Sstevel@tonic-gate 			    || ((fp->f_flags & SMNOW) != 0))
132*7c478bd9Sstevel@tonic-gate 			    && (size_t) fp->f_w < len)
133*7c478bd9Sstevel@tonic-gate 			{
134*7c478bd9Sstevel@tonic-gate 				size_t blen = fp->f_p - fp->f_bf.smb_base;
135*7c478bd9Sstevel@tonic-gate 				unsigned char *tbase;
136*7c478bd9Sstevel@tonic-gate 				int tsize;
137*7c478bd9Sstevel@tonic-gate 
138*7c478bd9Sstevel@tonic-gate 				/* Allocate space exponentially. */
139*7c478bd9Sstevel@tonic-gate 				tsize = fp->f_bf.smb_size;
140*7c478bd9Sstevel@tonic-gate 				do
141*7c478bd9Sstevel@tonic-gate 				{
142*7c478bd9Sstevel@tonic-gate 					tsize = (tsize << 1) + 1;
143*7c478bd9Sstevel@tonic-gate 				} while ((size_t) tsize < blen + len);
144*7c478bd9Sstevel@tonic-gate 				tbase = (unsigned char *) sm_realloc(fp->f_bf.smb_base,
145*7c478bd9Sstevel@tonic-gate 								     tsize + 1);
146*7c478bd9Sstevel@tonic-gate 				if (tbase == NULL)
147*7c478bd9Sstevel@tonic-gate 				{
148*7c478bd9Sstevel@tonic-gate 					errno = ENOMEM;
149*7c478bd9Sstevel@tonic-gate 					goto err; /* errno set */
150*7c478bd9Sstevel@tonic-gate 				}
151*7c478bd9Sstevel@tonic-gate 				fp->f_w += tsize - fp->f_bf.smb_size;
152*7c478bd9Sstevel@tonic-gate 				fp->f_bf.smb_base = tbase;
153*7c478bd9Sstevel@tonic-gate 				fp->f_bf.smb_size = tsize;
154*7c478bd9Sstevel@tonic-gate 				fp->f_p = tbase + blen;
155*7c478bd9Sstevel@tonic-gate 			}
156*7c478bd9Sstevel@tonic-gate 			w = fp->f_w;
157*7c478bd9Sstevel@tonic-gate 			errno = 0; /* needed to ensure EOF correctly found */
158*7c478bd9Sstevel@tonic-gate 			if (fp->f_flags & SMSTR)
159*7c478bd9Sstevel@tonic-gate 			{
160*7c478bd9Sstevel@tonic-gate 				if (len < (size_t) w)
161*7c478bd9Sstevel@tonic-gate 					w = len;
162*7c478bd9Sstevel@tonic-gate 				COPY(w);	/* copy SM_MIN(fp->f_w,len), */
163*7c478bd9Sstevel@tonic-gate 				fp->f_w -= w;
164*7c478bd9Sstevel@tonic-gate 				fp->f_p += w;
165*7c478bd9Sstevel@tonic-gate 				w = len;	/* but pretend copied all */
166*7c478bd9Sstevel@tonic-gate 			}
167*7c478bd9Sstevel@tonic-gate 			else if (fp->f_p > fp->f_bf.smb_base
168*7c478bd9Sstevel@tonic-gate 				 && len > (size_t) w)
169*7c478bd9Sstevel@tonic-gate 			{
170*7c478bd9Sstevel@tonic-gate 				/* fill and flush */
171*7c478bd9Sstevel@tonic-gate 				COPY(w);
172*7c478bd9Sstevel@tonic-gate 				fp->f_p += w;
173*7c478bd9Sstevel@tonic-gate 				if (sm_flush(fp, &timeout))
174*7c478bd9Sstevel@tonic-gate 					goto err; /* errno set */
175*7c478bd9Sstevel@tonic-gate 			}
176*7c478bd9Sstevel@tonic-gate 			else if (len >= (size_t) (w = fp->f_bf.smb_size))
177*7c478bd9Sstevel@tonic-gate 			{
178*7c478bd9Sstevel@tonic-gate 				/* write directly */
179*7c478bd9Sstevel@tonic-gate 				w = (*fp->f_write)(fp, p, w);
180*7c478bd9Sstevel@tonic-gate 				if (w <= 0)
181*7c478bd9Sstevel@tonic-gate 				{
182*7c478bd9Sstevel@tonic-gate 					if (w == 0 && errno == 0)
183*7c478bd9Sstevel@tonic-gate 						break; /* EOF found */
184*7c478bd9Sstevel@tonic-gate 					if (IS_IO_ERROR(fd, w, timeout))
185*7c478bd9Sstevel@tonic-gate 						goto err; /* errno set */
186*7c478bd9Sstevel@tonic-gate 
187*7c478bd9Sstevel@tonic-gate 					/* write would block */
188*7c478bd9Sstevel@tonic-gate 					SM_IO_WR_TIMEOUT(fp, fd, timeout);
189*7c478bd9Sstevel@tonic-gate 					w = 0;
190*7c478bd9Sstevel@tonic-gate 				}
191*7c478bd9Sstevel@tonic-gate 			}
192*7c478bd9Sstevel@tonic-gate 			else
193*7c478bd9Sstevel@tonic-gate 			{
194*7c478bd9Sstevel@tonic-gate 				/* fill and done */
195*7c478bd9Sstevel@tonic-gate 				w = len;
196*7c478bd9Sstevel@tonic-gate 				COPY(w);
197*7c478bd9Sstevel@tonic-gate 				fp->f_w -= w;
198*7c478bd9Sstevel@tonic-gate 				fp->f_p += w;
199*7c478bd9Sstevel@tonic-gate 			}
200*7c478bd9Sstevel@tonic-gate 			p += w;
201*7c478bd9Sstevel@tonic-gate 			len -= w;
202*7c478bd9Sstevel@tonic-gate 		} while ((uio->uio_resid -= w) != 0);
203*7c478bd9Sstevel@tonic-gate 
204*7c478bd9Sstevel@tonic-gate 		if ((fp->f_flags & SMNOW) != 0 && sm_flush(fp, &timeout))
205*7c478bd9Sstevel@tonic-gate 			goto err; /* errno set */
206*7c478bd9Sstevel@tonic-gate 	}
207*7c478bd9Sstevel@tonic-gate 	else
208*7c478bd9Sstevel@tonic-gate 	{
209*7c478bd9Sstevel@tonic-gate 		/*
210*7c478bd9Sstevel@tonic-gate 		**  Line buffered: like fully buffered, but we
211*7c478bd9Sstevel@tonic-gate 		**  must check for newlines.  Compute the distance
212*7c478bd9Sstevel@tonic-gate 		**  to the first newline (including the newline),
213*7c478bd9Sstevel@tonic-gate 		**  or `infinity' if there is none, then pretend
214*7c478bd9Sstevel@tonic-gate 		**  that the amount to write is SM_MIN(len,nldist).
215*7c478bd9Sstevel@tonic-gate 		*/
216*7c478bd9Sstevel@tonic-gate 
217*7c478bd9Sstevel@tonic-gate 		nlknown = 0;
218*7c478bd9Sstevel@tonic-gate 		nldist = 0;	/* XXX just to keep gcc happy */
219*7c478bd9Sstevel@tonic-gate 		do
220*7c478bd9Sstevel@tonic-gate 		{
221*7c478bd9Sstevel@tonic-gate 			GETIOV(nlknown = 0);
222*7c478bd9Sstevel@tonic-gate 			if (!nlknown)
223*7c478bd9Sstevel@tonic-gate 			{
224*7c478bd9Sstevel@tonic-gate 				nl = memchr((void *)p, '\n', len);
225*7c478bd9Sstevel@tonic-gate 				nldist = nl != NULL ? nl + 1 - p : len + 1;
226*7c478bd9Sstevel@tonic-gate 				nlknown = 1;
227*7c478bd9Sstevel@tonic-gate 			}
228*7c478bd9Sstevel@tonic-gate 			s = SM_MIN(len, ((size_t) nldist));
229*7c478bd9Sstevel@tonic-gate 			w = fp->f_w + fp->f_bf.smb_size;
230*7c478bd9Sstevel@tonic-gate 			errno = 0; /* needed to ensure EOF correctly found */
231*7c478bd9Sstevel@tonic-gate 			if (fp->f_p > fp->f_bf.smb_base && s > w)
232*7c478bd9Sstevel@tonic-gate 			{
233*7c478bd9Sstevel@tonic-gate 				COPY(w);
234*7c478bd9Sstevel@tonic-gate 				/* fp->f_w -= w; */
235*7c478bd9Sstevel@tonic-gate 				fp->f_p += w;
236*7c478bd9Sstevel@tonic-gate 				if (sm_flush(fp, &timeout))
237*7c478bd9Sstevel@tonic-gate 					goto err; /* errno set */
238*7c478bd9Sstevel@tonic-gate 			}
239*7c478bd9Sstevel@tonic-gate 			else if (s >= (w = fp->f_bf.smb_size))
240*7c478bd9Sstevel@tonic-gate 			{
241*7c478bd9Sstevel@tonic-gate 				w = (*fp->f_write)(fp, p, w);
242*7c478bd9Sstevel@tonic-gate 				if (w <= 0)
243*7c478bd9Sstevel@tonic-gate 				{
244*7c478bd9Sstevel@tonic-gate 					if (w == 0 && errno == 0)
245*7c478bd9Sstevel@tonic-gate 						break; /* EOF found */
246*7c478bd9Sstevel@tonic-gate 					if (IS_IO_ERROR(fd, w, timeout))
247*7c478bd9Sstevel@tonic-gate 						goto err; /* errno set */
248*7c478bd9Sstevel@tonic-gate 
249*7c478bd9Sstevel@tonic-gate 					/* write would block */
250*7c478bd9Sstevel@tonic-gate 					SM_IO_WR_TIMEOUT(fp, fd, timeout);
251*7c478bd9Sstevel@tonic-gate 					w = 0;
252*7c478bd9Sstevel@tonic-gate 				}
253*7c478bd9Sstevel@tonic-gate 			}
254*7c478bd9Sstevel@tonic-gate 			else
255*7c478bd9Sstevel@tonic-gate 			{
256*7c478bd9Sstevel@tonic-gate 				w = s;
257*7c478bd9Sstevel@tonic-gate 				COPY(w);
258*7c478bd9Sstevel@tonic-gate 				fp->f_w -= w;
259*7c478bd9Sstevel@tonic-gate 				fp->f_p += w;
260*7c478bd9Sstevel@tonic-gate 			}
261*7c478bd9Sstevel@tonic-gate 			if ((nldist -= w) == 0)
262*7c478bd9Sstevel@tonic-gate 			{
263*7c478bd9Sstevel@tonic-gate 				/* copied the newline: flush and forget */
264*7c478bd9Sstevel@tonic-gate 				if (sm_flush(fp, &timeout))
265*7c478bd9Sstevel@tonic-gate 					goto err; /* errno set */
266*7c478bd9Sstevel@tonic-gate 				nlknown = 0;
267*7c478bd9Sstevel@tonic-gate 			}
268*7c478bd9Sstevel@tonic-gate 			p += w;
269*7c478bd9Sstevel@tonic-gate 			len -= w;
270*7c478bd9Sstevel@tonic-gate 		} while ((uio->uio_resid -= w) != 0);
271*7c478bd9Sstevel@tonic-gate 	}
272*7c478bd9Sstevel@tonic-gate 
273*7c478bd9Sstevel@tonic-gate 	return 0;
274*7c478bd9Sstevel@tonic-gate 
275*7c478bd9Sstevel@tonic-gate err:
276*7c478bd9Sstevel@tonic-gate 	/* errno set before goto places us here */
277*7c478bd9Sstevel@tonic-gate 	fp->f_flags |= SMERR;
278*7c478bd9Sstevel@tonic-gate 	return SM_IO_EOF;
279*7c478bd9Sstevel@tonic-gate }
280