int db_hash_traverse(struct db_hash_context *dh, db_hash_record_parser_fn parser, void *private_data, int *count) { struct db_hash_traverse_state state; int ret; if (dh == NULL) { return EINVAL; } /* Special case, for counting records */ if (parser == NULL) { ret = tdb_traverse_read(dh->db, NULL, NULL); } else { state.parser = parser; state.private_data = private_data; ret = tdb_traverse_read(dh->db, db_hash_traverse_parser, &state); } if (ret == -1) { ret = db_hash_map_tdb_error(dh); } else { if (count != NULL) { *count = ret; } ret = 0; } 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 }; 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 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 restrictions malloc gives you. */ 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; bool in_transaction = (tdb->transaction != NULL); if (tdb->read_only || tdb->traverse_read) { return tdb_traverse_read(tdb, fn, private_data); } if (!in_transaction) { if (tdb_transaction_lock(tdb, F_WRLCK)) { return -1; } } tdb->traverse_write++; ret = tdb_traverse_internal(tdb, fn, private_data, &tl); tdb->traverse_write--; if (!in_transaction) { tdb_transaction_unlock(tdb); } return ret; }
static int db_tdb_traverse_read(struct db_context *db, int (*f)(struct db_record *rec, void *private_data), void *private_data) { struct db_tdb_ctx *db_ctx = talloc_get_type_abort(db->private_data, struct db_tdb_ctx); struct db_tdb_traverse_ctx ctx; ctx.db = db; ctx.f = f; ctx.private_data = private_data; return tdb_traverse_read(db_ctx->wtdb->tdb, db_tdb_traverse_read_func, &ctx); }
int main(int argc, char *argv[]) { struct tdb_context *tdb; TDB_DATA key, data; plan_tests(13); agent = prepare_external_agent(); if (!agent) err(1, "preparing agent"); tdb = tdb_open_ex("run-traverse-in-transaction.tdb", 1024, TDB_CLEAR_IF_FIRST, O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL); ok1(tdb); key.dsize = strlen("hi"); key.dptr = (void *)"hi"; data.dptr = (void *)"world"; data.dsize = strlen("world"); ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0); ok1(external_agent_operation(agent, OPEN, tdb_name(tdb)) == SUCCESS); ok1(tdb_transaction_start(tdb) == 0); ok1(external_agent_operation(agent, TRANSACTION_START, tdb_name(tdb)) == WOULD_HAVE_BLOCKED); tdb_traverse(tdb, traverse, NULL); /* That should *not* release the transaction lock! */ ok1(external_agent_operation(agent, TRANSACTION_START, tdb_name(tdb)) == WOULD_HAVE_BLOCKED); tdb_traverse_read(tdb, traverse, NULL); /* That should *not* release the transaction lock! */ ok1(external_agent_operation(agent, TRANSACTION_START, tdb_name(tdb)) == WOULD_HAVE_BLOCKED); ok1(tdb_transaction_commit(tdb) == 0); /* Now we should be fine. */ ok1(external_agent_operation(agent, TRANSACTION_START, tdb_name(tdb)) == SUCCESS); tdb_close(tdb); return exit_status(); }
static int run_child(const char *filename, int i, int seed, unsigned num_loops, unsigned start) { db = tdb_open_ex(filename, hash_size, TDB_DEFAULT, O_RDWR | O_CREAT, 0600, &log_ctx, NULL); if (!db) { fatal("db open failed"); } srand(seed + i); srandom(seed + i); /* Set global, then we're ready to handle being killed. */ loopnum = start; signal(SIGUSR1, send_count_and_suicide); for (;loopnum<num_loops && error_count == 0;loopnum++) { addrec_db(); } if (error_count == 0) { tdb_traverse_read(db, NULL, NULL); if (always_transaction) { while (in_transaction) { tdb_transaction_cancel(db); in_transaction--; } if (tdb_transaction_start(db) != 0) fatal("tdb_transaction_start failed"); } tdb_traverse(db, traverse_fn, NULL); tdb_traverse(db, traverse_fn, NULL); if (always_transaction) { if (tdb_transaction_commit(db) != 0) fatal("tdb_transaction_commit failed"); } } tdb_close(db); return (error_count < 100 ? error_count : 100); }
/* a write style traverse - needs to get the transaction lock to prevent deadlocks */ 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->methods->tdb_brlock(tdb, TRANSACTION_LOCK, F_WRLCK, F_SETLKW, 0, 1) == -1) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_traverse: failed to get transaction lock\n")); tdb->ecode = TDB_ERR_LOCK; return -1; } ret = tdb_traverse_internal(tdb, fn, private_data, &tl); tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, F_UNLCK, F_SETLKW, 0, 1); 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; }
int main(int argc, char *argv[]) { unsigned int i, j; int num; struct trav_data td; TDB_DATA k, k2; struct tdb_context *tdb; union tdb_attribute seed_attr; int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP, TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT }; seed_attr.base.attr = TDB_ATTRIBUTE_SEED; seed_attr.base.next = &tap_log_attr; seed_attr.seed.seed = 6334326220117065685ULL; plan_tests(sizeof(flags) / sizeof(flags[0]) * (NUM_RECORDS*4 + (NUM_RECORDS-1)*2 + 20) + 1); for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) { tdb = tdb_open("run-traverse.tdb", flags[i], O_RDWR|O_CREAT|O_TRUNC, 0600, &seed_attr); ok1(tdb); if (!tdb) continue; ok1(tdb_firstkey(tdb).dptr == NULL); ok1(tdb_error(tdb) == TDB_SUCCESS); /* One entry... */ k.dptr = (unsigned char *)# k.dsize = sizeof(num); num = 0; ok1(tdb_store(tdb, k, k, TDB_INSERT) == 0); k = tdb_firstkey(tdb); ok1(k.dsize == sizeof(num)); ok1(memcmp(k.dptr, &num, sizeof(num)) == 0); k2 = tdb_nextkey(tdb, k); ok1(k2.dsize == 0 && k2.dptr == NULL); free(k.dptr); /* Two entries. */ k.dptr = (unsigned char *)# k.dsize = sizeof(num); num = 1; ok1(tdb_store(tdb, k, k, TDB_INSERT) == 0); k = tdb_firstkey(tdb); ok1(k.dsize == sizeof(num)); memcpy(&num, k.dptr, sizeof(num)); ok1(num == 0 || num == 1); k2 = tdb_nextkey(tdb, k); ok1(k2.dsize == sizeof(j)); free(k.dptr); memcpy(&j, k2.dptr, sizeof(j)); ok1(j == 0 || j == 1); ok1(j != num); k = tdb_nextkey(tdb, k2); ok1(k.dsize == 0 && k.dptr == NULL); free(k2.dptr); /* Clean up. */ k.dptr = (unsigned char *)# k.dsize = sizeof(num); num = 0; ok1(tdb_delete(tdb, k) == 0); num = 1; ok1(tdb_delete(tdb, k) == 0); /* Now lots of records. */ ok1(store_records(tdb)); td.calls = 0; num = tdb_traverse_read(tdb, trav, &td); ok1(num == NUM_RECORDS); ok1(td.calls == NUM_RECORDS); /* Simple loop should match tdb_traverse_read */ for (j = 0, k = tdb_firstkey(tdb); j < td.calls; j++) { int val; ok1(k.dsize == sizeof(val)); memcpy(&val, k.dptr, k.dsize); ok1(td.records[j] == val); k2 = tdb_nextkey(tdb, k); free(k.dptr); k = k2; } /* But arbitrary orderings should work too. */ for (j = td.calls-1; j > 0; j--) { k.dptr = (unsigned char *)&td.records[j-1]; k.dsize = sizeof(td.records[j-1]); k = tdb_nextkey(tdb, k); ok1(k.dsize == sizeof(td.records[j])); ok1(memcmp(k.dptr, &td.records[j], k.dsize) == 0); free(k.dptr); } /* Even delete should work. */ for (j = 0, k = tdb_firstkey(tdb); k.dptr; j++) { ok1(k.dsize == 4); ok1(tdb_delete(tdb, k) == 0); k2 = tdb_nextkey(tdb, k); free(k.dptr); k = k2; } diag("delete using first/nextkey gave %u of %u records", j, NUM_RECORDS); ok1(j == NUM_RECORDS); tdb_close(tdb); } ok1(tap_log_messages == 0); return exit_status(); }
static void addrec_db(void) { int klen, dlen; char *k, *d; TDB_DATA key, data; klen = 1 + (rand() % KEYLEN); dlen = 1 + (rand() % DATALEN); k = randbuf(klen); d = randbuf(dlen); key.dptr = (unsigned char *)k; key.dsize = klen+1; data.dptr = (unsigned char *)d; data.dsize = dlen+1; #if REOPEN_PROB if (in_transaction == 0 && random() % REOPEN_PROB == 0) { tdb_reopen_all(0); goto next; } #endif #if TRANSACTION_PROB if (in_transaction == 0 && (always_transaction || random() % TRANSACTION_PROB == 0)) { if (tdb_transaction_start(db) != 0) { fatal("tdb_transaction_start failed"); } in_transaction++; goto next; } if (in_transaction && random() % TRANSACTION_PROB == 0) { if (random() % TRANSACTION_PREPARE_PROB == 0) { if (tdb_transaction_prepare_commit(db) != 0) { fatal("tdb_transaction_prepare_commit failed"); } } if (tdb_transaction_commit(db) != 0) { fatal("tdb_transaction_commit failed"); } in_transaction--; goto next; } if (in_transaction && random() % TRANSACTION_PROB == 0) { if (tdb_transaction_cancel(db) != 0) { fatal("tdb_transaction_cancel failed"); } in_transaction--; goto next; } #endif #if DELETE_PROB if (random() % DELETE_PROB == 0) { tdb_delete(db, key); goto next; } #endif #if STORE_PROB if (random() % STORE_PROB == 0) { if (tdb_store(db, key, data, TDB_REPLACE) != 0) { fatal("tdb_store failed"); } goto next; } #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, key); data = tdb_fetch(db, key); if (tdb_store(db, key, data, TDB_REPLACE) != 0) { fatal("tdb_store failed"); } if (data.dptr) free(data.dptr); tdb_chainunlock(db, key); goto next; } #endif #if TRAVERSE_PROB if (random() % TRAVERSE_PROB == 0) { tdb_traverse(db, cull_traverse, NULL); goto next; } #endif #if TRAVERSE_READ_PROB if (random() % TRAVERSE_READ_PROB == 0) { tdb_traverse_read(db, NULL, NULL); goto next; } #endif data = tdb_fetch(db, key); if (data.dptr) free(data.dptr); next: free(k); free(d); }
int main(int argc, char * const *argv) { int i, seed = -1; int num_procs = 3; int num_loops = 5000; int hash_size = 2; int c; extern char *optarg; pid_t *pids; struct tdb_logging_context log_ctx; log_ctx.log_fn = tdb_log; while ((c = getopt(argc, argv, "n:l:s:H:h")) != -1) { switch (c) { case 'n': num_procs = strtol(optarg, NULL, 0); break; case 'l': num_loops = strtol(optarg, NULL, 0); break; case 'H': hash_size = strtol(optarg, NULL, 0); break; case 's': seed = strtol(optarg, NULL, 0); break; default: usage(); } } unlink("torture.tdb"); pids = (pid_t *)calloc(sizeof(pid_t), num_procs); pids[0] = getpid(); for (i=0;i<num_procs-1;i++) { if ((pids[i+1]=fork()) == 0) break; } db = tdb_open_ex("torture.tdb", hash_size, TDB_CLEAR_IF_FIRST, O_RDWR | O_CREAT, 0600, &log_ctx, NULL); if (!db) { fatal("db open failed"); } if (seed == -1) { seed = (getpid() + time(NULL)) & 0x7FFFFFFF; } if (i == 0) { printf("testing with %d processes, %d loops, %d hash_size, seed=%d\n", num_procs, num_loops, hash_size, seed); } srand(seed + i); srandom(seed + i); for (i=0;i<num_loops && error_count == 0;i++) { addrec_db(); } if (error_count == 0) { tdb_traverse_read(db, NULL, NULL); tdb_traverse(db, traverse_fn, NULL); tdb_traverse(db, traverse_fn, NULL); } tdb_close(db); if (getpid() != pids[0]) { return error_count; } for (i=1;i<num_procs;i++) { int status, j; pid_t pid; if (error_count != 0) { /* try and stop the test on any failure */ for (j=1;j<num_procs;j++) { if (pids[j] != 0) { kill(pids[j], SIGTERM); } } } pid = waitpid(-1, &status, 0); if (pid == -1) { perror("failed to wait for child\n"); exit(1); } for (j=1;j<num_procs;j++) { if (pids[j] == pid) break; } if (j == num_procs) { printf("unknown child %d exited!?\n", (int)pid); exit(1); } if (WEXITSTATUS(status) != 0) { printf("child %d exited with status %d\n", (int)pid, WEXITSTATUS(status)); error_count++; } pids[j] = 0; } if (error_count == 0) { printf("OK\n"); } return error_count; }