diff options
| author | Henrik Rydberg <rydberg@euromail.se> | 2011-10-08 20:30:28 +0200 |
|---|---|---|
| committer | Henrik Rydberg <rydberg@euromail.se> | 2011-10-08 20:30:28 +0200 |
| commit | 5df79c53745fde5d6c3340a2979b1429cd5892c1 (patch) | |
| tree | 1a81af141708b826e9c61e8a04019994fcca8298 /src/hdb/filemap.cc | |
Initial import of htcd system 1.0
Signed-off-by: Henrik Rydberg <rydberg@euromail.se>
Diffstat (limited to 'src/hdb/filemap.cc')
| -rw-r--r-- | src/hdb/filemap.cc | 302 |
1 files changed, 302 insertions, 0 deletions
diff --git a/src/hdb/filemap.cc b/src/hdb/filemap.cc new file mode 100644 index 0000000..b40644f --- /dev/null +++ b/src/hdb/filemap.cc | |||
| @@ -0,0 +1,302 @@ | |||
| 1 | /************************************************************************* | ||
| 2 | * | ||
| 3 | * HTCd - Copyright (C) 1998-2006 Henrik Rydberg | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License as published by | ||
| 7 | * the Free Software Foundation; either version 2 of the License, or | ||
| 8 | * (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License | ||
| 16 | * along with this program; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <hdb/filemap.h> | ||
| 21 | #include <mt/split.h> | ||
| 22 | #include <fcntl.h> | ||
| 23 | #include <sys/stat.h> | ||
| 24 | #include <sys/mman.h> | ||
| 25 | #include <new> | ||
| 26 | using namespace std; | ||
| 27 | |||
| 28 | const int PAGESIZE=getpagesize(); | ||
| 29 | const filemap::char32 HEADNAME="HDB/1.2\n"; | ||
| 30 | const filemap::char32 COMPNAME="lt"; | ||
| 31 | const mstring dev_tag="/dev/"; | ||
| 32 | |||
| 33 | const int TIMEOUT=20; | ||
| 34 | |||
| 35 | /////////////////////////////////////////////////// | ||
| 36 | |||
| 37 | filemap::head_t::head_t() | ||
| 38 | { | ||
| 39 | memcpy(ident,HEADNAME,sizeof(char32)); | ||
| 40 | lock.set(0); | ||
| 41 | minor.set(0); | ||
| 42 | major.set(0); | ||
| 43 | fatal.set(0); | ||
| 44 | filesize.set(PAGESIZE); | ||
| 45 | filetop.set(sizeof(head_t)); | ||
| 46 | tables.set(0); | ||
| 47 | index.set(0); | ||
| 48 | memcpy(compname,COMPNAME,sizeof(char32)); | ||
| 49 | memset(tabs,0,maxtabs*sizeof(char32)); | ||
| 50 | frame.set(sizeof(head_t)); | ||
| 51 | } | ||
| 52 | |||
| 53 | filemap::head_t::head_t(const head_t* hd,int mem) | ||
| 54 | { | ||
| 55 | memcpy(ident,HEADNAME,sizeof(char32)); | ||
| 56 | lock.set(0); | ||
| 57 | minor.set(0); | ||
| 58 | major.set(0); | ||
| 59 | fatal.set(0); | ||
| 60 | filetop.set(sizeof(head_t)+mem); | ||
| 61 | filesize.set(PAGESIZE*int((filetop.get()+PAGESIZE)/PAGESIZE)); | ||
| 62 | tables=hd->tables; | ||
| 63 | index=hd->index; | ||
| 64 | memcpy(compname,hd->compname,sizeof(char32)); | ||
| 65 | memset(tabs,0,maxtabs*sizeof(char32)); | ||
| 66 | for(int i=0;i<tables.get();i++) | ||
| 67 | memcpy(tabs[i],hd->tabs[i],sizeof(char32)); | ||
| 68 | frame.set(sizeof(head_t)); | ||
| 69 | } | ||
| 70 | |||
| 71 | void filemap::head_t::setcomp(int idx,const sref& s) | ||
| 72 | { | ||
| 73 | index.set(idx); | ||
| 74 | s.copyto(compname,sizeof(char32)); | ||
| 75 | fatal.add(1); | ||
| 76 | } | ||
| 77 | |||
| 78 | void filemap::head_t::set(int col,const sref& s) | ||
| 79 | { | ||
| 80 | s.copyto(tabs[col],sizeof(char32)); | ||
| 81 | fatal.add(1); | ||
| 82 | } | ||
| 83 | |||
| 84 | void filemap::head_t::insert(int col,const sref& s) | ||
| 85 | { | ||
| 86 | for(int i=maxtabs-1;i>col;i--) | ||
| 87 | memcpy(tabs[i],tabs[i-1],sizeof(char32)); | ||
| 88 | if(index.get()>=col&&index.get()<tables.get()) | ||
| 89 | index.add(1); | ||
| 90 | s.copyto(tabs[col],sizeof(char32)); | ||
| 91 | tables.add(1); | ||
| 92 | fatal.add(1); | ||
| 93 | } | ||
| 94 | |||
| 95 | void filemap::head_t::remove(int col) | ||
| 96 | { | ||
| 97 | if(index.get()!=col) { | ||
| 98 | for(int i=col;i<maxtabs-1;i++) | ||
| 99 | memcpy(tabs[i],tabs[i+1],sizeof(char32)); | ||
| 100 | if(index.get()>col) index.set(index.get()-1); | ||
| 101 | tables.add(-1); | ||
| 102 | fatal.add(1); | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | /////////////////////////////////////////////////////// | ||
| 107 | |||
| 108 | filemap::chunk_t::chunk_t(chunk_t* p,int fd,int wok,int off,int size) | ||
| 109 | { | ||
| 110 | next=p; | ||
| 111 | end=(begin=off)+size; | ||
| 112 | map=(char*)mmap(0,size,PROT_READ|(wok?PROT_WRITE:0),MAP_SHARED,fd,off); | ||
| 113 | if(map==MAP_FAILED) { map=0; THROW("filemap: could not map chunk") } | ||
| 114 | } | ||
| 115 | |||
| 116 | filemap::chunk_t::~chunk_t() | ||
| 117 | { | ||
| 118 | if(map) munmap(map,end-begin); | ||
| 119 | } | ||
| 120 | |||
| 121 | int filemap::chunk_t::fill(int off) | ||
| 122 | { | ||
| 123 | if(own(off)) { | ||
| 124 | at(off)->mem.set(end-off); | ||
| 125 | at(off)->caps.set(0); | ||
| 126 | return end; | ||
| 127 | } | ||
| 128 | else return off; | ||
| 129 | } | ||
| 130 | |||
| 131 | void filemap::chunk_t::sync() | ||
| 132 | { | ||
| 133 | msync(map,end-begin,MS_SYNC); | ||
| 134 | } | ||
| 135 | |||
| 136 | /////////////////////////////////////////////////////// | ||
| 137 | |||
| 138 | void filemap::remap() | ||
| 139 | { | ||
| 140 | while(mem->end<hd->filesize.get()) { | ||
| 141 | mem=new chunk_t(mem,fd,wok,mem->end,mem->size()<<1); | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | filemap::rec_t* filemap::add(int n) | ||
| 146 | { | ||
| 147 | int caps=sizeof(rec_t),dim=caps+n; | ||
| 148 | while(caps<dim) caps<<=1; | ||
| 149 | remap(); | ||
| 150 | if(hd->filesize.get()<mem->end) { | ||
| 151 | ftruncate(fd,mem->end); | ||
| 152 | hd->filesize.set(mem->end); | ||
| 153 | } | ||
| 154 | while(hd->filetop.get()+caps>mem->end) { | ||
| 155 | hd->filetop.set(mem->fill(hd->filetop.get())); | ||
| 156 | mem=new chunk_t(mem,fd,wok,mem->end,mem->size()<<1); | ||
| 157 | ftruncate(fd,mem->end); | ||
| 158 | hd->filesize.set(mem->end); | ||
| 159 | } | ||
| 160 | rec_t* rec=at(hd->filetop.get()); | ||
| 161 | hd->filetop.add(caps); | ||
| 162 | rec->mem.set(caps); | ||
| 163 | rec->caps.set(n); | ||
| 164 | return rec; | ||
| 165 | } | ||
| 166 | |||
| 167 | void filemap::rem(rec_t* rec) | ||
| 168 | { | ||
| 169 | rec->caps.set(0); | ||
| 170 | } | ||
| 171 | |||
| 172 | /////////////////////////////////////////////////////// | ||
| 173 | |||
| 174 | filemap::filemap(filemap* p): | ||
| 175 | next(p),fd(-1),wok(0),refs(0),pathname(),hd(0),mem(0),nodelock(0) | ||
| 176 | { | ||
| 177 | memset(&mutex,0,sizeof(mutex_t)); | ||
| 178 | } | ||
| 179 | |||
| 180 | filemap::filemap(filemap* p,const mstring& path,int update,int prot): | ||
| 181 | next(p),fd(-1),wok(0),refs(0),pathname(),hd(0),mem(0),nodelock(0) | ||
| 182 | { | ||
| 183 | memset(&mutex,0,sizeof(mutex_t)); | ||
| 184 | open(path,update,prot); | ||
| 185 | } | ||
| 186 | |||
| 187 | filemap::~filemap() | ||
| 188 | { | ||
| 189 | close(); | ||
| 190 | } | ||
| 191 | |||
| 192 | /////////////////////////////////////////////////////// | ||
| 193 | |||
| 194 | void filemap::open(const mstring& path,int update,int prot) throw(merror_t) | ||
| 195 | { | ||
| 196 | close(); | ||
| 197 | if(update) { | ||
| 198 | if(path.left(5)!=dev_tag) nodelock=new nodelock_t(path); | ||
| 199 | else nodelock=0; | ||
| 200 | wok=1; | ||
| 201 | } | ||
| 202 | else { | ||
| 203 | nodelock=0; | ||
| 204 | wok=0; | ||
| 205 | } | ||
| 206 | fd=::open(path.c_str(),wok?O_RDWR|O_CREAT:O_RDONLY,prot); | ||
| 207 | if(fd<0) THROW("filemap: could not open "<<path); | ||
| 208 | int fsize=lseek(fd,0,SEEK_END); | ||
| 209 | int mapsize=PAGESIZE*int((fsize+PAGESIZE)/PAGESIZE); | ||
| 210 | hd=(head_t*)(mem=new chunk_t(0,fd,wok,0,mapsize))->map; | ||
| 211 | if(fsize<sizeof(head_t)) { | ||
| 212 | if(wok) { ftruncate(fd,fsize=mapsize); new(hd) head_t(); } | ||
| 213 | else THROW("filemap: not a HDB file"); | ||
| 214 | } | ||
| 215 | if(First(sref(hd->ident))!=First(sref(HEADNAME))) THROW("filemap: bad version"); | ||
| 216 | remap(); | ||
| 217 | if(fsize!=hd->filesize.get()) | ||
| 218 | THROW("filemap: "<<path<<" has bad filesize - run hdbfix"); | ||
| 219 | pathname=path; | ||
| 220 | } | ||
| 221 | |||
| 222 | void filemap::check() throw(merror_t) | ||
| 223 | { | ||
| 224 | // Make a consistency test, but NEVER change filetop!!! | ||
| 225 | // Severe loss of data may occur if you do | ||
| 226 | // Use hdbfix in case the db seems corrupted | ||
| 227 | int p=recs(),ft=hd->filetop.get(); | ||
| 228 | while(size(p)) { | ||
| 229 | rec_t* rec=at(p); | ||
| 230 | p+=rec->mem.get(); | ||
| 231 | } | ||
| 232 | if(p<ft) THROW("filemap: "<<pathname<<" is corrupted - run hdbfix"); | ||
| 233 | // OK, at least no data is missing. Usually p==ft, | ||
| 234 | // but since no locking is done here, another process | ||
| 235 | // may make changes that turn p>ft. | ||
| 236 | } | ||
| 237 | |||
| 238 | void filemap::save(const mstring& path,int prot) throw(merror_t) | ||
| 239 | { | ||
| 240 | if(path==pathname) THROW("filemap: cannot overwrite current file"); | ||
| 241 | int mfd=::open(path.c_str(),O_RDWR|O_CREAT|O_TRUNC,prot); | ||
| 242 | if(mfd<0) THROW("filemap: could not open "<<path<<" for writing"); | ||
| 243 | int caps=0,p=recs(); | ||
| 244 | while(size(p)) { | ||
| 245 | rec_t* rec=at(p); | ||
| 246 | if(rec->caps.get()) caps+=rec->mem.get(); | ||
| 247 | p+=rec->mem.get(); | ||
| 248 | } | ||
| 249 | head_t mhd(hd,caps); | ||
| 250 | ::write(mfd,&mhd,sizeof(head_t)); | ||
| 251 | p=recs(); | ||
| 252 | while(size(p)) { | ||
| 253 | rec_t* rec=at(p); | ||
| 254 | if(rec->caps.get()) ::write(mfd,rec,rec->mem.get()); | ||
| 255 | p+=rec->mem.get(); | ||
| 256 | } | ||
| 257 | ftruncate(mfd,mhd.filesize.get()); | ||
| 258 | ::close(mfd); | ||
| 259 | } | ||
| 260 | |||
| 261 | void filemap::close() | ||
| 262 | { | ||
| 263 | while(mem) { chunk_t* p=mem->next; delete mem; mem=p; } | ||
| 264 | hd=0; | ||
| 265 | wok=0; | ||
| 266 | refs=0; | ||
| 267 | if(fd>=0) { ::close(fd); fd=-1; } | ||
| 268 | delete nodelock; nodelock=0; | ||
| 269 | pathname.clear(); | ||
| 270 | } | ||
| 271 | |||
| 272 | void filemap::sync() | ||
| 273 | { | ||
| 274 | for(chunk_t* p=mem;p;p=p->next) p->sync(); | ||
| 275 | } | ||
| 276 | |||
| 277 | /////////////////////////////////////////////////////// | ||
| 278 | |||
| 279 | void filemap::lock() | ||
| 280 | { | ||
| 281 | if(hd) { | ||
| 282 | MLOCK(mutex); | ||
| 283 | if((volatile long&)hd->lock) { | ||
| 284 | time_t end=time(0)+TIMEOUT; | ||
| 285 | while((volatile long&)hd->lock&&time(0)<end); | ||
| 286 | } | ||
| 287 | hd->lock.set(1); | ||
| 288 | } | ||
| 289 | } | ||
| 290 | |||
| 291 | void filemap::unlock() | ||
| 292 | { | ||
| 293 | if(hd) hd->lock.set(0); | ||
| 294 | } | ||
| 295 | |||
| 296 | /////////////////////////////////////////////////////// | ||
| 297 | |||
| 298 | time_t filemap::Lastmod() const | ||
| 299 | { | ||
| 300 | struct stat fs; fstat(fd,&fs); return fs.st_mtime; | ||
| 301 | } | ||
| 302 | |||
