1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-12 09:18:10 +03:00

libctdb: test: add database save and restore

Once we do operations which alter the TDBs, we need to restore them to
pristine state after a failed child dies.

The method used here is a terrible hack: it should at least do a
tdb_lockall() on the database before blatting it.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>


(This used to be ctdb commit d48ec16bd2b4932442d95fc43bea52baa0425501)
This commit is contained in:
Rusty Russell 2010-06-21 15:00:46 +09:30
parent d2def0ee1f
commit 0be60b6a7a
5 changed files with 148 additions and 1 deletions

View File

@ -447,6 +447,7 @@ int main(int argc, char *argv[])
"unfulfilled remaining. / "
"Testing blossoms fail.");
check_allocations();
check_databases();
dump_failinfo();
return EXIT_SUCCESS;

View File

@ -16,4 +16,11 @@ struct ctdb_connection *get_ctdb(void);
/* Talloc bytes from an fd until EOF. Nul terminate. */
void *grab_fd(int fd, size_t *size);
/* Check the databases are still ok. */
void check_databases(void);
/* Save and restore databases, in case children do damage. */
void *save_databases(void);
void restore_databases(void *);
#endif /* __HAVE_CTDB_TEST_H */

View File

@ -0,0 +1,134 @@
/*
database code for libctdb
Copyright (C) Rusty Russell 2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <unistd.h>
#include <err.h>
#include <talloc.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include "utils.h"
#include "log.h"
#include "ctdb-test.h"
#include <tdb.h>
#include <ctdb_protocol.h>
/* FIXME */
#define DB_PATH "/tmp/ctdbd-test/dbs/"
static int check_header(TDB_DATA key, TDB_DATA data, void *unused)
{
struct ctdb_ltdb_header *hdr = (void *)data.dptr;
if (data.dsize < sizeof(*hdr)) {
log_line(LOG_ALWAYS, "tdb entry '%.*s' is truncated",
key.dsize, key.dptr);
return -1;
}
/* Currently a single-node cluster. */
if (hdr->dmaster != 0) {
log_line(LOG_ALWAYS, "tdb entry '%.*s' dmaster %u",
key.dsize, key.dptr, hdr->dmaster);
return -1;
}
/* Currently a single-node cluster. */
if (hdr->laccessor != 0) {
log_line(LOG_ALWAYS, "tdb entry '%.*s' laccessor %u",
key.dsize, key.dptr, hdr->laccessor);
return -1;
}
return 0;
}
static void check_database(const char *name)
{
struct tdb_context *tdb = tdb_open(name, 0, TDB_DEFAULT, O_RDWR, 0);
if (!tdb)
err(1, "Opening tdb %s", name);
if (tdb_check(tdb, check_header, NULL) != 0) {
log_line(LOG_ALWAYS, "tdb %s is corrupt", name);
exit(EXIT_FAILURE);
}
tdb_close(tdb);
}
void check_databases(void)
{
struct dirent *ent;
DIR *d = opendir(DB_PATH);
if (!d)
err(1, "Reading directory %s", DB_PATH);
while ((ent = readdir(d)) != NULL) {
if (strends(ent->d_name, ".tdb.0")) {
char *fullpath = talloc_asprintf(NULL, "%s/%s",
DB_PATH, ent->d_name);
check_database(fullpath);
talloc_free(fullpath);
}
}
closedir(d);
}
/* FIXME: We assume we don't need locks here. Not a solid assumption! */
void *save_databases(void)
{
struct tdb_context *tdb = tdb_open(NULL, 0, TDB_INTERNAL, 0, 0);
struct dirent *ent;
DIR *d = opendir(DB_PATH);
if (!d)
err(1, "Reading directory %s", DB_PATH);
while ((ent = readdir(d)) != NULL) {
if (strends(ent->d_name, ".tdb.0")) {
TDB_DATA data, key;
int fd;
char *fullpath = talloc_asprintf(NULL, "%s/%s",
DB_PATH, ent->d_name);
fd = open(fullpath, O_RDONLY);
if (fd < 0)
err(1, "Saving tdb %s", fullpath);
data.dptr = grab_fd(fd, &data.dsize);
key.dptr = (void *)fullpath;
key.dsize = strlen(fullpath) + 1;
tdb_store(tdb, key, data, TDB_INSERT);
talloc_free(fullpath);
close(fd);
}
}
closedir(d);
return tdb;
}
void restore_databases(void *_tdb)
{
struct tdb_context *tdb = _tdb;
TDB_DATA key, data;
for (key = tdb_firstkey(tdb); key.dptr; key = tdb_nextkey(tdb, key)) {
int fd = open((char *)key.dptr, O_WRONLY);
if (fd < 0)
err(1, "Restoring tdb %s", (char *)key.dptr);
data = tdb_fetch(tdb, key);
write(fd, data.dptr, data.dsize);
free(data.dptr);
close(fd);
}
tdb_close(tdb);
}

View File

@ -261,6 +261,7 @@ bool should_i_fail(const char *func, const char *caller)
size_t log_size;
char *log;
char *location = make_location(func, caller);
void *databases;
if (failpath)
return do_failpath(location);
@ -286,6 +287,8 @@ bool should_i_fail(const char *func, const char *caller)
if (pipe(pfd) != 0)
err(1, "pipe failed for failtest!");
databases = save_databases();
fflush(stdout);
child = fork();
if (child == -1)
@ -319,10 +322,11 @@ bool should_i_fail(const char *func, const char *caller)
if (WIFEXITED(status) && (WEXITSTATUS(status) == EXIT_SUCCESS
|| WEXITSTATUS(status) == EXIT_SCRIPTFAIL)) {
talloc_free(log);
restore_databases(databases);
return false;
}
/* Reproduce child's path */
/* Reproduce child's path: leave databases for post-mortem. */
dec->failed = true;
log_line(LOG_ALWAYS, "Child %s %i on failure path: %s",

View File

@ -196,6 +196,7 @@ void script_fail(const char *fmt, ...)
talloc_free(str);
check_allocations();
check_databases();
exit(EXIT_SCRIPTFAIL);
}