/*********************************************************************** * * * This software is part of the ast package * * Copyright (c) 1985-2009 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * * by AT&T Intellectual Property * * * * 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 * * David Korn * * Phong Vo * * * ***********************************************************************/ #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(Sfio_t* f, Void_t* buf, size_t n, Sfdisc_t* disc) #else ssize_t sfrd(f,buf,n,disc) Sfio_t* f; Void_t* buf; size_t n; Sfdisc_t* disc; #endif { Sfoff_t r; reg Sfdisc_t* dc; reg int local, rcrv, dosync, oerrno; SFMTXDECL(f); SFMTXENTER(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; } }