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