140266059SGregory Neil Shapiro /*
25dd76dd0SGregory Neil Shapiro * Copyright (c) 2000-2001, 2004 Proofpoint, Inc. and its suppliers.
340266059SGregory Neil Shapiro * All rights reserved.
440266059SGregory Neil Shapiro * Copyright (c) 1990, 1993
540266059SGregory Neil Shapiro * The Regents of the University of California. All rights reserved.
640266059SGregory Neil Shapiro *
740266059SGregory Neil Shapiro * This code is derived from software contributed to Berkeley by
840266059SGregory Neil Shapiro * Chris Torek.
940266059SGregory Neil Shapiro *
1040266059SGregory Neil Shapiro * By using this file, you agree to the terms and conditions set
1140266059SGregory Neil Shapiro * forth in the LICENSE file which can be found at the top level of
1240266059SGregory Neil Shapiro * the sendmail distribution.
1340266059SGregory Neil Shapiro */
1440266059SGregory Neil Shapiro
1540266059SGregory Neil Shapiro #include <sm/gen.h>
16*4313cc83SGregory Neil Shapiro SM_RCSID("@(#)$Id: fseek.c,v 1.48 2013-11-22 20:51:42 ca Exp $")
1740266059SGregory Neil Shapiro #include <sys/types.h>
1840266059SGregory Neil Shapiro #include <sys/stat.h>
1940266059SGregory Neil Shapiro #include <fcntl.h>
2040266059SGregory Neil Shapiro #include <stdlib.h>
2140266059SGregory Neil Shapiro #include <errno.h>
2240266059SGregory Neil Shapiro #include <setjmp.h>
234e4196cbSGregory Neil Shapiro #include <sm/time.h>
2440266059SGregory Neil Shapiro #include <sm/signal.h>
2540266059SGregory Neil Shapiro #include <sm/io.h>
2640266059SGregory Neil Shapiro #include <sm/assert.h>
2740266059SGregory Neil Shapiro #include <sm/clock.h>
2840266059SGregory Neil Shapiro #include "local.h"
2940266059SGregory Neil Shapiro
3040266059SGregory Neil Shapiro #define POS_ERR (-(off_t)1)
3140266059SGregory Neil Shapiro
32b6bacd31SGregory Neil Shapiro static void seekalrm __P((int));
3340266059SGregory Neil Shapiro static jmp_buf SeekTimeOut;
3440266059SGregory Neil Shapiro
3540266059SGregory Neil Shapiro /*
3640266059SGregory Neil Shapiro ** SEEKALRM -- handler when timeout activated for sm_io_seek()
3740266059SGregory Neil Shapiro **
3840266059SGregory Neil Shapiro ** Returns flow of control to where setjmp(SeekTimeOut) was set.
3940266059SGregory Neil Shapiro **
4040266059SGregory Neil Shapiro ** Parameters:
4140266059SGregory Neil Shapiro ** sig -- unused
4240266059SGregory Neil Shapiro **
4340266059SGregory Neil Shapiro ** Returns:
4440266059SGregory Neil Shapiro ** does not return
4540266059SGregory Neil Shapiro **
4640266059SGregory Neil Shapiro ** Side Effects:
4740266059SGregory Neil Shapiro ** returns flow of control to setjmp(SeekTimeOut).
4840266059SGregory Neil Shapiro **
4940266059SGregory Neil Shapiro ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
5040266059SGregory Neil Shapiro ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
5140266059SGregory Neil Shapiro ** DOING.
5240266059SGregory Neil Shapiro */
5340266059SGregory Neil Shapiro
5440266059SGregory Neil Shapiro /* ARGSUSED0 */
5540266059SGregory Neil Shapiro static void
seekalrm(sig)5640266059SGregory Neil Shapiro seekalrm(sig)
5740266059SGregory Neil Shapiro int sig;
5840266059SGregory Neil Shapiro {
5940266059SGregory Neil Shapiro longjmp(SeekTimeOut, 1);
6040266059SGregory Neil Shapiro }
6140266059SGregory Neil Shapiro
6240266059SGregory Neil Shapiro /*
6340266059SGregory Neil Shapiro ** SM_IO_SEEK -- position the file pointer
6440266059SGregory Neil Shapiro **
6540266059SGregory Neil Shapiro ** Parameters:
6640266059SGregory Neil Shapiro ** fp -- the file pointer to be seek'd
6740266059SGregory Neil Shapiro ** timeout -- time to complete seek (milliseconds)
6840266059SGregory Neil Shapiro ** offset -- seek offset based on 'whence'
6940266059SGregory Neil Shapiro ** whence -- indicates where seek is relative from.
7040266059SGregory Neil Shapiro ** One of SM_IO_SEEK_{CUR,SET,END}.
7140266059SGregory Neil Shapiro ** Returns:
7240266059SGregory Neil Shapiro ** Failure: returns -1 (minus 1) and sets errno
7340266059SGregory Neil Shapiro ** Success: returns 0 (zero)
7440266059SGregory Neil Shapiro */
7540266059SGregory Neil Shapiro
7640266059SGregory Neil Shapiro int
sm_io_seek(fp,timeout,offset,whence)7740266059SGregory Neil Shapiro sm_io_seek(fp, timeout, offset, whence)
7840266059SGregory Neil Shapiro register SM_FILE_T *fp;
7940266059SGregory Neil Shapiro int SM_NONVOLATILE timeout;
8040266059SGregory Neil Shapiro long SM_NONVOLATILE offset;
8140266059SGregory Neil Shapiro int SM_NONVOLATILE whence;
8240266059SGregory Neil Shapiro {
8340266059SGregory Neil Shapiro bool havepos;
8440266059SGregory Neil Shapiro off_t target, curoff;
8540266059SGregory Neil Shapiro size_t n;
8640266059SGregory Neil Shapiro struct stat st;
8740266059SGregory Neil Shapiro int ret;
8840266059SGregory Neil Shapiro SM_EVENT *evt = NULL;
8940266059SGregory Neil Shapiro register off_t (*seekfn) __P((SM_FILE_T *, off_t, int));
9040266059SGregory Neil Shapiro
9140266059SGregory Neil Shapiro SM_REQUIRE_ISA(fp, SmFileMagic);
9240266059SGregory Neil Shapiro
9340266059SGregory Neil Shapiro /* make sure stdio is set up */
9440266059SGregory Neil Shapiro if (!Sm_IO_DidInit)
9540266059SGregory Neil Shapiro sm_init();
9640266059SGregory Neil Shapiro
9740266059SGregory Neil Shapiro /* Have to be able to seek. */
9840266059SGregory Neil Shapiro if ((seekfn = fp->f_seek) == NULL)
9940266059SGregory Neil Shapiro {
10040266059SGregory Neil Shapiro errno = ESPIPE; /* historic practice */
10140266059SGregory Neil Shapiro return -1;
10240266059SGregory Neil Shapiro }
10340266059SGregory Neil Shapiro
10440266059SGregory Neil Shapiro if (timeout == SM_TIME_DEFAULT)
10540266059SGregory Neil Shapiro timeout = fp->f_timeout;
10640266059SGregory Neil Shapiro if (timeout == SM_TIME_IMMEDIATE)
10740266059SGregory Neil Shapiro {
10840266059SGregory Neil Shapiro /*
10940266059SGregory Neil Shapiro ** Filling the buffer will take time and we are wanted to
11040266059SGregory Neil Shapiro ** return immediately. So...
11140266059SGregory Neil Shapiro */
11240266059SGregory Neil Shapiro
11340266059SGregory Neil Shapiro errno = EAGAIN;
11440266059SGregory Neil Shapiro return -1;
11540266059SGregory Neil Shapiro }
11640266059SGregory Neil Shapiro
11740266059SGregory Neil Shapiro #define SM_SET_ALARM() \
11840266059SGregory Neil Shapiro if (timeout != SM_TIME_FOREVER) \
11940266059SGregory Neil Shapiro { \
12040266059SGregory Neil Shapiro if (setjmp(SeekTimeOut) != 0) \
12140266059SGregory Neil Shapiro { \
12240266059SGregory Neil Shapiro errno = EAGAIN; \
12340266059SGregory Neil Shapiro return -1; \
12440266059SGregory Neil Shapiro } \
12540266059SGregory Neil Shapiro evt = sm_seteventm(timeout, seekalrm, 0); \
12640266059SGregory Neil Shapiro }
12740266059SGregory Neil Shapiro
12840266059SGregory Neil Shapiro /*
12940266059SGregory Neil Shapiro ** Change any SM_IO_SEEK_CUR to SM_IO_SEEK_SET, and check `whence'
13040266059SGregory Neil Shapiro ** argument. After this, whence is either SM_IO_SEEK_SET or
13140266059SGregory Neil Shapiro ** SM_IO_SEEK_END.
13240266059SGregory Neil Shapiro */
13340266059SGregory Neil Shapiro
13440266059SGregory Neil Shapiro switch (whence)
13540266059SGregory Neil Shapiro {
13640266059SGregory Neil Shapiro case SM_IO_SEEK_CUR:
13740266059SGregory Neil Shapiro
13840266059SGregory Neil Shapiro /*
13940266059SGregory Neil Shapiro ** In order to seek relative to the current stream offset,
14040266059SGregory Neil Shapiro ** we have to first find the current stream offset a la
14140266059SGregory Neil Shapiro ** ftell (see ftell for details).
14240266059SGregory Neil Shapiro */
14340266059SGregory Neil Shapiro
14440266059SGregory Neil Shapiro /* may adjust seek offset on append stream */
14540266059SGregory Neil Shapiro sm_flush(fp, (int *) &timeout);
14640266059SGregory Neil Shapiro SM_SET_ALARM();
14740266059SGregory Neil Shapiro if (fp->f_flags & SMOFF)
14840266059SGregory Neil Shapiro curoff = fp->f_lseekoff;
14940266059SGregory Neil Shapiro else
15040266059SGregory Neil Shapiro {
15140266059SGregory Neil Shapiro curoff = (*seekfn)(fp, (off_t) 0, SM_IO_SEEK_CUR);
15240266059SGregory Neil Shapiro if (curoff == -1L)
15340266059SGregory Neil Shapiro {
15440266059SGregory Neil Shapiro ret = -1;
15540266059SGregory Neil Shapiro goto clean;
15640266059SGregory Neil Shapiro }
15740266059SGregory Neil Shapiro }
15840266059SGregory Neil Shapiro if (fp->f_flags & SMRD)
15940266059SGregory Neil Shapiro {
16040266059SGregory Neil Shapiro curoff -= fp->f_r;
16140266059SGregory Neil Shapiro if (HASUB(fp))
16240266059SGregory Neil Shapiro curoff -= fp->f_ur;
16340266059SGregory Neil Shapiro }
16440266059SGregory Neil Shapiro else if (fp->f_flags & SMWR && fp->f_p != NULL)
16540266059SGregory Neil Shapiro curoff += fp->f_p - fp->f_bf.smb_base;
16640266059SGregory Neil Shapiro
16740266059SGregory Neil Shapiro offset += curoff;
16840266059SGregory Neil Shapiro whence = SM_IO_SEEK_SET;
16940266059SGregory Neil Shapiro havepos = true;
17040266059SGregory Neil Shapiro break;
17140266059SGregory Neil Shapiro
17240266059SGregory Neil Shapiro case SM_IO_SEEK_SET:
17340266059SGregory Neil Shapiro case SM_IO_SEEK_END:
17440266059SGregory Neil Shapiro SM_SET_ALARM();
17540266059SGregory Neil Shapiro curoff = 0; /* XXX just to keep gcc quiet */
17640266059SGregory Neil Shapiro havepos = false;
17740266059SGregory Neil Shapiro break;
17840266059SGregory Neil Shapiro
17940266059SGregory Neil Shapiro default:
18040266059SGregory Neil Shapiro errno = EINVAL;
18140266059SGregory Neil Shapiro return -1;
18240266059SGregory Neil Shapiro }
18340266059SGregory Neil Shapiro
18440266059SGregory Neil Shapiro /*
18540266059SGregory Neil Shapiro ** Can only optimise if:
18640266059SGregory Neil Shapiro ** reading (and not reading-and-writing);
18740266059SGregory Neil Shapiro ** not unbuffered; and
18840266059SGregory Neil Shapiro ** this is a `regular' Unix file (and hence seekfn==sm_stdseek).
18940266059SGregory Neil Shapiro ** We must check SMNBF first, because it is possible to have SMNBF
19040266059SGregory Neil Shapiro ** and SMSOPT both set.
19140266059SGregory Neil Shapiro */
19240266059SGregory Neil Shapiro
19340266059SGregory Neil Shapiro if (fp->f_bf.smb_base == NULL)
19440266059SGregory Neil Shapiro sm_makebuf(fp);
19540266059SGregory Neil Shapiro if (fp->f_flags & (SMWR | SMRW | SMNBF | SMNPT))
19640266059SGregory Neil Shapiro goto dumb;
19740266059SGregory Neil Shapiro if ((fp->f_flags & SMOPT) == 0)
19840266059SGregory Neil Shapiro {
19940266059SGregory Neil Shapiro if (seekfn != sm_stdseek ||
20040266059SGregory Neil Shapiro fp->f_file < 0 || fstat(fp->f_file, &st) ||
20140266059SGregory Neil Shapiro (st.st_mode & S_IFMT) != S_IFREG)
20240266059SGregory Neil Shapiro {
20340266059SGregory Neil Shapiro fp->f_flags |= SMNPT;
20440266059SGregory Neil Shapiro goto dumb;
20540266059SGregory Neil Shapiro }
20640266059SGregory Neil Shapiro fp->f_blksize = st.st_blksize;
20740266059SGregory Neil Shapiro fp->f_flags |= SMOPT;
20840266059SGregory Neil Shapiro }
20940266059SGregory Neil Shapiro
21040266059SGregory Neil Shapiro /*
21140266059SGregory Neil Shapiro ** We are reading; we can try to optimise.
21240266059SGregory Neil Shapiro ** Figure out where we are going and where we are now.
21340266059SGregory Neil Shapiro */
21440266059SGregory Neil Shapiro
21540266059SGregory Neil Shapiro if (whence == SM_IO_SEEK_SET)
21640266059SGregory Neil Shapiro target = offset;
21740266059SGregory Neil Shapiro else
21840266059SGregory Neil Shapiro {
21940266059SGregory Neil Shapiro if (fstat(fp->f_file, &st))
22040266059SGregory Neil Shapiro goto dumb;
22140266059SGregory Neil Shapiro target = st.st_size + offset;
22240266059SGregory Neil Shapiro }
22340266059SGregory Neil Shapiro
22440266059SGregory Neil Shapiro if (!havepos)
22540266059SGregory Neil Shapiro {
22640266059SGregory Neil Shapiro if (fp->f_flags & SMOFF)
22740266059SGregory Neil Shapiro curoff = fp->f_lseekoff;
22840266059SGregory Neil Shapiro else
22940266059SGregory Neil Shapiro {
23040266059SGregory Neil Shapiro curoff = (*seekfn)(fp, (off_t) 0, SM_IO_SEEK_CUR);
23140266059SGregory Neil Shapiro if (curoff == POS_ERR)
23240266059SGregory Neil Shapiro goto dumb;
23340266059SGregory Neil Shapiro }
23440266059SGregory Neil Shapiro curoff -= fp->f_r;
23540266059SGregory Neil Shapiro if (HASUB(fp))
23640266059SGregory Neil Shapiro curoff -= fp->f_ur;
23740266059SGregory Neil Shapiro }
23840266059SGregory Neil Shapiro
23940266059SGregory Neil Shapiro /*
24040266059SGregory Neil Shapiro ** Compute the number of bytes in the input buffer (pretending
24140266059SGregory Neil Shapiro ** that any ungetc() input has been discarded). Adjust current
24240266059SGregory Neil Shapiro ** offset backwards by this count so that it represents the
24340266059SGregory Neil Shapiro ** file offset for the first byte in the current input buffer.
24440266059SGregory Neil Shapiro */
24540266059SGregory Neil Shapiro
24640266059SGregory Neil Shapiro if (HASUB(fp))
24740266059SGregory Neil Shapiro {
24840266059SGregory Neil Shapiro curoff += fp->f_r; /* kill off ungetc */
24940266059SGregory Neil Shapiro n = fp->f_up - fp->f_bf.smb_base;
25040266059SGregory Neil Shapiro curoff -= n;
25140266059SGregory Neil Shapiro n += fp->f_ur;
25240266059SGregory Neil Shapiro }
25340266059SGregory Neil Shapiro else
25440266059SGregory Neil Shapiro {
25540266059SGregory Neil Shapiro n = fp->f_p - fp->f_bf.smb_base;
25640266059SGregory Neil Shapiro curoff -= n;
25740266059SGregory Neil Shapiro n += fp->f_r;
25840266059SGregory Neil Shapiro }
25940266059SGregory Neil Shapiro
26040266059SGregory Neil Shapiro /*
26140266059SGregory Neil Shapiro ** If the target offset is within the current buffer,
26240266059SGregory Neil Shapiro ** simply adjust the pointers, clear SMFEOF, undo ungetc(),
26340266059SGregory Neil Shapiro ** and return. (If the buffer was modified, we have to
26440266059SGregory Neil Shapiro ** skip this; see getln in fget.c.)
26540266059SGregory Neil Shapiro */
26640266059SGregory Neil Shapiro
26740266059SGregory Neil Shapiro if (target >= curoff && target < curoff + (off_t) n)
26840266059SGregory Neil Shapiro {
26940266059SGregory Neil Shapiro register int o = target - curoff;
27040266059SGregory Neil Shapiro
27140266059SGregory Neil Shapiro fp->f_p = fp->f_bf.smb_base + o;
27240266059SGregory Neil Shapiro fp->f_r = n - o;
27340266059SGregory Neil Shapiro if (HASUB(fp))
27440266059SGregory Neil Shapiro FREEUB(fp);
27540266059SGregory Neil Shapiro fp->f_flags &= ~SMFEOF;
27640266059SGregory Neil Shapiro ret = 0;
27740266059SGregory Neil Shapiro goto clean;
27840266059SGregory Neil Shapiro }
27940266059SGregory Neil Shapiro
28040266059SGregory Neil Shapiro /*
28140266059SGregory Neil Shapiro ** The place we want to get to is not within the current buffer,
28240266059SGregory Neil Shapiro ** but we can still be kind to the kernel copyout mechanism.
28340266059SGregory Neil Shapiro ** By aligning the file offset to a block boundary, we can let
28440266059SGregory Neil Shapiro ** the kernel use the VM hardware to map pages instead of
28540266059SGregory Neil Shapiro ** copying bytes laboriously. Using a block boundary also
28640266059SGregory Neil Shapiro ** ensures that we only read one block, rather than two.
28740266059SGregory Neil Shapiro */
28840266059SGregory Neil Shapiro
28940266059SGregory Neil Shapiro curoff = target & ~(fp->f_blksize - 1);
29040266059SGregory Neil Shapiro if ((*seekfn)(fp, curoff, SM_IO_SEEK_SET) == POS_ERR)
29140266059SGregory Neil Shapiro goto dumb;
29240266059SGregory Neil Shapiro fp->f_r = 0;
29340266059SGregory Neil Shapiro fp->f_p = fp->f_bf.smb_base;
29440266059SGregory Neil Shapiro if (HASUB(fp))
29540266059SGregory Neil Shapiro FREEUB(fp);
29640266059SGregory Neil Shapiro fp->f_flags &= ~SMFEOF;
29740266059SGregory Neil Shapiro n = target - curoff;
29840266059SGregory Neil Shapiro if (n)
29940266059SGregory Neil Shapiro {
30040266059SGregory Neil Shapiro /* Note: SM_TIME_FOREVER since fn timeout already set */
30140266059SGregory Neil Shapiro if (sm_refill(fp, SM_TIME_FOREVER) || fp->f_r < (int) n)
30240266059SGregory Neil Shapiro goto dumb;
30340266059SGregory Neil Shapiro fp->f_p += n;
30440266059SGregory Neil Shapiro fp->f_r -= n;
30540266059SGregory Neil Shapiro }
30640266059SGregory Neil Shapiro
30740266059SGregory Neil Shapiro ret = 0;
30840266059SGregory Neil Shapiro clean:
30940266059SGregory Neil Shapiro /* We're back. So undo our timeout and handler */
31040266059SGregory Neil Shapiro if (evt != NULL)
31140266059SGregory Neil Shapiro sm_clrevent(evt);
31240266059SGregory Neil Shapiro return ret;
31340266059SGregory Neil Shapiro dumb:
31440266059SGregory Neil Shapiro /*
31540266059SGregory Neil Shapiro ** We get here if we cannot optimise the seek ... just
31640266059SGregory Neil Shapiro ** do it. Allow the seek function to change fp->f_bf.smb_base.
31740266059SGregory Neil Shapiro */
31840266059SGregory Neil Shapiro
31940266059SGregory Neil Shapiro /* Note: SM_TIME_FOREVER since fn timeout already set */
32040266059SGregory Neil Shapiro ret = SM_TIME_FOREVER;
32140266059SGregory Neil Shapiro if (sm_flush(fp, &ret) != 0 ||
32240266059SGregory Neil Shapiro (*seekfn)(fp, (off_t) offset, whence) == POS_ERR)
32340266059SGregory Neil Shapiro {
32440266059SGregory Neil Shapiro ret = -1;
32540266059SGregory Neil Shapiro goto clean;
32640266059SGregory Neil Shapiro }
32740266059SGregory Neil Shapiro
32840266059SGregory Neil Shapiro /* success: clear SMFEOF indicator and discard ungetc() data */
32940266059SGregory Neil Shapiro if (HASUB(fp))
33040266059SGregory Neil Shapiro FREEUB(fp);
33140266059SGregory Neil Shapiro fp->f_p = fp->f_bf.smb_base;
33240266059SGregory Neil Shapiro fp->f_r = 0;
33340266059SGregory Neil Shapiro fp->f_flags &= ~SMFEOF;
33440266059SGregory Neil Shapiro ret = 0;
33540266059SGregory Neil Shapiro goto clean;
33640266059SGregory Neil Shapiro }
337