]> git.ozlabs.org Git - ccan/blobdiff - ccan/tdb/test/run-open-during-transaction.c
tdb: add test for tdb_summary
[ccan] / ccan / tdb / test / run-open-during-transaction.c
index 5119004b97fb6d2af561f83ced86fc9c4afa3af8..e7feb03fab960ec8c2a0f75af2f3e2fd3eae6d01 100644 (file)
@@ -1,13 +1,14 @@
 #define _XOPEN_SOURCE 500
 #include <unistd.h>
+#include "lock-tracking.h"
+
 static ssize_t pwrite_check(int fd, const void *buf, size_t count, off_t offset);
 static ssize_t write_check(int fd, const void *buf, size_t count);
-static int fcntl_check(int fd, int cmd, ... /* arg */ );
 static int ftruncate_check(int fd, off_t length);
 
 #define pwrite pwrite_check
 #define write write_check
-#define fcntl fcntl_check
+#define fcntl fcntl_with_lockcheck
 #define ftruncate ftruncate_check
 
 #include <ccan/tdb/tdb.h>
@@ -19,197 +20,128 @@ static int ftruncate_check(int fd, off_t length);
 #include <ccan/tdb/transaction.c>
 #include <ccan/tdb/error.c>
 #include <ccan/tdb/open.c>
+#include <ccan/tdb/check.c>
+#include <ccan/tdb/hash.c>
 #include <ccan/tap/tap.h>
 #include <stdlib.h>
 #include <stdbool.h>
 #include <stdarg.h>
 #include <err.h>
-#include "external-transaction.h"
+#include "external-agent.h"
+#include "logging.h"
 
 static struct agent *agent;
-static bool agent_pending;
-static bool in_transaction;
+static bool opened;
 static int errors = 0;
-static bool snapshot_uptodate;
-static char *snapshot;
-static off_t snapshot_len;
 static bool clear_if_first;
-#define TEST_DBNAME "/tmp/test7.tdb"
+#define TEST_DBNAME "run-open-during-transaction.tdb"
 
 #undef write
 #undef pwrite
 #undef fcntl
 #undef ftruncate
 
-static void taplog(struct tdb_context *tdb,
-                  enum tdb_debug_level level,
-                  const char *fmt, ...)
+static bool is_same(const char *snapshot, const char *latest, off_t len)
 {
-       va_list ap;
-       char line[200];
-
-       va_start(ap, fmt);
-       vsprintf(line, fmt, ap);
-       va_end(ap);
+       unsigned i;
 
-       diag("%s", line);
+       for (i = 0; i < len; i++) {
+               if (snapshot[i] != latest[i])
+                       return false;
+       }
+       return true;
 }
 
-static void save_file_contents(int fd)
+static bool compare_file(int fd, const char *snapshot, off_t snapshot_len)
 {
-       struct stat st;
-       int res;
-
-       /* Save copy of file. */
-       stat(TEST_DBNAME, &st);
-       if (snapshot_len != st.st_size) {
-               snapshot = realloc(snapshot, st.st_size * 2);
-               snapshot_len = st.st_size;
-       }
-       res = pread(fd, snapshot, snapshot_len, 0);
-       if (res != snapshot_len)
-               err(1, "Reading %zu bytes = %u", (size_t)snapshot_len, res);
-       snapshot_uptodate = true;
+       char *contents;
+       bool same;
+
+       /* over-length read serves as length check. */
+       contents = malloc(snapshot_len+1);
+       same = pread(fd, contents, snapshot_len+1, 0) == snapshot_len
+               && is_same(snapshot, contents, snapshot_len);
+       free(contents);
+       return same;
 }
 
