int sdbm_store(register DBM *db, datum key, datum val, int flags) { int need; register long hash; if (db == NULL || bad(key)) return errno = EINVAL, -1; if (sdbm_rdonly(db)) return errno = EPERM, -1; need = key.dsize + val.dsize; /* * is the pair too big (or too small) for this database ?? */ if (need < 0 || need > PAIRMAX) return errno = EINVAL, -1; if (getpage(db, (hash = exhash(key)))) { /* * if we need to replace, delete the key/data pair * first. If it is not there, ignore. */ if (flags == DBM_REPLACE) (void) delpair(db->pagbuf, key); #ifdef SEEDUPS else if (duppair(db->pagbuf, key)) return 1; #endif /* * if we do not have enough room, we have to split. */ if (!fitpair(db->pagbuf, need)) if (!makroom(db, hash, need)) return ioerr(db), -1; /* * we have enough room or split is successful. insert the key, * and update the page file. */ (void) putpair(db->pagbuf, key, val); if (lseek(db->pagf, OFF_PAG(db->pagbno), SEEK_SET) < 0 || write(db->pagf, db->pagbuf, PBLKSIZ) < 0) return ioerr(db), -1; /* * success */ return 0; } return ioerr(db), -1; }
/** * Read n-th bitmap page. * * @return TRUE on success. */ static gboolean fetch_bitbuf(DBM *db, long num) { DBMBIG *dbg = db->big; long bno = num * BIG_BITCOUNT; /* address of n-th bitmap in file */ dbg->bitfetch++; if (bno != dbg->bitbno) { ssize_t got; if (dbg->bitbuf_dirty && !flush_bitbuf(db)) return FALSE; dbg->bitread++; got = compat_pread(dbg->fd, dbg->bitbuf, BIG_BLKSIZE, OFF_DAT(bno)); if (got < 0) { g_warning("sdbm: \"%s\": could not read bitmap block #%ld: %s", sdbm_name(db), num, g_strerror(errno)); ioerr(db, FALSE); return FALSE; } if (0 == got) { memset(dbg->bitbuf, 0, BIG_BLKSIZE); } dbg->bitbno = bno; dbg->bitbuf_dirty = FALSE; } else { dbg->bitbno_hit++; } return TRUE; }
/* * getnext - get the next key in the page, and if done with * the page, try the next page in sequence */ static datum getnext(register DBM *db) { datum key; for (;;) { db->keyptr++; key = getnkey(db->pagbuf, db->keyptr); if (key.dptr != NULL) return key; /* * we either run out, or there is nothing on this page.. * try the next one... If we lost our position on the * file, we will have to seek. */ db->keyptr = 0; if (db->pagbno != db->blkptr++) if (lseek(db->pagf, OFF_PAG(db->blkptr), SEEK_SET) < 0) break; db->pagbno = db->blkptr; if (read(db->pagf, db->pagbuf, PBLKSIZ) <= 0) break; if (!chkpage(db->pagbuf)) break; } return ioerr(db), nullitem; }
int sdbm_exists(register DBM *db, datum key) { if (db == NULL || bad(key)) return errno = EINVAL, -1; if (getpage(db, exhash(key))) return exipair(db->pagbuf, key); return ioerr(db), -1; }
datum sdbm_fetch(register DBM *db, datum key) { if (db == NULL || bad(key)) return errno = EINVAL, nullitem; if (getpage(db, exhash(key))) return getpair(db->pagbuf, key); return ioerr(db), nullitem; }
int sdbm_delete(register DBM *db, datum key) { if (db == NULL || bad(key)) return errno = EINVAL, -1; if (sdbm_rdonly(db)) return errno = EPERM, -1; if (getpage(db, exhash(key))) { if (!delpair(db->pagbuf, key)) return -1; /* * update the page file */ if (lseek(db->pagf, OFF_PAG(db->pagbno), SEEK_SET) < 0 || write(db->pagf, db->pagbuf, PBLKSIZ) < 0) return ioerr(db), -1; return 0; } return ioerr(db), -1; }
/* * the following two routines will break if * deletions aren't taken into account. (ndbm bug) */ datum sdbm_firstkey(register DBM *db) { if (db == NULL) return errno = EINVAL, nullitem; /* * start at page 0 */ if (lseek(db->pagf, OFF_PAG(0), SEEK_SET) < 0 || read(db->pagf, db->pagbuf, PBLKSIZ) < 0) return ioerr(db), nullitem; db->pagbno = 0; db->blkptr = 0; db->keyptr = 0; return getnext(db); }
/** * Flush bitmap to disk. * @return TRUE on sucess */ static gboolean flush_bitbuf(DBM *db) { DBMBIG *dbg = db->big; ssize_t w; dbg->bitwrite++; w = compat_pwrite(dbg->fd, dbg->bitbuf, BIG_BLKSIZE, OFF_DAT(dbg->bitbno)); if (BIG_BLKSIZE == w) { dbg->bitbuf_dirty = FALSE; return TRUE; } g_warning("sdbm: \"%s\": cannot flush bitmap #%ld: %s", sdbm_name(db), dbg->bitbno / BIG_BITCOUNT, -1 == w ? g_strerror(errno) : "partial write"); ioerr(db, TRUE); return FALSE; }
/** * Flush page to disk. * @return TRUE on success */ gboolean flushpag(DBM *db, char *pag, long num) { ssize_t w; g_assert(num >= 0); db->pagwrite++; w = compat_pwrite(db->pagf, pag, DBM_PBLKSIZ, OFF_PAG(num)); if (w < 0 || w != DBM_PBLKSIZ) { if (w < 0) g_warning("sdbm: \"%s\": cannot flush page #%ld: %s", sdbm_name(db), num, g_strerror(errno)); else g_warning("sdbm: \"%s\": could only flush %u bytes from page #%ld", sdbm_name(db), (unsigned) w, num); ioerr(db, TRUE); db->flush_errors++; return FALSE; } return TRUE; }
/** * Store big value in the .dat file, writing to the supplied block numbers. * * @param db the sdbm database * @param bvec start of block vector, containing block numbers * @param data start of data to write * @param len length of data to write * * @return -1 on error with errno set, 0 if OK. */ static int big_store(DBM *db, const void *bvec, const void *data, size_t len) { DBMBIG *dbg = db->big; int bcnt = bigbcnt(len); int n; const void *p; const char *q; size_t remain; g_return_val_if_fail(NULL == dbg->bitcheck, -1); if (-1 == dbg->fd && -1 == big_open(dbg)) return -1; /* * Look at the amount of consecutive block numbers we have to be able * to write into them via a single system call. */ n = bcnt; p = bvec; q = data; remain = len; while (n > 0) { size_t towrite = MIN(remain, BIG_BLKSIZE); guint32 bno = peek_be32(p); guint32 prev_bno = bno; p = const_ptr_add_offset(p, sizeof(guint32)); n--; remain = size_saturate_sub(remain, towrite); while (n > 0) { guint32 next_bno = peek_be32(p); size_t amount; if (next_bno <= prev_bno) /* Block numbers are sorted */ goto corrupted_page; if (next_bno - prev_bno != 1) break; /* Not consecutive */ prev_bno = next_bno; p = const_ptr_add_offset(p, sizeof(guint32)); amount = MIN(remain, BIG_BLKSIZE); towrite += amount; n--; remain = size_saturate_sub(remain, amount); } dbg->bigwrite++; if (-1 == compat_pwrite(dbg->fd, q, towrite, OFF_DAT(bno))) { g_warning("sdbm: \"%s\": " "could not write %lu bytes starting at data block #%u: %s", sdbm_name(db), (unsigned long) towrite, bno, g_strerror(errno)); ioerr(db, TRUE); return -1; } q += towrite; dbg->bigwrite_blk += bigblocks(towrite); g_assert(ptr_diff(q, data) <= len); } g_assert(ptr_diff(q, data) == len); return 0; corrupted_page: g_warning("sdbm: \"%s\": corrupted page: %d big data block%s not sorted", sdbm_name(db), bcnt, 1 == bcnt ? "" : "s"); ioerr(db, FALSE); errno = EFAULT; /* Data corrupted somehow (.pag file) */ return -1; }
/** * Fetch data block from the .dat file, reading from the supplied block numbers. * * @param db the sdbm database * @param bvec start of block vector, containing block numbers * @param len length of the data to be read * * @return -1 on error with errno set, 0 if OK. Read data is left in the * scratch buffer. */ static int big_fetch(DBM *db, const void *bvec, size_t len) { int bcnt = bigbcnt(len); DBMBIG *dbg = db->big; int n; const void *p; char *q; size_t remain; guint32 prev_bno; if (-1 == dbg->fd && -1 == big_open(dbg)) return -1; if (dbg->scratch_len < len) big_scratch_grow(dbg, len); /* * Read consecutive blocks in one single system call. */ n = bcnt; p = bvec; q = dbg->scratch; remain = len; while (n > 0) { size_t toread = MIN(remain, BIG_BLKSIZE); guint32 bno = peek_be32(p); prev_bno = bno; if (!big_block_is_allocated(db, prev_bno)) goto corrupted_database; p = const_ptr_add_offset(p, sizeof(guint32)); n--; remain = size_saturate_sub(remain, toread); while (n > 0) { guint32 next_bno = peek_be32(p); size_t amount; if (next_bno <= prev_bno) /* Block numbers are sorted */ goto corrupted_page; if (next_bno - prev_bno != 1) break; /* Not consecutive */ prev_bno = next_bno; if (!big_block_is_allocated(db, prev_bno)) goto corrupted_database; p = const_ptr_add_offset(p, sizeof(guint32)); amount = MIN(remain, BIG_BLKSIZE); toread += amount; n--; remain = size_saturate_sub(remain, amount); } dbg->bigread++; if (-1 == compat_pread(dbg->fd, q, toread, OFF_DAT(bno))) { g_warning("sdbm: \"%s\": " "could not read %lu bytes starting at data block #%u: %s", sdbm_name(db), (unsigned long) toread, bno, g_strerror(errno)); ioerr(db, FALSE); return -1; } q += toread; dbg->bigread_blk += bigblocks(toread); g_assert(UNSIGNED(q - dbg->scratch) <= dbg->scratch_len); } g_assert(UNSIGNED(q - dbg->scratch) == len); return 0; corrupted_database: g_warning("sdbm: \"%s\": cannot read unallocated data block #%u", sdbm_name(db), prev_bno); goto fault; corrupted_page: g_warning("sdbm: \"%s\": corrupted page: %d big data block%s not sorted", sdbm_name(db), bcnt, 1 == bcnt ? "" : "s"); /* FALL THROUGH */ fault: ioerr(db, FALSE); errno = EFAULT; /* Data corrupted somehow (.pag or .dat file) */ return -1; }