- for (i = 0; lines[i]; i++) {
- char **words = strsplit(lines, lines[i], " ", NULL);
- if (!tdb && !streq(words[0], "tdb_open"))
- errx(1, "Line %u is not tdb_open", i+1);
-
- if (streq(words[0], "tdb_open")) {
- if (tdb)
- errx(1, "Line %u: tdb_open again?", i+1);
- tdb = tdb_open_ex(argv[2], atoi(words[2]),
- strtoul(words[3], NULL, 0),
- strtoul(words[4], NULL, 0), 0600,
- NULL, hash_key);
- if (!tdb)
- err(1, "Opening tdb %s", argv[2]);
- } else if (streq(words[0], "tdb_lockall")) {
- add_op(&op, i, OP_TDB_LOCKALL, NULL, NULL, 0);
- } else if (streq(words[0], "tdb_lockall_mark")) {
- add_op(&op, i, OP_TDB_LOCKALL_MARK, NULL, NULL, 0);
- } else if (streq(words[0], "tdb_lockall_unmark")) {
- add_op(&op, i, OP_TDB_LOCKALL_UNMARK, NULL, NULL, 0);
- } else if (streq(words[0], "tdb_lockall_nonblock")) {
- add_op(&op, i, OP_TDB_LOCKALL_NONBLOCK, NULL, NULL,
- atoi(words[1]));
- } else if (streq(words[0], "tdb_unlockall")) {
- add_op(&op, i, OP_TDB_UNLOCKALL, NULL, NULL, 0);
- } else if (streq(words[0], "tdb_lockall_read")) {
- add_op(&op, i, OP_TDB_LOCKALL_READ, NULL, NULL, 0);
- } else if (streq(words[0], "tdb_lockall_read_nonblock")) {
- add_op(&op, i, OP_TDB_LOCKALL_READ_NONBLOCK, NULL, NULL,
- atoi(words[1]));
- } else if (streq(words[0], "tdb_unlockall_read\n")) {
- add_op(&op, i, OP_TDB_UNLOCKALL_READ, NULL, NULL, 0);
- } else if (streq(words[0], "tdb_chainlock")) {
- add_op(&op, i, OP_TDB_CHAINLOCK, words[1], NULL, 0);
- } else if (streq(words[0], "tdb_chainlock_nonblock")) {
- add_op(&op, i, OP_TDB_CHAINLOCK_NONBLOCK,
- words[1], NULL, atoi(words[3]));
- } else if (streq(words[0], "tdb_chainlock_mark")) {
- add_op(&op, i, OP_TDB_CHAINLOCK_MARK, words[1], NULL,
- 0);
- } else if (streq(words[0], "tdb_chainlock_unmark")) {
- add_op(&op, i, OP_TDB_CHAINLOCK_UNMARK, words[1], NULL,
- 0);
- } else if (streq(words[0], "tdb_chainunlock")) {
- add_op(&op, i, OP_TDB_CHAINUNLOCK, words[1], NULL, 0);
- } else if (streq(words[0], "tdb_chainlock_read")) {
- add_op(&op, i, OP_TDB_CHAINLOCK_READ, words[1],
- NULL, 0);
- } else if (streq(words[0], "tdb_chainunlock_read")) {
- add_op(&op, i, OP_TDB_CHAINUNLOCK_READ, words[1],
- NULL, 0);
- } else if (streq(words[0], "tdb_close")) {
- add_op(&op, i, OP_TDB_CLOSE, NULL, NULL, 0);
- } else if (streq(words[0], "tdb_increment_seqnum_nonblock")) {
- add_op(&op, i, OP_TDB_INCREMENT_SEQNUM_NONBLOCK,
- NULL, NULL, 0);
- } else if (streq(words[0], "tdb_fetch")) {
- if (streq(words[3], "ENOENT"))
- add_op(&op, i, OP_TDB_FETCH, words[1], NULL,
- -TDB_ERR_NOEXIST);
- else
- add_op(&op, i, OP_TDB_FETCH, words[1], words[3],
- 0);
- } else if (streq(words[0], "tdb_parse_record")) {
- if (streq(words[3], "ENOENT"))
- add_op(&op, i, OP_TDB_PARSE_RECORD,
- words[1], NULL, -TDB_ERR_NOEXIST);
- else
- add_op(&op, i, OP_TDB_PARSE_RECORD,
- words[1], NULL, atoi(words[3]));
- } else if (streq(words[0], "tdb_exists")) {
- add_op(&op, i, OP_TDB_EXISTS, words[1], NULL,
- atoi(words[3]));
- } else if (streq(words[0], "tdb_delete")) {
- add_op(&op, i, OP_TDB_DELETE, words[1], NULL,
- streq(words[3], "ENOENT")
- ? -TDB_ERR_NOEXIST : 0);
- } else if (streq(words[0], "tdb_store")) {
- struct op *new;
-
- if (streq(words[5], "EEXIST"))
- new = add_op(&op, i, OP_TDB_STORE, words[2],
- words[3], -TDB_ERR_EXISTS);
- else if (streq(words[5], "ENOENT"))
- new = add_op(&op, i, OP_TDB_STORE, words[2],
- words[3], -TDB_ERR_NOEXIST);
- else
- new = add_op(&op, i, OP_TDB_STORE, words[2],
- words[3], 0);
- if (streq(words[1], "insert"))
- new->flag = TDB_INSERT;
- else if (streq(words[1], "modify"))
- new->flag = TDB_MODIFY;
- else if (streq(words[1], "normal"))
- new->flag = 0;
- else
- errx(1, "Line %u: invalid tdb_store", i+1);
- } else if (streq(words[0], "tdb_append")) {
- add_op(&op, i, OP_TDB_APPEND, words[1], words[2], 0);
- } else if (streq(words[0], "tdb_get_seqnum")) {
- add_op(&op, i, OP_TDB_GET_SEQNUM, NULL, NULL,
- atoi(words[2]));
- } else if (streq(words[0], "tdb_wipe_all")) {
- add_op(&op, i, OP_TDB_WIPE_ALL, NULL, NULL, 0);
- } else if (streq(words[0], "tdb_transaction_start")) {
- add_op(&op, i, OP_TDB_TRANSACTION_START, NULL, NULL, 0);
- } else if (streq(words[0], "tdb_transaction_cancel")) {
- add_op(&op, i, OP_TDB_TRANSACTION_CANCEL, NULL, NULL,
- 0);
- } else if (streq(words[0], "tdb_transaction_commit")) {
- add_op(&op, i, OP_TDB_TRANSACTION_COMMIT, NULL, NULL,
- 0);
- } else if (streq(words[0], "tdb_traverse_read_start")) {
- add_op(&op, i, OP_TDB_TRAVERSE_READ_START, NULL, NULL,
- 0)->trav = NULL;
- } else if (streq(words[0], "tdb_traverse_start")) {
- add_op(&op, i, OP_TDB_TRAVERSE_START, NULL, NULL, 0)
- ->trav = NULL;
- } else if (streq(words[0], "tdb_traverse_end")) {
- /* = %u means traverse function terminated. */
- if (words[1] == NULL)
- add_op(&op, i, OP_TDB_TRAVERSE_END, NULL, NULL,
- 0);
- else
- add_op(&op, i, OP_TDB_TRAVERSE_END, NULL, NULL,
- atoi(words[2]));
- analyze_traverse(op, i);
- } else if (streq(words[0], "traverse")) {
- add_op(&op, i, OP_TDB_TRAVERSE, words[1], words[2], 0);
- } else if (streq(words[0], "tdb_firstkey")) {
- if (streq(words[2], "ENOENT"))
- add_op(&op, i, OP_TDB_FIRSTKEY, NULL, NULL,
- -TDB_ERR_NOEXIST);
+ words = strsplit(lines, lines[0], " ", NULL);
+ if (!streq(words[1], "tdb_open"))
+ fail(filename, 1, "does not start with tdb_open");
+
+ *hashsize = atoi(words[2]);
+ *tdb_flags = strtoul(words[3], NULL, 0);
+ *open_flags = strtoul(words[4], NULL, 0);
+
+ for (i = 1; lines[i]; i++) {
+ const struct op_table *opt;
+
+ words = strsplit(lines, lines[i], " ", NULL);
+ if (!words[0] || !words[1])
+ fail(filename, i+1, "Expected serial number and op");
+
+ opt = find_keyword(words[1], strlen(words[1]));
+ if (!opt) {
+ if (streq(words[1], "tdb_close")) {
+ if (lines[i+1])
+ fail(filename, i+2,
+ "lines after tdb_close");
+ *num = i;
+ talloc_free(lines);
+ return op;
+ }
+ fail(filename, i+1, "Unknown operation '%s'", words[1]);
+ }
+
+ add_op(filename, &op, i, atoi(words[0]), opt->type);
+ opt->enhance_op(filename, op, i, words);
+ }
+
+ fprintf(stderr, "%s:%u:last operation is not tdb_close: incomplete?",
+ filename, i);
+ talloc_free(lines);
+ *num = i - 1;
+ return op;
+}
+
+/* We remember all the keys we've ever seen, and who has them. */
+struct key_user {
+ unsigned int file;
+ unsigned int op_num;
+};
+
+struct keyinfo {
+ TDB_DATA key;
+ unsigned int num_users;
+ struct key_user *user;
+};
+
+static const TDB_DATA must_not_exist;
+static const TDB_DATA must_exist;
+static const TDB_DATA not_exists_or_empty;
+
+/* NULL means doesn't care if it exists or not, &must_exist means
+ * it must exist but we don't care what, &must_not_exist means it must
+ * not exist, otherwise the data it needs. */
+static const TDB_DATA *needs(const struct op *op)
+{
+ switch (op->op) {
+ /* FIXME: Pull forward deps, since we can deadlock */
+ case OP_TDB_CHAINLOCK:
+ case OP_TDB_CHAINLOCK_NONBLOCK:
+ case OP_TDB_CHAINLOCK_MARK:
+ case OP_TDB_CHAINLOCK_UNMARK:
+ case OP_TDB_CHAINUNLOCK:
+ case OP_TDB_CHAINLOCK_READ:
+ case OP_TDB_CHAINUNLOCK_READ:
+ return NULL;
+
+ case OP_TDB_APPEND:
+ if (op->append.pre.dsize == 0)
+ return ¬_exists_or_empty;
+ return &op->append.pre;
+
+ case OP_TDB_STORE:
+ if (op->flag == TDB_INSERT) {
+ if (op->ret < 0)
+ return &must_exist;