summaryrefslogtreecommitdiff
path: root/lib/tdb
diff options
context:
space:
mode:
authorVolker Lendecke <vl@samba.org>2015-09-15 17:08:24 +0200
committerJeremy Allison <jra@samba.org>2015-09-18 03:04:35 +0200
commitea53e86c256bec08373dbe86e190a7f5a06eff9d (patch)
tree9eaa028c1add9f87767391637c507c6ec0487127 /lib/tdb
parent1d2a1a685ebdf479c511e01764e5148dbcbb37c9 (diff)
downloadsamba-ea53e86c256bec08373dbe86e190a7f5a06eff9d.tar.gz
tdb: Add tdbdump -u
The reverse of tdbdump Signed-off-by: Volker Lendecke <vl@samba.org> Reviewed-by: Jeremy Allison <jra@samba.org> Autobuild-User(master): Jeremy Allison <jra@samba.org> Autobuild-Date(master): Fri Sep 18 03:04:35 CEST 2015 on sn-devel-104
Diffstat (limited to 'lib/tdb')
-rw-r--r--lib/tdb/man/tdbdump.8.xml9
-rw-r--r--lib/tdb/tools/tdbdump.c285
2 files changed, 293 insertions, 1 deletions
diff --git a/lib/tdb/man/tdbdump.8.xml b/lib/tdb/man/tdbdump.8.xml
index 31e6888862e..238e88161d1 100644
--- a/lib/tdb/man/tdbdump.8.xml
+++ b/lib/tdb/man/tdbdump.8.xml
@@ -22,6 +22,7 @@
<command>tdbdump</command>
<arg choice="opt">-k <replaceable>keyname</replaceable></arg>
<arg choice="opt">-e</arg>
+ <arg choice="opt">-u</arg>
<arg choice="opt">-h</arg>
<arg choice="req">filename</arg>
</cmdsynopsis>
@@ -69,6 +70,14 @@
</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term>-u</term>
+ <listitem><para>
+ 'Undumps' a database, turning the tdbdump output on stdin
+ into a tdb again.
+ </para></listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
diff --git a/lib/tdb/tools/tdbdump.c b/lib/tdb/tools/tdbdump.c
index 9a0a7fe6d49..aa5054023a7 100644
--- a/lib/tdb/tools/tdbdump.c
+++ b/lib/tdb/tools/tdbdump.c
@@ -23,6 +23,7 @@
#include "system/filesys.h"
#include "system/wait.h"
#include "tdb.h"
+#include <regex.h>
static void print_data(TDB_DATA d)
{
@@ -134,18 +135,293 @@ static int dump_tdb(const char *fname, const char *keyname, bool emergency)
return 0;
}
+static bool file_parse_lines(FILE *f,
+ bool (*cb)(char *buf, size_t buflen,
+ void *private_data),
+ void *private_data)
+{
+ char *buf;
+ size_t buflen;
+
+ buflen = 1024;
+ buf = malloc(1024);
+ if (buf == NULL) {
+ return false;
+ }
+
+ while (true) {
+ size_t pos = 0;
+ int c;
+ bool ok;
+
+ while ((c = fgetc(f)) != EOF) {
+
+ buf[pos++] = c;
+
+ if (pos == (buflen-1)) {
+ char *tmp;
+ tmp = realloc(buf, buflen*2);
+ if (tmp == NULL) {
+ free(buf);
+ return false;
+ }
+ buf = tmp;
+ buflen *= 2;
+ }
+
+ if (c == '\n') {
+ break;
+ }
+ }
+
+ if (c == EOF) {
+ free(buf);
+ return (pos == 0);
+ }
+
+ buf[pos] = '\0';
+
+ ok = cb(buf, buflen, private_data);
+ if (!ok) {
+ break;
+ }
+ }
+ free(buf);
+ return true;
+}
+
+struct undump_state {
+ struct tdb_context *tdb;
+ TDB_DATA key;
+ TDB_DATA data;
+ int line;
+};
+
+static ssize_t match_len(const regmatch_t *m, size_t buflen)
+{
+ if ((m->rm_eo < m->rm_so) ||
+ (m->rm_eo > buflen) || (m->rm_so > buflen)) {
+ return -1;
+ }
+ return m->rm_eo - m->rm_so;
+}
+
+static int nibble(char c)
+{
+ if ((c >= '0') && (c <= '9')) {
+ return c - '0';
+ }
+ if ((c >= 'A') && (c <= 'F')) {
+ return c - 'A' + 10;
+ }
+ if ((c >= 'a') && (c <= 'f')) {
+ return c - 'a' + 10;
+ }
+ return -1;
+}
+
+static bool undump_regmatch(int line, char *buf, size_t buflen,
+ const regmatch_t *nummatch,
+ const regmatch_t *datamatch,
+ TDB_DATA *pret)
+{
+ ssize_t numlen = match_len(nummatch, buflen);
+ ssize_t datalen = match_len(datamatch, buflen);
+ long long num;
+ size_t col;
+
+ TDB_DATA ret = {0};
+
+ if ((numlen == -1) || (datalen == -1)) {
+ fprintf(stderr, "No matches in line %d\n", line);
+ return false;
+ }
+
+ {
+ char numbuf[numlen+1];
+ memcpy(numbuf, buf+nummatch->rm_so, numlen);
+ numbuf[numlen] = '\0';
+ num = atoll(numbuf);
+ }
+
+ if (num == 0) {
+ *pret = ret;
+ return true;
+ }
+
+ ret.dptr = malloc(datalen);
+ if (ret.dptr == NULL) {
+ fprintf(stderr, "malloc failed for line %d\n", line);
+ return false;
+ }
+
+ col = datamatch->rm_so;
+ while (col < datamatch->rm_eo) {
+ int n;
+
+ if (buf[col] != '\\') {
+ ret.dptr[ret.dsize++] = buf[col++];
+ continue;
+ }
+
+ if ((datamatch->rm_eo - col) < 3) {
+ fprintf(stderr, "hex char too short in line %d, "
+ "col %d\n", line, (int)col);
+ goto fail;
+ }
+
+ n = nibble(buf[col+1]);
+ if (n == -1) {
+ fprintf(stderr, "Could not convert '%c' in line %d "
+ "col %d\n", buf[col+1], line, (int)col);
+ goto fail;
+ }
+ ret.dptr[ret.dsize] = n << 4;
+
+ n = nibble(buf[col+2]);
+ if (n == -1) {
+ fprintf(stderr, "Could not convert '%c' in line %d, "
+ "col %d\n", buf[col+2], line, (int)col);
+ goto fail;
+ }
+ ret.dptr[ret.dsize] |= n;
+
+ ret.dsize += 1;
+ col += 3;
+ }
+
+ if (ret.dsize != num) {
+ fprintf(stderr, "Expected %d chars, got %d in line %d\n",
+ (int)num, (int)ret.dsize, line);
+ goto fail;
+ }
+
+ *pret = ret;
+ return true;
+
+fail:
+ free(ret.dptr);
+ return false;
+}
+
+static bool undump_cb(char *buf, size_t buflen, void *private_data)
+{
+ struct undump_state *state = private_data;
+ regex_t regex;
+ regmatch_t matches[3];
+ int ret;
+ bool ok;
+
+ state->line++;
+
+ ret = regcomp(&regex, "^key(\\([[:digit:]]*\\)) = \"\\(.*\\)\"\n$", 0);
+ if (ret != 0) {
+ return false;
+ }
+
+ ret = regexec(&regex, buf, 3, matches, 0);
+ if (ret == 0) {
+ if (state->key.dsize != 0) {
+ fprintf(stderr, "line %d has duplicate key\n",
+ state->line);
+ regfree(&regex);
+ return false;
+ }
+ ok = undump_regmatch(state->line, buf, buflen,
+ &matches[1], &matches[2],
+ &state->key);
+ if (!ok) {
+ regfree(&regex);
+ return false;
+ }
+ }
+ regfree(&regex);
+
+ ret = regcomp(&regex, "^data(\\([[:digit:]]*\\)) = \"\\(.*\\)\"\n$",
+ 0);
+ if (ret != 0) {
+ return false;
+ }
+
+ ret = regexec(&regex, buf, 3, matches, 0);
+ if (ret == 0) {
+ if (state->key.dsize == 0) {
+ fprintf(stderr, "line %d has data without key\n",
+ state->line);
+ regfree(&regex);
+ return false;
+ }
+ if (state->data.dsize != 0) {
+ fprintf(stderr, "line %d has duplicate data\n",
+ state->line);
+ regfree(&regex);
+ return false;
+ }
+ ok = undump_regmatch(state->line, buf, buflen,
+ &matches[1], &matches[2],
+ &state->data);
+ if (!ok) {
+ return false;
+ }
+
+ ret = tdb_store(state->tdb, state->key, state->data, 0);
+
+ free(state->key.dptr);
+ state->key = (TDB_DATA) {0};
+
+ free(state->data.dptr);
+ state->data = (TDB_DATA) {0};
+
+ if (ret == -1) {
+ fprintf(stderr, "tdb_store for line %d failed: %s\n",
+ state->line, tdb_errorstr(state->tdb));
+ return false;
+ }
+ }
+
+ regfree(&regex);
+
+ return true;
+}
+
+static int undump_tdb(const char *fname)
+{
+ struct tdb_logging_context logfn = { log_stderr };
+ struct undump_state state = {0};
+ bool ok;
+
+ state.tdb = tdb_open_ex(fname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600,
+ &logfn, NULL);
+ if (state.tdb == NULL) {
+ printf("Failed to open %s\n", fname);
+ return 1;
+ }
+
+ ok = file_parse_lines(stdin, undump_cb, &state);
+ if (!ok) {
+ printf("Failed to parse stdin\n");
+ return 1;
+ }
+
+ tdb_close(state.tdb);
+
+ return 0;
+}
+
static void usage( void)
{
printf( "Usage: tdbdump [options] <filename>\n\n");
printf( " -h this help message\n");
printf( " -k keyname dumps value of keyname\n");
printf( " -e emergency dump, for corrupt databases\n");
+ printf( " -u undump stdin\n");
}
int main(int argc, char *argv[])
{
char *fname, *keyname=NULL;
bool emergency = false;
+ bool undump = false;
int c;
if (argc < 2) {
@@ -153,7 +429,7 @@ static void usage( void)
exit(1);
}
- while ((c = getopt( argc, argv, "hk:e")) != -1) {
+ while ((c = getopt( argc, argv, "hk:eu")) != -1) {
switch (c) {
case 'h':
usage();
@@ -164,6 +440,9 @@ static void usage( void)
case 'e':
emergency = true;
break;
+ case 'u':
+ undump = true;
+ break;
default:
usage();
exit( 1);
@@ -172,5 +451,9 @@ static void usage( void)
fname = argv[optind];
+ if (undump) {
+ return undump_tdb(fname);
+ }
+
return dump_tdb(fname, keyname, emergency);
}