/************************************************************************* * * HTCd - Copyright (C) 1998-2006 Henrik Rydberg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include using namespace std; const int PAGESIZE=getpagesize(); const filemap::char32 HEADNAME="HDB/1.2\n"; const filemap::char32 COMPNAME="lt"; const mstring dev_tag="/dev/"; const int TIMEOUT=20; /////////////////////////////////////////////////// filemap::head_t::head_t() { memcpy(ident,HEADNAME,sizeof(char32)); lock.set(0); minor.set(0); major.set(0); fatal.set(0); filesize.set(PAGESIZE); filetop.set(sizeof(head_t)); tables.set(0); index.set(0); memcpy(compname,COMPNAME,sizeof(char32)); memset(tabs,0,maxtabs*sizeof(char32)); frame.set(sizeof(head_t)); } filemap::head_t::head_t(const head_t* hd,int mem) { memcpy(ident,HEADNAME,sizeof(char32)); lock.set(0); minor.set(0); major.set(0); fatal.set(0); filetop.set(sizeof(head_t)+mem); filesize.set(PAGESIZE*int((filetop.get()+PAGESIZE)/PAGESIZE)); tables=hd->tables; index=hd->index; memcpy(compname,hd->compname,sizeof(char32)); memset(tabs,0,maxtabs*sizeof(char32)); for(int i=0;itabs[i],sizeof(char32)); frame.set(sizeof(head_t)); } void filemap::head_t::setcomp(int idx,const sref& s) { index.set(idx); s.copyto(compname,sizeof(char32)); fatal.add(1); } void filemap::head_t::set(int col,const sref& s) { s.copyto(tabs[col],sizeof(char32)); fatal.add(1); } void filemap::head_t::insert(int col,const sref& s) { for(int i=maxtabs-1;i>col;i--) memcpy(tabs[i],tabs[i-1],sizeof(char32)); if(index.get()>=col&&index.get()col) index.set(index.get()-1); tables.add(-1); fatal.add(1); } } /////////////////////////////////////////////////////// filemap::chunk_t::chunk_t(chunk_t* p,int fd,int wok,int off,int size) { next=p; end=(begin=off)+size; map=(char*)mmap(0,size,PROT_READ|(wok?PROT_WRITE:0),MAP_SHARED,fd,off); if(map==MAP_FAILED) { map=0; THROW("filemap: could not map chunk") } } filemap::chunk_t::~chunk_t() { if(map) munmap(map,end-begin); } int filemap::chunk_t::fill(int off) { if(own(off)) { at(off)->mem.set(end-off); at(off)->caps.set(0); return end; } else return off; } void filemap::chunk_t::sync() { msync(map,end-begin,MS_SYNC); } /////////////////////////////////////////////////////// void filemap::remap() { while(mem->endfilesize.get()) { mem=new chunk_t(mem,fd,wok,mem->end,mem->size()<<1); } } filemap::rec_t* filemap::add(int n) { int caps=sizeof(rec_t),dim=caps+n; while(capsfilesize.get()end) { ftruncate(fd,mem->end); hd->filesize.set(mem->end); } while(hd->filetop.get()+caps>mem->end) { hd->filetop.set(mem->fill(hd->filetop.get())); mem=new chunk_t(mem,fd,wok,mem->end,mem->size()<<1); ftruncate(fd,mem->end); hd->filesize.set(mem->end); } rec_t* rec=at(hd->filetop.get()); hd->filetop.add(caps); rec->mem.set(caps); rec->caps.set(n); return rec; } void filemap::rem(rec_t* rec) { rec->caps.set(0); } /////////////////////////////////////////////////////// filemap::filemap(filemap* p): next(p),fd(-1),wok(0),refs(0),pathname(),hd(0),mem(0),nodelock(0) { memset(&mutex,0,sizeof(mutex_t)); } filemap::filemap(filemap* p,const mstring& path,int update,int prot): next(p),fd(-1),wok(0),refs(0),pathname(),hd(0),mem(0),nodelock(0) { memset(&mutex,0,sizeof(mutex_t)); open(path,update,prot); } filemap::~filemap() { close(); } /////////////////////////////////////////////////////// void filemap::open(const mstring& path,int update,int prot) throw(merror_t) { close(); if(update) { if(path.left(5)!=dev_tag) nodelock=new nodelock_t(path); else nodelock=0; wok=1; } else { nodelock=0; wok=0; } fd=::open(path.c_str(),wok?O_RDWR|O_CREAT:O_RDONLY,prot); if(fd<0) THROW("filemap: could not open "<map; if(fsizeident))!=First(sref(HEADNAME))) THROW("filemap: bad version"); remap(); if(fsize!=hd->filesize.get()) THROW("filemap: "<filetop.get(); while(size(p)) { rec_t* rec=at(p); p+=rec->mem.get(); } if(pft. } void filemap::save(const mstring& path,int prot) throw(merror_t) { if(path==pathname) THROW("filemap: cannot overwrite current file"); int mfd=::open(path.c_str(),O_RDWR|O_CREAT|O_TRUNC,prot); if(mfd<0) THROW("filemap: could not open "<caps.get()) caps+=rec->mem.get(); p+=rec->mem.get(); } head_t mhd(hd,caps); ::write(mfd,&mhd,sizeof(head_t)); p=recs(); while(size(p)) { rec_t* rec=at(p); if(rec->caps.get()) ::write(mfd,rec,rec->mem.get()); p+=rec->mem.get(); } ftruncate(mfd,mhd.filesize.get()); ::close(mfd); } void filemap::close() { while(mem) { chunk_t* p=mem->next; delete mem; mem=p; } hd=0; wok=0; refs=0; if(fd>=0) { ::close(fd); fd=-1; } delete nodelock; nodelock=0; pathname.clear(); } void filemap::sync() { for(chunk_t* p=mem;p;p=p->next) p->sync(); } /////////////////////////////////////////////////////// void filemap::lock() { if(hd) { MLOCK(mutex); if((volatile long&)hd->lock) { time_t end=time(0)+TIMEOUT; while((volatile long&)hd->lock&&time(0)lock.set(1); } } void filemap::unlock() { if(hd) hd->lock.set(0); } /////////////////////////////////////////////////////// time_t filemap::Lastmod() const { struct stat fs; fstat(fd,&fs); return fs.st_mtime; }