mirror of
https://github.com/samba-team/samba.git
synced 2024-12-25 23:21:54 +03:00
Added tdb_append() call. Efficiently adds to an entry. Used by new messaging
code. Also added torture tests for it. Jeremy.
This commit is contained in:
parent
04243e39cf
commit
b515525a06
149
source/tdb/tdb.c
149
source/tdb/tdb.c
@ -4,7 +4,7 @@
|
||||
Copyright (C) Andrew Tridgell 1999-2000
|
||||
Copyright (C) Luke Kenneth Casson Leighton 2000
|
||||
Copyright (C) Paul `Rusty' Russell 2000
|
||||
Copyright (C) Jeremy Allison 2000
|
||||
Copyright (C) Jeremy Allison 2000-2003
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -1018,37 +1018,35 @@ const char *tdb_errorstr(TDB_CONTEXT *tdb)
|
||||
|
||||
/* update an entry in place - this only works if the new data size
|
||||
is <= the old data size and the key exists.
|
||||
on failure return -1
|
||||
on failure return -1.
|
||||
*/
|
||||
|
||||
static int tdb_update(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf)
|
||||
{
|
||||
struct list_struct rec;
|
||||
tdb_off rec_ptr;
|
||||
int ret = -1;
|
||||
|
||||
/* find entry */
|
||||
if (!(rec_ptr = tdb_find_lock(tdb, key, F_WRLCK, &rec)))
|
||||
if (!(rec_ptr = tdb_find(tdb, key, tdb_hash(&key), &rec)))
|
||||
return -1;
|
||||
|
||||
/* must be long enough key, data and tailer */
|
||||
if (rec.rec_len < key.dsize + dbuf.dsize + sizeof(tdb_off)) {
|
||||
tdb->ecode = TDB_SUCCESS; /* Not really an error */
|
||||
goto out;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len,
|
||||
dbuf.dptr, dbuf.dsize) == -1)
|
||||
goto out;
|
||||
return -1;
|
||||
|
||||
if (dbuf.dsize != rec.data_len) {
|
||||
/* update size */
|
||||
rec.data_len = dbuf.dsize;
|
||||
ret = rec_write(tdb, rec_ptr, &rec);
|
||||
} else
|
||||
ret = 0;
|
||||
out:
|
||||
tdb_unlock(tdb, BUCKET(rec.full_hash), F_WRLCK);
|
||||
return ret;
|
||||
return rec_write(tdb, rec_ptr, &rec);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* find an entry in the database given a key */
|
||||
@ -1476,6 +1474,133 @@ fail:
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Attempt to append data to an entry in place - this only works if the new data size
|
||||
is <= the old data size and the key exists.
|
||||
on failure return -1. Record must be locked before calling.
|
||||
*/
|
||||
static int tdb_append_inplace(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA new_dbuf)
|
||||
{
|
||||
struct list_struct rec;
|
||||
tdb_off rec_ptr;
|
||||
|
||||
/* find entry */
|
||||
if (!(rec_ptr = tdb_find(tdb, key, tdb_hash(&key), &rec)))
|
||||
return -1;
|
||||
|
||||
/* Append of 0 is always ok. */
|
||||
if (new_dbuf.dsize == 0)
|
||||
return 0;
|
||||
|
||||
/* must be long enough for key, old data + new data and tailer */
|
||||
if (rec.rec_len < key.dsize + rec.data_len + new_dbuf.dsize + sizeof(tdb_off)) {
|
||||
/* No room. */
|
||||
tdb->ecode = TDB_SUCCESS; /* Not really an error */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len + rec.data_len,
|
||||
new_dbuf.dptr, new_dbuf.dsize) == -1)
|
||||
return -1;
|
||||
|
||||
/* update size */
|
||||
rec.data_len += new_dbuf.dsize;
|
||||
return rec_write(tdb, rec_ptr, &rec);
|
||||
}
|
||||
|
||||
/* Append to an entry. Create if not exist. */
|
||||
|
||||
int tdb_append(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA new_dbuf)
|
||||
{
|
||||
struct list_struct rec;
|
||||
u32 hash;
|
||||
tdb_off rec_ptr;
|
||||
char *p = NULL;
|
||||
int ret = 0;
|
||||
size_t new_data_size = 0;
|
||||
|
||||
/* find which hash bucket it is in */
|
||||
hash = tdb_hash(&key);
|
||||
if (!tdb_keylocked(tdb, hash))
|
||||
return -1;
|
||||
if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
|
||||
return -1;
|
||||
|
||||
/* first try in-place. */
|
||||
if (tdb_append_inplace(tdb, key, new_dbuf) == 0)
|
||||
goto out;
|
||||
|
||||
/* reset the error code potentially set by the tdb_append_inplace() */
|
||||
tdb->ecode = TDB_SUCCESS;
|
||||
|
||||
/* find entry */
|
||||
if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) {
|
||||
if (tdb->ecode != TDB_ERR_NOEXIST)
|
||||
goto fail;
|
||||
|
||||
/* Not found - create. */
|
||||
|
||||
ret = tdb_store(tdb, key, new_dbuf, TDB_INSERT);
|
||||
goto out;
|
||||
}
|
||||
|
||||
new_data_size = rec.data_len + new_dbuf.dsize;
|
||||
|
||||
/* Copy key+old_value+value *before* allocating free space in case malloc
|
||||
fails and we are left with a dead spot in the tdb. */
|
||||
|
||||
if (!(p = (char *)malloc(key.dsize + new_data_size))) {
|
||||
tdb->ecode = TDB_ERR_OOM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Copy the key in place. */
|
||||
memcpy(p, key.dptr, key.dsize);
|
||||
|
||||
/* Now read the old data into place. */
|
||||
if (rec.data_len &&
|
||||
tdb_read(tdb, rec_ptr + sizeof(rec) + rec.key_len, p + key.dsize, rec.data_len, 0) == -1)
|
||||
goto fail;
|
||||
|
||||
/* Finally append the new data. */
|
||||
if (new_dbuf.dsize)
|
||||
memcpy(p+key.dsize+rec.data_len, new_dbuf.dptr, new_dbuf.dsize);
|
||||
|
||||
/* delete any existing record - if it doesn't exist we don't
|
||||
care. Doing this first reduces fragmentation, and avoids
|
||||
coalescing with `allocated' block before it's updated. */
|
||||
|
||||
tdb_delete(tdb, key);
|
||||
|
||||
if (!(rec_ptr = tdb_allocate(tdb, key.dsize + new_data_size, &rec)))
|
||||
goto fail;
|
||||
|
||||
/* Read hash top into next ptr */
|
||||
if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1)
|
||||
goto fail;
|
||||
|
||||
rec.key_len = key.dsize;
|
||||
rec.data_len = new_data_size;
|
||||
rec.full_hash = hash;
|
||||
rec.magic = TDB_MAGIC;
|
||||
|
||||
/* write out and point the top of the hash chain at it */
|
||||
if (rec_write(tdb, rec_ptr, &rec) == -1
|
||||
|| tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+new_data_size)==-1
|
||||
|| ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) {
|
||||
/* Need to tdb_unallocate() here */
|
||||
goto fail;
|
||||
}
|
||||
|
||||
out:
|
||||
SAFE_FREE(p);
|
||||
tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int tdb_already_open(dev_t device,
|
||||
ino_t ino)
|
||||
{
|
||||
|
@ -115,6 +115,7 @@ const char *tdb_errorstr(TDB_CONTEXT *tdb);
|
||||
TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key);
|
||||
int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key);
|
||||
int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
|
||||
int tdb_append(TDB_CONTEXT *tdb, TDB_DATA key);
|
||||
int tdb_close(TDB_CONTEXT *tdb);
|
||||
TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb);
|
||||
TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key);
|
||||
|
@ -21,6 +21,7 @@
|
||||
#define REOPEN_PROB 30
|
||||
#define DELETE_PROB 8
|
||||
#define STORE_PROB 4
|
||||
#define APPEND_PROB 6
|
||||
#define LOCKSTORE_PROB 0
|
||||
#define TRAVERSE_PROB 20
|
||||
#define CULL_PROB 100
|
||||
@ -122,6 +123,15 @@ static void addrec_db(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if APPEND_PROB
|
||||
if (random() % APPEND_PROB == 0) {
|
||||
if (tdb_append(db, key, data) != 0) {
|
||||
fatal("tdb_append failed");
|
||||
}
|
||||
goto next;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LOCKSTORE_PROB
|
||||
if (random() % LOCKSTORE_PROB == 0) {
|
||||
tdb_chainlock(db, lockkey);
|
||||
|
Loading…
Reference in New Issue
Block a user