xref: /freebsd/contrib/sendmail/libsm/fseek.c (revision ee7b0571c2c18bdec848ed2044223cc88db29bd8)
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