summaryrefslogtreecommitdiff
path: root/src/third_party/wiredtiger/test/readonly/readonly.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/third_party/wiredtiger/test/readonly/readonly.c')
-rw-r--r--src/third_party/wiredtiger/test/readonly/readonly.c409
1 files changed, 409 insertions, 0 deletions
diff --git a/src/third_party/wiredtiger/test/readonly/readonly.c b/src/third_party/wiredtiger/test/readonly/readonly.c
new file mode 100644
index 00000000000..41400da2605
--- /dev/null
+++ b/src/third_party/wiredtiger/test/readonly/readonly.c
@@ -0,0 +1,409 @@
+/*-
+ * Public Domain 2014-2016 MongoDB, Inc.
+ * Public Domain 2008-2014 WiredTiger, Inc.
+ *
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or
+ * distribute this software, either in source code form or as a compiled
+ * binary, for any purpose, commercial or non-commercial, and by any
+ * means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors
+ * of this software dedicate any and all copyright interest in the
+ * software to the public domain. We make this dedication for the benefit
+ * of the public at large and to the detriment of our heirs and
+ * successors. We intend this dedication to be an overt act of
+ * relinquishment in perpetuity of all present and future rights to this
+ * software under copyright law.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <sys/wait.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+#include <wiredtiger.h>
+
+#include "test_util.i"
+
+#define HOME_SIZE 512
+static char home[HOME_SIZE]; /* Program working dir lock file */
+#define HOME_WR_SUFFIX ".WRNOLOCK" /* Writable dir copy no lock file */
+static char home_wr[HOME_SIZE + sizeof(HOME_WR_SUFFIX)];
+#define HOME_RD_SUFFIX ".RD" /* Read-only dir */
+static char home_rd[HOME_SIZE + sizeof(HOME_RD_SUFFIX)];
+#define HOME_RD2_SUFFIX ".RDNOLOCK" /* Read-only dir no lock file */
+static char home_rd2[HOME_SIZE + sizeof(HOME_RD2_SUFFIX)];
+
+static const char *progname; /* Program name */
+static const char *saved_argv0; /* Program command */
+static const char * const uri = "table:main";
+
+#define ENV_CONFIG \
+ "create,log=(file_max=10M,archive=false,enabled)," \
+ "transaction_sync=(enabled,method=none)"
+#define ENV_CONFIG_RD "readonly=true"
+#define ENV_CONFIG_WR "readonly=false"
+#define MAX_VAL 4096
+#define MAX_KV 10000
+
+#define EXPECT_ERR 1
+#define EXPECT_SUCCESS 0
+
+#define OP_READ 0
+#define OP_WRITE 1
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: %s [-h dir]\n", progname);
+ exit(EXIT_FAILURE);
+}
+
+static int
+run_child(const char *homedir, int op, int expect)
+{
+ WT_CONNECTION *conn;
+ WT_CURSOR *cursor;
+ WT_SESSION *session;
+ int i, ret;
+ const char *cfg;
+
+ /*
+ * We expect the read-only database will allow the second read-only
+ * handle to succeed because no one can create or set the lock file.
+ */
+ if (op == OP_READ)
+ cfg = ENV_CONFIG_RD;
+ else
+ cfg = ENV_CONFIG_WR;
+ if ((ret = wiredtiger_open(homedir, NULL, cfg, &conn)) == 0) {
+ if (expect == EXPECT_ERR)
+ testutil_die(
+ ret, "wiredtiger_open expected error, succeeded");
+ } else {
+ if (expect == EXPECT_SUCCESS)
+ testutil_die(
+ ret, "wiredtiger_open expected success, error");
+ /*
+ * If we expect an error and got one, we're done.
+ */
+ return (0);
+ }
+
+ /*
+ * Make sure we can read the data.
+ */
+ if ((ret = conn->open_session(conn, NULL, NULL, &session)) != 0)
+ testutil_die(ret, "WT_CONNECTION:open_session");
+
+ if ((ret =
+ session->open_cursor(session, uri, NULL, NULL, &cursor)) != 0)
+ testutil_die(ret, "WT_SESSION.open_cursor: %s", uri);
+
+ i = 0;
+ while ((ret = cursor->next(cursor)) == 0)
+ ++i;
+ if (i != MAX_KV)
+ testutil_die(EPERM, "cursor walk");
+ if ((ret = conn->close(conn, NULL)) != 0)
+ testutil_die(ret, "conn_close");
+ return (0);
+}
+
+/*
+ * Child process opens both databases readonly.
+ */
+static void
+open_dbs(int op, const char *dir,
+ const char *dir_wr, const char *dir_rd, const char *dir_rd2)
+{
+ int expect, ret;
+
+ /*
+ * The parent has an open connection to all directories.
+ * We expect opening the writeable homes to return an error.
+ * It is a failure if the child successfully opens that.
+ */
+ expect = EXPECT_ERR;
+ if ((ret = run_child(dir, op, expect)) != 0)
+ testutil_die(ret, "wiredtiger_open readonly allowed");
+ if ((ret = run_child(dir_wr, op, expect)) != 0)
+ testutil_die(ret, "wiredtiger_open readonly allowed");
+
+ /*
+ * The parent must have a read-only connection open to the
+ * read-only databases. If the child is opening read-only
+ * too, we expect success. Otherwise an error if the child
+ * attempts to open read/write (permission error).
+ */
+ if (op == OP_READ)
+ expect = EXPECT_SUCCESS;
+ if ((ret = run_child(dir_rd, op, expect)) != 0)
+ testutil_die(ret, "run child 1");
+ if ((ret = run_child(dir_rd2, op, expect)) != 0)
+ testutil_die(ret, "run child 2");
+ exit(EXIT_SUCCESS);
+}
+
+extern int __wt_optind;
+extern char *__wt_optarg;
+
+void (*custom_die)(void) = NULL;
+
+int
+main(int argc, char *argv[])
+{
+ WT_CONNECTION *conn, *conn2, *conn3, *conn4;
+ WT_CURSOR *cursor;
+ WT_ITEM data;
+ WT_SESSION *session;
+ uint64_t i;
+ int ch, status, op, ret;
+ bool child;
+ const char *working_dir;
+ char cmd[512];
+ uint8_t buf[MAX_VAL];
+
+ if ((progname = strrchr(argv[0], DIR_DELIM)) == NULL)
+ progname = argv[0];
+ else
+ ++progname;
+ /*
+ * Needed unaltered for system command later.
+ */
+ saved_argv0 = argv[0];
+
+ working_dir = "WT_RD";
+ child = false;
+ op = OP_READ;
+ while ((ch = __wt_getopt(progname, argc, argv, "Rh:W")) != EOF)
+ switch (ch) {
+ case 'R':
+ child = true;
+ op = OP_READ;
+ break;
+ case 'W':
+ child = true;
+ op = OP_WRITE;
+ break;
+ case 'h':
+ working_dir = __wt_optarg;
+ break;
+ default:
+ usage();
+ }
+ argc -= __wt_optind;
+ argv += __wt_optind;
+ if (argc != 0)
+ usage();
+
+ /*
+ * Set up all the directory names.
+ */
+ testutil_work_dir_from_path(home, sizeof(home), working_dir);
+ (void)snprintf(home_wr, sizeof(home_wr), "%s%s", home, HOME_WR_SUFFIX);
+ (void)snprintf(home_rd, sizeof(home_rd), "%s%s", home, HOME_RD_SUFFIX);
+ (void)snprintf(
+ home_rd2, sizeof(home_rd2), "%s%s", home, HOME_RD2_SUFFIX);
+ if (!child) {
+ testutil_make_work_dir(home);
+ testutil_make_work_dir(home_wr);
+ testutil_make_work_dir(home_rd);
+ testutil_make_work_dir(home_rd2);
+ } else
+ /*
+ * We are a child process, we just want to call
+ * the open_dbs with the directories we have.
+ * The child function will exit.
+ */
+ open_dbs(op, home, home_wr, home_rd, home_rd2);
+
+ /*
+ * Parent creates a database and table. Then cleanly shuts down.
+ * Then copy database to read-only directory and chmod.
+ * Also copy database to read-only directory and remove the lock
+ * file. One read-only database will have a lock file in the
+ * file system and the other will not.
+ * Parent opens all databases with read-only configuration flag.
+ * Parent forks off child who tries to also open all databases
+ * with the read-only flag. It should error on the writeable
+ * directory, but allow it on the read-only directories.
+ * The child then confirms it can read all the data.
+ */
+ /*
+ * Run in the home directory and create the table.
+ */
+ if ((ret = wiredtiger_open(home, NULL, ENV_CONFIG, &conn)) != 0)
+ testutil_die(ret, "wiredtiger_open");
+ if ((ret = conn->open_session(conn, NULL, NULL, &session)) != 0)
+ testutil_die(ret, "WT_CONNECTION:open_session");
+ if ((ret = session->create(session,
+ uri, "key_format=Q,value_format=u")) != 0)
+ testutil_die(ret, "WT_SESSION.create: %s", uri);
+ if ((ret =
+ session->open_cursor(session, uri, NULL, NULL, &cursor)) != 0)
+ testutil_die(ret, "WT_SESSION.open_cursor: %s", uri);
+
+ /*
+ * Write data into the table and then cleanly shut down connection.
+ */
+ memset(buf, 0, sizeof(buf));
+ data.data = buf;
+ data.size = MAX_VAL;
+ for (i = 0; i < MAX_KV; ++i) {
+ cursor->set_key(cursor, i);
+ cursor->set_value(cursor, &data);
+ if ((ret = cursor->insert(cursor)) != 0)
+ testutil_die(ret, "WT_CURSOR.insert");
+ }
+ if ((ret = conn->close(conn, NULL)) != 0)
+ testutil_die(ret, "WT_CONNECTION:close");
+
+ /*
+ * Copy the database. Remove any lock file from one copy
+ * and chmod the copies to be read-only permissions.
+ */
+ (void)snprintf(cmd, sizeof(cmd),
+ "cp -rp %s/* %s; rm -f %s/WiredTiger.lock",
+ home, home_wr, home_wr);
+ (void)system(cmd);
+
+ (void)snprintf(cmd, sizeof(cmd),
+ "cp -rp %s/* %s; chmod 0555 %s; chmod -R 0444 %s/*",
+ home, home_rd, home_rd, home_rd);
+ (void)system(cmd);
+
+ (void)snprintf(cmd, sizeof(cmd),
+ "cp -rp %s/* %s; rm -f %s/WiredTiger.lock; "
+ "chmod 0555 %s; chmod -R 0444 %s/*",
+ home, home_rd2, home_rd2, home_rd2, home_rd2);
+ (void)system(cmd);
+
+ /*
+ * Run four scenarios. Sometimes expect errors, sometimes success.
+ * The writable database directories should always fail to allow the
+ * child to open due to the lock file. The read-only ones will only
+ * succeed when the child attempts read-only.
+ *
+ * 1. Parent has read-only handle to all databases. Child opens
+ * read-only also.
+ * 2. Parent has read-only handle to all databases. Child opens
+ * read-write.
+ * 3. Parent has read-write handle to writable databases and
+ * read-only to read-only databases. Child opens read-only.
+ * 4. Parent has read-write handle to writable databases and
+ * read-only to read-only databases. Child opens read-write.
+ */
+ /*
+ * Open a connection handle to all databases.
+ */
+ fprintf(stderr, " *** Expect several error messages from WT ***\n");
+ /*
+ * Scenario 1.
+ */
+ if ((ret = wiredtiger_open(home, NULL, ENV_CONFIG_RD, &conn)) != 0)
+ testutil_die(ret, "wiredtiger_open original home");
+ if ((ret = wiredtiger_open(home_wr, NULL, ENV_CONFIG_RD, &conn2)) != 0)
+ testutil_die(ret, "wiredtiger_open write nolock");
+ if ((ret = wiredtiger_open(home_rd, NULL, ENV_CONFIG_RD, &conn3)) != 0)
+ testutil_die(ret, "wiredtiger_open readonly");
+ if ((ret = wiredtiger_open(home_rd2, NULL, ENV_CONFIG_RD, &conn4)) != 0)
+ testutil_die(ret, "wiredtiger_open readonly nolock");
+
+ /*
+ * Create a child to also open a connection handle to the databases.
+ * We cannot use fork here because using fork the child inherits the
+ * same memory image. Therefore the WT process structure is set in
+ * the child even though it should not be. So use 'system' to spawn
+ * an entirely new process.
+ */
+ (void)snprintf(
+ cmd, sizeof(cmd), "%s -h %s -R", saved_argv0, working_dir);
+ if ((status = system(cmd)) < 0)
+ testutil_die(status, "system");
+ /*
+ * The child will exit with success if its test passes.
+ */
+ if (WEXITSTATUS(status) != 0)
+ testutil_die(WEXITSTATUS(status), "system");
+
+ /*
+ * Scenario 2. Run child with writable config.
+ */
+ (void)snprintf(
+ cmd, sizeof(cmd), "%s -h %s -W", saved_argv0, working_dir);
+ if ((status = system(cmd)) < 0)
+ testutil_die(status, "system");
+
+ if (WEXITSTATUS(status) != 0)
+ testutil_die(WEXITSTATUS(status), "system");
+
+ /*
+ * Reopen the two writable directories and rerun the child.
+ */
+ if ((ret = conn->close(conn, NULL)) != 0)
+ testutil_die(ret, "WT_CONNECTION:close");
+ if ((ret = conn2->close(conn2, NULL)) != 0)
+ testutil_die(ret, "WT_CONNECTION:close");
+ if ((ret = wiredtiger_open(home, NULL, ENV_CONFIG_RD, &conn)) != 0)
+ testutil_die(ret, "wiredtiger_open original home");
+ if ((ret = wiredtiger_open(home_wr, NULL, ENV_CONFIG_RD, &conn2)) != 0)
+ testutil_die(ret, "wiredtiger_open write nolock");
+ /*
+ * Scenario 3. Child read-only.
+ */
+ (void)snprintf(
+ cmd, sizeof(cmd), "%s -h %s -R", saved_argv0, working_dir);
+ if ((status = system(cmd)) < 0)
+ testutil_die(status, "system");
+ if (WEXITSTATUS(status) != 0)
+ testutil_die(WEXITSTATUS(status), "system");
+
+ /*
+ * Scenario 4. Run child with writable config.
+ */
+ (void)snprintf(
+ cmd, sizeof(cmd), "%s -h %s -W", saved_argv0, working_dir);
+ if ((status = system(cmd)) < 0)
+ testutil_die(status, "system");
+ if (WEXITSTATUS(status) != 0)
+ testutil_die(WEXITSTATUS(status), "system");
+
+ /*
+ * Clean-up.
+ */
+ if ((ret = conn->close(conn, NULL)) != 0)
+ testutil_die(ret, "WT_CONNECTION:close");
+ if ((ret = conn2->close(conn2, NULL)) != 0)
+ testutil_die(ret, "WT_CONNECTION:close");
+ if ((ret = conn3->close(conn3, NULL)) != 0)
+ testutil_die(ret, "WT_CONNECTION:close");
+ if ((ret = conn4->close(conn4, NULL)) != 0)
+ testutil_die(ret, "WT_CONNECTION:close");
+ /*
+ * We need to chmod the read-only databases back so that they can
+ * be removed by scripts.
+ */
+ (void)snprintf(cmd, sizeof(cmd), "chmod 0777 %s %s", home_rd, home_rd2);
+ (void)system(cmd);
+ (void)snprintf(cmd, sizeof(cmd), "chmod -R 0666 %s/* %s/*",
+ home_rd, home_rd2);
+ (void)system(cmd);
+ printf(" *** Readonly test successful ***\n");
+ return (EXIT_SUCCESS);
+}