diff options
Diffstat (limited to 'usr/src/lib/libast/common/sfio/sfrd.c')
-rw-r--r-- | usr/src/lib/libast/common/sfio/sfrd.c | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/usr/src/lib/libast/common/sfio/sfrd.c b/usr/src/lib/libast/common/sfio/sfrd.c new file mode 100644 index 0000000000..6ad4c5fb3d --- /dev/null +++ b/usr/src/lib/libast/common/sfio/sfrd.c @@ -0,0 +1,316 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#include "sfhdr.h" + +/* Internal function to do a hard read. +** This knows about discipline and memory mapping, peek read. +** +** Written by Kiem-Phong Vo. +*/ + +/* synchronize unseekable write streams */ +#if __STD_C +static void _sfwrsync(void) +#else +static void _sfwrsync() +#endif +{ reg Sfpool_t* p; + reg Sfio_t* f; + reg int n; + + /* sync all pool heads */ + for(p = _Sfpool.next; p; p = p->next) + { if(p->n_sf <= 0) + continue; + f = p->sf[0]; + if(!SFFROZEN(f) && f->next > f->data && + (f->mode&SF_WRITE) && f->extent < 0 ) + (void)_sfflsbuf(f,-1); + } + + /* and all the ones in the discrete pool */ + for(n = 0; n < _Sfpool.n_sf; ++n) + { f = _Sfpool.sf[n]; + + if(!SFFROZEN(f) && f->next > f->data && + (f->mode&SF_WRITE) && f->extent < 0 ) + (void)_sfflsbuf(f,-1); + } +} + +#if __STD_C +ssize_t sfrd(reg Sfio_t* f, reg Void_t* buf, reg size_t n, Sfdisc_t* disc) +#else +ssize_t sfrd(f,buf,n,disc) +reg Sfio_t* f; +reg Void_t* buf; +reg size_t n; +Sfdisc_t* disc; +#endif +{ + Sfoff_t r; + reg Sfdisc_t* dc; + reg int local, rcrv, dosync, oerrno; + + SFMTXSTART(f,-1); + + GETLOCAL(f,local); + if((rcrv = f->mode & (SF_RC|SF_RV)) ) + f->mode &= ~(SF_RC|SF_RV); + f->bits &= ~SF_JUSTSEEK; + + if(f->mode&SF_PKRD) + SFMTXRETURN(f, -1); + + if(!local && !(f->bits&SF_DCDOWN)) /* an external user's call */ + { if(f->mode != SF_READ && _sfmode(f,SF_READ,0) < 0) + SFMTXRETURN(f, -1); + if(f->next < f->endb) + { if(SFSYNC(f) < 0) + SFMTXRETURN(f, -1); + if((f->mode&(SF_SYNCED|SF_READ)) == (SF_SYNCED|SF_READ) ) + { f->endb = f->next = f->endr = f->data; + f->mode &= ~SF_SYNCED; + } +#ifdef MAP_TYPE + if((f->bits&SF_MMAP) && f->data) + { SFMUNMAP(f, f->data, f->endb-f->data); + f->data = NIL(uchar*); + } +#endif + f->next = f->endb = f->endr = f->endw = f->data; + } + } + + for(dosync = 0;;) + { /* stream locked by sfsetfd() */ + if(!(f->flags&SF_STRING) && f->file < 0) + SFMTXRETURN(f, 0); + + f->flags &= ~(SF_EOF|SF_ERROR); + + dc = disc; + if(f->flags&SF_STRING) + { if((r = (f->data+f->extent) - f->next) < 0) + r = 0; + if(r <= 0) + goto do_except; + SFMTXRETURN(f, (ssize_t)r); + } + + /* warn that a read is about to happen */ + SFDISC(f,dc,readf); + if(dc && dc->exceptf && (f->flags&SF_IOCHECK) ) + { reg int rv; + if(local) + SETLOCAL(f); + if((rv = _sfexcept(f,SF_READ,n,dc)) > 0) + n = rv; + else if(rv < 0) + { f->flags |= SF_ERROR; + SFMTXRETURN(f, (ssize_t)rv); + } + } + +#ifdef MAP_TYPE + if(f->bits&SF_MMAP) + { reg ssize_t a, round; + sfstat_t st; + + /* determine if we have to copy data to buffer */ + if((uchar*)buf >= f->data && (uchar*)buf <= f->endb) + { n += f->endb - f->next; + buf = NIL(char*); + } + + /* actual seek location */ + if((f->flags&(SF_SHARE|SF_PUBLIC)) == (SF_SHARE|SF_PUBLIC) && + (r = SFSK(f,(Sfoff_t)0,SEEK_CUR,dc)) != f->here) + f->here = r; + else f->here -= f->endb-f->next; + + /* before mapping, make sure we have data to map */ + if((f->flags&SF_SHARE) || (size_t)(r = f->extent-f->here) < n) + { if((r = sysfstatf(f->file,&st)) < 0) + goto do_except; + if((r = (f->extent = st.st_size) - f->here) <= 0 ) + { r = 0; /* eof */ + goto do_except; + } + } + + /* make sure current position is page aligned */ + if((a = (size_t)(f->here%_Sfpage)) != 0) + { f->here -= a; + r += a; + } + + /* map minimal requirement */ + if(r > (round = (1 + (n+a)/f->size)*f->size) ) + r = round; + + if(f->data) + SFMUNMAP(f, f->data, f->endb-f->data); + + for(;;) + { f->data = (uchar*) sysmmapf((caddr_t)0, (size_t)r, + (PROT_READ|PROT_WRITE), + MAP_PRIVATE, + f->file, (sfoff_t)f->here); + if(f->data && (caddr_t)f->data != (caddr_t)(-1)) + break; + else + { f->data = NIL(uchar*); + if((r >>= 1) < (_Sfpage*SF_NMAP) || + (errno != EAGAIN && errno != ENOMEM) ) + break; + } + } + + if(f->data) + { if(f->bits&SF_SEQUENTIAL) + SFMMSEQON(f,f->data,r); + f->next = f->data+a; + f->endr = f->endb = f->data+r; + f->endw = f->data; + f->here += r; + + /* make known our seek location */ + (void)SFSK(f,f->here,SEEK_SET,dc); + + if(buf) + { if(n > (size_t)(r-a)) + n = (ssize_t)(r-a); + memcpy(buf,f->next,n); + f->next += n; + } + else n = f->endb - f->next; + + SFMTXRETURN(f, n); + } + else + { r = -1; + f->here += a; + + /* reset seek pointer to its physical location */ + (void)SFSK(f,f->here,SEEK_SET,dc); + + /* make a buffer */ + (void)SFSETBUF(f,(Void_t*)f->tiny,(size_t)SF_UNBOUND); + + if(!buf) + { buf = (Void_t*)f->data; + n = f->size; + } + } + } +#endif + + /* sync unseekable write streams to prevent deadlock */ + if(!dosync && f->extent < 0) + { dosync = 1; + _sfwrsync(); + } + + /* make sure file pointer is right */ + if(f->extent >= 0 && (f->flags&SF_SHARE) ) + { if(!(f->flags&SF_PUBLIC) ) + f->here = SFSK(f,f->here,SEEK_SET,dc); + else f->here = SFSK(f,(Sfoff_t)0,SEEK_CUR,dc); + } + + oerrno = errno; + errno = 0; + + if(dc && dc->readf) + { int share = f->flags&SF_SHARE; + + if(rcrv) /* pass on rcrv for possible continuations */ + f->mode |= rcrv; + /* tell readf that no peeking necessary */ + else f->flags &= ~SF_SHARE; + + SFDCRD(f,buf,n,dc,r); + + /* reset flags */ + if(rcrv) + f->mode &= ~rcrv; + else f->flags |= share; + } + else if(SFISNULL(f)) + r = 0; + else if(f->extent < 0 && (f->flags&SF_SHARE) && rcrv) + { /* try peek read */ + r = sfpkrd(f->file, (char*)buf, n, + (rcrv&SF_RC) ? (int)f->getr : -1, + -1L, (rcrv&SF_RV) ? 1 : 0); + if(r > 0) + { if(rcrv&SF_RV) + f->mode |= SF_PKRD; + else f->mode |= SF_RC; + } + } + else r = sysreadf(f->file,buf,n); + + if(errno == 0 ) + errno = oerrno; + + if(r > 0 ) + { if(!(f->bits&SF_DCDOWN) ) /* not a continuation call */ + { if(!(f->mode&SF_PKRD) ) + { f->here += r; + if(f->extent >= 0 && f->extent < f->here) + f->extent = f->here; + } + if((uchar*)buf >= f->data && + (uchar*)buf < f->data+f->size) + f->endb = f->endr = ((uchar*)buf) + r; + } + + SFMTXRETURN(f, (ssize_t)r); + } + + do_except: + if(local) + SETLOCAL(f); + switch(_sfexcept(f,SF_READ,(ssize_t)r,dc)) + { + case SF_ECONT : + goto do_continue; + case SF_EDONE : + n = local ? 0 : (ssize_t)r; + SFMTXRETURN(f,n); + case SF_EDISC : + if(!local && !(f->flags&SF_STRING)) + goto do_continue; + /* else fall thru */ + case SF_ESTACK : + SFMTXRETURN(f, -1); + } + + do_continue: + for(dc = f->disc; dc; dc = dc->disc) + if(dc == disc) + break; + disc = dc; + } +} |