Esempio n. 1
0
/*
 * 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;
}
Esempio n. 2
0
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;
}
Esempio n. 3
0
/**
 * Write back cached page to disk.
 * @return TRUE on success.
 */
static bool
writebuf(DBM *db, long oldnum, long idx)
{
	struct lru_cache *cache = db->cache;
	char *pag = cache->arena + OFF_PAG(idx);

	g_assert(idx >= 0 && idx < cache->pages);

	if (!flushpag(db, pag, oldnum))
		return FALSE;

	cache->dirty[idx] = FALSE;
	return TRUE;
}
Esempio n. 4
0
/*
 * 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);
}
Esempio n. 5
0
/**
 * Discard any pending data for cached pages whose block number is greater
 * or equal than the given base block number.
 */
void
lru_discard(DBM *db, long bno)
{
	struct lru_cache *cache = db->cache;
	int n;
	long pages = MIN(cache->pages, cache->next);

	for (n = 0; n < pages; n++) {
		long num = cache->numpag[n];

		if (num >= bno) {
			void *base = cache->arena + OFF_PAG(n);
			cache->dirty[n] = FALSE;
			memset(base, 0, DBM_PBLKSIZ);
		}
	}
}
Esempio n. 6
0
/*
 * all important binary trie traversal
 */
static int
getpage(register DBM *db, register long int hash)
{
	register int hbit;
	register long dbit;
	register long pagb;

	dbit = 0;
	hbit = 0;
	while (dbit < db->maxbno && getdbit(db, dbit))
		dbit = 2 * dbit + ((hash & (1 << hbit++)) ? 2 : 1);

	debug(("dbit: %d...", dbit));

	db->curbit = dbit;
	db->hmask = masks[hbit];

	pagb = hash & db->hmask;
/*
 * see if the block we need is already in memory.
 * note: this lookaside cache has about 10% hit rate.
 */
	if (pagb != db->pagbno) { 
/*
 * note: here, we assume a "hole" is read as 0s.
 * if not, must zero pagbuf first.
 */
		if (lseek(db->pagf, OFF_PAG(pagb), SEEK_SET) < 0
		    || read(db->pagf, db->pagbuf, PBLKSIZ) < 0)
			return 0;
		if (!chkpage(db->pagbuf))
			return 0;
		db->pagbno = pagb;

		debug(("pag read: %d\n", pagb));
	}
	return 1;
}
Esempio n. 7
0
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;
}
Esempio n. 8
0
/**
 * Get the address in the cache of a given page number.
 *
 * @param db		the database
 * @param num		the page number in the DB
 *
 * @return page address if found, NULL if not cached.
 */
char *
lru_cached_page(DBM *db, long num)
{
	struct lru_cache *cache = db->cache;
	void *value;

	g_assert(num >= 0);

	if (
		cache != NULL &&
		g_hash_table_lookup_extended(cache->pagnum,
			ulong_to_pointer(num), NULL, &value)
	) {
		long idx = pointer_to_int(value);

		g_assert(idx >= 0 && idx < cache->pages);
		g_assert(cache->numpag[idx] == num);

		return cache->arena + OFF_PAG(idx);
	}

	return NULL;
}
Esempio n. 9
0
/**
 * Get a suitable buffer in the cache to read a page and set db->pagbuf
 * accordingly.
 *
 * The '`loaded'' parameter, if non-NULL, is set to TRUE if page was already
 * held in the cache, FALSE when it needs to be loaded.
 *
 * @return TRUE if OK, FALSE if we could not allocate a suitable buffer, leaving
 * the old db->pagbuf intact.
 */
gboolean
readbuf(DBM *db, long num, gboolean *loaded)
{
	struct lru_cache *cache = db->cache;
	void *value;
	long idx;
	gboolean good_page;

	g_assert(num >= 0);

	if (
		g_hash_table_lookup_extended(cache->pagnum,
			ulong_to_pointer(num), NULL, &value)
	) {
		hash_list_moveto_head(cache->used, value);
		idx = pointer_to_int(value);

		g_assert(idx >= 0 && idx < cache->pages);
		g_assert(cache->numpag[idx] == num);

		good_page = TRUE;
		cache->rhits++;
	} else {
		idx = getidx(db, num);
		if (-1 == idx)
			return FALSE;	/* Do not update db->pagbuf */

		good_page = FALSE;
		cache->rmisses++;
	}

	db->pagbuf = cache->arena + OFF_PAG(idx);
	if (loaded != NULL)
		*loaded = good_page;

	return TRUE;
}
Esempio n. 10
0
/**
 * 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;
}
Esempio n. 11
0
/*
 * makroom - make room by splitting the overfull page
 * this routine will attempt to make room for SPLTMAX times before
 * giving up.
 */
