source: trunk/third/sendmail/libsm/fseek.c @ 19204

Revision 19204, 7.2 KB checked in by zacheiss, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r19203, which included commits to RCS files with non-trunk default branches.
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: fseek.c,v 1.1.1.1 2003-04-08 15:06:04 zacheiss Exp $")
17#include <sys/types.h>
18#include <sys/stat.h>
19#include <fcntl.h>
20#include <stdlib.h>
21#include <errno.h>
22#include <setjmp.h>
23#include <sys/time.h>
24#include <sm/signal.h>
25#include <sm/io.h>
26#include <sm/assert.h>
27#include <sm/clock.h>
28#include "local.h"
29
30#define POS_ERR (-(off_t)1)
31
32static jmp_buf SeekTimeOut;
33
34/*
35**  SEEKALRM -- handler when timeout activated for sm_io_seek()
36**
37**  Returns flow of control to where setjmp(SeekTimeOut) was set.
38**
39**      Parameters:
40**              sig -- unused
41**
42**      Returns:
43**              does not return
44**
45**      Side Effects:
46**              returns flow of control to setjmp(SeekTimeOut).
47**
48**      NOTE:   THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
49**              ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
50**              DOING.
51*/
52
53/* ARGSUSED0 */
54static void
55seekalrm(sig)
56        int sig;
57{
58        longjmp(SeekTimeOut, 1);
59}
60
61/*
62**  SM_IO_SEEK -- position the file pointer
63**
64**      Parameters:
65**              fp -- the file pointer to be seek'd
66**              timeout -- time to complete seek (milliseconds)
67**              offset -- seek offset based on 'whence'
68**              whence -- indicates where seek is relative from.
69**                      One of SM_IO_SEEK_{CUR,SET,END}.
70**      Returns:
71**              Failure: returns -1 (minus 1) and sets errno
72**              Success: returns 0 (zero)
73*/
74
75int
76sm_io_seek(fp, timeout, offset, whence)
77        register SM_FILE_T *fp;
78        int SM_NONVOLATILE timeout;
79        long SM_NONVOLATILE offset;
80        int SM_NONVOLATILE whence;
81{
82        bool havepos;
83        off_t target, curoff;
84        size_t n;
85        struct stat st;
86        int ret;
87        SM_EVENT *evt = NULL;
88        register off_t (*seekfn) __P((SM_FILE_T *, off_t, int));
89
90        SM_REQUIRE_ISA(fp, SmFileMagic);
91
92        /* make sure stdio is set up */
93        if (!Sm_IO_DidInit)
94                sm_init();
95
96        /* Have to be able to seek. */
97        if ((seekfn = fp->f_seek) == NULL)
98        {
99                errno = ESPIPE;                 /* historic practice */
100                return -1;
101        }
102
103        if (timeout == SM_TIME_DEFAULT)
104                timeout = fp->f_timeout;
105        if (timeout == SM_TIME_IMMEDIATE)
106        {
107                /*
108                **  Filling the buffer will take time and we are wanted to
109                **  return immediately. So...
110                */
111
112                errno = EAGAIN;
113                return -1;
114        }
115
116#define SM_SET_ALARM()                                          \
117        if (timeout != SM_TIME_FOREVER)                         \
118        {                                                       \
119                if (setjmp(SeekTimeOut) != 0)                   \
120                {                                               \
121                        errno = EAGAIN;                         \
122                        return -1;                              \
123                }                                               \
124                evt = sm_seteventm(timeout, seekalrm, 0);       \
125        }
126
127        /*
128        **  Change any SM_IO_SEEK_CUR to SM_IO_SEEK_SET, and check `whence'
129        **  argument. After this, whence is either SM_IO_SEEK_SET or
130        **  SM_IO_SEEK_END.
131        */
132
133        switch (whence)
134        {
135          case SM_IO_SEEK_CUR:
136
137                /*
138                **  In order to seek relative to the current stream offset,
139                **  we have to first find the current stream offset a la
140                **  ftell (see ftell for details).
141                */
142
143                /* may adjust seek offset on append stream */
144                sm_flush(fp, (int *) &timeout);
145                SM_SET_ALARM();
146                if (fp->f_flags & SMOFF)
147                        curoff = fp->f_lseekoff;
148                else
149                {
150                        curoff = (*seekfn)(fp, (off_t) 0, SM_IO_SEEK_CUR);
151                        if (curoff == -1L)
152                        {
153                                ret = -1;
154                                goto clean;
155                        }
156                }
157                if (fp->f_flags & SMRD)
158                {
159                        curoff -= fp->f_r;
160                        if (HASUB(fp))
161                                curoff -= fp->f_ur;
162                }
163                else if (fp->f_flags & SMWR && fp->f_p != NULL)
164                        curoff += fp->f_p - fp->f_bf.smb_base;
165
166                offset += curoff;
167                whence = SM_IO_SEEK_SET;
168                havepos = true;
169                break;
170
171          case SM_IO_SEEK_SET:
172          case SM_IO_SEEK_END:
173                SM_SET_ALARM();
174                curoff = 0;             /* XXX just to keep gcc quiet */
175                havepos = false;
176                break;
177
178          default:
179                errno = EINVAL;
180                return -1;
181        }
182
183        /*
184        **  Can only optimise if:
185        **      reading (and not reading-and-writing);
186        **      not unbuffered; and
187        **      this is a `regular' Unix file (and hence seekfn==sm_stdseek).
188        **  We must check SMNBF first, because it is possible to have SMNBF
189        **  and SMSOPT both set.
190        */
191
192        if (fp->f_bf.smb_base == NULL)
193                sm_makebuf(fp);
194        if (fp->f_flags & (SMWR | SMRW | SMNBF | SMNPT))
195                goto dumb;
196        if ((fp->f_flags & SMOPT) == 0)
197        {
198                if (seekfn != sm_stdseek ||
199                    fp->f_file < 0 || fstat(fp->f_file, &st) ||
200                    (st.st_mode & S_IFMT) != S_IFREG)
201                {
202                        fp->f_flags |= SMNPT;
203                        goto dumb;
204                }
205                fp->f_blksize = st.st_blksize;
206                fp->f_flags |= SMOPT;
207        }
208
209        /*
210        **  We are reading; we can try to optimise.
211        **  Figure out where we are going and where we are now.
212        */
213
214        if (whence == SM_IO_SEEK_SET)
215                target = offset;
216        else
217        {
218                if (fstat(fp->f_file, &st))
219                        goto dumb;
220                target = st.st_size + offset;
221        }
222
223        if (!havepos)
224        {
225                if (fp->f_flags & SMOFF)
226                        curoff = fp->f_lseekoff;
227                else
228                {
229                        curoff = (*seekfn)(fp, (off_t) 0, SM_IO_SEEK_CUR);
230                        if (curoff == POS_ERR)
231                                goto dumb;
232                }
233                curoff -= fp->f_r;
234                if (HASUB(fp))
235                        curoff -= fp->f_ur;
236        }
237
238        /*
239        **  Compute the number of bytes in the input buffer (pretending
240        **  that any ungetc() input has been discarded).  Adjust current
241        **  offset backwards by this count so that it represents the
242        **  file offset for the first byte in the current input buffer.
243        */
244
245        if (HASUB(fp))
246        {
247                curoff += fp->f_r;      /* kill off ungetc */
248                n = fp->f_up - fp->f_bf.smb_base;
249                curoff -= n;
250                n += fp->f_ur;
251        }
252        else
253        {
254                n = fp->f_p - fp->f_bf.smb_base;
255                curoff -= n;
256                n += fp->f_r;
257        }
258
259        /*
260        **  If the target offset is within the current buffer,
261        **  simply adjust the pointers, clear SMFEOF, undo ungetc(),
262        **  and return.  (If the buffer was modified, we have to
263        **  skip this; see getln in fget.c.)
264        */
265
266        if (target >= curoff && target < curoff + (off_t) n)
267        {
268                register int o = target - curoff;
269
270                fp->f_p = fp->f_bf.smb_base + o;
271                fp->f_r = n - o;
272                if (HASUB(fp))
273                        FREEUB(fp);
274                fp->f_flags &= ~SMFEOF;
275                ret = 0;
276                goto clean;
277        }
278
279        /*
280        **  The place we want to get to is not within the current buffer,
281        **  but we can still be kind to the kernel copyout mechanism.
282        **  By aligning the file offset to a block boundary, we can let
283        **  the kernel use the VM hardware to map pages instead of
284        **  copying bytes laboriously.  Using a block boundary also
285        **  ensures that we only read one block, rather than two.
286        */
287
288        curoff = target & ~(fp->f_blksize - 1);
289        if ((*seekfn)(fp, curoff, SM_IO_SEEK_SET) == POS_ERR)
290                goto dumb;
291        fp->f_r = 0;
292        fp->f_p = fp->f_bf.smb_base;
293        if (HASUB(fp))
294                FREEUB(fp);
295        fp->f_flags &= ~SMFEOF;
296        n = target - curoff;
297        if (n)
298        {
299                /* Note: SM_TIME_FOREVER since fn timeout already set */
300                if (sm_refill(fp, SM_TIME_FOREVER) || fp->f_r < (int) n)
301                        goto dumb;
302                fp->f_p += n;
303                fp->f_r -= n;
304        }
305
306        ret = 0;
307clean:
308        /*  We're back. So undo our timeout and handler */
309        if (evt != NULL)
310                sm_clrevent(evt);
311        return ret;
312dumb:
313        /*
314        **  We get here if we cannot optimise the seek ... just
315        **  do it.  Allow the seek function to change fp->f_bf.smb_base.
316        */
317
318        /* Note: SM_TIME_FOREVER since fn timeout already set */
319        ret = SM_TIME_FOREVER;
320        if (sm_flush(fp, &ret) != 0 ||
321            (*seekfn)(fp, (off_t) offset, whence) == POS_ERR)
322        {
323                ret = -1;
324                goto clean;
325        }
326
327        /* success: clear SMFEOF indicator and discard ungetc() data */
328        if (HASUB(fp))
329                FREEUB(fp);
330        fp->f_p = fp->f_bf.smb_base;
331        fp->f_r = 0;
332        fp->f_flags &= ~SMFEOF;
333        ret = 0;
334        goto clean;
335}
Note: See TracBrowser for help on using the repository browser.