00001 #include "config.h"
00002
00003 #include <unistd.h>
00004 #include <sys/types.h>
00005 #include <sys/stat.h>
00006 #include <sys/mman.h>
00007 #include <fcntl.h>
00008 #include <netinet/in.h>
00009
00010 #include <string.h>
00011 #include <stdint.h>
00012 #include <stdio.h>
00013 #include <stdlib.h>
00014 #include <assert.h>
00015 #include <errno.h>
00016 #include <alloca.h>
00017 #include <dirent.h>
00018 #include <time.h>
00019
00020 #include "panic.h"
00021 #include "vsdb.h"
00022 #include "util.h"
00023
00024 #ifndef HAVE_FSEEKO
00025 #define fseeko fseek
00026 #define ftello ftell
00027 #endif
00028
00029 static const char magic0[] = { 'J', 'W', 'M', 1};
00030
00031 #define KEYDATA_OFFSET 12
00032 #define MAGIC_OFFSET 0
00033 #define SIZE_OFFSET 8
00034 #define GENERATION_OFFSET 4
00035
00036 #define BAD_OVERFLOW ((uint32_t)(-1))
00037
00038 #define TEMP_NAME_MAX (512)
00039
00040 typedef struct vsdb_hashentry vsdb_hashentry_t;
00041 typedef struct vsdb_make vsdb_make_t;
00042 struct revision;
00043
00044 #define MODE_READONLY(x) (!MODE_WRITEABLE(x))
00045 #define MODE_WRITEABLE(x) ((x) & O_WRONLY || (x) & O_RDWR)
00046 #define MODE_READABLE(x) ((x) & O_RDONLY || (x) & O_RDWR)
00047 #define MODE_READONLY(x) (!MODE_WRITEABLE(x))
00048
00049
00050
00051
00052
00053
00054 static uint32_t vsdb_hash(void *data, size_t sz);
00055 static vsdb_make_t *vsdb_make_start(int fd);
00056 static void vsdb_make_add(vsdb_make_t *vm, void *key, size_t keysz, void *data, size_t datasz);
00057 static void vsdb_make_done(vsdb_make_t *vm, int foo);
00058 static int reload_revision(vsdb_t *vsdb);
00059 static int vsdb_check_latest(vsdb_t *vsdb);
00060 static void revision_unref(struct revision *);
00061 static int vsdb_init_names(vsdb_t *v, char *name);
00062
00063
00064 static void (*errfn)(char *) = NULL;
00065
00066
00067 struct revision {
00068 uint32_t generation;
00069 vsdb_hashentry_t *h;
00070 uintmax_t num;
00071 FILE *file;
00072 int ref;
00073 vsdb_t *vsdb;
00074 ino_t inode;
00075 time_t mtime;
00076 };
00077
00078 struct vsdb {
00079 struct revision *cr;
00080 char *name;
00081 char *dbname;
00082 int mode;
00083 int ref;
00084 };
00085
00086
00087 struct vsdb_transaction {
00088 struct revision *r;
00089 struct event *event;
00090 int mode;
00091 };
00092
00093 struct event {
00094 void *key;
00095 void *data;
00096 uintmax_t keysize;
00097 uintmax_t datasize;
00098 struct event *next;
00099 };
00100
00101
00102 struct vsdb_hashentry {
00103 uint32_t hash;
00104 uint32_t offset;
00105 uint32_t keysize;
00106 uint32_t datasize;
00107 uint32_t overflow;
00108 };
00109
00110
00111 struct vsdb_make {
00112 FILE *file;
00113 vsdb_hashentry_t *h;
00114 size_t nh;
00115 size_t bh;
00116 };
00117
00118
00119 static void
00120 warn_errfn(char *s)
00121 {
00122 fputs(s, stderr);
00123 fputc('\n', stderr);
00124 }
00125
00126 static void
00127 die_errfn(char *s)
00128 {
00129 fputs(s, stderr);
00130 fputc('\n', stderr);
00131 exit(1);
00132 }
00133
00140 void
00141 vsdb_set_verbose_mode(bool val)
00142 {
00143 verbose_mode = val;
00144 if(val)
00145 errfn = warn_errfn;
00146 else
00147 errfn = NULL;
00148 }
00149
00155 void
00156 vsdb_set_die_on_error(void)
00157 {
00158 errfn = die_errfn;
00159 }
00160
00168 void
00169 vsdb_set_errfn(void (*fn)(char *))
00170 {
00171 errfn = fn;
00172 }
00173
00174
00175 static void
00176 errorf(char *fmt, ...)
00177 {
00178 va_list ap;
00179 char buf[256];
00180 if(!errfn)
00181 return;
00182 va_start(ap, fmt);
00183 vsnprintf(buf, 256, fmt, ap);
00184 va_end(ap);
00185 buf[255] = '\0';
00186 errfn(buf);
00187 }
00188
00189 static void
00190 errornof(char *fmt, ...)
00191 {
00192 int err;
00193 va_list ap;
00194 char buf[256];
00195 char buf2[256];
00196 if(!errfn)
00197 return;
00198 err = errno;
00199 va_start(ap, fmt);
00200 vsnprintf(buf, 256, fmt, ap);
00201 va_end(ap);
00202 buf[255] = '\0';
00203 snprintf(buf2, 256, "%s: %s", buf, strerror(err));
00204 buf2[255] = '\0';
00205 errfn(buf2);
00206 }
00207
00212 void
00213 vsdb_get_info(vsdb_t *vsdb, struct vsdb_info *info)
00214 {
00215 int i;
00216 assert(vsdb);
00217 assert(info);
00218 assert(vsdb->cr);
00219 info->mtime = vsdb->cr->mtime;
00220 info->num_entries = vsdb->cr->num;
00221 info->generation = vsdb->cr->generation;
00222 info->name = vsdb->name;
00223 info->key_size = 0;
00224 info->data_size = 0;
00225 for(i = 0; i < info->num_entries; i++) {
00226 info->key_size += ntohl(vsdb->cr->h[i].keysize);
00227 info->data_size += ntohl(vsdb->cr->h[i].datasize);
00228 }
00229 }
00230
00231 uintmax_t
00232 vsdb_transaction_num_entries(vsdb_transaction_t *vt)
00233 {
00234 assert(vt);
00235 return vt->r->num;
00236 }
00237
00238
00239
00240 static int
00241 nfs_link(char *fin, char *fout)
00242 {
00243 struct stat st;
00244 int ret, ret2;
00245 ret = link(fin, fout);
00246 if(ret != 0) {
00247 ret2 = stat(fin, &st);
00248 if(!ret2 && st.st_nlink == 2)
00249 ret = 0;
00250 }
00251 return ret;
00252 }
00253
00254 static void
00255 db_file(char *home, char **name, uint32_t dgen, uint32_t fgen)
00256 {
00257 int ret;
00258 bfree((void *)name);
00259 ret = asprintf(name, "%s/dbd.%u/db.%u", home, dgen, fgen);
00260 if(ret == -1)
00261 panicno("asprintf");
00262 }
00263
00264 static void
00265 dbd_dir(char *home, char **name, uint32_t dgen)
00266 {
00267 int ret;
00268 bfree((void *)name);
00269 ret = asprintf(name, "%s/dbd.%u", home, dgen);
00270 if(ret == -1)
00271 panicno("asprintf");
00272 }
00273
00274 static void
00275 unlink_old(char *home, uint32_t gen, uint32_t num)
00276 {
00277 char *n = NULL;
00278 assert(num >= 0);
00279 while(num) {
00280 db_file(home, &n, gen, gen - num);
00281 unlink(n);
00282 num--;
00283 }
00284 free(n);
00285 }
00286
00287 static int
00288 rename_dir(char *home, uint32_t from, uint32_t to)
00289 {
00290 int ret;
00291 char *da = NULL, *db = NULL;
00292 assert (to - 1 == from);
00293 dbd_dir(home, &da, from);
00294 dbd_dir(home, &db, to);
00295 ret = rename(da, db);
00296 free(da);
00297 free(db);
00298 return ret;
00299 }
00300
00301 static int
00302 update_db_cache(vsdb_t *vsdb)
00303 {
00304 int ret;
00305 DIR *dir = NULL;
00306 struct dirent *dirent = NULL;
00307
00308
00309 print_verbose("[update_db_cache] started for %s", vsdb->name);
00310 dir = opendir(vsdb->name);
00311 if(!dir) {
00312 errornof("[update_db_cache] cannot opendir %s", vsdb->name);
00313 return -1;
00314 }
00315
00316 for(dirent = readdir(dir);dirent;dirent = readdir(dir)) {
00317 if(!strncmp(dirent->d_name,"dbd.", 4)) {
00318 char *temp_file = NULL;
00319 char *rn = NULL;
00320 char *d = NULL;
00321 unsigned ui;
00322 ret = sscanf(dirent->d_name, "dbd.%u", &ui);
00323 print_verbose("update_db_cache: found dir %s %u", dirent->d_name, ui);
00324 if(ret != 1) {
00325 print_verbose("update_db_cache: unable to parse dirname %s", dirent->d_name);
00326 continue;
00327 }
00328 create_temporary_filename(vsdb->name, &temp_file);
00329
00330 db_file(vsdb->name, &rn, ui, ui + 1);
00331 ret = nfs_link(rn, temp_file);
00332 if(ret == 0) {
00333 rename(temp_file, vsdb->dbname);
00334 rename_dir(vsdb->name, ui, ui + 1);
00335 } else {
00336
00337 db_file(vsdb->name, &rn, ui, ui );
00338 ret = nfs_link(rn, temp_file);
00339 if(ret == 0) {
00340 rename(temp_file, vsdb->dbname);
00341 } else
00342 unlink(temp_file);
00343 }
00344
00345 free(temp_file);
00346 free(rn);
00347 free(d);
00348 closedir(dir);
00349 return 0;
00350
00351 }
00352 }
00353 closedir(dir);
00354 errorf("[update_db_cache] No dbd.<num> directory found in %s", vsdb->name);
00355 return -1;
00356 }
00357
00362 vsdb_transaction_t *
00363 vsdb_transaction_begin(vsdb_t *vsdb, int mode)
00364 {
00365
00366 vsdb_transaction_t *vt = (vsdb_transaction_t *)malloc(sizeof(vsdb_transaction_t));
00367 assert(vt);
00368 assert(vsdb);
00369 assert(mode == O_RDONLY || mode == O_RDWR || mode == O_WRONLY);
00370
00371 if(MODE_WRITEABLE(mode) && ! MODE_WRITEABLE(vsdb->mode)) {
00372 errno = EPERM;
00373 errornof("transaction opened writeable for non-writable database");
00374 return NULL;
00375 }
00376 if(MODE_READABLE(mode) && ! MODE_READABLE(vsdb->mode)) {
00377 errno = EPERM;
00378 errornof("transaction opened readable for non-readable database");
00379 return NULL;
00380 }
00381
00382 vsdb_check_latest(vsdb);
00383
00384 vt->mode = mode;
00385 vt->r = vsdb->cr;
00386 vt->event = NULL;
00387 vt->r->ref++;
00388 return vt;
00389 }
00390
00395 void
00396 vsdb_transaction_rollback(vsdb_transaction_t *vt)
00397 {
00398 revision_unref(vt->r);
00399 struct event *e = vt->event;
00400 while(e) {
00401 struct event *et = e->next;
00402 free(e);
00403 e = et;
00404 }
00405 free(vt);
00406 }
00407
00416 int
00417 vsdb_transaction_commit(vsdb_transaction_t *vt)
00418 {
00419 char *s = NULL;
00420 int i;
00421 int fd;
00422 int ret;
00423 int err;
00424 vsdb_t *vsdb = vt->r->vsdb;
00425 vsdb_make_t *vm = NULL;
00426 struct event *e = NULL;
00427 char *temp_file = NULL;
00428 int attempts = 0;
00429
00430 if(MODE_READONLY(vt->mode) || vt->event == NULL) {
00431 vsdb_transaction_rollback(vt);
00432 return 0;
00433 }
00434
00435 fd = create_temporary_file(vt->r->vsdb->name, &temp_file);
00436 if(fd == -1) {
00437 errornof("[vsdb_transaction_commit] cannot create tempfile");
00438 return -1;
00439 }
00440
00441 assert(fd >= 0);
00442 assert(temp_file);
00443
00444 vm = vsdb_make_start(fd);
00445 assert(vm);
00446 for(e = vt->event; e; e = e->next) {
00447 if(e->data)
00448 vsdb_make_add(vm, e->key, e->keysize, e->data, e->datasize);
00449 }
00450 if(!(vt->r->vsdb->mode & O_TRUNC))
00451 for(i = 0; i < vt->r->num; i++) {
00452 uint32_t keysz, datasz;
00453 void *kptr;
00454 void *dptr;
00455 keysz = ntohl(vt->r->h[i].keysize);
00456 datasz = ntohl(vt->r->h[i].datasize);
00457 kptr = malloc(keysz);
00458 dptr = malloc(datasz);
00459 assert(kptr);
00460 assert(dptr);
00461 ret = fseek(vt->r->file, ntohl(vt->r->h[i].offset), SEEK_SET);
00462 assert(ret != -1);
00463
00464 ret = fread(kptr, keysz, 1, vt->r->file);
00465 assert(ret != -1);
00466 ret = fread(dptr, datasz, 1, vt->r->file);
00467 assert(ret != -1);
00468
00469 for(e = vt->event; e; e = e->next) {
00470 if(e->keysize == keysz && !memcmp(e->key, kptr, keysz)) break;
00471 }
00472
00473 if(!e)
00474 vsdb_make_add(vm, kptr, keysz, dptr, datasz);
00475
00476 free(kptr);
00477 free(dptr);
00478 }
00479
00480 vsdb_make_done(vm, vt->r->generation + 1);
00481
00482
00483 db_file(vt->r->vsdb->name, &s, vt->r->generation, vt->r->generation + 1);
00484 assert(s);
00485
00486 ret = nfs_link(temp_file, s);
00487 if(ret == 0) {
00488
00489
00490 rename(temp_file, vt->r->vsdb->dbname);
00491 rename_dir(vt->r->vsdb->name, vt->r->generation, vt->r->generation + 1);
00492 unlink_old(vt->r->vsdb->name, vt->r->generation + 1, 1);
00493
00494
00495
00496 free(s);
00497 free(temp_file);
00498 vsdb = vt->r->vsdb;
00499 vt->r->vsdb->mode &= ~O_TRUNC;
00500 vsdb_transaction_rollback(vt);
00501 sync();
00502 ret = vsdb_check_latest(vsdb);
00503 assert(ret != -1);
00504 assert(ret == 0);
00505
00506 return 0;
00507 }
00508 err = errno;
00509 remove(temp_file);
00510 free(temp_file);
00511 free(s);
00512
00513 attempts = 0;
00514 do {
00515 struct timespec ts;
00516 ret = vsdb_check_latest(vsdb);
00517 if(ret <= 0) {
00518 vsdb_transaction_rollback(vt);
00519 if(ret == -1) {
00520 errorf("[vsdb_transaction_commit] vsdb_check_latest had error");
00521 return -1;
00522 }
00523 assert(ret == 0);
00524 print_verbose("new database found");
00525 return 1;
00526 }
00527 ts.tv_sec = 0;
00528 ts.tv_nsec = 2000;
00529 nanosleep(&ts, NULL);
00530 attempts++;
00531 } while(attempts < 7);
00532
00533
00534
00535
00536
00537 if(err == EEXIST) {
00538
00539 ret = rename_dir(vt->r->vsdb->name, vt->r->generation, vt->r->generation + 1);
00540 unlink_old(vt->r->vsdb->name, vt->r->generation + 1, 1);
00541 }
00542
00543 update_db_cache(vsdb);
00544
00545 vsdb_transaction_rollback(vt);
00546 return 1;
00547 }
00548
00549
00550
00551 static void
00552 revision_unref(struct revision *r)
00553 {
00554 if(!r)
00555 return;
00556 assert(r->ref > 0);
00557 if(r->ref == 1) {
00558 fclose(r->file);
00559 free(r->h);
00560 free(r);
00561 } else
00562 --r->ref;
00563 }
00564
00571 int vsdb_check_latest(vsdb_t *vsdb) { int ret; struct stat st; assert(vsdb);
00572 ret = stat(vsdb->dbname, &st); if(ret == -1) { errornof("stat(%s)",
00573 vsdb->dbname);
00574 return -1; } if(st.st_mtime != vsdb->cr->mtime || st.st_ino !=
00575 vsdb->cr->inode) { return reload_revision(vsdb); }
00576 return 1; }
00577
00578 static int
00579 reload_revision(vsdb_t *vsdb)
00580 {
00581 int err;
00582 int ret;
00583 off_t offset;
00584 struct revision *r = malloc(sizeof(struct revision));
00585 uint32_t u32;
00586 struct stat st;
00587
00588 assert(vsdb);
00589 assert(r);
00590
00591 revision_unref(vsdb->cr);
00592 vsdb->cr = r;
00593
00594 r->ref = 1;
00595 r->vsdb = vsdb;
00596 r->file = fopen(vsdb->dbname, "r");
00597 if(!r->file) {
00598 errornof("fopen(%s, \"r\")", vsdb->dbname);
00599 return -1;
00600 }
00601 ret = fstat(fileno(r->file), &st);
00602 assert(ret != -1);
00603 r->inode = st.st_ino;
00604 r->mtime = st.st_mtime;
00605
00606
00607 err = fseeko(r->file, MAGIC_OFFSET, SEEK_SET);
00608 assert(err != -1);
00609 err = fread(&u32, 4, 1, r->file);
00610 assert(err != -1);
00611 assert(!memcmp(&u32, magic0, 4));
00612
00613
00614 err = fseeko(r->file, SIZE_OFFSET, SEEK_SET);
00615 assert(err != -1);
00616 err = fread(&u32, 4, 1, r->file);
00617 assert(err != -1);
00618 r->num = ntohl(u32);
00619
00620
00621 err = fseeko(r->file, GENERATION_OFFSET, SEEK_SET);
00622 assert(err != -1);
00623 err = fread(&u32, 4, 1, r->file);
00624 assert(err != -1);
00625 r->generation = ntohl(u32);
00626
00627
00628 err = fseeko(r->file, 0, SEEK_END);
00629 assert(err != -1);
00630 offset = ftello(r->file);
00631 assert(offset != -1);
00632
00633 offset -= sizeof(vsdb_hashentry_t)*r->num;
00634
00635
00636
00637 err = fseeko(r->file, offset, SEEK_SET);
00638 assert(err != -1);
00639
00640
00641
00642 r->h = malloc(sizeof(vsdb_hashentry_t)*r->num);
00643 assert(r->h);
00644
00645 err = fread(r->h, sizeof(vsdb_hashentry_t), r->num, r->file);
00646 if(err == -1) {
00647 panicno("fread");
00648 }
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661 return 0;
00662
00663 }
00664
00665
00666 static void
00667 create_database(char *s)
00668 {
00669 int fd, ret;
00670 vsdb_make_t *vm = NULL;
00671 char *temp_file = NULL;
00672 char *temp_file2 = NULL;
00673
00674 print_verbose("Creating database: %s", s);
00675
00676 fd = create_temporary_file(s, &temp_file);
00677 if(fd < 0) {
00678 panicno("creating database");
00679 return;
00680 }
00681
00682 vm = vsdb_make_start(fd);
00683 if(!vm)
00684 return;
00685 vsdb_make_done(vm, 0);
00686
00687 asprintf(&temp_file2, "%s/dbd.0", s);
00688 mkdir(temp_file2, 0777);
00689 free(temp_file2);
00690 asprintf(&temp_file2, "%s/dbd.0/db.0", s);
00691 ret = link(temp_file, temp_file2);
00692 assert(ret != -1);
00693 free(temp_file2);
00694 asprintf(&temp_file2, "%s/db.cache", s);
00695 ret = rename(temp_file, temp_file2);
00696 assert(ret != -1);
00697 free(temp_file2);
00698 free(temp_file);
00699 }
00700
00701 static void
00702 vsdb_new_database(char *s)
00703 {
00704 int ret;
00705 ret = mkdir(s, 0777);
00706 if(!ret) {
00707 create_database(s);
00708 }
00709
00710 }
00711
00716 int
00717 vsdb_repair_database(char *s)
00718 {
00719
00720
00721
00722
00723 vsdb_t vsdb;
00724 vsdb_init_names(&vsdb, s);
00725
00726 return update_db_cache(&vsdb);
00727 }
00728
00729 static int
00730 vsdb_init_names(vsdb_t *v, char *name)
00731 {
00732 int ret;
00733 if(name[0] == '/') {
00734 v->name = strdup(name);
00735 if(!v->name) {
00736 errornof("[vsdb_init_names] strdup");
00737 return -1;
00738 }
00739 } else {
00740 char *cwd = get_current_dir_name();
00741 if(!cwd) {
00742 errornof("[vsdb_init_names] get_current_dir_name");
00743 return -1;
00744 }
00745 ret = asprintf(&v->name, "%s/%s", cwd, name);
00746 free(cwd);
00747 if(ret == -1) {
00748 errornof("[vsdb_init_names] asprintf");
00749 return -1;
00750 }
00751 }
00752
00753 if(v->name[strlen(v->name) - 1] == '/')
00754 v->name[strlen(v->name) - 1] = '\0';
00755
00756 ret = asprintf(&v->dbname, "%s/db.cache", v->name);
00757 if(ret == -1) {
00758 free(v->name);
00759 free(v);
00760 errornof("[vsdb_init_names] asprintf");
00761 return -1;
00762 }
00763 assert(v->dbname);
00764
00765 return 0;
00766 }
00767
00774 vsdb_t *
00775 vsdb_open(char *name, int mode)
00776 {
00777 vsdb_t *v;
00778 int ret;
00779
00780 assert(name);
00781 if(!name[0])
00782 return NULL;
00783 print_verbose("Attempting open: %s", name);
00784
00785 v = (vsdb_t *)malloc(sizeof(vsdb_t));
00786 assert(v != NULL);
00787 v->mode = mode;
00788 v->ref = 1;
00789 v->cr = NULL;
00790 v->name = NULL;
00791 v->dbname = NULL;
00792
00793 ret = vsdb_init_names(v, name);
00794 if(ret == -1) {
00795 free(v);
00796 return NULL;
00797 }
00798
00799
00800
00801
00802 if(mode & O_CREAT) {
00803 print_verbose("creating database: %s", name);
00804 vsdb_new_database(v->name);
00805 }
00806
00807
00808
00809 if(reload_revision(v) != 0) {
00810 free(v->name);
00811 free(v->dbname);
00812 free(v);
00813 errorf("[vsdb_open] reload_revision");
00814 return NULL;
00815 }
00816
00817 return v;
00818 }
00819
00825 void
00826 vsdb_close(vsdb_t *v)
00827 {
00828 if(v->mode & O_TRUNC) {
00829 vsdb_transaction_t *vt;
00830 do {
00831 vt = vsdb_transaction_begin(v, O_WRONLY);
00832 } while(vsdb_transaction_commit(vt) > 0);
00833 }
00834 revision_unref(v->cr);
00835 free(v->name);
00836 free(v->dbname);
00837 free(v);
00838 }
00839
00844 void
00845 vsdb_clear(vsdb_transaction_t *vt, void *key, size_t keysz)
00846 {
00847 struct event *e = (struct event *)malloc(sizeof(struct event));
00848 assert(key || keysz == 0);
00849 e->key = key;
00850 e->keysize = keysz;
00851 e->data = NULL;
00852 e->datasize = 0;
00853
00854 e->next = vt->event;
00855 vt->event = e;
00856 }
00857
00862 void
00863 vsdb_set(vsdb_transaction_t *vt, void *key, size_t keysz, void *data, size_t datasz)
00864 {
00865 struct event *e = (struct event *)malloc(sizeof(struct event));
00866 assert(key || keysz == 0);
00867 assert(data || datasz == 0);
00868 e->key = key;
00869 e->keysize = keysz;
00870 if(data == NULL && datasz == 0)
00871 e->data = e;
00872 else
00873 e->data = data;
00874 e->datasize = datasz;
00875
00876 e->next = vt->event;
00877 vt->event = e;
00878
00879 }
00880
00887 int
00888 vsdb_lookup(vsdb_transaction_t *vt, void *key, size_t keysz, uintmax_t *which)
00889 {
00890 int i;
00891 int ret;
00892 uint32_t hash = vsdb_hash(key, keysz);
00893 uint32_t u32;
00894 *which = 0;
00895 if(vt->r->num == 0)
00896 return 1;
00897 if(vt->r->vsdb->mode & O_TRUNC)
00898 return 1;
00899 i = hash % vt->r->num;
00900 while(1) {
00901 if(hash == ntohl(vt->r->h[i].hash) && ntohl(vt->r->h[i].keysize) == keysz) {
00902 off_t offset = ntohl(vt->r->h[i].offset);
00903 void *keyt = alloca(keysz);
00904 assert(keyt);
00905 ret = fseeko(vt->r->file, offset, SEEK_SET);
00906 if(ret == -1)
00907 return -1;
00908 ret = fread(keyt, keysz, 1, vt->r->file);
00909 if(ret == -1)
00910 return -1;
00911 if(!memcmp(keyt, key, keysz)) {
00912 *which = i;
00913 return 0;
00914 }
00915
00916 }
00917 u32 = ntohl(vt->r->h[i].overflow);
00918 if(u32 != BAD_OVERFLOW)
00919 i = u32;
00920 else
00921 return 1;
00922 }
00923
00924 }
00925
00932 void
00933 vsdb_aread_entry(vsdb_transaction_t *vt, uintmax_t entry, void **key, size_t *keysz, void **data, size_t *datasz)
00934 {
00935 int ret;
00936 size_t datasize = ntohl(vt->r->h[entry].datasize);
00937 size_t keysize = ntohl(vt->r->h[entry].keysize);
00938
00939 ret = fseeko(vt->r->file, ntohl(vt->r->h[entry].offset), SEEK_SET);
00940 assert(ret != -1);
00941
00942
00943 if(keysz)
00944 *keysz = keysize;
00945 if(key) {
00946 *key = malloc(keysize);
00947 ret = fread(*key, keysize, 1, vt->r->file);
00948 assert(ret != -1);
00949 } else {
00950 ret = fseeko(vt->r->file, keysize, SEEK_CUR);
00951 assert(ret != -1);
00952 }
00953 if(datasz)
00954 *datasz = datasize;
00955 if(data) {
00956 *data = malloc(datasize);
00957 ret = fread(*data, datasize, 1, vt->r->file);
00958 assert(ret != -1);
00959 }
00960 }
00961
00962
00963
00964
00965
00966
00967
00968
00969
00970
00971
00972
00973
00974
00975
00976
00983 char *
00984 vsdb_lookup_ss(vsdb_transaction_t *vt, char *key)
00985 {
00986 void *data;
00987 size_t datasz;
00988 uintmax_t which;
00989 int ret;
00990 ret = vsdb_lookup(vt, key, strlen(key), &which);
00991 if(ret == 0) {
00992 char *ndata;
00993 vsdb_aread_entry(vt, which, NULL, NULL, &data, &datasz);
00994 ndata = malloc(datasz + 1);
00995 assert(ndata);
00996 memcpy(ndata, data, datasz);
00997 free(data);
00998 ndata[datasz] = '\0';
00999 return ndata;
01000 }
01001 return NULL;
01002 }
01003
01004 static uint32_t
01005 vsdb_hash(void *data, size_t sz)
01006 {
01007 int i;
01008 char *c = data;
01009 uint32_t hash = 5381;
01010 for(i = 0; i < sz; i++)
01011 hash = ((hash << 5) + hash) ^ *c++;
01012 return hash;
01013 }
01014
01015
01016 static vsdb_make_t *
01017 vsdb_make_start(int fd)
01018 {
01019 int ret;
01020
01021 vsdb_make_t *vm = malloc(sizeof(vsdb_make_t));
01022 if(!vm)
01023 return vm;
01024
01025 vm->file = fdopen(fd, "w");
01026 if(!vm->file)
01027 {
01028 perror("fdopen");
01029 fclose(vm->file);
01030 return NULL;
01031 }
01032 ret = fwrite(magic0, 4, 1, vm->file);
01033 assert(ret != -1);
01034
01035 ret = fseeko(vm->file, KEYDATA_OFFSET, SEEK_SET);
01036 if(ret == -1) {
01037 perror("fseeko");
01038 fclose(vm->file);
01039 free(vm);
01040 return NULL;
01041 }
01042 vm->h = NULL;
01043 vm->nh = 0;
01044 vm->bh = 0;
01045 return vm;
01046 }
01047
01048
01049 static void
01050 vsdb_make_add(vsdb_make_t *vm, void *key, size_t keysz,
01051 void *data, size_t datasz)
01052 {
01053 ssize_t werr;
01054 vsdb_hashentry_t vh;
01055 off_t off = ftello(vm->file);
01056 if(off == -1)
01057 panicno("ftello");
01058
01059 vh.hash = vsdb_hash(key, keysz);
01060 vh.offset = off;
01061 vh.keysize = keysz;
01062 vh.datasize = datasz;
01063 vh.overflow = BAD_OVERFLOW;
01064 assert(vm->bh >= vm->nh);
01065 if(vm->bh == vm->nh) {
01066 if (!vm->bh)
01067 vm->bh = 4;
01068 vm->bh *= 2;
01069 vm->h = realloc(vm->h, vm->bh*sizeof(vsdb_hashentry_t));
01070 assert(vm->h);
01071 }
01072 vm->h[vm->nh] = vh;
01073 vm->nh++;
01074
01075 werr = fwrite(key, keysz, 1, vm->file);
01076 if (werr == -1 )
01077 panicno("fwrite");
01078 werr = fwrite(data, datasz, 1, vm->file);
01079 if (werr == -1 )
01080 panicno("fwrite");
01081
01082 }
01083
01084 static void
01085 vsdb_make_done(vsdb_make_t *vm, int generation)
01086 {
01087 int err;
01088 uint32_t buf;
01089 off_t offset;
01090 int i,j;
01091 vsdb_hashentry_t *he = malloc(vm->nh * sizeof(vsdb_hashentry_t));
01092 assert(sizeof(vsdb_hashentry_t) == 20);
01093 assert(he);
01094 memset(he, 0, vm->nh * sizeof(vsdb_hashentry_t));
01095 for(i = 0; i < vm->nh; i++) {
01096 uint32_t loc = (vm->h[i].hash % vm->nh);
01097 if(he[loc].offset == 0) {
01098 he[loc] = vm->h[i];
01099 vm->h[i].offset = 0;
01100 }
01101 }
01102
01103 i = 0; j = 0;
01104 while(1) {
01105 while(i < vm->nh && vm->h[i].offset == 0) i++;
01106 if(i >= vm->nh) break;
01107 while(j < vm->nh && he[j].offset) j++;
01108 if(j >= vm->nh) assert(!"this shouldn't happen");
01109
01110 assert(vm->h[i].offset);
01111 assert(!he[j].offset);
01112
01113 he[j] = vm->h[i];
01114 he[j].overflow = he[vm->h[i].hash % vm->nh].overflow;
01115 he[vm->h[i].hash % vm->nh].overflow = j;
01116 i++; j++;
01117 };
01118
01119 offset = ftello(vm->file);
01120
01121
01122 for(i = 0; i < vm->nh*sizeof(vsdb_hashentry_t)/4; i++)
01123 {
01124
01125 buf = htonl(((uint32_t *)he)[i]);
01126 err = fwrite(&buf, 4, 1, vm->file);
01127 assert(err != -1);
01128 }
01129
01130 err = fseek(vm->file, SIZE_OFFSET, SEEK_SET);
01131 assert(err != -1);
01132 buf = htonl(vm->nh);
01133 err = fwrite(&buf, 4, 1, vm->file);
01134 assert(err != -1);
01135
01136
01137 err = fseek(vm->file, GENERATION_OFFSET, SEEK_SET);
01138 assert(err != -1);
01139 buf = htonl(generation);
01140 err = fwrite(&buf, 4, 1, vm->file);
01141 assert(err != -1);
01142
01143 fflush(vm->file);
01144
01145 err = fclose(vm->file);
01146 assert(err != -1);
01147 free(vm->h);
01148 free(he);
01149 free(vm);
01150 }
01151
01152