/* a write style traverse - needs to get the transaction lock to prevent deadlocks WARNING: The data buffer given to the callback fn does NOT meet the alignment guarantees malloc gives you. */ _PUBLIC_ int tdb_traverse(struct tdb_context *tdb, tdb_traverse_func fn, void *private_data) { struct tdb_traverse_lock tl = { NULL, 0, 0, F_WRLCK }; enum tdb_lock_flags lock_flags; int ret; if (tdb->read_only || tdb->traverse_read) { return tdb_traverse_read(tdb, fn, private_data); } lock_flags = TDB_LOCK_WAIT; if (tdb->allrecord_lock.count != 0) { /* * This avoids a deadlock between tdb_lockall() and * tdb_traverse(). See * https://bugzilla.samba.org/show_bug.cgi?id=11381 */ lock_flags = TDB_LOCK_NOWAIT; } if (tdb_transaction_lock(tdb, F_WRLCK, lock_flags)) { return -1; } tdb->traverse_write++; tdb_trace(tdb, "tdb_traverse_start"); ret = tdb_traverse_internal(tdb, fn, private_data, &tl); tdb->traverse_write--; tdb_transaction_unlock(tdb, F_WRLCK); return ret; }
/* a read style traverse - temporarily marks each record read only */ _PUBLIC_ int tdb_traverse_read(struct tdb_context *tdb, tdb_traverse_func fn, void *private_data) { struct tdb_traverse_lock tl = { NULL, 0, 0, F_RDLCK }; int ret; tdb->traverse_read++; tdb_trace(tdb, "tdb_traverse_read_start"); ret = tdb_traverse_internal(tdb, fn, private_data, &tl); tdb->traverse_read--; return ret; }
/* a read style traverse - temporarily marks the db read only */ _PUBLIC_ int tdb_traverse_read(struct tdb_context *tdb, tdb_traverse_func fn, void *private_data) { struct tdb_traverse_lock tl = { NULL, 0, 0, F_RDLCK }; int ret; /* we need to get a read lock on the transaction lock here to cope with the lock ordering semantics of solaris10 */ if (tdb_transaction_lock(tdb, F_RDLCK, TDB_LOCK_WAIT)) { return -1; } tdb->traverse_read++; tdb_trace(tdb, "tdb_traverse_read_start"); ret = tdb_traverse_internal(tdb, fn, private_data, &tl); tdb->traverse_read--; tdb_transaction_unlock(tdb, F_RDLCK); return ret; }
/* a write style traverse - needs to get the transaction lock to prevent deadlocks WARNING: The data buffer given to the callback fn does NOT meet the alignment guarantees malloc gives you. */ _PUBLIC_ int tdb_traverse(struct tdb_context *tdb, tdb_traverse_func fn, void *private_data) { struct tdb_traverse_lock tl = { NULL, 0, 0, F_WRLCK }; int ret; if (tdb->read_only || tdb->traverse_read) { return tdb_traverse_read(tdb, fn, private_data); } if (tdb_transaction_lock(tdb, F_WRLCK, TDB_LOCK_WAIT)) { return -1; } tdb->traverse_write++; tdb_trace(tdb, "tdb_traverse_start"); ret = tdb_traverse_internal(tdb, fn, private_data, &tl); tdb->traverse_write--; tdb_transaction_unlock(tdb, F_WRLCK); return ret; }
/* traverse the entire database - calling fn(tdb, key, data) on each element. return -1 on error or the record count traversed if fn is NULL then it is not called a non-zero return value from fn() indicates that the traversal should stop */ static int tdb_traverse_internal(struct tdb_context *tdb, tdb_traverse_func fn, void *private_data, struct tdb_traverse_lock *tl) { TDB_DATA key, dbuf; struct tdb_record rec; int ret = 0, count = 0; tdb_off_t off; size_t recbuf_len; recbuf_len = 4096; key.dptr = malloc(recbuf_len); if (key.dptr == NULL) { return -1; } /* This was in the initialization, above, but the IRIX compiler * did not like it. crh */ tl->next = tdb->travlocks.next; /* fcntl locks don't stack: beware traverse inside traverse */ tdb->travlocks.next = tl; /* tdb_next_lock places locks on the record returned, and its chain */ while ((off = tdb_next_lock(tdb, tl, &rec)) != 0) { tdb_len_t full_len; int nread; if (off == TDB_NEXT_LOCK_ERR) { ret = -1; goto out; } full_len = rec.key_len + rec.data_len; if (full_len > recbuf_len) { recbuf_len = full_len; /* * No realloc, we don't need the old data and thus can * do without the memcpy */ free(key.dptr); key.dptr = malloc(recbuf_len); if (key.dptr == NULL) { ret = -1; if (tdb_unlock(tdb, tl->list, tl->lock_rw) != 0) { goto out; } if (tdb_unlock_record(tdb, tl->off) != 0) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_traverse: malloc " "failed and unlock_record " "failed!\n")); } goto out; } } count++; /* now read the full record */ nread = tdb->methods->tdb_read(tdb, tl->off + sizeof(rec), key.dptr, full_len, 0); if (nread == -1) { ret = -1; if (tdb_unlock(tdb, tl->list, tl->lock_rw) != 0) goto out; if (tdb_unlock_record(tdb, tl->off) != 0) TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_traverse: key.dptr == NULL and unlock_record failed!\n")); goto out; } key.dsize = rec.key_len; dbuf.dptr = key.dptr + rec.key_len; dbuf.dsize = rec.data_len; tdb_trace_1rec_retrec(tdb, "traverse", key, dbuf); /* Drop chain lock, call out */ if (tdb_unlock(tdb, tl->list, tl->lock_rw) != 0) { ret = -1; goto out; } if (fn && fn(tdb, key, dbuf, private_data)) { /* They want us to terminate traversal */ tdb_trace_ret(tdb, "tdb_traverse_end", count); if (tdb_unlock_record(tdb, tl->off) != 0) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_traverse: unlock_record failed!\n"));; ret = -1; } goto out; } } tdb_trace(tdb, "tdb_traverse_end"); out: SAFE_FREE(key.dptr); tdb->travlocks.next = tl->next; if (ret < 0) return -1; else return count; }
/* unlock entire database with read lock */ _PUBLIC_ int tdb_unlockall_read(struct tdb_context *tdb) { tdb_trace(tdb, "tdb_unlockall_read"); return tdb_allrecord_unlock(tdb, F_RDLCK, false); }
/* lock entire database with read lock */ _PUBLIC_ int tdb_lockall_read(struct tdb_context *tdb) { tdb_trace(tdb, "tdb_lockall_read"); return tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_WAIT, false); }
/* unlock entire database with write lock - unmark only */ _PUBLIC_ int tdb_lockall_unmark(struct tdb_context *tdb) { tdb_trace(tdb, "tdb_lockall_unmark"); return tdb_allrecord_unlock(tdb, F_WRLCK, true); }
/* lock entire database with write lock - mark only */ _PUBLIC_ int tdb_lockall_mark(struct tdb_context *tdb) { tdb_trace(tdb, "tdb_lockall_mark"); return tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_MARK_ONLY, false); }
/* unlock entire database with read lock */ int tdb_unlockall_read(struct tdb_context *tdb) { tdb_trace(tdb, "tdb_unlockall_read"); return _tdb_unlockall(tdb, F_RDLCK); }
/* lock entire database with read lock */ int tdb_lockall_read(struct tdb_context *tdb) { tdb_trace(tdb, "tdb_lockall_read"); return _tdb_lockall(tdb, F_RDLCK, F_SETLKW); }
/* unlock entire database with write lock */ int tdb_unlockall(struct tdb_context *tdb) { tdb_trace(tdb, "tdb_unlockall"); return _tdb_unlockall(tdb, F_WRLCK); }
/* unlock entire database with write lock - unmark only */ int tdb_lockall_unmark(struct tdb_context *tdb) { tdb_trace(tdb, "tdb_lockall_unmark"); return _tdb_unlockall(tdb, F_WRLCK | TDB_MARK_LOCK); }
/* lock entire database with write lock */ int tdb_lockall(struct tdb_context *tdb) { tdb_trace(tdb, "tdb_lockall"); return _tdb_lockall(tdb, F_WRLCK, F_SETLKW); }
/* traverse the entire database - calling fn(tdb, key, data) on each element. return -1 on error or the record count traversed if fn is NULL then it is not called a non-zero return value from fn() indicates that the traversal should stop */ static int tdb_traverse_internal(struct tdb_context *tdb, tdb_traverse_func fn, void *private_data, struct tdb_traverse_lock *tl) { TDB_DATA key, dbuf; struct tdb_record rec; int ret = 0, count = 0; tdb_off_t off; /* This was in the initialization, above, but the IRIX compiler * did not like it. crh */ tl->next = tdb->travlocks.next; /* fcntl locks don't stack: beware traverse inside traverse */ tdb->travlocks.next = tl; /* tdb_next_lock places locks on the record returned, and its chain */ while ((off = tdb_next_lock(tdb, tl, &rec)) != 0) { if (off == TDB_NEXT_LOCK_ERR) { ret = -1; goto out; } count++; /* now read the full record */ key.dptr = tdb_alloc_read(tdb, tl->off + sizeof(rec), rec.key_len + rec.data_len); if (!key.dptr) { ret = -1; if (tdb_unlock(tdb, tl->hash, tl->lock_rw) != 0) goto out; if (tdb_unlock_record(tdb, tl->off) != 0) TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_traverse: key.dptr == NULL and unlock_record failed!\n")); goto out; } key.dsize = rec.key_len; dbuf.dptr = key.dptr + rec.key_len; dbuf.dsize = rec.data_len; tdb_trace_1rec_retrec(tdb, "traverse", key, dbuf); /* Drop chain lock, call out */ if (tdb_unlock(tdb, tl->hash, tl->lock_rw) != 0) { ret = -1; SAFE_FREE(key.dptr); goto out; } if (fn && fn(tdb, key, dbuf, private_data)) { /* They want us to terminate traversal */ tdb_trace_ret(tdb, "tdb_traverse_end", count); if (tdb_unlock_record(tdb, tl->off) != 0) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_traverse: unlock_record failed!\n"));; ret = -1; } SAFE_FREE(key.dptr); goto out; } SAFE_FREE(key.dptr); } tdb_trace(tdb, "tdb_traverse_end"); out: tdb->travlocks.next = tl->next; if (ret < 0) return -1; else return count; }