Beispiel #1
0
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;
}
Beispiel #2
0
/*
  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;
}
Beispiel #3
0
/*
  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;
}
Beispiel #4
0
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);
}
Beispiel #7
0
/*
  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;
}
Beispiel #8
0
/*
  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 *)&num;
		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 *)&num;
		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 *)&num;
		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);
}
Beispiel #11
0
 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;
}