1 | #include "../common/tdb_private.h"
|
---|
2 | #include "lock-tracking.h"
|
---|
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 ftruncate_check(int fd, off_t length);
|
---|
6 |
|
---|
7 | #define pwrite pwrite_check
|
---|
8 | #define write write_check
|
---|
9 | #define fcntl fcntl_with_lockcheck
|
---|
10 | #define ftruncate ftruncate_check
|
---|
11 |
|
---|
12 | #include "../common/io.c"
|
---|
13 | #include "../common/tdb.c"
|
---|
14 | #include "../common/lock.c"
|
---|
15 | #include "../common/freelist.c"
|
---|
16 | #include "../common/traverse.c"
|
---|
17 | #include "../common/transaction.c"
|
---|
18 | #include "../common/error.c"
|
---|
19 | #include "../common/open.c"
|
---|
20 | #include "../common/check.c"
|
---|
21 | #include "../common/hash.c"
|
---|
22 | #include "../common/mutex.c"
|
---|
23 | #include "tap-interface.h"
|
---|
24 | #include <stdlib.h>
|
---|
25 | #include <stdbool.h>
|
---|
26 | #include <stdarg.h>
|
---|
27 | #include <setjmp.h>
|
---|
28 | #include "external-agent.h"
|
---|
29 | #include "logging.h"
|
---|
30 |
|
---|
31 | #undef write
|
---|
32 | #undef pwrite
|
---|
33 | #undef fcntl
|
---|
34 | #undef ftruncate
|
---|
35 |
|
---|
36 | static bool in_transaction;
|
---|
37 | static int target, current;
|
---|
38 | static jmp_buf jmpbuf;
|
---|
39 | #define TEST_DBNAME "run-die-during-transaction.tdb"
|
---|
40 | #define KEY_STRING "helloworld"
|
---|
41 |
|
---|
42 | static void maybe_die(int fd)
|
---|
43 | {
|
---|
44 | if (in_transaction && current++ == target) {
|
---|
45 | longjmp(jmpbuf, 1);
|
---|
46 | }
|
---|
47 | }
|
---|
48 |
|
---|
49 | static ssize_t pwrite_check(int fd,
|
---|
50 | const void *buf, size_t count, off_t offset)
|
---|
51 | {
|
---|
52 | ssize_t ret;
|
---|
53 |
|
---|
54 | maybe_die(fd);
|
---|
55 |
|
---|
56 | ret = pwrite(fd, buf, count, offset);
|
---|
57 | if (ret != count)
|
---|
58 | return ret;
|
---|
59 |
|
---|
60 | maybe_die(fd);
|
---|
61 | return ret;
|
---|
62 | }
|
---|
63 |
|
---|
64 | static ssize_t write_check(int fd, const void *buf, size_t count)
|
---|
65 | {
|
---|
66 | ssize_t ret;
|
---|
67 |
|
---|
68 | maybe_die(fd);
|
---|
69 |
|
---|
70 | ret = write(fd, buf, count);
|
---|
71 | if (ret != count)
|
---|
72 | return ret;
|
---|
73 |
|
---|
74 | maybe_die(fd);
|
---|
75 | return ret;
|
---|
76 | }
|
---|
77 |
|
---|
78 | static int ftruncate_check(int fd, off_t length)
|
---|
79 | {
|
---|
80 | int ret;
|
---|
81 |
|
---|
82 | maybe_die(fd);
|
---|
83 |
|
---|
84 | ret = ftruncate(fd, length);
|
---|
85 |
|
---|
86 | maybe_die(fd);
|
---|
87 | return ret;
|
---|
88 | }
|
---|
89 |
|
---|
90 | static bool test_death(enum operation op, struct agent *agent)
|
---|
91 | {
|
---|
92 | struct tdb_context *tdb = NULL;
|
---|
93 | TDB_DATA key;
|
---|
94 | enum agent_return ret;
|
---|
95 | int needed_recovery = 0;
|
---|
96 |
|
---|
97 | current = target = 0;
|
---|
98 | reset:
|
---|
99 | unlink(TEST_DBNAME);
|
---|
100 | tdb = tdb_open_ex(TEST_DBNAME, 1024, TDB_NOMMAP,
|
---|
101 | O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
|
---|
102 |
|
---|
103 | if (setjmp(jmpbuf) != 0) {
|
---|
104 | /* We're partway through. Simulate our death. */
|
---|
105 | close(tdb->fd);
|
---|
106 | forget_locking();
|
---|
107 | in_transaction = false;
|
---|
108 |
|
---|
109 | ret = external_agent_operation(agent, NEEDS_RECOVERY, "");
|
---|
110 | if (ret == SUCCESS)
|
---|
111 | needed_recovery++;
|
---|
112 | else if (ret != FAILED) {
|
---|
113 | diag("Step %u agent NEEDS_RECOVERY = %s", current,
|
---|
114 | agent_return_name(ret));
|
---|
115 | return false;
|
---|
116 | }
|
---|
117 |
|
---|
118 | ret = external_agent_operation(agent, op, KEY_STRING);
|
---|
119 | if (ret != SUCCESS) {
|
---|
120 | diag("Step %u op %s failed = %s", current,
|
---|
121 | operation_name(op),
|
---|
122 | agent_return_name(ret));
|
---|
123 | return false;
|
---|
124 | }
|
---|
125 |
|
---|
126 | ret = external_agent_operation(agent, NEEDS_RECOVERY, "");
|
---|
127 | if (ret != FAILED) {
|
---|
128 | diag("Still needs recovery after step %u = %s",
|
---|
129 | current, agent_return_name(ret));
|
---|
130 | return false;
|
---|
131 | }
|
---|
132 |
|
---|
133 | ret = external_agent_operation(agent, CHECK, "");
|
---|
134 | if (ret != SUCCESS) {
|
---|
135 | diag("Step %u check failed = %s", current,
|
---|
136 | agent_return_name(ret));
|
---|
137 | return false;
|
---|
138 | }
|
---|
139 |
|
---|
140 | ret = external_agent_operation(agent, CLOSE, "");
|
---|
141 | if (ret != SUCCESS) {
|
---|
142 | diag("Step %u close failed = %s", current,
|
---|
143 | agent_return_name(ret));
|
---|
144 | return false;
|
---|
145 | }
|
---|
146 |
|
---|
147 | /* Suppress logging as this tries to use closed fd. */
|
---|
148 | suppress_logging = true;
|
---|
149 | suppress_lockcheck = true;
|
---|
150 | tdb_close(tdb);
|
---|
151 | suppress_logging = false;
|
---|
152 | suppress_lockcheck = false;
|
---|
153 | target++;
|
---|
154 | current = 0;
|
---|
155 | goto reset;
|
---|
156 | }
|
---|
157 |
|
---|
158 | /* Put key for agent to fetch. */
|
---|
159 | key.dsize = strlen(KEY_STRING);
|
---|
160 | key.dptr = discard_const_p(uint8_t, KEY_STRING);
|
---|
161 | if (tdb_store(tdb, key, key, TDB_INSERT) != 0)
|
---|
162 | return false;
|
---|
163 |
|
---|
164 | /* This is the key we insert in transaction. */
|
---|
165 | key.dsize--;
|
---|
166 |
|
---|
167 | ret = external_agent_operation(agent, OPEN, TEST_DBNAME);
|
---|
168 | if (ret != SUCCESS) {
|
---|
169 | fprintf(stderr, "Agent failed to open: %s\n",
|
---|
170 | agent_return_name(ret));
|
---|
171 | exit(1);
|
---|
172 | }
|
---|
173 |
|
---|
174 | ret = external_agent_operation(agent, FETCH, KEY_STRING);
|
---|
175 | if (ret != SUCCESS) {
|
---|
176 | fprintf(stderr, "Agent failed find key: %s\n",
|
---|
177 | agent_return_name(ret));
|
---|
178 | exit(1);
|
---|
179 | }
|
---|
180 |
|
---|
181 | in_transaction = true;
|
---|
182 | if (tdb_transaction_start(tdb) != 0)
|
---|
183 | return false;
|
---|
184 |
|
---|
185 | if (tdb_store(tdb, key, key, TDB_INSERT) != 0)
|
---|
186 | return false;
|
---|
187 |
|
---|
188 | if (tdb_transaction_commit(tdb) != 0)
|
---|
189 | return false;
|
---|
190 |
|
---|
191 | in_transaction = false;
|
---|
192 |
|
---|
193 | /* We made it! */
|
---|
194 | diag("Completed %u runs", current);
|
---|
195 | tdb_close(tdb);
|
---|
196 | ret = external_agent_operation(agent, CLOSE, "");
|
---|
197 | if (ret != SUCCESS) {
|
---|
198 | diag("Step %u close failed = %s", current,
|
---|
199 | agent_return_name(ret));
|
---|
200 | return false;
|
---|
201 | }
|
---|
202 |
|
---|
203 | #ifdef HAVE_INCOHERENT_MMAP
|
---|
204 | /* This means we always mmap, which makes this test a noop. */
|
---|
205 | ok1(1);
|
---|
206 | #else
|
---|
207 | ok1(needed_recovery);
|
---|
208 | #endif
|
---|
209 | ok1(locking_errors == 0);
|
---|
210 | ok1(forget_locking() == 0);
|
---|
211 | locking_errors = 0;
|
---|
212 | return true;
|
---|
213 | }
|
---|
214 |
|
---|
215 | int main(int argc, char *argv[])
|
---|
216 | {
|
---|
217 | enum operation ops[] = { FETCH, STORE, TRANSACTION_START };
|
---|
218 | struct agent *agent;
|
---|
219 | int i;
|
---|
220 |
|
---|
221 | plan_tests(12);
|
---|
222 | unlock_callback = maybe_die;
|
---|
223 |
|
---|
224 | agent = prepare_external_agent();
|
---|
225 |
|
---|
226 | for (i = 0; i < sizeof(ops)/sizeof(ops[0]); i++) {
|
---|
227 | diag("Testing %s after death", operation_name(ops[i]));
|
---|
228 | ok1(test_death(ops[i], agent));
|
---|
229 | }
|
---|
230 |
|
---|
231 | return exit_status();
|
---|
232 | }
|
---|