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