tdb: Add tdbdump -u
authorVolker Lendecke <vl@samba.org>
Tue, 15 Sep 2015 15:08:24 +0000 (17:08 +0200)
committerJeremy Allison <jra@samba.org>
Fri, 18 Sep 2015 01:04:35 +0000 (03:04 +0200)
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

lib/tdb/man/tdbdump.8.xml
lib/tdb/tools/tdbdump.c

index 31e6888862e2261423484b46e19e8becb7b97565..238e88161d14a262122d91c300dc17d0e9d1c370 100644 (file)
@@ -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>
                </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>
 
index 9a0a7fe6d4934f7531c27095d3ede6977941ee0e..aa5054023a710bc6412118322427ced46a71f302 100644 (file)
@@ -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);
 }