00001 #include"NVcontainer.h"
00002 #include"Error.h"
00003
00004 #include <iostream>
00005 #include<fcntl.h>
00006 #include<stdio.h>
00007 #include<sys/mman.h>
00008 #include<sys/stat.h>
00009 #include<sys/types.h>
00010 #include<time.h>
00011
00012 using namespace std;
00013
00014 int nvflock(int fd, int cmnd, int block)
00015 {
00016 #ifdef USE_FLOCK
00017 int ncmnd;
00018
00019 switch (cmnd) {
00020 case NVcontainer::UnLock:
00021 ncmnd = LOCK_UN;
00022 break;
00023 case NVcontainer::ShrdLock:
00024 ncmnd = LOCK_SH;
00025 break;
00026 case NVcontainer::ExclLock:
00027 ncmnd = LOCK_EX;
00028 break;
00029 default:
00030 DEBUG(cdbg << "myflockerr\n");
00031 return EINVAL;
00032 }
00033 if (block == NVList::NoBlock)
00034 ncmnd = ncmnd | LOCK_NB;
00035
00036 return flock(fd, ncmnd);
00037 #else
00038 int lck;
00039 struct flock ncmnd;
00040 int r;
00041
00042 switch (cmnd) {
00043 case NVcontainer::UnLock:
00044 ncmnd.l_type = F_UNLCK;
00045 break;
00046 case NVcontainer::ShrdLock:
00047 ncmnd.l_type = F_RDLCK;
00048 break;
00049 case NVcontainer::ExclLock:
00050 ncmnd.l_type = F_WRLCK;
00051 break;
00052 default:
00053 ASSERT(cerr << "myflockerr\n");
00054 return EINVAL;
00055 }
00056 ncmnd.l_whence = SEEK_SET;
00057 ncmnd.l_start = 0;
00058 ncmnd.l_len = 0;
00059 if (block == NVcontainer::NoBlock)
00060 lck = F_SETLK;
00061 else
00062 lck = F_SETLKW;
00063
00064
00065 if (ncmnd.l_type != F_UNLCK) {
00066 struct flock ulck;
00067 ulck.l_type = F_UNLCK;
00068 ulck.l_whence = SEEK_SET;
00069 ulck.l_start = 0;
00070 ulck.l_len = 0;
00071 if (fcntl(fd, lck, &ulck) < 0)
00072 return -1;
00073 }
00074 if ((r = fcntl(fd, lck, &ncmnd)) < 0) {
00075 if (errno == EACCES || errno == EAGAIN)
00076 errno = EWOULDBLOCK;
00077 return -1;
00078 }
00079 return 0;
00080 #endif
00081 }
00082
00083 nvtime_t nvtime(nvtime_t * nvt)
00084 {
00085 time_t tm;
00086 nvtime_t nvtm;
00087
00088 time(&tm);
00089 nvtm = tm;
00090 if (nvt)
00091 *nvt = nvtm;
00092
00093 return nvtm;
00094 }
00095
00096 NVcontainer::NVcontainer()
00097 {
00098 lck_stackp = NVcontainer_LOCKSTACKSIZE;
00099 mem_fn[0] = '\0';
00100 mem_fd = -1;
00101 mem_p = NULL;
00102 mem_hdr = (Header *) mem_p;
00103 }
00104
00105 NVcontainer::NVcontainer(const char *dbname, int flags)
00106 {
00107 lck_stackp = NVcontainer_LOCKSTACKSIZE;
00108 mem_fd = -1;
00109 open(dbname, flags);
00110 }
00111
00112 NVcontainer::~NVcontainer()
00113 {
00114 close();
00115 }
00116
00117 void NVcontainer::make_current()
00118 {
00119 struct stat st;
00120
00121 if (mem_p) {
00122 if (is_current())
00123 return;
00124 munmap(mem_p, mem_sz);
00125 }
00126
00127 if (fstat(mem_fd, &st) < 0) {
00128 throw SystemError("NVContainer(20801): fstat failed",
00129 errno, ERROR_LOCATION);
00130 }
00131
00132 if ((nvoff_t) st.st_size <=
00133 (nvoff_t) sizeof(Header) + (nvoff_t) sizeof(FreeList)) {
00134 throw Error("NVConatiner(19603): info-record shrunk",
00135 ERROR_LOCATION);
00136 }
00137
00138 mem_sz = st.st_size;
00139 mem_p = (char *) mmap(NULL, mem_sz,
00140 PROT_READ | PROT_WRITE, MAP_SHARED,
00141 mem_fd, 0);
00142 if (mem_p == (char *) -1) {
00143 mem_p = NULL;
00144 throw SystemError("NVContainer(13784): mmap failed", errno,
00145 ERROR_LOCATION);
00146 }
00147 mem_hdr = (Header *) mem_p;
00148
00149 if (mem_hdr->hlen != sizeof(Header) || mem_hdr->version != 2) {
00150 close();
00151 throw Error("NVContainer(28196): no nvcontainer",
00152 ERROR_LOCATION);
00153 }
00154 if (mem_sz != mem_hdr->size) {
00155 VERB(slog.
00156 p(Logger::
00157 Critical) <<
00158 "NVcontainer: wrong database size stored in db-header - may be a hazard\n");
00159 }
00160 }
00161
00162 size_t NVcontainer::resize(size_t nsz)
00163 {
00164 if (nsz < mem_sz) {
00165 VERB(slog.
00166 p(Logger::
00167 Warning) <<
00168 "NVContainer: reduction of database size not supported, ignored\n");
00169 return mem_sz;
00170 }
00171
00172 lock(ExclLock);
00173 FreeList *flhead = o2fl(mem_hdr->freelist);
00174
00175 nsz = (nsz + 0x10000) & (~0xffff);
00176 if (ftruncate(mem_fd, nsz) < 0) {
00177 throw SystemError("NVList(2756): ftruncate failed", errno,
00178 ERROR_LOCATION);
00179 }
00180 mem_hdr->bytes_free += nsz - mem_sz;
00181 flhead->size += nsz - mem_sz;
00182 mem_hdr->size = nsz;
00183 make_current();
00184 lock(UnLock);
00185 return mem_sz;
00186 }
00187
00188 nvoff_t NVcontainer::getdata()
00189 {
00190 return mem_hdr->userdata;
00191 }
00192
00193 nvoff_t NVcontainer::getdatap()
00194 {
00195 return ((char *) &mem_hdr->userdata - mem_p);
00196 }
00197
00198 void NVcontainer::setdata(nvoff_t d)
00199 {
00200 mem_hdr->userdata = d;
00201 }
00202
00203 nvoff_t NVcontainer::nvalloc(size_t rsz)
00204 {
00205 lock(ExclLock);
00206 FreeList *flhead = o2fl(mem_hdr->freelist);
00207 FreeList *cur, *prv, *res;
00208
00209
00210
00211
00212
00213
00214 rsz = (rsz + sizeof(unsigned long) + 0xf) & (~0xf);
00215 if (flhead == NULL) {
00216 throw
00217 Error
00218 ("NVContainer(5946): Database file corrupted (freelist==NULL)",
00219 ERROR_LOCATION);
00220 }
00221
00222 prv = flhead;
00223 cur = o2fl(prv->next);
00224
00225 while (cur->size < rsz && cur != flhead) {
00226 prv = cur;
00227 cur = o2fl(cur->next);
00228 }
00229
00230 if (cur == flhead) {
00231
00232
00233
00234
00235 if (flhead->size < rsz + sizeof(FreeList)) {
00236 nvoff_t prvoff = fl2o(prv);
00237
00238
00239
00240 int needsz = rsz + sizeof(FreeList) - flhead->size;
00241 resize(mem_sz + needsz);
00242 cur = flhead = o2fl(mem_hdr->freelist);
00243 prv = o2fl(prvoff);
00244 ASSERT(if (cur->size < rsz + sizeof(FreeList)) {
00245 throw
00246 AssertionError
00247 ("NVContainer::nvalloc: resize failed",
00248 ERROR_LOCATION);}
00249 );
00250 }
00251 }
00252 ASSERT(if (cur->size < rsz) {
00253 throw
00254 AssertionError
00255 ("NVContainer::nvalloc: did not find a large enough free memory block",
00256 ERROR_LOCATION);}
00257 );
00258
00259 res = cur;
00260
00261 if (cur->size < rsz + sizeof(FreeList)) {
00262
00263
00264 ASSERT(if (res == flhead) {
00265 throw
00266 AssertionError
00267 ("NVContainer::nvalloc: Cannot split block of free memory at tail of database",
00268 ERROR_LOCATION);}
00269 );
00270 prv->next = cur->next;
00271 rsz = cur->size;
00272 } else {
00273
00274 cur = (FreeList *) ((char *) cur + rsz);
00275 cur->size = res->size - rsz;
00276 if (prv != res) {
00277
00278 cur->next = res->next;
00279 prv->next = fl2o(cur);
00280 if (res == flhead) {
00281 mem_hdr->freelist = fl2o(cur);
00282 }
00283 } else {
00284
00285
00286 ASSERT(if (res != flhead) {
00287 throw
00288 AssertionError
00289 ("NVList::nvalloc: Lost block of free memory at tail of database",
00290 ERROR_LOCATION);}
00291 );
00292 mem_hdr->freelist = cur->next = fl2o(cur);
00293 }
00294 }
00295 mem_hdr->bytes_free -= rsz;
00296 lock(UnLock);
00297
00298 *(unsigned long *) res = rsz;
00299 #ifdef FREELIST_ASSERT_ON
00300 char buf[256];
00301 int i = 10000;
00302 prv = flhead = o2fl(mem_hdr->freelist);;
00303 cur = o2fl(prv->next);
00304
00305 VERB(sprintf
00306 (buf, "nvalloc: checking freelist flhead=%p\n", flhead);
00307 slog.p(Logger::Debug) << buf);
00308 while (cur != flhead && i) {
00309 VERB(sprintf
00310 (buf, "nvalloc: elem(%lu)={%lu,%lu}\n",
00311 (nvoff_t) ((char *) cur - mem_p), cur->next,
00312 cur->size); slog.p(Logger::Debug) << buf);
00313 prv = cur;
00314 cur = o2fl(cur->next);
00315 ASSERT(if (prv >= cur) {
00316 throw
00317 AssertionError
00318 ("NVContainer::nvalloc: freelist out of order!",
00319 ERROR_LOCATION);}
00320 );
00321 i--;
00322 }
00323 VERB(sprintf
00324 (buf, "nvfree: tail(%lu)={%lu,%lu}\n",
00325 (nvoff_t) ((char *) cur - mem_p), cur->next, cur->size);
00326 slog.p(Logger::Debug) << buf);
00327 if (!i)
00328 VERB(slog.
00329 p(Logger::
00330 Debug) <<
00331 "nvalloc: more than 10000 free blocks!\n");
00332 #endif
00333 return (nvoff_t) ((char *) res - mem_p) + sizeof(unsigned long);
00334 }
00335
00336 void NVcontainer::nvfree(nvoff_t p)
00337 {
00338 #ifdef FREELIST_ASSERT_ON
00339 VERB(char buf[256];
00340 sprintf(buf, "NVcontainer::nvfree(%lu)\n", p);
00341 slog.p(Logger::Debug) << buf);
00342 #endif
00343 lock(ExclLock);
00344
00345 FreeList *pb, *pe;
00346 int psz;
00347 #ifdef FREELIST_ASSERT_ON
00348 FreeList *flhead;
00349 FreeList *cur, *prv;
00350 int i = 10000;
00351 prv = flhead = o2fl(mem_hdr->freelist);;
00352 cur = o2fl(prv->next);
00353
00354 VERB(sprintf(buf, "nvfree: checking freelist flhead=%p\n", flhead);
00355 slog.p(Logger::Debug) << buf);
00356 while (cur != flhead && i) {
00357 VERB(sprintf
00358 (buf, "nvfree: elem(%lu)={%lu,%lu}\n",
00359 (nvoff_t) ((char *) cur - mem_p), cur->next,
00360 cur->size); slog.p(Logger::Debug) << buf);
00361 prv = cur;
00362 cur = o2fl(cur->next);
00363 ASSERT(if (prv >= cur) {
00364 throw
00365 AssertionError
00366 ("NVContainer::nvfree: freelist out of order!",
00367 ERROR_LOCATION);}
00368 );
00369 i--;
00370 }
00371 VERB(sprintf
00372 (buf, "nvfree: tail(%lu)={%lu,%lu}\n",
00373 (nvoff_t) ((char *) cur - mem_p), cur->next, cur->size);
00374 slog.p(Logger::Debug) << buf);
00375 if (!i)
00376 VERB(slog.
00377 p(Logger::
00378 Debug) << "nvfree: more than 10000 free blocks!\n");
00379 #else
00380 FreeList *flhead = o2fl(mem_hdr->freelist);
00381 FreeList *cur = NULL, *prv = NULL;
00382 #endif
00383
00384 pb = o2fl(p - sizeof(unsigned long));
00385 psz = *(unsigned long *) pb;
00386 pe = (FreeList *) ((char *) pb + psz);
00387
00388
00389 ASSERT(if ((char *) pb < mem_p || mem_p + mem_sz < (char *) pe) {
00390 char buf[1024];
00391 close();
00392 sprintf(buf,
00393 "NVcontainer::nvfree: tried to free illegal memory block ([%p,%p])!\nNVcontainer mapped from [%p,%p]",
00394 pb, pe, mem_p, mem_p + mem_sz);
00395 throw AssertionError(buf, ERROR_LOCATION);});
00396
00397
00398 prv = flhead;
00399 cur = o2fl(prv->next);
00400 while (cur < pb && cur != flhead) {
00401 prv = cur;
00402 cur = o2fl(cur->next);
00403 ASSERT(if (prv >= cur) {
00404 throw
00405 AssertionError
00406 ("NVContainer::nvfree: freelist out of order!",
00407 ERROR_LOCATION);}
00408 );
00409 }
00410 ASSERT(if (cur < pb) {
00411 char buf[1024];
00412 close();
00413 sprintf(buf,
00414 "NVcontainer::nvfree: tried to free unallocated memory block ([%p,%p])!\n Address must be smaller than flhead (%p)!\nNVcontainer mapped from[%p,%p]",
00415 pb, pe, flhead, mem_p, mem_p + mem_sz);
00416 throw AssertionError(buf, ERROR_LOCATION);});
00417
00418 if ((char *) prv + prv->size == (char *) pb) {
00419 #ifdef FREELIST_ASSERT_ON
00420 VERB(slog.
00421 p(Logger::Debug) << "nvfree: adjacent to previous\n");
00422 #endif
00423
00424 prv->size += psz;
00425 if (pe == cur) {
00426 #ifdef FREELIST_ASSERT_ON
00427 VERB(slog.
00428 p(Logger::
00429 Debug) <<
00430 "nvfree: adjacent to next---cool\n");
00431 #endif
00432
00433 if (cur == flhead)
00434 mem_hdr->freelist = fl2o(prv);
00435 prv->size += cur->size;
00436 prv->next = cur->next;
00437 }
00438 } else if (pe == cur) {
00439 #ifdef FREELIST_ASSERT_ON
00440 VERB(slog.
00441 p(Logger::Debug) << "nvfree: adjacent to next\n");
00442 #endif
00443
00444 if (cur->next == fl2o(cur)) {
00445
00446 ASSERT(if (flhead != cur) {
00447 throw
00448 AssertionError
00449 ("NVContainer::nvfree: flhead lost trailing block of free memory",
00450 ERROR_LOCATION);}
00451 );
00452 mem_hdr->freelist = pb->next = fl2o(pb);
00453 pb->size = psz + cur->size;
00454 } else {
00455 pb->next = cur->next;
00456 pb->size = psz + cur->size;
00457 prv->next = fl2o(pb);
00458 if (cur == flhead)
00459 mem_hdr->freelist = fl2o(pb);
00460 }
00461 } else {
00462 VERB(slog.p(Logger::Debug) << "nvfree: not adjacent\n");
00463
00464
00465 pb->next = fl2o(cur);
00466 pb->size = psz;
00467 prv->next = fl2o(pb);
00468 }
00469 mem_hdr->bytes_free += psz;
00470 #ifdef FREELIST_ASSERT_ON
00471 i = 10000;
00472 prv = flhead = o2fl(mem_hdr->freelist);;
00473 cur = o2fl(prv->next);
00474
00475 VERB(sprintf(buf, "nvfree: checking freelist flhead=%p\n", flhead);
00476 slog.p(Logger::Debug) << buf);
00477 while (cur != flhead && i) {
00478 VERB(sprintf
00479 (buf, "nvfree: elem(%lu)={%lu,%lu}\n",
00480 (nvoff_t) ((char *) cur - mem_p), cur->next,
00481 cur->size); slog.p(Logger::Debug) << buf);
00482 prv = cur;
00483 cur = o2fl(cur->next);
00484 ASSERT(if (prv >= cur) {
00485 throw
00486 AssertionError
00487 ("NVContainer::nvfree: freelist out of order!",
00488 ERROR_LOCATION);}
00489 );
00490 i--;
00491 }
00492 VERB(sprintf
00493 (buf, "nvfree: tail(%lu)={%lu,%lu}\n",
00494 (nvoff_t) ((char *) cur - mem_p), cur->next, cur->size);
00495 slog.p(Logger::Debug) << buf);
00496 if (!i)
00497 VERB(slog.
00498 p(Logger::
00499 Debug) << "nvfree: more than 10000 free blocks!\n");
00500 #endif
00501 lock(UnLock);
00502 }
00503
00504 int NVcontainer::lock(int command, int block)
00505 {
00506 int res;
00507 int curLock = UnLock;
00508
00509 if (lck_stackp < NVcontainer_LOCKSTACKSIZE) {
00510 curLock = lck_stack[lck_stackp];
00511 }
00512 if (command == UnLock) {
00513
00514 if (lck_stackp >= NVcontainer_LOCKSTACKSIZE) {
00515 throw
00516 Error
00517 ("NVContainer(14364): Lock Stack underflow",
00518 ERROR_LOCATION);
00519 }
00520 lck_stackp++;
00521 command =
00522 lck_stackp <
00523 NVcontainer_LOCKSTACKSIZE ? lck_stack[lck_stackp] :
00524 UnLock;
00525 } else {
00526
00527 if (lck_stackp == 0) {
00528 throw
00529 Error
00530 ("NVContainer(24929): Lock Stack overflow",
00531 ERROR_LOCATION);
00532 }
00533
00534 if (curLock == ExclLock)
00535 command = ExclLock;
00536 lck_stack[--lck_stackp] = command;
00537 }
00538
00539 if (curLock != command) {
00540 if ((res = nvflock(mem_fd, command, block)) < 0) {
00541 if (errno == EWOULDBLOCK && block == NoBlock) {
00542 lck_stackp++;
00543 return -1;
00544 }
00545 throw
00546 SystemError("NVContainer(8402): flock failed",
00547 errno, ERROR_LOCATION);
00548 }
00549
00550
00551 if (command != UnLock)
00552 make_current();
00553 }
00554 return 0;
00555 }
00556
00557 int NVcontainer::get_lock()
00558 {
00559 return lck_stackp <
00560 NVcontainer_LOCKSTACKSIZE ? lck_stack[lck_stackp] : UnLock;
00561 }
00562
00563
00564
00565 void NVcontainer::open(const char *dbname, int flags)
00566 {
00567 ASSERT(if (!dbname) throw
00568 AssertionError
00569 ("NVContainer(1a): open called with null-ointer\n",
00570 ERROR_LOCATION));
00571
00572 if (strcmp(mem_fn, dbname) == 0)
00573 return;
00574
00575 if (mem_fd >= 0)
00576 close();
00577
00578 char errbuf[MAXPATHLEN + 256];
00579 struct stat st;
00580
00581 flags = 0;
00582 strcpy(mem_fn, dbname);
00583 if ((mem_fd =::open(mem_fn, O_RDWR | O_CREAT, 0644)) < 0) {
00584 sprintf(errbuf,
00585 "NVList(15485): Cannot open(%s,O_RDWR|O_CREAT)",
00586 dbname);
00587 throw SystemError(errbuf, errno, ERROR_LOCATION);
00588 }
00589
00590 nvflock(mem_fd, ExclLock, Block);
00591 if (fstat(mem_fd, &st) < 0) {
00592 throw SystemError("NVContainer(16974): fstat failed",
00593 errno, ERROR_LOCATION);
00594 }
00595 if (st.st_size == 0) {
00596 Header hdr;
00597 FreeList fl;
00598 hdr.freelist = hdr.hlen;
00599 hdr.size = 0x10000;
00600 fl.next = hdr.freelist;
00601 fl.size = hdr.bytes_free = hdr.size - hdr.hlen;
00602
00603 if (ftruncate(mem_fd, hdr.size) < 0) {
00604 unlink(mem_fn);
00605 throw
00606 SystemError
00607 ("NVContainer(13947): cannot create nvcontainer",
00608 errno, ERROR_LOCATION);
00609 }
00610 if (write(mem_fd, &hdr, sizeof(Header)) != sizeof(Header)) {
00611 unlink(mem_fn);
00612 throw
00613 SystemError
00614 ("NVContainer(13948): write(info-record) failed",
00615 errno, ERROR_LOCATION);
00616 }
00617 if (write(mem_fd, &fl, sizeof(FreeList)) !=
00618 sizeof(FreeList)) {
00619 unlink(mem_fn);
00620 throw
00621 SystemError
00622 ("NVContainer(23367): write(free-record) failed",
00623 errno, ERROR_LOCATION);
00624 }
00625 }
00626 nvflock(mem_fd, UnLock, Block);
00627
00628
00629 make_current();
00630 }
00631
00632 int NVcontainer::is_open(void)
00633 {
00634 return mem_fd >= 0;
00635 }
00636
00637 void NVcontainer::close()
00638 {
00639 if (mem_fd < 0)
00640 return;
00641 munmap(mem_p, mem_sz);
00642 ::close(mem_fd);
00643 if (lck_stackp != NVcontainer_LOCKSTACKSIZE) {
00644 VERB(slog.
00645 p(Logger::
00646 Critical) <<
00647 "NVcontainer::close: Forgot to unlock the container before closing it!\n");
00648 lck_stackp = NVcontainer_LOCKSTACKSIZE;
00649 }
00650 mem_fn[0] = '\0';
00651 mem_fd = -1;
00652 mem_p = NULL;
00653 mem_hdr = (Header *) mem_p;
00654 }
00655
00656 void NVcontainer::setmtime(nvtime_t tm, int force)
00657 {
00658 lock(ExclLock);
00659 unsigned long ctm = mem_hdr->mtime;
00660 if (force || tm > ctm)
00661 mem_hdr->mtime = tm;
00662 lock(UnLock);
00663 }
00664
00665 void NVcontainer::getmtime(nvtime_t * tm)
00666 {
00667 lock(ShrdLock);
00668 (*tm) = mem_hdr->mtime;
00669 lock(UnLock);
00670 }