tests: now we run in tmp dir, always create temporary files in this dir.
[ccan] / ccan / tdb / test / run-open-during-transaction.c
1 #define _XOPEN_SOURCE 500
2 #include <unistd.h>
3 #include "lock-tracking.h"
4
5 static ssize_t pwrite_check(int fd, const void *buf, size_t count, off_t offset);
6 static ssize_t write_check(int fd, const void *buf, size_t count);
7 static int ftruncate_check(int fd, off_t length);
8
9 #define pwrite pwrite_check
10 #define write write_check
11 #define fcntl fcntl_with_lockcheck
12 #define ftruncate ftruncate_check
13
14 #include <ccan/tdb/tdb.h>
15 #include <ccan/tdb/io.c>
16 #include <ccan/tdb/tdb.c>
17 #include <ccan/tdb/lock.c>
18 #include <ccan/tdb/freelist.c>
19 #include <ccan/tdb/traverse.c>
20 #include <ccan/tdb/transaction.c>
21 #include <ccan/tdb/error.c>
22 #include <ccan/tdb/open.c>
23 #include <ccan/tdb/check.c>
24 #include <ccan/tap/tap.h>
25 #include <stdlib.h>
26 #include <stdbool.h>
27 #include <stdarg.h>
28 #include <err.h>
29 #include "external-transaction.h"
30
31 static struct agent *agent;
32 static bool agent_pending;
33 static bool in_transaction;
34 static int errors = 0;
35 static bool snapshot_uptodate;
36 static char *snapshot;
37 static off_t snapshot_len;
38 static bool clear_if_first;
39 #define TEST_DBNAME "run-open-during-transaction.tdb"
40
41 #undef write
42 #undef pwrite
43 #undef fcntl
44 #undef ftruncate
45
46 static void taplog(struct tdb_context *tdb,
47                    enum tdb_debug_level level,
48                    const char *fmt, ...)
49 {
50         va_list ap;
51         char line[200];
52
53         va_start(ap, fmt);
54         vsprintf(line, fmt, ap);
55         va_end(ap);
56
57         diag("%s", line);
58 }
59
60 static void save_file_contents(int fd)
61 {
62         struct stat st;
63         int res;
64
65         /* Save copy of file. */
66         stat(TEST_DBNAME, &st);
67         if (snapshot_len != st.st_size) {
68                 snapshot = realloc(snapshot, st.st_size * 2);
69                 snapshot_len = st.st_size;
70         }
71         res = pread(fd, snapshot, snapshot_len, 0);
72         if (res != snapshot_len)
73                 err(1, "Reading %zu bytes = %u", (size_t)snapshot_len, res);
74         snapshot_uptodate = true;
75 }
76
77 static void check_for_agent(int fd, bool block)
78 {
79         struct stat st;
80         int res;
81
82         if (!external_agent_operation_check(agent, block, &res))
83                 return;
84
85         if (res != 1)
86                 err(1, "Agent failed open");
87         agent_pending = false;
88
89         if (!snapshot_uptodate)
90                 return;
91
92         stat(TEST_DBNAME, &st);
93         if (st.st_size != snapshot_len) {
94                 diag("Other open changed size from %zu -> %zu",
95                      (size_t)snapshot_len, (size_t)st.st_size);
96                 errors++;
97                 return;
98         }
99
100         if (pread(fd, snapshot+snapshot_len, snapshot_len, 0) != snapshot_len)
101                 err(1, "Reading %zu bytes", (size_t)snapshot_len);
102         if (memcmp(snapshot, snapshot+snapshot_len, snapshot_len) != 0) {
103                 diag("File changed");
104                 errors++;
105                 return;
106         }
107 }
108
109 static void check_file_contents(int fd)
110 {
111         if (!in_transaction)
112                 return;
113
114         if (agent_pending)
115                 check_for_agent(fd, false);
116
117         if (!agent_pending) {
118                 save_file_contents(fd);
119
120                 /* Ask agent to open file. */
121                 external_agent_operation_start(agent,
122                                                clear_if_first ?
123                                                OPEN_WITH_CLEAR_IF_FIRST :
124                                                OPEN,
125                                                TEST_DBNAME);
126                 agent_pending = true;
127                 /* Hack: give it a chance to run. */
128                 sleep(0);
129         }
130
131         check_for_agent(fd, false);
132 }
133
134 static ssize_t pwrite_check(int fd,
135                             const void *buf, size_t count, off_t offset)
136 {
137         ssize_t ret;
138
139         check_file_contents(fd);
140
141         snapshot_uptodate = false;
142         ret = pwrite(fd, buf, count, offset);
143         if (ret != count)
144                 return ret;
145
146         check_file_contents(fd);
147         return ret;
148 }
149
150 static ssize_t write_check(int fd, const void *buf, size_t count)
151 {
152         ssize_t ret;
153
154         check_file_contents(fd);
155
156         snapshot_uptodate = false;
157
158         ret = write(fd, buf, count);
159         if (ret != count)
160                 return ret;
161
162         check_file_contents(fd);
163         return ret;
164 }
165
166 static int ftruncate_check(int fd, off_t length)
167 {
168         int ret;
169
170         check_file_contents(fd);
171
172         snapshot_uptodate = false;
173
174         ret = ftruncate(fd, length);
175
176         check_file_contents(fd);
177         return ret;
178 }
179
180 int main(int argc, char *argv[])
181 {
182         struct tdb_logging_context logctx = { taplog, NULL };
183         const int flags[] = { TDB_DEFAULT,
184                               TDB_CLEAR_IF_FIRST,
185                               TDB_NOMMAP, 
186                               TDB_CLEAR_IF_FIRST | TDB_NOMMAP };
187         int i;
188         struct tdb_context *tdb;
189         TDB_DATA key, data;
190
191         plan_tests(20);
192         unlock_callback = check_file_contents;
193         agent = prepare_external_agent();
194         if (!agent)
195                 err(1, "preparing agent");
196
197         /* Nice ourselves down: we can't tell the difference between agent
198          * blocking on lock, and agent not scheduled. */
199         nice(15);
200
201         for (i = 0; i < sizeof(flags)/sizeof(flags[0]); i++) {
202                 clear_if_first = (flags[i] & TDB_CLEAR_IF_FIRST);
203                 diag("Test with %s and %s\n",
204                      clear_if_first ? "CLEAR" : "DEFAULT",
205                      (flags[i] & TDB_NOMMAP) ? "no mmap" : "mmap");
206                 unlink(TEST_DBNAME);
207                 tdb = tdb_open_ex(TEST_DBNAME, 1024, flags[i],
208                                   O_CREAT|O_TRUNC|O_RDWR, 0600,
209                                   &logctx, NULL);
210                 ok1(tdb);
211
212                 ok1(tdb_transaction_start(tdb) == 0);
213                 in_transaction = true;
214                 key.dsize = strlen("hi");
215                 key.dptr = (void *)"hi";
216                 data.dptr = (void *)"world";
217                 data.dsize = strlen("world");
218
219                 ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
220                 ok1(tdb_transaction_commit(tdb) == 0);
221                 if (agent_pending)
222                         check_for_agent(tdb->fd, true);
223                 ok(errors == 0, "We had %u unexpected changes", errors);
224
225                 tdb_close(tdb);
226         }
227
228         return exit_status();
229 }