strsplit: remove nump argument
[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/tdb/hash.c>
25 #include <ccan/tap/tap.h>
26 #include <stdlib.h>
27 #include <stdbool.h>
28 #include <stdarg.h>
29 #include <err.h>
30 #include "external-agent.h"
31 #include "logging.h"
32
33 static struct agent *agent;
34 static bool opened;
35 static int errors = 0;
36 static bool clear_if_first;
37 #define TEST_DBNAME "run-open-during-transaction.tdb"
38
39 #undef write
40 #undef pwrite
41 #undef fcntl
42 #undef ftruncate
43
44 static bool is_same(const char *snapshot, const char *latest, off_t len)
45 {
46         unsigned i;
47
48         for (i = 0; i < len; i++) {
49                 if (snapshot[i] != latest[i])
50                         return false;
51         }
52         return true;
53 }
54
55 static bool compare_file(int fd, const char *snapshot, off_t snapshot_len)
56 {
57         char *contents;
58         bool same;
59
60         /* over-length read serves as length check. */
61         contents = malloc(snapshot_len+1);
62         same = pread(fd, contents, snapshot_len+1, 0) == snapshot_len
63                 && is_same(snapshot, contents, snapshot_len);
64         free(contents);
65         return same;
66 }
67
68 static void check_file_intact(int fd)
69 {
70         enum agent_return ret;
71         struct stat st;
72         char *contents;
73
74         fstat(fd, &st);
75         contents = malloc(st.st_size);
76         if (pread(fd, contents, st.st_size, 0) != st.st_size) {
77                 diag("Read fail");
78                 errors++;
79                 return;
80         }
81
82         /* Ask agent to open file. */
83         ret = external_agent_operation(agent, clear_if_first ?
84                                        OPEN_WITH_CLEAR_IF_FIRST :
85                                        OPEN,
86                                        TEST_DBNAME);
87
88         /* It's OK to open it, but it must not have changed! */
89         if (!compare_file(fd, contents, st.st_size)) {
90                 diag("Agent changed file after opening %s",
91                      agent_return_name(ret));
92                 errors++;
93         }
94
95         if (ret == SUCCESS) {
96                 ret = external_agent_operation(agent, CLOSE, NULL);
97                 if (ret != SUCCESS) {
98                         diag("Agent failed to close tdb: %s",
99                              agent_return_name(ret));
100                         errors++;
101                 }
102         } else if (ret != WOULD_HAVE_BLOCKED) {
103                 diag("Agent opening file gave %s",
104                      agent_return_name(ret));
105                 errors++;
106         }
107
108         free(contents);
109 }
110
111 static void after_unlock(int fd)
112 {
113         if (opened)
114                 check_file_intact(fd);
115 }
116         
117 static ssize_t pwrite_check(int fd,
118                             const void *buf, size_t count, off_t offset)
119 {
120         if (opened)
121                 check_file_intact(fd);
122
123         return pwrite(fd, buf, count, offset);
124 }
125
126 static ssize_t write_check(int fd, const void *buf, size_t count)
127 {
128         if (opened)
129                 check_file_intact(fd);
130
131         return write(fd, buf, count);
132 }
133
134 static int ftruncate_check(int fd, off_t length)
135 {
136         if (opened)
137                 check_file_intact(fd);
138
139         return ftruncate(fd, length);
140
141 }
142
143 int main(int argc, char *argv[])
144 {
145         const int flags[] = { TDB_DEFAULT,
146                               TDB_CLEAR_IF_FIRST,
147                               TDB_NOMMAP, 
148                               TDB_CLEAR_IF_FIRST | TDB_NOMMAP };
149         int i;
150         struct tdb_context *tdb;
151         TDB_DATA key, data;
152
153         plan_tests(20);
154         agent = prepare_external_agent();
155         if (!agent)
156                 err(1, "preparing agent");
157
158         unlock_callback = after_unlock;
159         for (i = 0; i < sizeof(flags)/sizeof(flags[0]); i++) {
160                 clear_if_first = (flags[i] & TDB_CLEAR_IF_FIRST);
161                 diag("Test with %s and %s\n",
162                      clear_if_first ? "CLEAR" : "DEFAULT",
163                      (flags[i] & TDB_NOMMAP) ? "no mmap" : "mmap");
164                 unlink(TEST_DBNAME);
165                 tdb = tdb_open_ex(TEST_DBNAME, 1024, flags[i],
166                                   O_CREAT|O_TRUNC|O_RDWR, 0600,
167                                   &taplogctx, NULL);
168                 ok1(tdb);
169
170                 opened = true;
171                 ok1(tdb_transaction_start(tdb) == 0);
172                 key.dsize = strlen("hi");
173                 key.dptr = (void *)"hi";
174                 data.dptr = (void *)"world";
175                 data.dsize = strlen("world");
176
177                 ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
178                 ok1(tdb_transaction_commit(tdb) == 0);
179                 ok(!errors, "We had %u open errors", errors);
180
181                 opened = false;
182                 tdb_close(tdb);
183         }
184
185         return exit_status();
186 }