1 #define _XOPEN_SOURCE 500
3 static ssize_t pwrite_check(int fd, const void *buf, size_t count, off_t offset);
4 static ssize_t write_check(int fd, const void *buf, size_t count);
5 static int fcntl_check(int fd, int cmd, ... /* arg */ );
6 static int ftruncate_check(int fd, off_t length);
8 #define pwrite pwrite_check
9 #define write write_check
10 #define fcntl fcntl_check
11 #define ftruncate ftruncate_check
13 #include <ccan/tdb/tdb.h>
14 #include <ccan/tdb/io.c>
15 #include <ccan/tdb/tdb.c>
16 #include <ccan/tdb/lock.c>
17 #include <ccan/tdb/freelist.c>
18 #include <ccan/tdb/traverse.c>
19 #include <ccan/tdb/transaction.c>
20 #include <ccan/tdb/error.c>
21 #include <ccan/tdb/open.c>
22 #include <ccan/tdb/check.c>
23 #include <ccan/tap/tap.h>
29 #include "external-transaction.h"
36 static bool in_transaction;
37 static bool suppress_logging;
38 static int target, current;
39 static jmp_buf jmpbuf;
40 #define TEST_DBNAME "/tmp/test7.tdb"
42 /* We save the locks so we can reaquire them. */
49 static struct lock *locks;
51 static void taplog(struct tdb_context *tdb,
52 enum tdb_debug_level level,
62 vsprintf(line, fmt, ap);
68 static void check_file_contents(int fd)
70 if (current++ == target) {
75 static ssize_t pwrite_check(int fd,
76 const void *buf, size_t count, off_t offset)
81 check_file_contents(fd);
83 ret = pwrite(fd, buf, count, offset);
88 check_file_contents(fd);
92 static ssize_t write_check(int fd, const void *buf, size_t count)
97 check_file_contents(fd);
99 ret = write(fd, buf, count);
104 check_file_contents(fd);
108 extern int fcntl(int fd, int cmd, ... /* arg */ );
110 static int fcntl_check(int fd, int cmd, ... /* arg */ )
116 if (cmd != F_SETLK && cmd != F_SETLKW) {
117 /* This may be totally bogus, but we don't know in general. */
119 arg3 = va_arg(ap, int);
122 return fcntl(fd, cmd, arg3);
126 fl = va_arg(ap, struct flock *);
129 ret = fcntl(fd, cmd, fl);
131 if (fl->l_type == F_UNLCK) {
133 struct lock *old = NULL;
135 for (l = &locks; *l; l = &(*l)->next) {
136 if ((*l)->off == fl->l_start
137 && (*l)->len == fl->l_len) {
145 errx(1, "Unknown lock");
147 struct lock *new = malloc(sizeof *new);
148 new->off = fl->l_start;
149 new->len = fl->l_len;
150 new->type = fl->l_type;
156 if (in_transaction && fl->l_type == F_UNLCK)
157 check_file_contents(fd);
161 static int ftruncate_check(int fd, off_t length)
166 check_file_contents(fd);
168 ret = ftruncate(fd, length);
171 check_file_contents(fd);
175 static bool test_death(enum operation op, struct agent *agent)
177 struct tdb_context *tdb;
179 struct tdb_logging_context logctx = { taplog, NULL };
180 int needed_recovery = 0;
182 current = target = 0;
184 if (setjmp(jmpbuf) != 0) {
185 /* We're partway through. Simulate our death. */
187 in_transaction = false;
189 if (external_agent_operation(agent, NEEDS_RECOVERY_KEEP_OPENED,
193 if (external_agent_operation(agent, op, "") != 1) {
194 diag("Step %u op failed", current);
198 if (external_agent_operation(agent, NEEDS_RECOVERY_KEEP_OPENED,
200 diag("Still needs recovery after step %u", current);
204 if (external_agent_operation(agent, CHECK_KEEP_OPENED, "")
206 diag("Step %u check failed", current);
210 external_agent_operation(agent, CLOSE, "");
211 /* Suppress logging as this tries to use closed fd. */
212 suppress_logging = true;
214 suppress_logging = false;
221 tdb = tdb_open_ex(TEST_DBNAME, 1024, TDB_NOMMAP,
222 O_CREAT|O_TRUNC|O_RDWR, 0600, &logctx, NULL);
224 if (external_agent_operation(agent, KEEP_OPENED, TEST_DBNAME) != 0)
225 errx(1, "Agent failed to open?");
227 if (tdb_transaction_start(tdb) != 0)
230 in_transaction = true;
231 key.dsize = strlen("hi");
232 key.dptr = (void *)"hi";
233 data.dptr = (void *)"world";
234 data.dsize = strlen("world");
236 if (tdb_store(tdb, key, data, TDB_INSERT) != 0)
238 if (tdb_transaction_commit(tdb) != 0)
241 in_transaction = false;
244 diag("Completed %u runs", current);
246 external_agent_operation(agent, CLOSE, "");
248 ok1(needed_recovery);
252 int main(int argc, char *argv[])
254 enum operation ops[] = { FETCH_KEEP_OPENED,
256 TRANSACTION_KEEP_OPENED };
261 agent = prepare_external_agent();
263 err(1, "preparing agent");
265 /* Nice ourselves down: we can't tell the difference between agent
266 * blocking on lock, and agent not scheduled. */
269 for (i = 0; i < sizeof(ops)/sizeof(ops[0]); i++) {
270 diag("Testing %s after death",
271 ops[i] == TRANSACTION_KEEP_OPENED ? "transaction"
272 : ops[i] == FETCH_KEEP_OPENED ? "fetch"
273 : ops[i] == STORE_KEEP_OPENED ? "store"
276 ok1(test_death(ops[i], agent));
279 return exit_status();