190792Sgshapiro/* 2261363Sgshapiro * Copyright (c) 2000-2001, 2004 Proofpoint, Inc. and its suppliers. 390792Sgshapiro * All rights reserved. 490792Sgshapiro * Copyright (c) 1990, 1993 590792Sgshapiro * The Regents of the University of California. All rights reserved. 690792Sgshapiro * 790792Sgshapiro * This code is derived from software contributed to Berkeley by 890792Sgshapiro * Chris Torek. 990792Sgshapiro * 1090792Sgshapiro * By using this file, you agree to the terms and conditions set 1190792Sgshapiro * forth in the LICENSE file which can be found at the top level of 1290792Sgshapiro * the sendmail distribution. 1390792Sgshapiro */ 1490792Sgshapiro 1590792Sgshapiro#include <sm/gen.h> 16266692SgshapiroSM_RCSID("@(#)$Id: fseek.c,v 1.48 2013-11-22 20:51:42 ca Exp $") 1790792Sgshapiro#include <sys/types.h> 1890792Sgshapiro#include <sys/stat.h> 1990792Sgshapiro#include <fcntl.h> 2090792Sgshapiro#include <stdlib.h> 2190792Sgshapiro#include <errno.h> 2290792Sgshapiro#include <setjmp.h> 23157001Sgshapiro#include <sm/time.h> 2490792Sgshapiro#include <sm/signal.h> 2590792Sgshapiro#include <sm/io.h> 2690792Sgshapiro#include <sm/assert.h> 2790792Sgshapiro#include <sm/clock.h> 2890792Sgshapiro#include "local.h" 2990792Sgshapiro 3090792Sgshapiro#define POS_ERR (-(off_t)1) 3190792Sgshapiro 32141858Sgshapirostatic void seekalrm __P((int)); 3390792Sgshapirostatic jmp_buf SeekTimeOut; 3490792Sgshapiro 3590792Sgshapiro/* 3690792Sgshapiro** SEEKALRM -- handler when timeout activated for sm_io_seek() 3790792Sgshapiro** 3890792Sgshapiro** Returns flow of control to where setjmp(SeekTimeOut) was set. 3990792Sgshapiro** 4090792Sgshapiro** Parameters: 4190792Sgshapiro** sig -- unused 4290792Sgshapiro** 4390792Sgshapiro** Returns: 4490792Sgshapiro** does not return 4590792Sgshapiro** 4690792Sgshapiro** Side Effects: 4790792Sgshapiro** returns flow of control to setjmp(SeekTimeOut). 4890792Sgshapiro** 4990792Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 5090792Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 5190792Sgshapiro** DOING. 5290792Sgshapiro*/ 5390792Sgshapiro 5490792Sgshapiro/* ARGSUSED0 */ 5590792Sgshapirostatic void 5690792Sgshapiroseekalrm(sig) 5790792Sgshapiro int sig; 5890792Sgshapiro{ 5990792Sgshapiro longjmp(SeekTimeOut, 1); 6090792Sgshapiro} 6190792Sgshapiro 6290792Sgshapiro/* 6390792Sgshapiro** SM_IO_SEEK -- position the file pointer 6490792Sgshapiro** 6590792Sgshapiro** Parameters: 6690792Sgshapiro** fp -- the file pointer to be seek'd 6790792Sgshapiro** timeout -- time to complete seek (milliseconds) 6890792Sgshapiro** offset -- seek offset based on 'whence' 6990792Sgshapiro** whence -- indicates where seek is relative from. 7090792Sgshapiro** One of SM_IO_SEEK_{CUR,SET,END}. 7190792Sgshapiro** Returns: 7290792Sgshapiro** Failure: returns -1 (minus 1) and sets errno 7390792Sgshapiro** Success: returns 0 (zero) 7490792Sgshapiro*/ 7590792Sgshapiro 7690792Sgshapiroint 7790792Sgshapirosm_io_seek(fp, timeout, offset, whence) 7890792Sgshapiro register SM_FILE_T *fp; 7990792Sgshapiro int SM_NONVOLATILE timeout; 8090792Sgshapiro long SM_NONVOLATILE offset; 8190792Sgshapiro int SM_NONVOLATILE whence; 8290792Sgshapiro{ 8390792Sgshapiro bool havepos; 8490792Sgshapiro off_t target, curoff; 8590792Sgshapiro size_t n; 8690792Sgshapiro struct stat st; 8790792Sgshapiro int ret; 8890792Sgshapiro SM_EVENT *evt = NULL; 8990792Sgshapiro register off_t (*seekfn) __P((SM_FILE_T *, off_t, int)); 9090792Sgshapiro 9190792Sgshapiro SM_REQUIRE_ISA(fp, SmFileMagic); 9290792Sgshapiro 9390792Sgshapiro /* make sure stdio is set up */ 9490792Sgshapiro if (!Sm_IO_DidInit) 9590792Sgshapiro sm_init(); 9690792Sgshapiro 9790792Sgshapiro /* Have to be able to seek. */ 9890792Sgshapiro if ((seekfn = fp->f_seek) == NULL) 9990792Sgshapiro { 10090792Sgshapiro errno = ESPIPE; /* historic practice */ 10190792Sgshapiro return -1; 10290792Sgshapiro } 10390792Sgshapiro 10490792Sgshapiro if (timeout == SM_TIME_DEFAULT) 10590792Sgshapiro timeout = fp->f_timeout; 10690792Sgshapiro if (timeout == SM_TIME_IMMEDIATE) 10790792Sgshapiro { 10890792Sgshapiro /* 10990792Sgshapiro ** Filling the buffer will take time and we are wanted to 11090792Sgshapiro ** return immediately. So... 11190792Sgshapiro */ 11290792Sgshapiro 11390792Sgshapiro errno = EAGAIN; 11490792Sgshapiro return -1; 11590792Sgshapiro } 11690792Sgshapiro 11790792Sgshapiro#define SM_SET_ALARM() \ 11890792Sgshapiro if (timeout != SM_TIME_FOREVER) \ 11990792Sgshapiro { \ 12090792Sgshapiro if (setjmp(SeekTimeOut) != 0) \ 12190792Sgshapiro { \ 12290792Sgshapiro errno = EAGAIN; \ 12390792Sgshapiro return -1; \ 12490792Sgshapiro } \ 12590792Sgshapiro evt = sm_seteventm(timeout, seekalrm, 0); \ 12690792Sgshapiro } 12790792Sgshapiro 12890792Sgshapiro /* 12990792Sgshapiro ** Change any SM_IO_SEEK_CUR to SM_IO_SEEK_SET, and check `whence' 13090792Sgshapiro ** argument. After this, whence is either SM_IO_SEEK_SET or 13190792Sgshapiro ** SM_IO_SEEK_END. 13290792Sgshapiro */ 13390792Sgshapiro 13490792Sgshapiro switch (whence) 13590792Sgshapiro { 13690792Sgshapiro case SM_IO_SEEK_CUR: 13790792Sgshapiro 13890792Sgshapiro /* 13990792Sgshapiro ** In order to seek relative to the current stream offset, 14090792Sgshapiro ** we have to first find the current stream offset a la 14190792Sgshapiro ** ftell (see ftell for details). 14290792Sgshapiro */ 14390792Sgshapiro 14490792Sgshapiro /* may adjust seek offset on append stream */ 14590792Sgshapiro sm_flush(fp, (int *) &timeout); 14690792Sgshapiro SM_SET_ALARM(); 14790792Sgshapiro if (fp->f_flags & SMOFF) 14890792Sgshapiro curoff = fp->f_lseekoff; 14990792Sgshapiro else 15090792Sgshapiro { 15190792Sgshapiro curoff = (*seekfn)(fp, (off_t) 0, SM_IO_SEEK_CUR); 15290792Sgshapiro if (curoff == -1L) 15390792Sgshapiro { 15490792Sgshapiro ret = -1; 15590792Sgshapiro goto clean; 15690792Sgshapiro } 15790792Sgshapiro } 15890792Sgshapiro if (fp->f_flags & SMRD) 15990792Sgshapiro { 16090792Sgshapiro curoff -= fp->f_r; 16190792Sgshapiro if (HASUB(fp)) 16290792Sgshapiro curoff -= fp->f_ur; 16390792Sgshapiro } 16490792Sgshapiro else if (fp->f_flags & SMWR && fp->f_p != NULL) 16590792Sgshapiro curoff += fp->f_p - fp->f_bf.smb_base; 16690792Sgshapiro 16790792Sgshapiro offset += curoff; 16890792Sgshapiro whence = SM_IO_SEEK_SET; 16990792Sgshapiro havepos = true; 17090792Sgshapiro break; 17190792Sgshapiro 17290792Sgshapiro case SM_IO_SEEK_SET: 17390792Sgshapiro case SM_IO_SEEK_END: 17490792Sgshapiro SM_SET_ALARM(); 17590792Sgshapiro curoff = 0; /* XXX just to keep gcc quiet */ 17690792Sgshapiro havepos = false; 17790792Sgshapiro break; 17890792Sgshapiro 17990792Sgshapiro default: 18090792Sgshapiro errno = EINVAL; 18190792Sgshapiro return -1; 18290792Sgshapiro } 18390792Sgshapiro 18490792Sgshapiro /* 18590792Sgshapiro ** Can only optimise if: 18690792Sgshapiro ** reading (and not reading-and-writing); 18790792Sgshapiro ** not unbuffered; and 18890792Sgshapiro ** this is a `regular' Unix file (and hence seekfn==sm_stdseek). 18990792Sgshapiro ** We must check SMNBF first, because it is possible to have SMNBF 19090792Sgshapiro ** and SMSOPT both set. 19190792Sgshapiro */ 19290792Sgshapiro 19390792Sgshapiro if (fp->f_bf.smb_base == NULL) 19490792Sgshapiro sm_makebuf(fp); 19590792Sgshapiro if (fp->f_flags & (SMWR | SMRW | SMNBF | SMNPT)) 19690792Sgshapiro goto dumb; 19790792Sgshapiro if ((fp->f_flags & SMOPT) == 0) 19890792Sgshapiro { 19990792Sgshapiro if (seekfn != sm_stdseek || 20090792Sgshapiro fp->f_file < 0 || fstat(fp->f_file, &st) || 20190792Sgshapiro (st.st_mode & S_IFMT) != S_IFREG) 20290792Sgshapiro { 20390792Sgshapiro fp->f_flags |= SMNPT; 20490792Sgshapiro goto dumb; 20590792Sgshapiro } 20690792Sgshapiro fp->f_blksize = st.st_blksize; 20790792Sgshapiro fp->f_flags |= SMOPT; 20890792Sgshapiro } 20990792Sgshapiro 21090792Sgshapiro /* 21190792Sgshapiro ** We are reading; we can try to optimise. 21290792Sgshapiro ** Figure out where we are going and where we are now. 21390792Sgshapiro */ 21490792Sgshapiro 21590792Sgshapiro if (whence == SM_IO_SEEK_SET) 21690792Sgshapiro target = offset; 21790792Sgshapiro else 21890792Sgshapiro { 21990792Sgshapiro if (fstat(fp->f_file, &st)) 22090792Sgshapiro goto dumb; 22190792Sgshapiro target = st.st_size + offset; 22290792Sgshapiro } 22390792Sgshapiro 22490792Sgshapiro if (!havepos) 22590792Sgshapiro { 22690792Sgshapiro if (fp->f_flags & SMOFF) 22790792Sgshapiro curoff = fp->f_lseekoff; 22890792Sgshapiro else 22990792Sgshapiro { 23090792Sgshapiro curoff = (*seekfn)(fp, (off_t) 0, SM_IO_SEEK_CUR); 23190792Sgshapiro if (curoff == POS_ERR) 23290792Sgshapiro goto dumb; 23390792Sgshapiro } 23490792Sgshapiro curoff -= fp->f_r; 23590792Sgshapiro if (HASUB(fp)) 23690792Sgshapiro curoff -= fp->f_ur; 23790792Sgshapiro } 23890792Sgshapiro 23990792Sgshapiro /* 24090792Sgshapiro ** Compute the number of bytes in the input buffer (pretending 24190792Sgshapiro ** that any ungetc() input has been discarded). Adjust current 24290792Sgshapiro ** offset backwards by this count so that it represents the 24390792Sgshapiro ** file offset for the first byte in the current input buffer. 24490792Sgshapiro */ 24590792Sgshapiro 24690792Sgshapiro if (HASUB(fp)) 24790792Sgshapiro { 24890792Sgshapiro curoff += fp->f_r; /* kill off ungetc */ 24990792Sgshapiro n = fp->f_up - fp->f_bf.smb_base; 25090792Sgshapiro curoff -= n; 25190792Sgshapiro n += fp->f_ur; 25290792Sgshapiro } 25390792Sgshapiro else 25490792Sgshapiro { 25590792Sgshapiro n = fp->f_p - fp->f_bf.smb_base; 25690792Sgshapiro curoff -= n; 25790792Sgshapiro n += fp->f_r; 25890792Sgshapiro } 25990792Sgshapiro 26090792Sgshapiro /* 26190792Sgshapiro ** If the target offset is within the current buffer, 26290792Sgshapiro ** simply adjust the pointers, clear SMFEOF, undo ungetc(), 26390792Sgshapiro ** and return. (If the buffer was modified, we have to 26490792Sgshapiro ** skip this; see getln in fget.c.) 26590792Sgshapiro */ 26690792Sgshapiro 26790792Sgshapiro if (target >= curoff && target < curoff + (off_t) n) 26890792Sgshapiro { 26990792Sgshapiro register int o = target - curoff; 27090792Sgshapiro 27190792Sgshapiro fp->f_p = fp->f_bf.smb_base + o; 27290792Sgshapiro fp->f_r = n - o; 27390792Sgshapiro if (HASUB(fp)) 27490792Sgshapiro FREEUB(fp); 27590792Sgshapiro fp->f_flags &= ~SMFEOF; 27690792Sgshapiro ret = 0; 27790792Sgshapiro goto clean; 27890792Sgshapiro } 27990792Sgshapiro 28090792Sgshapiro /* 28190792Sgshapiro ** The place we want to get to is not within the current buffer, 28290792Sgshapiro ** but we can still be kind to the kernel copyout mechanism. 28390792Sgshapiro ** By aligning the file offset to a block boundary, we can let 28490792Sgshapiro ** the kernel use the VM hardware to map pages instead of 28590792Sgshapiro ** copying bytes laboriously. Using a block boundary also 28690792Sgshapiro ** ensures that we only read one block, rather than two. 28790792Sgshapiro */ 28890792Sgshapiro 28990792Sgshapiro curoff = target & ~(fp->f_blksize - 1); 29090792Sgshapiro if ((*seekfn)(fp, curoff, SM_IO_SEEK_SET) == POS_ERR) 29190792Sgshapiro goto dumb; 29290792Sgshapiro fp->f_r = 0; 29390792Sgshapiro fp->f_p = fp->f_bf.smb_base; 29490792Sgshapiro if (HASUB(fp)) 29590792Sgshapiro FREEUB(fp); 29690792Sgshapiro fp->f_flags &= ~SMFEOF; 29790792Sgshapiro n = target - curoff; 29890792Sgshapiro if (n) 29990792Sgshapiro { 30090792Sgshapiro /* Note: SM_TIME_FOREVER since fn timeout already set */ 30190792Sgshapiro if (sm_refill(fp, SM_TIME_FOREVER) || fp->f_r < (int) n) 30290792Sgshapiro goto dumb; 30390792Sgshapiro fp->f_p += n; 30490792Sgshapiro fp->f_r -= n; 30590792Sgshapiro } 30690792Sgshapiro 30790792Sgshapiro ret = 0; 30890792Sgshapiroclean: 30990792Sgshapiro /* We're back. So undo our timeout and handler */ 31090792Sgshapiro if (evt != NULL) 31190792Sgshapiro sm_clrevent(evt); 31290792Sgshapiro return ret; 31390792Sgshapirodumb: 31490792Sgshapiro /* 31590792Sgshapiro ** We get here if we cannot optimise the seek ... just 31690792Sgshapiro ** do it. Allow the seek function to change fp->f_bf.smb_base. 31790792Sgshapiro */ 31890792Sgshapiro 31990792Sgshapiro /* Note: SM_TIME_FOREVER since fn timeout already set */ 32090792Sgshapiro ret = SM_TIME_FOREVER; 32190792Sgshapiro if (sm_flush(fp, &ret) != 0 || 32290792Sgshapiro (*seekfn)(fp, (off_t) offset, whence) == POS_ERR) 32390792Sgshapiro { 32490792Sgshapiro ret = -1; 32590792Sgshapiro goto clean; 32690792Sgshapiro } 32790792Sgshapiro 32890792Sgshapiro /* success: clear SMFEOF indicator and discard ungetc() data */ 32990792Sgshapiro if (HASUB(fp)) 33090792Sgshapiro FREEUB(fp); 33190792Sgshapiro fp->f_p = fp->f_bf.smb_base; 33290792Sgshapiro fp->f_r = 0; 33390792Sgshapiro fp->f_flags &= ~SMFEOF; 33490792Sgshapiro ret = 0; 33590792Sgshapiro goto clean; 33690792Sgshapiro} 337