Main Page   Compound List   File List   Compound Members   File Members  

vsdb.c

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  * file layout:
00051  * |magic/flags 4|generation 4 | num 4|record/key data|hash entries 20*num|
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 /* link which does an extra check useful for certain filesystems (like NFS) */
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) { //extra check for broken NFS
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         //if(vsdb_check_latest(vsdb) == 0) 
00308         //        return;
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                         //link( dbd.ui + 1 , tempfile) || link(dbd.ui tempfile) && mv tempfile db.cache
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                 //char *odir = NULL, *ndir = NULL, *ofile = NULL;
00489 
00490                 rename(temp_file, vt->r->vsdb->dbname); // update db.cache
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); //always should happen since we just commited succesfully.
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 { //look for new database
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         // something odd might be happening. investigate
00535         // update failed, let's find out why.
00536 
00537         if(err == EEXIST) {
00538                 //link exists. possible partial update.
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         /* verify magic */
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         /* read size */
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         /* read generation */
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         /* find hashtable */
00628         err = fseeko(r->file,  0, SEEK_END);
00629         assert(err != -1);
00630         offset = ftello(r->file);
00631         assert(offset != -1);
00632         //printf("filesize: %u\n", (unsigned)offset);
00633         offset -= sizeof(vsdb_hashentry_t)*r->num;
00634         //printf("reading offset: %u\n", (unsigned)offset);
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         for(i = 0; i < r->num; i++) {
00652                 printf("Number: %i\n", (int)i);
00653                 printf("keysize: %u\n", ntohl(r->h[i].keysize));
00654                 printf("datasize: %u\n", ntohl(r->h[i].datasize));
00655                 printf("hash: %X\n", ntohl(r->h[i].hash));
00656                 printf("overflow: %u\n", ntohl(r->h[i].overflow));
00657                 printf("offset: %u\n\n", ntohl(r->h[i].offset));
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         // ensure db.cache is pointing to proper database
00720         // ensure only single dbd.# exists
00721         // ensure directory is properly named
00722         // erase any old db.# files.
00723         vsdb_t vsdb;
00724         vsdb_init_names(&vsdb, s);
00725 
00726         return update_db_cache(&vsdb); //TODO hacky
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         /* create database if it doesn't exist */
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;  // simulate empty database
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 static int
00964 vsdb_lookup_rr(vsdb_transaction_t *vt, void *key, size_t keysz, void **data, size_t *datasz)
00965 {
00966 
00967         int ret;
00968         uintmax_t which;
00969         ret = vsdb_lookup(vt, key, keysz, &which);
00970         if(ret != 0)
00971                 return ret;
00972         vsdb_aread_entry(vt, which, NULL, NULL, data, datasz);
00973         return 0;
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; //indicates this was placed in this stage
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         //printf("make offset: %u\n", (unsigned)offset);
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         /* write size */
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         /* write generation */
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         /* clean up */
01145         err = fclose(vm->file);
01146         assert(err != -1);
01147         free(vm->h);
01148         free(he);
01149         free(vm);
01150 }
01151 
01152 

Generated on Wed Jun 18 17:30:39 2003 for libvsdb by doxygen1.2.18