diff options
Diffstat (limited to 'source4/lib/tdb/tdb.c')
-rw-r--r-- | source4/lib/tdb/tdb.c | 91 |
1 files changed, 67 insertions, 24 deletions
diff --git a/source4/lib/tdb/tdb.c b/source4/lib/tdb/tdb.c index 513fbce519..fc210f42d4 100644 --- a/source4/lib/tdb/tdb.c +++ b/source4/lib/tdb/tdb.c @@ -20,6 +20,27 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +/* NOTE: If you use tdbs under valgrind, and in particular if you run + * tdbtorture, you may get spurious "uninitialized value" warnings. I + * think this is because valgrind doesn't understand that the mmap'd + * area may be written to by other processes. Memory can, from the + * point of view of the grinded process, spontaneously become + * initialized. + * + * I can think of a few solutions. [mbp 20030311] + * + * 1 - Write suppressions for Valgrind so that it doesn't complain + * about this. Probably the most reasonable but people need to + * remember to use them. + * + * 2 - Use IO not mmap when running under valgrind. Not so nice. + * + * 3 - Use the special valgrind macros to mark memory as valid at the + * right time. Probably too hard -- the process just doesn't know. + */ + #ifdef STANDALONE #if HAVE_CONFIG_H #include <config.h> @@ -56,6 +77,8 @@ #define TDB_DEAD(r) ((r)->magic == TDB_DEAD_MAGIC) #define TDB_BAD_MAGIC(r) ((r)->magic != TDB_MAGIC && !TDB_DEAD(r)) #define TDB_HASH_TOP(hash) (FREELIST_TOP + (BUCKET(hash)+1)*sizeof(tdb_off)) +#define TDB_DATA_START(hash_size) (TDB_HASH_TOP(hash_size-1) + TDB_SPINLOCK_SIZE(hash_size)) + /* NB assumes there is a local variable called "tdb" that is the * current context, also takes doubly-parenthesized print-style @@ -214,10 +237,15 @@ static int tdb_brlock(TDB_CONTEXT *tdb, tdb_off offset, tdb->fd, offset, rw_type, lck_type)); } /* Was it an alarm timeout ? */ - if (errno == EINTR && palarm_fired && *palarm_fired) + if (errno == EINTR && palarm_fired && *palarm_fired) { + TDB_LOG((tdb, 5, "tdb_brlock timed out (fd=%d) at offset %d rw_type=%d lck_type=%d\n", + tdb->fd, offset, rw_type, lck_type)); return TDB_ERRCODE(TDB_ERR_LOCK_TIMEOUT, -1); + } /* Otherwise - generic lock error. */ /* errno set by fcntl */ + TDB_LOG((tdb, 5, "tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d: %s\n", + tdb->fd, offset, rw_type, lck_type, strerror(errno))); return TDB_ERRCODE(TDB_ERR_LOCK, -1); } return 0; @@ -642,10 +670,10 @@ static int tdb_free(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec) left: /* Look left */ left = offset - sizeof(tdb_off); - if (left > TDB_HASH_TOP(tdb->header.hash_size-1)) { + if (left > TDB_DATA_START(tdb->header.hash_size)) { struct list_struct l; tdb_off leftsize; - + /* Read in tailer and jump back to header */ if (ofs_read(tdb, left, &leftsize) == -1) { TDB_LOG((tdb, 0, "tdb_free: left offset read failed at %u\n", left)); @@ -994,12 +1022,11 @@ static int tdb_keylocked(TDB_CONTEXT *tdb, u32 hash) } /* As tdb_find, but if you succeed, keep the lock */ -static tdb_off tdb_find_lock(TDB_CONTEXT *tdb, TDB_DATA key, int locktype, +static tdb_off tdb_find_lock_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash, int locktype, struct list_struct *rec) { - u32 hash, rec_ptr; + u32 rec_ptr; - hash = tdb_hash(&key); if (!tdb_keylocked(tdb, hash)) return 0; if (tdb_lock(tdb, BUCKET(hash), locktype) == -1) @@ -1040,13 +1067,13 @@ const char *tdb_errorstr(TDB_CONTEXT *tdb) on failure return -1. */ -static int tdb_update(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf) +static int tdb_update_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash, TDB_DATA dbuf) { struct list_struct rec; tdb_off rec_ptr; /* find entry */ - if (!(rec_ptr = tdb_find(tdb, key, tdb_hash(&key), &rec))) + if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) return -1; /* must be long enough key, data and tailer */ @@ -1080,9 +1107,11 @@ TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key) tdb_off rec_ptr; struct list_struct rec; TDB_DATA ret; + u32 hash; /* find which hash bucket it is in */ - if (!(rec_ptr = tdb_find_lock(tdb,key,F_RDLCK,&rec))) + hash = tdb_hash(&key); + if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec))) return tdb_null; if (rec.data_len) @@ -1101,16 +1130,22 @@ TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key) this doesn't match the conventions in the rest of this module, but is compatible with gdbm */ -int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key) +static int tdb_exists_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash) { struct list_struct rec; - if (tdb_find_lock(tdb, key, F_RDLCK, &rec) == 0) + if (tdb_find_lock_hash(tdb, key, hash, F_RDLCK, &rec) == 0) return 0; tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK); return 1; } +int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key) +{ + u32 hash = tdb_hash(&key); + return tdb_exists_hash(tdb, key, hash); +} + /* record lock stops delete underneath */ static int lock_record(TDB_CONTEXT *tdb, tdb_off off) { @@ -1236,7 +1271,8 @@ static int tdb_next_lock(TDB_CONTEXT *tdb, struct tdb_traverse_lock *tlock, /* Try to clean dead ones from old traverses */ current = tlock->off; tlock->off = rec->next; - if (do_delete(tdb, current, rec) != 0) + if (!tdb->read_only && + do_delete(tdb, current, rec) != 0) goto fail; } tdb_unlock(tdb, tlock->hash, F_WRLCK); @@ -1366,7 +1402,7 @@ TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA oldkey) if (!tdb->travlocks.off) { /* No previous element: do normal find, and lock record */ - tdb->travlocks.off = tdb_find_lock(tdb, oldkey, F_WRLCK, &rec); + tdb->travlocks.off = tdb_find_lock_hash(tdb, oldkey, tdb_hash(&oldkey), F_WRLCK, &rec); if (!tdb->travlocks.off) return tdb_null; tdb->travlocks.hash = BUCKET(rec.full_hash); @@ -1394,13 +1430,13 @@ TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA oldkey) } /* delete an entry in the database given a key */ -int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key) +static int tdb_delete_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash) { tdb_off rec_ptr; struct list_struct rec; int ret; - if (!(rec_ptr = tdb_find_lock(tdb, key, F_WRLCK, &rec))) + if (!(rec_ptr = tdb_find_lock_hash(tdb, key, hash, F_WRLCK, &rec))) return -1; ret = do_delete(tdb, rec_ptr, &rec); if (tdb_unlock(tdb, BUCKET(rec.full_hash), F_WRLCK) != 0) @@ -1408,6 +1444,12 @@ int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key) return ret; } +int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key) +{ + u32 hash = tdb_hash(&key); + return tdb_delete_hash(tdb, key, hash); +} + /* store an element in the database, replacing any existing element with the same key @@ -1430,13 +1472,13 @@ int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag) /* check for it existing, on insert. */ if (flag == TDB_INSERT) { - if (tdb_exists(tdb, key)) { + if (tdb_exists_hash(tdb, key, hash)) { tdb->ecode = TDB_ERR_EXISTS; goto fail; } } else { /* first try in-place update, on modify or replace. */ - if (tdb_update(tdb, key, dbuf) == 0) + if (tdb_update_hash(tdb, key, hash, dbuf) == 0) goto out; if (flag == TDB_MODIFY && tdb->ecode == TDB_ERR_NOEXIST) goto fail; @@ -1448,7 +1490,7 @@ int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag) care. Doing this first reduces fragmentation, and avoids coalescing with `allocated' block before it's updated. */ if (flag != TDB_INSERT) - tdb_delete(tdb, key); + tdb_delete_hash(tdb, key, hash); /* Copy key+value *before* allocating free space in case malloc fails and we are left with a dead spot in the tdb. */ @@ -1497,13 +1539,13 @@ fail: 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) +static int tdb_append_inplace(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash, 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))) + if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) return -1; /* Append of 0 is always ok. */ @@ -1545,7 +1587,7 @@ int tdb_append(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA new_dbuf) return -1; /* first try in-place. */ - if (tdb_append_inplace(tdb, key, new_dbuf) == 0) + if (tdb_append_inplace(tdb, key, hash, new_dbuf) == 0) goto out; /* reset the error code potentially set by the tdb_append_inplace() */ @@ -1588,7 +1630,7 @@ int tdb_append(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA new_dbuf) care. Doing this first reduces fragmentation, and avoids coalescing with `allocated' block before it's updated. */ - tdb_delete(tdb, key); + tdb_delete_hash(tdb, key, hash); if (!(rec_ptr = tdb_allocate(tdb, key.dsize + new_data_size, &rec))) goto fail; @@ -1728,8 +1770,7 @@ TDB_CONTEXT *tdb_open_ex(const char *name, int hash_size, int tdb_flags, if (read(tdb->fd, &tdb->header, sizeof(tdb->header)) != sizeof(tdb->header) || strcmp(tdb->header.magic_food, TDB_MAGIC_FOOD) != 0 - || tdb->header.version != TDB_VERSION - || (tdb->header.hash_size != hash_size + || (tdb->header.version != TDB_VERSION && !(rev = (tdb->header.version==TDB_BYTEREV(TDB_VERSION))))) { /* its not a valid database - possibly initialise it */ if (!(open_flags & O_CREAT) || tdb_new_database(tdb, hash_size) == -1) { @@ -1934,6 +1975,8 @@ int tdb_lockkeys(TDB_CONTEXT *tdb, u32 number, TDB_DATA keys[]) void tdb_unlockkeys(TDB_CONTEXT *tdb) { u32 i; + if (!tdb->lockedkeys) + return; for (i = 0; i < tdb->lockedkeys[0]; i++) tdb_unlock(tdb, tdb->lockedkeys[i+1], F_WRLCK); SAFE_FREE(tdb->lockedkeys); |