-static void check_for_agent(int fd, bool block)
+static void check_file_intact(int fd)
 {
+       enum agent_return ret;
        struct stat st;
-       int res;
+       char *contents;
 
-       if (!external_agent_operation_check(agent, block, &res))
+       fstat(fd, &st);
+       contents = malloc(st.st_size);
+       if (pread(fd, contents, st.st_size, 0) != st.st_size) {
+               diag("Read fail");
+               errors++;
                return;
+       }
 
-       if (res != 0)
-               err(1, "Agent failed open");
-       agent_pending = false;
-
-       if (!snapshot_uptodate)
-               return;
+       /* Ask agent to open file. */
+       ret = external_agent_operation(agent, clear_if_first ?
+                                      OPEN_WITH_CLEAR_IF_FIRST :
+                                      OPEN,
+                                      TEST_DBNAME);
 
-       stat(TEST_DBNAME, &st);
-       if (st.st_size != snapshot_len) {
-               diag("Other open changed size from %zu -> %zu",
-                    (size_t)snapshot_len, (size_t)st.st_size);
+       /* It's OK to open it, but it must not have changed! */
+       if (!compare_file(fd, contents, st.st_size)) {
+               diag("Agent changed file after opening %s",
+                    agent_return_name(ret));
                errors++;
-               return;
        }
 
-       if (pread(fd, snapshot+snapshot_len, snapshot_len, 0) != snapshot_len)
-               err(1, "Reading %zu bytes", (size_t)snapshot_len);
-       if (memcmp(snapshot, snapshot+snapshot_len, snapshot_len) != 0) {
-               diag("File changed");
+       if (ret == SUCCESS) {
+               ret = external_agent_operation(agent, CLOSE, NULL);
+               if (ret != SUCCESS) {
+                       diag("Agent failed to close tdb: %s",
+                            agent_return_name(ret));
+                       errors++;
+               }
+       } else if (ret != WOULD_HAVE_BLOCKED) {
+               diag("Agent opening file gave %s",
+                    agent_return_name(ret));
                errors++;
-               return;
        }
+
+       free(contents);
 }
 
-static void check_file_contents(int fd)
+static void after_unlock(int fd)
 {
-       if (agent_pending)
-               check_for_agent(fd, false);
-
-       if (!agent_pending) {
-               save_file_contents(fd);
-
-               /* Ask agent to open file. */
-               external_agent_operation_start(agent,
-                                              clear_if_first ?
-                                              OPEN_WITH_CLEAR_IF_FIRST :
-                                              OPEN,
-                                              TEST_DBNAME);
-               agent_pending = true;
-               /* Hack: give it a chance to run. */
-               sleep(0);
-       }
-
-       check_for_agent(fd, false);
+       if (opened)
+               check_file_intact(fd);
 }
-
+       
 static ssize_t pwrite_check(int fd,
                            const void *buf, size_t count, off_t offset)
 {
-       ssize_t ret;
+       if (opened)
+               check_file_intact(fd);
 
-       if (in_transaction)
-               check_file_contents(fd);
-
-       snapshot_uptodate = false;
-       ret = pwrite(fd, buf, count, offset);
-       if (ret != count)
-               return ret;
-
-       if (in_transaction)
-               check_file_contents(fd);
-       return ret;
+       return pwrite(fd, buf, count, offset);
 }
 
 static ssize_t write_check(int fd, const void *buf, size_t count)
 {
-       ssize_t ret;
-
-       if (in_transaction)
-               check_file_contents(fd);
-
-       snapshot_uptodate = false;
-
-       ret = write(fd, buf, count);
-       if (ret != count)
-               return ret;
-
-       if (in_transaction)
-               check_file_contents(fd);
-       return ret;
-}
-
-/* This seems to be a macro for glibc... */
-extern int fcntl(int fd, int cmd, ... /* arg */ );
-
-static int fcntl_check(int fd, int cmd, ... /* arg */ )
-{
-       va_list ap;
-       int ret, arg3;
-       struct flock *fl;
-
-       if (cmd != F_SETLK && cmd != F_SETLKW) {
-               /* This may be totally bogus, but we don't know in general. */
-               va_start(ap, cmd);
-               arg3 = va_arg(ap, int);
-               va_end(ap);
-
-               return fcntl(fd, cmd, arg3);
-       }
-
-       va_start(ap, cmd);
-       fl = va_arg(ap, struct flock *);
-       va_end(ap);
+       if (opened)
+               check_file_intact(fd);
 
-       ret = fcntl(fd, cmd, fl);
-
-       if (in_transaction && fl->l_type == F_UNLCK)
-               check_file_contents(fd);
-       return ret;
+       return write(fd, buf, count);
 }
 
 static int ftruncate_check(int fd, off_t length)
 {
-       int ret;
-
-       if (in_transaction)
-               check_file_contents(fd);
+       if (opened)
+               check_file_intact(fd);
 
-       snapshot_uptodate = false;
+       return ftruncate(fd, length);
 
-       ret = ftruncate(fd, length);
-
-       if (in_transaction)
-               check_file_contents(fd);
-       return ret;
 }
 
 int main(int argc, char *argv[])
 {
-       struct tdb_logging_context logctx = { taplog, NULL };
        const int flags[] = { TDB_DEFAULT,
                              TDB_CLEAR_IF_FIRST,
                              TDB_NOMMAP, 
@@ -223,10 +155,7 @@ int main(int argc, char *argv[])
        if (!agent)
                err(1, "preparing agent");
 
-       /* Nice ourselves down: we can't tell the difference between agent
-        * blocking on lock, and agent not scheduled. */
-       nice(15);
-
+       unlock_callback = after_unlock;
        for (i = 0; i < sizeof(flags)/sizeof(flags[0]); i++) {
                clear_if_first = (flags[i] & TDB_CLEAR_IF_FIRST);
                diag("Test with %s and %s\n",
@@ -235,11 +164,11 @@ int main(int argc, char *argv[])
                unlink(TEST_DBNAME);
                tdb = tdb_open_ex(TEST_DBNAME, 1024, flags[i],
                                  O_CREAT|O_TRUNC|O_RDWR, 0600,
-                                 &logctx, NULL);
+                                 &taplogctx, NULL);
                ok1(tdb);
 
+               opened = true;
                ok1(tdb_transaction_start(tdb) == 0);
-               in_transaction = true;
                key.dsize = strlen("hi");
                key.dptr = (void *)"hi";
                data.dptr = (void *)"world";
@@ -247,10 +176,9 @@ int main(int argc, char *argv[])
 
                ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0);
                ok1(tdb_transaction_commit(tdb) == 0);
-               if (agent_pending)
-                       check_for_agent(tdb->fd, true);
-               ok(errors == 0, "We had %u unexpected changes", errors);
+               ok(!errors, "We had %u open errors", errors);
 
+               opened = false;
                tdb_close(tdb);
        }