static int
makroom(register DBM *db, long int hash, int need)
{
	long newp;
	char twin[PBLKSIZ];
#if defined(DOSISH) || defined(WIN32)
	char zer[PBLKSIZ];
	long oldtail;
#endif
	char *pag = db->pagbuf;
	char *New = twin;
	register int smax = SPLTMAX;

	do {
/*
 * split the current page
 */
		(void) splpage(pag, New, db->hmask + 1);
/*
 * address of the new page
 */
		newp = (hash & db->hmask) | (db->hmask + 1);

/*
 * write delay, read avoidance/cache shuffle:
 * select the page for incoming pair: if key is to go to the new page,
 * write out the previous one, and copy the new one over, thus making
 * it the current page. If not, simply write the new page, and we are
 * still looking at the page of interest. current page is not updated
 * here, as sdbm_store will do so, after it inserts the incoming pair.
 */

#if defined(DOSISH) || defined(WIN32)
		/*
		 * Fill hole with 0 if made it.
		 * (hole is NOT read as 0)
		 */
		oldtail = lseek(db->pagf, 0L, SEEK_END);
		memset(zer, 0, PBLKSIZ);
		while (OFF_PAG(newp) > oldtail) {
			if (lseek(db->pagf, 0L, SEEK_END) < 0 ||
			    write(db->pagf, zer, PBLKSIZ) < 0) {

				return 0;
			}
			oldtail += PBLKSIZ;
		}
#endif
		if (hash & (db->hmask + 1)) {
			if (lseek(db->pagf, OFF_PAG(db->pagbno), SEEK_SET) < 0
			    || write(db->pagf, db->pagbuf, PBLKSIZ) < 0)
				return 0;
			db->pagbno = newp;
			(void) memcpy(pag, New, PBLKSIZ);
		}
		else if (lseek(db->pagf, OFF_PAG(newp), SEEK_SET) < 0
			 || write(db->pagf, New, PBLKSIZ) < 0)
			return 0;

		if (!setdbit(db, db->curbit))
			return 0;
/*
 * see if we have enough room now
 */
		if (fitpair(pag, need))
			return 1;
/*
 * try again... update curbit and hmask as getpage would have
 * done. because of our update of the current page, we do not
 * need to read in anything. BUT we have to write the current
 * [deferred] page out, as the window of failure is too great.
 */
		db->curbit = 2 * db->curbit +
			((hash & (db->hmask + 1)) ? 2 : 1);
		db->hmask |= db->hmask + 1;

		if (lseek(db->pagf, OFF_PAG(db->pagbno), SEEK_SET) < 0
		    || write(db->pagf, db->pagbuf, PBLKSIZ) < 0)
			return 0;

	} while (--smax);
/*
 * if we are here, this is real bad news. After SPLTMAX splits,
 * we still cannot fit the key. say goodnight.
 */
#ifdef BADMESS
	(void) write(2, "sdbm: cannot insert after SPLTMAX attempts.\n", 44);
#endif
	return 0;

}
Esempio n. 12
0
/**
 * Cache new page held in memory if there are deferred writes configured.
 * @return TRUE on success.
 */
gboolean
cachepag(DBM *db, char *pag, long num)
{
	struct lru_cache *cache = db->cache;
	void *value;

	g_assert(num >= 0);

	/*
	 * Coming from makroom() where we allocated a new page, starting at "pag".
	 *
	 * Normally the page should not be cached, but it is possible we iterated
	 * over the hash table and traversed the page on disk as a hole, and cached
	 * it during the process.  If present, it must be clean and should hold
	 * no data (or the bitmap forest in the .dir file is corrupted).
	 *
	 * Otherwise, we cache the new page and hold it there if we we can defer
	 * writes, or flush it to disk immediately (without caching it).
	 */

	if (
		g_hash_table_lookup_extended(cache->pagnum,
			ulong_to_pointer(num), NULL, &value)
	) {
		long idx;
		unsigned short *ino;
		unsigned weird = 0;
		char *cpag;

		/*
		 * Do not move the page to the head of the cache list.
		 *
		 * This page should not have been cached (it was supposed to be a
		 * hole up to now) and its being cached now does not constitute usage.
		 */

		idx = pointer_to_int(value);
		g_assert(idx >= 0 && idx < cache->pages);
		g_assert(cache->numpag[idx] == num);

		/*
		 * Not a read hit since we're about to supersede the data
		 */

		cpag = cache->arena + OFF_PAG(idx);
		ino = (unsigned short *) cpag;

		if (ino[0] != 0) {
			weird++;
			g_warning("sdbm: \"%s\": new page #%ld was cached but not empty",
				db->name, num);
		}
		if (cache->dirty[idx]) {
			weird++;
			g_warning("sdbm: \"%s\": new page #%ld was cached and not clean",
				db->name, num);
		}
		if (weird > 0) {
			g_warning("sdbm: \"%s\": previous warning%s indicate possible "
				"corruption in the bitmap forest",
				db->name, 1 == weird ? "" : "s");
		}

		/*
		 * Supersede cached page with new page created by makroom().
		 */

		memmove(cpag, pag, DBM_PBLKSIZ);

		if (cache->write_deferred) {
			cache->dirty[idx] = TRUE;
		} else {
			cache->dirty[idx] = !flushpag(db, pag, num);
		}
		return TRUE;
	} else if (cache->write_deferred) {
		long idx;
		char *cpag;

		idx = getidx(db, num);
		if (-1 == idx)
			return FALSE;

		cpag = cache->arena + OFF_PAG(idx);
		memmove(cpag, pag, DBM_PBLKSIZ);
		cache->dirty[idx] = TRUE;
		return TRUE;
	} else {
		return flushpag(db, pag, num);
	}
}