Exemple #1
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;
}
Exemple #2
0
/**
 * 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;
}
Exemple #3
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;
}
Exemple #4
0
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;
}
Exemple #5
0
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;
}
Exemple #6
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;
}
Exemple #7
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);
}
Exemple #8
0
/**
 * 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;
}
Exemple #9
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;
}
Exemple #10
0
/**
 * 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;
}
Exemple #11
0
/**
 * 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;
}