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