From e342ed0c1fcfad2c8478397c7496bd972b4d4e7e Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Tue, 15 Sep 2015 17:08:24 +0200 Subject: [PATCH] tdb: Add tdbdump -u The reverse of tdbdump Signed-off-by: Volker Lendecke --- lib/tdb/man/tdbdump.8.xml | 9 ++ lib/tdb/tools/tdbdump.c | 285 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 293 insertions(+), 1 deletion(-) diff --git a/lib/tdb/man/tdbdump.8.xml b/lib/tdb/man/tdbdump.8.xml index 31e6888..238e881 100644 --- a/lib/tdb/man/tdbdump.8.xml +++ b/lib/tdb/man/tdbdump.8.xml @@ -22,6 +22,7 @@ tdbdump -k keyname -e + -u -h filename @@ -69,6 +70,14 @@ + + -u + + 'Undumps' a database, turning the tdbdump output on stdin + into a tdb again. + + + diff --git a/lib/tdb/tools/tdbdump.c b/lib/tdb/tools/tdbdump.c index 9a0a7fe..aa50540 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 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(®ex, "^key(\\([[:digit:]]*\\)) = \"\\(.*\\)\"\n$", 0); + if (ret != 0) { + return false; + } + + ret = regexec(®ex, buf, 3, matches, 0); + if (ret == 0) { + if (state->key.dsize != 0) { + fprintf(stderr, "line %d has duplicate key\n", + state->line); + regfree(®ex); + return false; + } + ok = undump_regmatch(state->line, buf, buflen, + &matches[1], &matches[2], + &state->key); + if (!ok) { + regfree(®ex); + return false; + } + } + regfree(®ex); + + ret = regcomp(®ex, "^data(\\([[:digit:]]*\\)) = \"\\(.*\\)\"\n$", + 0); + if (ret != 0) { + return false; + } + + ret = regexec(®ex, buf, 3, matches, 0); + if (ret == 0) { + if (state->key.dsize == 0) { + fprintf(stderr, "line %d has data without key\n", + state->line); + regfree(®ex); + return false; + } + if (state->data.dsize != 0) { + fprintf(stderr, "line %d has duplicate data\n", + state->line); + regfree(®ex); + 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(®ex); + + 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] \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); } -- 1.9.1