1 #define _XOPEN_SOURCE 500
3 #include "lock-tracking.h"
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);
9 #define pwrite pwrite_check
10 #define write write_check
11 #define fcntl fcntl_with_lockcheck
12 #define ftruncate ftruncate_check
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>
29 #include "external-transaction.h"
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"
46 static void taplog(struct tdb_context *tdb,
47 enum tdb_debug_level level,
54 vsprintf(line, fmt, ap);
60 static void save_file_contents(int fd)
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;
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;
77 static void check_for_agent(int fd, bool block)
82 if (!external_agent_operation_check(agent, block, &res))
86 err(1, "Agent failed open");
87 agent_pending = false;
89 if (!snapshot_uptodate)
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);
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");
109 static void check_file_contents(int fd)
115 check_for_agent(fd, false);
117 if (!agent_pending) {
118 save_file_contents(fd);
120 /* Ask agent to open file. */
121 external_agent_operation_start(agent,
123 OPEN_WITH_CLEAR_IF_FIRST :
126 agent_pending = true;
127 /* Hack: give it a chance to run. */
131 check_for_agent(fd, false);
134 static ssize_t pwrite_check(int fd,
135 const void *buf, size_t count, off_t offset)
139 check_file_contents(fd);
141 snapshot_uptodate = false;
142 ret = pwrite(fd, buf, count, offset);
146 check_file_contents(fd);
150 static ssize_t write_check(int fd, const void *buf, size_t count)
154 check_file_contents(fd);
156 snapshot_uptodate = false;
158 ret = write(fd, buf, count);
162 check_file_contents(fd);
166 static int ftruncate_check(int fd, off_t length)
170 check_file_contents(fd);
172 snapshot_uptodate = false;
174 ret = ftruncate(fd, length);
176 check_file_contents(fd);
180 int main(int argc, char *argv[])
182 struct tdb_logging_context logctx = { taplog, NULL };
183 const int flags[] = { TDB_DEFAULT,
186 TDB_CLEAR_IF_FIRST | TDB_NOMMAP };
188 struct tdb_context *tdb;
192 unlock_callback = check_file_contents;
193 agent = prepare_external_agent();
195 err(1, "preparing agent");
197 /* Nice ourselves down: we can't tell the difference between agent
198 * blocking on lock, and agent not scheduled. */
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");
207 tdb = tdb_open_ex(TEST_DBNAME, 1024, flags[i],
208 O_CREAT|O_TRUNC|O_RDWR, 0600,
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");
219 ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
220 ok1(tdb_transaction_commit(tdb) == 0);
222 check_for_agent(tdb->fd, true);
223 ok(errors == 0, "We had %u unexpected changes", errors);
228 return exit_status();