17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * Copyright (c) 2000-2001, 2004 Sendmail, Inc. and its suppliers. 37c478bd9Sstevel@tonic-gate * All rights reserved. 47c478bd9Sstevel@tonic-gate * Copyright (c) 1990, 1993 57c478bd9Sstevel@tonic-gate * The Regents of the University of California. All rights reserved. 67c478bd9Sstevel@tonic-gate * 77c478bd9Sstevel@tonic-gate * This code is derived from software contributed to Berkeley by 87c478bd9Sstevel@tonic-gate * Chris Torek. 97c478bd9Sstevel@tonic-gate * 107c478bd9Sstevel@tonic-gate * By using this file, you agree to the terms and conditions set 117c478bd9Sstevel@tonic-gate * forth in the LICENSE file which can be found at the top level of 127c478bd9Sstevel@tonic-gate * the sendmail distribution. 137c478bd9Sstevel@tonic-gate */ 147c478bd9Sstevel@tonic-gate 157c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 167c478bd9Sstevel@tonic-gate 177c478bd9Sstevel@tonic-gate #include <sm/gen.h> 18*49218d4fSjbeck SM_RCSID("@(#)$Id: fseek.c,v 1.47 2005/06/14 23:07:20 ca Exp $") 197c478bd9Sstevel@tonic-gate #include <sys/types.h> 207c478bd9Sstevel@tonic-gate #include <sys/stat.h> 217c478bd9Sstevel@tonic-gate #include <fcntl.h> 227c478bd9Sstevel@tonic-gate #include <stdlib.h> 237c478bd9Sstevel@tonic-gate #include <errno.h> 247c478bd9Sstevel@tonic-gate #include <setjmp.h> 25*49218d4fSjbeck #include <sm/time.h> 267c478bd9Sstevel@tonic-gate #include <sm/signal.h> 277c478bd9Sstevel@tonic-gate #include <sm/io.h> 287c478bd9Sstevel@tonic-gate #include <sm/assert.h> 297c478bd9Sstevel@tonic-gate #include <sm/clock.h> 307c478bd9Sstevel@tonic-gate #include "local.h" 317c478bd9Sstevel@tonic-gate 327c478bd9Sstevel@tonic-gate #define POS_ERR (-(off_t)1) 337c478bd9Sstevel@tonic-gate 347c478bd9Sstevel@tonic-gate static void seekalrm __P((int)); 357c478bd9Sstevel@tonic-gate static jmp_buf SeekTimeOut; 367c478bd9Sstevel@tonic-gate 377c478bd9Sstevel@tonic-gate /* 387c478bd9Sstevel@tonic-gate ** SEEKALRM -- handler when timeout activated for sm_io_seek() 397c478bd9Sstevel@tonic-gate ** 407c478bd9Sstevel@tonic-gate ** Returns flow of control to where setjmp(SeekTimeOut) was set. 417c478bd9Sstevel@tonic-gate ** 427c478bd9Sstevel@tonic-gate ** Parameters: 437c478bd9Sstevel@tonic-gate ** sig -- unused 447c478bd9Sstevel@tonic-gate ** 457c478bd9Sstevel@tonic-gate ** Returns: 467c478bd9Sstevel@tonic-gate ** does not return 477c478bd9Sstevel@tonic-gate ** 487c478bd9Sstevel@tonic-gate ** Side Effects: 497c478bd9Sstevel@tonic-gate ** returns flow of control to setjmp(SeekTimeOut). 507c478bd9Sstevel@tonic-gate ** 517c478bd9Sstevel@tonic-gate ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 527c478bd9Sstevel@tonic-gate ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 537c478bd9Sstevel@tonic-gate ** DOING. 547c478bd9Sstevel@tonic-gate */ 557c478bd9Sstevel@tonic-gate 567c478bd9Sstevel@tonic-gate /* ARGSUSED0 */ 577c478bd9Sstevel@tonic-gate static void 587c478bd9Sstevel@tonic-gate seekalrm(sig) 597c478bd9Sstevel@tonic-gate int sig; 607c478bd9Sstevel@tonic-gate { 617c478bd9Sstevel@tonic-gate longjmp(SeekTimeOut, 1); 627c478bd9Sstevel@tonic-gate } 637c478bd9Sstevel@tonic-gate 647c478bd9Sstevel@tonic-gate /* 657c478bd9Sstevel@tonic-gate ** SM_IO_SEEK -- position the file pointer 667c478bd9Sstevel@tonic-gate ** 677c478bd9Sstevel@tonic-gate ** Parameters: 687c478bd9Sstevel@tonic-gate ** fp -- the file pointer to be seek'd 697c478bd9Sstevel@tonic-gate ** timeout -- time to complete seek (milliseconds) 707c478bd9Sstevel@tonic-gate ** offset -- seek offset based on 'whence' 717c478bd9Sstevel@tonic-gate ** whence -- indicates where seek is relative from. 727c478bd9Sstevel@tonic-gate ** One of SM_IO_SEEK_{CUR,SET,END}. 737c478bd9Sstevel@tonic-gate ** Returns: 747c478bd9Sstevel@tonic-gate ** Failure: returns -1 (minus 1) and sets errno 757c478bd9Sstevel@tonic-gate ** Success: returns 0 (zero) 767c478bd9Sstevel@tonic-gate */ 777c478bd9Sstevel@tonic-gate 787c478bd9Sstevel@tonic-gate int 797c478bd9Sstevel@tonic-gate sm_io_seek(fp, timeout, offset, whence) 807c478bd9Sstevel@tonic-gate register SM_FILE_T *fp; 817c478bd9Sstevel@tonic-gate int SM_NONVOLATILE timeout; 827c478bd9Sstevel@tonic-gate long SM_NONVOLATILE offset; 837c478bd9Sstevel@tonic-gate int SM_NONVOLATILE whence; 847c478bd9Sstevel@tonic-gate { 857c478bd9Sstevel@tonic-gate bool havepos; 867c478bd9Sstevel@tonic-gate off_t target, curoff; 877c478bd9Sstevel@tonic-gate size_t n; 887c478bd9Sstevel@tonic-gate struct stat st; 897c478bd9Sstevel@tonic-gate int ret; 907c478bd9Sstevel@tonic-gate SM_EVENT *evt = NULL; 917c478bd9Sstevel@tonic-gate register off_t (*seekfn) __P((SM_FILE_T *, off_t, int)); 927c478bd9Sstevel@tonic-gate 937c478bd9Sstevel@tonic-gate SM_REQUIRE_ISA(fp, SmFileMagic); 947c478bd9Sstevel@tonic-gate 957c478bd9Sstevel@tonic-gate /* make sure stdio is set up */ 967c478bd9Sstevel@tonic-gate if (!Sm_IO_DidInit) 977c478bd9Sstevel@tonic-gate sm_init(); 987c478bd9Sstevel@tonic-gate 997c478bd9Sstevel@tonic-gate /* Have to be able to seek. */ 1007c478bd9Sstevel@tonic-gate if ((seekfn = fp->f_seek) == NULL) 1017c478bd9Sstevel@tonic-gate { 1027c478bd9Sstevel@tonic-gate errno = ESPIPE; /* historic practice */ 1037c478bd9Sstevel@tonic-gate return -1; 1047c478bd9Sstevel@tonic-gate } 1057c478bd9Sstevel@tonic-gate 1067c478bd9Sstevel@tonic-gate if (timeout == SM_TIME_DEFAULT) 1077c478bd9Sstevel@tonic-gate timeout = fp->f_timeout; 1087c478bd9Sstevel@tonic-gate if (timeout == SM_TIME_IMMEDIATE) 1097c478bd9Sstevel@tonic-gate { 1107c478bd9Sstevel@tonic-gate /* 1117c478bd9Sstevel@tonic-gate ** Filling the buffer will take time and we are wanted to 1127c478bd9Sstevel@tonic-gate ** return immediately. So... 1137c478bd9Sstevel@tonic-gate */ 1147c478bd9Sstevel@tonic-gate 1157c478bd9Sstevel@tonic-gate errno = EAGAIN; 1167c478bd9Sstevel@tonic-gate return -1; 1177c478bd9Sstevel@tonic-gate } 1187c478bd9Sstevel@tonic-gate 1197c478bd9Sstevel@tonic-gate #define SM_SET_ALARM() \ 1207c478bd9Sstevel@tonic-gate if (timeout != SM_TIME_FOREVER) \ 1217c478bd9Sstevel@tonic-gate { \ 1227c478bd9Sstevel@tonic-gate if (setjmp(SeekTimeOut) != 0) \ 1237c478bd9Sstevel@tonic-gate { \ 1247c478bd9Sstevel@tonic-gate errno = EAGAIN; \ 1257c478bd9Sstevel@tonic-gate return -1; \ 1267c478bd9Sstevel@tonic-gate } \ 1277c478bd9Sstevel@tonic-gate evt = sm_seteventm(timeout, seekalrm, 0); \ 1287c478bd9Sstevel@tonic-gate } 1297c478bd9Sstevel@tonic-gate 1307c478bd9Sstevel@tonic-gate /* 1317c478bd9Sstevel@tonic-gate ** Change any SM_IO_SEEK_CUR to SM_IO_SEEK_SET, and check `whence' 1327c478bd9Sstevel@tonic-gate ** argument. After this, whence is either SM_IO_SEEK_SET or 1337c478bd9Sstevel@tonic-gate ** SM_IO_SEEK_END. 1347c478bd9Sstevel@tonic-gate */ 1357c478bd9Sstevel@tonic-gate 1367c478bd9Sstevel@tonic-gate switch (whence) 1377c478bd9Sstevel@tonic-gate { 1387c478bd9Sstevel@tonic-gate case SM_IO_SEEK_CUR: 1397c478bd9Sstevel@tonic-gate 1407c478bd9Sstevel@tonic-gate /* 1417c478bd9Sstevel@tonic-gate ** In order to seek relative to the current stream offset, 1427c478bd9Sstevel@tonic-gate ** we have to first find the current stream offset a la 1437c478bd9Sstevel@tonic-gate ** ftell (see ftell for details). 1447c478bd9Sstevel@tonic-gate */ 1457c478bd9Sstevel@tonic-gate 1467c478bd9Sstevel@tonic-gate /* may adjust seek offset on append stream */ 1477c478bd9Sstevel@tonic-gate sm_flush(fp, (int *) &timeout); 1487c478bd9Sstevel@tonic-gate SM_SET_ALARM(); 1497c478bd9Sstevel@tonic-gate if (fp->f_flags & SMOFF) 1507c478bd9Sstevel@tonic-gate curoff = fp->f_lseekoff; 1517c478bd9Sstevel@tonic-gate else 1527c478bd9Sstevel@tonic-gate { 1537c478bd9Sstevel@tonic-gate curoff = (*seekfn)(fp, (off_t) 0, SM_IO_SEEK_CUR); 1547c478bd9Sstevel@tonic-gate if (curoff == -1L) 1557c478bd9Sstevel@tonic-gate { 1567c478bd9Sstevel@tonic-gate ret = -1; 1577c478bd9Sstevel@tonic-gate goto clean; 1587c478bd9Sstevel@tonic-gate } 1597c478bd9Sstevel@tonic-gate } 1607c478bd9Sstevel@tonic-gate if (fp->f_flags & SMRD) 1617c478bd9Sstevel@tonic-gate { 1627c478bd9Sstevel@tonic-gate curoff -= fp->f_r; 1637c478bd9Sstevel@tonic-gate if (HASUB(fp)) 1647c478bd9Sstevel@tonic-gate curoff -= fp->f_ur; 1657c478bd9Sstevel@tonic-gate } 1667c478bd9Sstevel@tonic-gate else if (fp->f_flags & SMWR && fp->f_p != NULL) 1677c478bd9Sstevel@tonic-gate curoff += fp->f_p - fp->f_bf.smb_base; 1687c478bd9Sstevel@tonic-gate 1697c478bd9Sstevel@tonic-gate offset += curoff; 1707c478bd9Sstevel@tonic-gate whence = SM_IO_SEEK_SET; 1717c478bd9Sstevel@tonic-gate havepos = true; 1727c478bd9Sstevel@tonic-gate break; 1737c478bd9Sstevel@tonic-gate 1747c478bd9Sstevel@tonic-gate case SM_IO_SEEK_SET: 1757c478bd9Sstevel@tonic-gate case SM_IO_SEEK_END: 1767c478bd9Sstevel@tonic-gate SM_SET_ALARM(); 1777c478bd9Sstevel@tonic-gate curoff = 0; /* XXX just to keep gcc quiet */ 1787c478bd9Sstevel@tonic-gate havepos = false; 1797c478bd9Sstevel@tonic-gate break; 1807c478bd9Sstevel@tonic-gate 1817c478bd9Sstevel@tonic-gate default: 1827c478bd9Sstevel@tonic-gate errno = EINVAL; 1837c478bd9Sstevel@tonic-gate return -1; 1847c478bd9Sstevel@tonic-gate } 1857c478bd9Sstevel@tonic-gate 1867c478bd9Sstevel@tonic-gate /* 1877c478bd9Sstevel@tonic-gate ** Can only optimise if: 1887c478bd9Sstevel@tonic-gate ** reading (and not reading-and-writing); 1897c478bd9Sstevel@tonic-gate ** not unbuffered; and 1907c478bd9Sstevel@tonic-gate ** this is a `regular' Unix file (and hence seekfn==sm_stdseek). 1917c478bd9Sstevel@tonic-gate ** We must check SMNBF first, because it is possible to have SMNBF 1927c478bd9Sstevel@tonic-gate ** and SMSOPT both set. 1937c478bd9Sstevel@tonic-gate */ 1947c478bd9Sstevel@tonic-gate 1957c478bd9Sstevel@tonic-gate if (fp->f_bf.smb_base == NULL) 1967c478bd9Sstevel@tonic-gate sm_makebuf(fp); 1977c478bd9Sstevel@tonic-gate if (fp->f_flags & (SMWR | SMRW | SMNBF | SMNPT)) 1987c478bd9Sstevel@tonic-gate goto dumb; 1997c478bd9Sstevel@tonic-gate if ((fp->f_flags & SMOPT) == 0) 2007c478bd9Sstevel@tonic-gate { 2017c478bd9Sstevel@tonic-gate if (seekfn != sm_stdseek || 2027c478bd9Sstevel@tonic-gate fp->f_file < 0 || fstat(fp->f_file, &st) || 2037c478bd9Sstevel@tonic-gate (st.st_mode & S_IFMT) != S_IFREG) 2047c478bd9Sstevel@tonic-gate { 2057c478bd9Sstevel@tonic-gate fp->f_flags |= SMNPT; 2067c478bd9Sstevel@tonic-gate goto dumb; 2077c478bd9Sstevel@tonic-gate } 2087c478bd9Sstevel@tonic-gate fp->f_blksize = st.st_blksize; 2097c478bd9Sstevel@tonic-gate fp->f_flags |= SMOPT; 2107c478bd9Sstevel@tonic-gate } 2117c478bd9Sstevel@tonic-gate 2127c478bd9Sstevel@tonic-gate /* 2137c478bd9Sstevel@tonic-gate ** We are reading; we can try to optimise. 2147c478bd9Sstevel@tonic-gate ** Figure out where we are going and where we are now. 2157c478bd9Sstevel@tonic-gate */ 2167c478bd9Sstevel@tonic-gate 2177c478bd9Sstevel@tonic-gate if (whence == SM_IO_SEEK_SET) 2187c478bd9Sstevel@tonic-gate target = offset; 2197c478bd9Sstevel@tonic-gate else 2207c478bd9Sstevel@tonic-gate { 2217c478bd9Sstevel@tonic-gate if (fstat(fp->f_file, &st)) 2227c478bd9Sstevel@tonic-gate goto dumb; 2237c478bd9Sstevel@tonic-gate target = st.st_size + offset; 2247c478bd9Sstevel@tonic-gate } 2257c478bd9Sstevel@tonic-gate 2267c478bd9Sstevel@tonic-gate if (!havepos) 2277c478bd9Sstevel@tonic-gate { 2287c478bd9Sstevel@tonic-gate if (fp->f_flags & SMOFF) 2297c478bd9Sstevel@tonic-gate curoff = fp->f_lseekoff; 2307c478bd9Sstevel@tonic-gate else 2317c478bd9Sstevel@tonic-gate { 2327c478bd9Sstevel@tonic-gate curoff = (*seekfn)(fp, (off_t) 0, SM_IO_SEEK_CUR); 2337c478bd9Sstevel@tonic-gate if (curoff == POS_ERR) 2347c478bd9Sstevel@tonic-gate goto dumb; 2357c478bd9Sstevel@tonic-gate } 2367c478bd9Sstevel@tonic-gate curoff -= fp->f_r; 2377c478bd9Sstevel@tonic-gate if (HASUB(fp)) 2387c478bd9Sstevel@tonic-gate curoff -= fp->f_ur; 2397c478bd9Sstevel@tonic-gate } 2407c478bd9Sstevel@tonic-gate 2417c478bd9Sstevel@tonic-gate /* 2427c478bd9Sstevel@tonic-gate ** Compute the number of bytes in the input buffer (pretending 2437c478bd9Sstevel@tonic-gate ** that any ungetc() input has been discarded). Adjust current 2447c478bd9Sstevel@tonic-gate ** offset backwards by this count so that it represents the 2457c478bd9Sstevel@tonic-gate ** file offset for the first byte in the current input buffer. 2467c478bd9Sstevel@tonic-gate */ 2477c478bd9Sstevel@tonic-gate 2487c478bd9Sstevel@tonic-gate if (HASUB(fp)) 2497c478bd9Sstevel@tonic-gate { 2507c478bd9Sstevel@tonic-gate curoff += fp->f_r; /* kill off ungetc */ 2517c478bd9Sstevel@tonic-gate n = fp->f_up - fp->f_bf.smb_base; 2527c478bd9Sstevel@tonic-gate curoff -= n; 2537c478bd9Sstevel@tonic-gate n += fp->f_ur; 2547c478bd9Sstevel@tonic-gate } 2557c478bd9Sstevel@tonic-gate else 2567c478bd9Sstevel@tonic-gate { 2577c478bd9Sstevel@tonic-gate n = fp->f_p - fp->f_bf.smb_base; 2587c478bd9Sstevel@tonic-gate curoff -= n; 2597c478bd9Sstevel@tonic-gate n += fp->f_r; 2607c478bd9Sstevel@tonic-gate } 2617c478bd9Sstevel@tonic-gate 2627c478bd9Sstevel@tonic-gate /* 2637c478bd9Sstevel@tonic-gate ** If the target offset is within the current buffer, 2647c478bd9Sstevel@tonic-gate ** simply adjust the pointers, clear SMFEOF, undo ungetc(), 2657c478bd9Sstevel@tonic-gate ** and return. (If the buffer was modified, we have to 2667c478bd9Sstevel@tonic-gate ** skip this; see getln in fget.c.) 2677c478bd9Sstevel@tonic-gate */ 2687c478bd9Sstevel@tonic-gate 2697c478bd9Sstevel@tonic-gate if (target >= curoff && target < curoff + (off_t) n) 2707c478bd9Sstevel@tonic-gate { 2717c478bd9Sstevel@tonic-gate register int o = target - curoff; 2727c478bd9Sstevel@tonic-gate 2737c478bd9Sstevel@tonic-gate fp->f_p = fp->f_bf.smb_base + o; 2747c478bd9Sstevel@tonic-gate fp->f_r = n - o; 2757c478bd9Sstevel@tonic-gate if (HASUB(fp)) 2767c478bd9Sstevel@tonic-gate FREEUB(fp); 2777c478bd9Sstevel@tonic-gate fp->f_flags &= ~SMFEOF; 2787c478bd9Sstevel@tonic-gate ret = 0; 2797c478bd9Sstevel@tonic-gate goto clean; 2807c478bd9Sstevel@tonic-gate } 2817c478bd9Sstevel@tonic-gate 2827c478bd9Sstevel@tonic-gate /* 2837c478bd9Sstevel@tonic-gate ** The place we want to get to is not within the current buffer, 2847c478bd9Sstevel@tonic-gate ** but we can still be kind to the kernel copyout mechanism. 2857c478bd9Sstevel@tonic-gate ** By aligning the file offset to a block boundary, we can let 2867c478bd9Sstevel@tonic-gate ** the kernel use the VM hardware to map pages instead of 2877c478bd9Sstevel@tonic-gate ** copying bytes laboriously. Using a block boundary also 2887c478bd9Sstevel@tonic-gate ** ensures that we only read one block, rather than two. 2897c478bd9Sstevel@tonic-gate */ 2907c478bd9Sstevel@tonic-gate 2917c478bd9Sstevel@tonic-gate curoff = target & ~(fp->f_blksize - 1); 2927c478bd9Sstevel@tonic-gate if ((*seekfn)(fp, curoff, SM_IO_SEEK_SET) == POS_ERR) 2937c478bd9Sstevel@tonic-gate goto dumb; 2947c478bd9Sstevel@tonic-gate fp->f_r = 0; 2957c478bd9Sstevel@tonic-gate fp->f_p = fp->f_bf.smb_base; 2967c478bd9Sstevel@tonic-gate if (HASUB(fp)) 2977c478bd9Sstevel@tonic-gate FREEUB(fp); 2987c478bd9Sstevel@tonic-gate fp->f_flags &= ~SMFEOF; 2997c478bd9Sstevel@tonic-gate n = target - curoff; 3007c478bd9Sstevel@tonic-gate if (n) 3017c478bd9Sstevel@tonic-gate { 3027c478bd9Sstevel@tonic-gate /* Note: SM_TIME_FOREVER since fn timeout already set */ 3037c478bd9Sstevel@tonic-gate if (sm_refill(fp, SM_TIME_FOREVER) || fp->f_r < (int) n) 3047c478bd9Sstevel@tonic-gate goto dumb; 3057c478bd9Sstevel@tonic-gate fp->f_p += n; 3067c478bd9Sstevel@tonic-gate fp->f_r -= n; 3077c478bd9Sstevel@tonic-gate } 3087c478bd9Sstevel@tonic-gate 3097c478bd9Sstevel@tonic-gate ret = 0; 3107c478bd9Sstevel@tonic-gate clean: 3117c478bd9Sstevel@tonic-gate /* We're back. So undo our timeout and handler */ 3127c478bd9Sstevel@tonic-gate if (evt != NULL) 3137c478bd9Sstevel@tonic-gate sm_clrevent(evt); 3147c478bd9Sstevel@tonic-gate return ret; 3157c478bd9Sstevel@tonic-gate dumb: 3167c478bd9Sstevel@tonic-gate /* 3177c478bd9Sstevel@tonic-gate ** We get here if we cannot optimise the seek ... just 3187c478bd9Sstevel@tonic-gate ** do it. Allow the seek function to change fp->f_bf.smb_base. 3197c478bd9Sstevel@tonic-gate */ 3207c478bd9Sstevel@tonic-gate 3217c478bd9Sstevel@tonic-gate /* Note: SM_TIME_FOREVER since fn timeout already set */ 3227c478bd9Sstevel@tonic-gate ret = SM_TIME_FOREVER; 3237c478bd9Sstevel@tonic-gate if (sm_flush(fp, &ret) != 0 || 3247c478bd9Sstevel@tonic-gate (*seekfn)(fp, (off_t) offset, whence) == POS_ERR) 3257c478bd9Sstevel@tonic-gate { 3267c478bd9Sstevel@tonic-gate ret = -1; 3277c478bd9Sstevel@tonic-gate goto clean; 3287c478bd9Sstevel@tonic-gate } 3297c478bd9Sstevel@tonic-gate 3307c478bd9Sstevel@tonic-gate /* success: clear SMFEOF indicator and discard ungetc() data */ 3317c478bd9Sstevel@tonic-gate if (HASUB(fp)) 3327c478bd9Sstevel@tonic-gate FREEUB(fp); 3337c478bd9Sstevel@tonic-gate fp->f_p = fp->f_bf.smb_base; 3347c478bd9Sstevel@tonic-gate fp->f_r = 0; 3357c478bd9Sstevel@tonic-gate fp->f_flags &= ~SMFEOF; 3367c478bd9Sstevel@tonic-gate ret = 0; 3377c478bd9Sstevel@tonic-gate goto clean; 3387c478bd9Sstevel@tonic-gate } 339