source: trunk/third/sendmail/libsm/refill.c @ 22421

Revision 22421, 7.1 KB checked in by zacheiss, 19 years ago (diff)
Apply patches from 3-22-06 CERT advisory.
Line 
1/*
2 * Copyright (c) 2000-2001 Sendmail, 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>
16SM_RCSID("@(#)$Id: refill.c,v 1.2 2006-03-23 21:02:45 zacheiss Exp $")
17#include <stdlib.h>
18#include <unistd.h>
19#include <errno.h>
20#include <setjmp.h>
21#include <signal.h>
22#include <sys/time.h>
23#include <fcntl.h>
24#include <string.h>
25#include <sm/io.h>
26#include <sm/conf.h>
27#include <sm/assert.h>
28#include "local.h"
29
30static int sm_lflush __P((SM_FILE_T *, int *));
31
32/*
33**  SM_IO_RD_TIMEOUT -- measured timeout for reads
34**
35**  This #define uses a select() to wait for the 'fd' to become readable.
36**  The select() can be active for up to 'To' time. The select() may not
37**  use all of the the 'To' time. Hence, the amount of "wall-clock" time is
38**  measured to decide how much to subtract from 'To' to update it. On some
39**  BSD-based/like systems the timeout for a select() is updated for the
40**  amount of time used. On many/most systems this does not happen. Therefore
41**  the updating of 'To' must be done ourselves; a copy of 'To' is passed
42**  since a BSD-like system will have updated it and we don't want to
43**  double the time used!
44**  Note: if a valid 'fd' doesn't exist yet, don't use this (e.g. the
45**  sendmail buffered file type in sendmail/bf.c; see use below).
46**
47**      Parameters
48**              fp -- the file pointer for the active file
49**              fd -- raw file descriptor (from 'fp') to use for select()
50**              to -- struct timeval of the timeout
51**              timeout -- the original timeout value
52**              sel_ret -- the return value from the select()
53**
54**      Returns:
55**              nothing, flow through code
56*/
57
58#define SM_IO_RD_TIMEOUT(fp, fd, to, timeout, sel_ret)                  \
59{                                                                       \
60        struct timeval sm_io_to_before, sm_io_to_after, sm_io_to_diff;  \
61        fd_set sm_io_to_mask, sm_io_x_mask;                             \
62        errno = 0;                                                      \
63        if (timeout == SM_TIME_IMMEDIATE)                               \
64        {                                                               \
65                errno = EAGAIN;                                         \
66                return SM_IO_EOF;                                       \
67        }                                                               \
68        if (FD_SETSIZE > 0 && (fd) >= FD_SETSIZE)                       \
69        {                                                               \
70                errno = EINVAL;                                         \
71                return SM_IO_EOF;                                       \
72        }                                                               \
73        FD_ZERO(&sm_io_to_mask);                                        \
74        FD_SET((fd), &sm_io_to_mask);                                   \
75        FD_ZERO(&sm_io_x_mask);                                         \
76        FD_SET((fd), &sm_io_x_mask);                                    \
77        if (gettimeofday(&sm_io_to_before, NULL) < 0)                   \
78                return SM_IO_EOF;                                       \
79        do                                                              \
80        {                                                               \
81                (sel_ret) = select((fd) + 1, &sm_io_to_mask, NULL,      \
82                                &sm_io_x_mask, (to));                   \
83        } while ((sel_ret) < 0 && errno == EINTR);                      \
84        if ((sel_ret) < 0)                                              \
85        {                                                               \
86                /* something went wrong, errno set */                   \
87                fp->f_r = 0;                                            \
88                fp->f_flags |= SMERR;                                   \
89                return SM_IO_EOF;                                       \
90        }                                                               \
91        else if ((sel_ret) == 0)                                        \
92        {                                                               \
93                /* timeout */                                           \
94                errno = EAGAIN;                                         \
95                return SM_IO_EOF;                                       \
96        }                                                               \
97        /* calulate wall-clock time used */                             \
98        if (gettimeofday(&sm_io_to_after, NULL) < 0)                    \
99                return SM_IO_EOF;                                       \
100        timersub(&sm_io_to_after, &sm_io_to_before, &sm_io_to_diff);    \
101        timersub((to), &sm_io_to_diff, (to));                           \
102}
103
104/*
105**  SM_LFLUSH -- flush a file if it is line buffered and writable
106**
107**      Parameters:
108**              fp -- file pointer to flush
109**              timeout -- original timeout value (in milliseconds)
110**
111**      Returns:
112**              Failure: returns SM_IO_EOF and sets errno
113**              Success: returns 0
114*/
115
116static int
117sm_lflush(fp, timeout)
118        SM_FILE_T *fp;
119        int *timeout;
120{
121
122        if ((fp->f_flags & (SMLBF|SMWR)) == (SMLBF|SMWR))
123                return sm_flush(fp, timeout);
124        return 0;
125}
126
127/*
128**  SM_REFILL -- refill a buffer
129**
130**      Parameters:
131**              fp -- file pointer for buffer refill
132**              timeout -- time to complete filling the buffer in milliseconds
133**
134**      Returns:
135**              Success: returns 0
136**              Failure: returns SM_IO_EOF
137*/
138
139int
140sm_refill(fp, timeout)
141        register SM_FILE_T *fp;
142        int timeout;
143{
144        int ret, r;
145        struct timeval to;
146        int fd;
147
148        if (timeout == SM_TIME_DEFAULT)
149                timeout = fp->f_timeout;
150        if (timeout == SM_TIME_IMMEDIATE)
151        {
152                /*
153                **  Filling the buffer will take time and we are wanted to
154                **  return immediately. And we're not EOF or ERR really.
155                **  So... the failure is we couldn't do it in time.
156                */
157
158                errno = EAGAIN;
159                fp->f_r = 0; /* just to be sure */
160                return 0;
161        }
162
163        /* make sure stdio is set up */
164        if (!Sm_IO_DidInit)
165                sm_init();
166
167        fp->f_r = 0;            /* largely a convenience for callers */
168
169        if (fp->f_flags & SMFEOF)
170                return SM_IO_EOF;
171
172        SM_CONVERT_TIME(fp, fd, timeout, &to);
173
174        /* if not already reading, have to be reading and writing */
175        if ((fp->f_flags & SMRD) == 0)
176        {
177                if ((fp->f_flags & SMRW) == 0)
178                {
179                        errno = EBADF;
180                        fp->f_flags |= SMERR;
181                        return SM_IO_EOF;
182                }
183
184                /* switch to reading */
185                if (fp->f_flags & SMWR)
186                {
187                        if (sm_flush(fp, &timeout))
188                                return SM_IO_EOF;
189                        fp->f_flags &= ~SMWR;
190                        fp->f_w = 0;
191                        fp->f_lbfsize = 0;
192                }
193                fp->f_flags |= SMRD;
194        }
195        else
196        {
197                /*
198                **  We were reading.  If there is an ungetc buffer,
199                **  we must have been reading from that.  Drop it,
200                **  restoring the previous buffer (if any).  If there
201                **  is anything in that buffer, return.
202                */
203
204                if (HASUB(fp))
205                {
206                        FREEUB(fp);
207                        if ((fp->f_r = fp->f_ur) != 0)
208                        {
209                                fp->f_p = fp->f_up;
210
211                                /* revert blocking state */
212                                return 0;
213                        }
214                }
215        }
216
217        if (fp->f_bf.smb_base == NULL)
218                sm_makebuf(fp);
219
220        /*
221        **  Before reading from a line buffered or unbuffered file,
222        **  flush all line buffered output files, per the ANSI C standard.
223        */
224
225        if (fp->f_flags & (SMLBF|SMNBF))
226                (void) sm_fwalk(sm_lflush, &timeout);
227
228        /*
229        **  If this file is linked to another, and we are going to hang
230        **  on the read, flush the linked file before continuing.
231        */
232
233        if (fp->f_flushfp != NULL &&
234            (*fp->f_getinfo)(fp, SM_IO_IS_READABLE, NULL) <= 0)
235                sm_flush(fp->f_flushfp, &timeout);
236
237        fp->f_p = fp->f_bf.smb_base;
238
239        /*
240        **  The do-while loop stops trying to read when something is read
241        **  or it appears that the timeout has expired before finding
242        **  something available to be read (via select()).
243        */
244
245        ret = 0;
246        do
247        {
248                errno = 0; /* needed to ensure EOF correctly found */
249                r = (*fp->f_read)(fp, (char *)fp->f_p, fp->f_bf.smb_size);
250                if (r <= 0)
251                {
252                        if (r == 0 && errno == 0)
253                                break; /* EOF found */
254                        if (IS_IO_ERROR(fd, r, timeout))
255                                goto err; /* errno set */
256
257                        /* read would block */
258                        SM_IO_RD_TIMEOUT(fp, fd, &to, timeout, ret);
259                }
260        } while (r <= 0 && ret > 0);
261
262err:
263        if (r <= 0)
264        {
265                if (r == 0)
266                        fp->f_flags |= SMFEOF;
267                else
268                        fp->f_flags |= SMERR;
269                fp->f_r = 0;
270                return SM_IO_EOF;
271        }
272        fp->f_r = r;
273        return 0;
274}
275
276/*
277**  SM_RGET -- refills buffer and returns first character
278**
279**  Handle sm_getc() when the buffer ran out:
280**  Refill, then return the first character in the newly-filled buffer.
281**
282**      Parameters:
283**              fp -- file pointer to work on
284**              timeout -- time to complete refill
285**
286**      Returns:
287**              Success: first character in refilled buffer as an int
288**              Failure: SM_IO_EOF
289*/
290
291int
292sm_rget(fp, timeout)
293        register SM_FILE_T *fp;
294        int timeout;
295{
296        if (sm_refill(fp, timeout) == 0)
297        {
298                fp->f_r--;
299                return *fp->f_p++;
300        }
301        return SM_IO_EOF;
302}
Note: See TracBrowser for help on using the repository browser.