openldap/libraries/liblmdb/mdb_load.c

497 lines
11 KiB
C
Raw Normal View History

2014-06-20 23:49:59 +08:00
/* mdb_load.c - memory-mapped database load tool */
/*
2021-01-12 03:21:58 +08:00
* Copyright 2011-2021 Howard Chu, Symas Corp.
2014-06-20 23:49:59 +08:00
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in the file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include "lmdb.h"
#define PRINT 1
#define NOHDR 2
static int mode;
static char *subname = NULL;
static size_t lineno;
static int version;
static int flags;
static char *prog;
2014-07-03 22:49:22 +08:00
static int Eof;
2014-06-20 23:49:59 +08:00
2014-07-02 09:53:47 +08:00
static MDB_envinfo info;
2014-06-20 23:49:59 +08:00
static MDB_val kbuf, dbuf;
static MDB_val k0buf;
2014-06-20 23:49:59 +08:00
2014-07-03 22:49:22 +08:00
#ifdef _WIN32
#define Z "I"
#else
#define Z "z"
#endif
2014-06-20 23:49:59 +08:00
#define STRLENOF(s) (sizeof(s)-1)
typedef struct flagbit {
int bit;
char *name;
int len;
} flagbit;
#define S(s) s, STRLENOF(s)
flagbit dbflags[] = {
{ MDB_REVERSEKEY, S("reversekey") },
{ MDB_DUPSORT, S("dupsort") },
{ MDB_INTEGERKEY, S("integerkey") },
{ MDB_DUPFIXED, S("dupfixed") },
{ MDB_INTEGERDUP, S("integerdup") },
{ MDB_REVERSEDUP, S("reversedup") },
{ 0, NULL, 0 }
};
2014-07-21 23:11:33 +08:00
static void readhdr(void)
2014-06-20 23:49:59 +08:00
{
char *ptr;
flags = 0;
2014-06-20 23:49:59 +08:00
while (fgets(dbuf.mv_data, dbuf.mv_size, stdin) != NULL) {
lineno++;
if (!strncmp(dbuf.mv_data, "VERSION=", STRLENOF("VERSION="))) {
2014-06-21 23:34:45 +08:00
version=atoi((char *)dbuf.mv_data+STRLENOF("VERSION="));
2014-06-20 23:49:59 +08:00
if (version > 3) {
2014-07-03 22:49:22 +08:00
fprintf(stderr, "%s: line %" Z "d: unsupported VERSION %d\n",
2014-06-20 23:49:59 +08:00
prog, lineno, version);
exit(EXIT_FAILURE);
}
} else if (!strncmp(dbuf.mv_data, "HEADER=END", STRLENOF("HEADER=END"))) {
break;
} else if (!strncmp(dbuf.mv_data, "format=", STRLENOF("format="))) {
2014-06-21 23:34:45 +08:00
if (!strncmp((char *)dbuf.mv_data+STRLENOF("FORMAT="), "print", STRLENOF("print")))
2014-06-20 23:49:59 +08:00
mode |= PRINT;
2014-06-21 23:34:45 +08:00
else if (strncmp((char *)dbuf.mv_data+STRLENOF("FORMAT="), "bytevalue", STRLENOF("bytevalue"))) {
2014-07-03 22:49:22 +08:00
fprintf(stderr, "%s: line %" Z "d: unsupported FORMAT %s\n",
2014-06-20 23:49:59 +08:00
prog, lineno, (char *)dbuf.mv_data+STRLENOF("FORMAT="));
exit(EXIT_FAILURE);
}
} else if (!strncmp(dbuf.mv_data, "database=", STRLENOF("database="))) {
ptr = memchr(dbuf.mv_data, '\n', dbuf.mv_size);
if (ptr) *ptr = '\0';
if (subname) free(subname);
2014-06-21 23:34:45 +08:00
subname = strdup((char *)dbuf.mv_data+STRLENOF("database="));
2014-06-20 23:49:59 +08:00
} else if (!strncmp(dbuf.mv_data, "type=", STRLENOF("type="))) {
2014-06-21 23:34:45 +08:00
if (strncmp((char *)dbuf.mv_data+STRLENOF("type="), "btree", STRLENOF("btree"))) {
2014-07-03 22:49:22 +08:00
fprintf(stderr, "%s: line %" Z "d: unsupported type %s\n",
2014-06-20 23:49:59 +08:00
prog, lineno, (char *)dbuf.mv_data+STRLENOF("type="));
exit(EXIT_FAILURE);
}
2014-07-02 09:53:47 +08:00
} else if (!strncmp(dbuf.mv_data, "mapaddr=", STRLENOF("mapaddr="))) {
int i;
ptr = memchr(dbuf.mv_data, '\n', dbuf.mv_size);
if (ptr) *ptr = '\0';
i = sscanf((char *)dbuf.mv_data+STRLENOF("mapaddr="), "%p", &info.me_mapaddr);
if (i != 1) {
2014-07-03 22:49:22 +08:00
fprintf(stderr, "%s: line %" Z "d: invalid mapaddr %s\n",
2014-07-02 09:53:47 +08:00
prog, lineno, (char *)dbuf.mv_data+STRLENOF("mapaddr="));
exit(EXIT_FAILURE);
}
} else if (!strncmp(dbuf.mv_data, "mapsize=", STRLENOF("mapsize="))) {
int i;
ptr = memchr(dbuf.mv_data, '\n', dbuf.mv_size);
if (ptr) *ptr = '\0';
2014-07-03 22:49:22 +08:00
i = sscanf((char *)dbuf.mv_data+STRLENOF("mapsize="), "%" Z "u", &info.me_mapsize);
2014-07-02 09:53:47 +08:00
if (i != 1) {
2014-07-03 22:49:22 +08:00
fprintf(stderr, "%s: line %" Z "d: invalid mapsize %s\n",
2014-07-02 09:53:47 +08:00
prog, lineno, (char *)dbuf.mv_data+STRLENOF("mapsize="));
exit(EXIT_FAILURE);
}
} else if (!strncmp(dbuf.mv_data, "maxreaders=", STRLENOF("maxreaders="))) {
int i;
ptr = memchr(dbuf.mv_data, '\n', dbuf.mv_size);
if (ptr) *ptr = '\0';
i = sscanf((char *)dbuf.mv_data+STRLENOF("maxreaders="), "%u", &info.me_maxreaders);
if (i != 1) {
2014-07-03 22:49:22 +08:00
fprintf(stderr, "%s: line %" Z "d: invalid maxreaders %s\n",
2014-07-02 09:53:47 +08:00
prog, lineno, (char *)dbuf.mv_data+STRLENOF("maxreaders="));
exit(EXIT_FAILURE);
}
2014-06-20 23:49:59 +08:00
} else {
int i;
for (i=0; dbflags[i].bit; i++) {
if (!strncmp(dbuf.mv_data, dbflags[i].name, dbflags[i].len) &&
((char *)dbuf.mv_data)[dbflags[i].len] == '=') {
flags |= dbflags[i].bit;
break;
}
}
if (!dbflags[i].bit) {
ptr = memchr(dbuf.mv_data, '=', dbuf.mv_size);
if (!ptr) {
2014-07-03 22:49:22 +08:00
fprintf(stderr, "%s: line %" Z "d: unexpected format\n",
2014-06-20 23:49:59 +08:00
prog, lineno);
exit(EXIT_FAILURE);
} else {
*ptr = '\0';
2014-07-03 22:49:22 +08:00
fprintf(stderr, "%s: line %" Z "d: unrecognized keyword ignored: %s\n",
2014-06-20 23:49:59 +08:00
prog, lineno, (char *)dbuf.mv_data);
}
}
}
}
}
2014-07-21 23:11:33 +08:00
static void badend(void)
2014-06-20 23:49:59 +08:00
{
2014-07-03 22:49:22 +08:00
fprintf(stderr, "%s: line %" Z "d: unexpected end of input\n",
2014-06-20 23:49:59 +08:00
prog, lineno);
}
static int unhex(unsigned char *c2)
{
int x, c;
x = *c2++ & 0x4f;
if (x & 0x40)
2014-06-20 23:56:52 +08:00
x -= 55;
2014-06-20 23:49:59 +08:00
c = x << 4;
x = *c2 & 0x4f;
if (x & 0x40)
2014-06-20 23:56:52 +08:00
x -= 55;
2014-06-20 23:49:59 +08:00
c |= x;
return c;
}
static int readline(MDB_val *out, MDB_val *buf)
{
unsigned char *c1, *c2, *end;
size_t len, l2;
2014-06-20 23:49:59 +08:00
int c;
if (!(mode & NOHDR)) {
c = fgetc(stdin);
if (c == EOF) {
2014-07-03 22:49:22 +08:00
Eof = 1;
2014-06-20 23:49:59 +08:00
return EOF;
}
if (c != ' ') {
2014-06-21 00:03:41 +08:00
lineno++;
2014-06-20 23:49:59 +08:00
if (fgets(buf->mv_data, buf->mv_size, stdin) == NULL) {
badend:
2014-07-03 22:49:22 +08:00
Eof = 1;
2014-06-20 23:49:59 +08:00
badend();
return EOF;
}
if (c == 'D' && !strncmp(buf->mv_data, "ATA=END", STRLENOF("ATA=END")))
return EOF;
goto badend;
}
}
if (fgets(buf->mv_data, buf->mv_size, stdin) == NULL) {
2014-07-03 22:49:22 +08:00
Eof = 1;
2014-06-20 23:49:59 +08:00
return EOF;
}
lineno++;
c1 = buf->mv_data;
len = strlen((char *)c1);
l2 = len;
2014-06-20 23:49:59 +08:00
/* Is buffer too short? */
while (c1[len-1] != '\n') {
buf->mv_data = realloc(buf->mv_data, buf->mv_size*2);
if (!buf->mv_data) {
2014-07-03 22:49:22 +08:00
Eof = 1;
fprintf(stderr, "%s: line %" Z "d: out of memory, line too long\n",
2014-06-20 23:49:59 +08:00
prog, lineno);
return EOF;
}
c1 = buf->mv_data;
c1 += l2;
if (fgets((char *)c1, buf->mv_size+1, stdin) == NULL) {
2014-07-03 22:49:22 +08:00
Eof = 1;
2014-06-20 23:49:59 +08:00
badend();
return EOF;
}
buf->mv_size *= 2;
len = strlen((char *)c1);
l2 += len;
2014-06-20 23:49:59 +08:00
}
c1 = c2 = buf->mv_data;
len = l2;
2014-06-20 23:49:59 +08:00
c1[--len] = '\0';
end = c1 + len;
if (mode & PRINT) {
while (c2 < end) {
if (*c2 == '\\') {
if (c2[1] == '\\') {
*c1++ = *c2;
2014-06-20 23:49:59 +08:00
} else {
if (c2+3 > end || !isxdigit(c2[1]) || !isxdigit(c2[2])) {
2014-07-03 22:49:22 +08:00
Eof = 1;
2014-06-20 23:49:59 +08:00
badend();
return EOF;
}
*c1++ = unhex(++c2);
}
c2 += 2;
2014-06-20 23:49:59 +08:00
} else {
/* copies are redundant when no escapes were used */
*c1++ = *c2++;
2014-06-20 23:49:59 +08:00
}
}
} else {
/* odd length not allowed */
if (len & 1) {
2014-07-03 22:49:22 +08:00
Eof = 1;
2014-06-20 23:49:59 +08:00
badend();
return EOF;
}
while (c2 < end) {
if (!isxdigit(*c2) || !isxdigit(c2[1])) {
2014-07-03 22:49:22 +08:00
Eof = 1;
2014-06-20 23:49:59 +08:00
badend();
return EOF;
}
*c1++ = unhex(c2);
c2 += 2;
}
}
c2 = out->mv_data = buf->mv_data;
out->mv_size = c1 - c2;
return 0;
}
2014-07-21 23:11:33 +08:00
static void usage(void)
2014-06-20 23:49:59 +08:00
{
fprintf(stderr, "usage: %s [-V] [-a] [-f input] [-n] [-s name] [-N] [-T] dbpath\n", prog);
2014-06-20 23:49:59 +08:00
exit(EXIT_FAILURE);
}
static int greater(const MDB_val *a, const MDB_val *b)
{
return 1;
}
2014-06-20 23:49:59 +08:00
int main(int argc, char *argv[])
{
int i, rc;
MDB_env *env;
MDB_txn *txn;
MDB_cursor *mc;
MDB_dbi dbi;
char *envname;
int envflags = MDB_NOSYNC, putflags = 0;
int dohdr = 0, append = 0;
MDB_val prevk;
2014-06-20 23:49:59 +08:00
prog = argv[0];
if (argc < 2) {
2014-07-21 23:11:33 +08:00
usage();
2014-06-20 23:49:59 +08:00
}
/* -a: append records in input order
* -f: load file instead of stdin
2014-06-20 23:49:59 +08:00
* -n: use NOSUBDIR flag on env_open
* -s: load into named subDB
* -N: use NOOVERWRITE on puts
* -T: read plaintext
* -V: print version and exit
*/
while ((i = getopt(argc, argv, "af:ns:NTV")) != EOF) {
2014-06-20 23:49:59 +08:00
switch(i) {
case 'V':
printf("%s\n", MDB_VERSION_STRING);
exit(0);
break;
case 'a':
append = 1;
break;
2014-06-20 23:49:59 +08:00
case 'f':
if (freopen(optarg, "r", stdin) == NULL) {
fprintf(stderr, "%s: %s: reopen: %s\n",
prog, optarg, strerror(errno));
exit(EXIT_FAILURE);
}
break;
case 'n':
envflags |= MDB_NOSUBDIR;
break;
case 's':
subname = strdup(optarg);
break;
case 'N':
putflags = MDB_NOOVERWRITE|MDB_NODUPDATA;
break;
case 'T':
mode |= NOHDR | PRINT;
2014-06-20 23:49:59 +08:00
break;
default:
2014-07-21 23:11:33 +08:00
usage();
2014-06-20 23:49:59 +08:00
}
}
if (optind != argc - 1)
2014-07-21 23:11:33 +08:00
usage();
2014-06-20 23:49:59 +08:00
2014-07-02 09:53:47 +08:00
dbuf.mv_size = 4096;
dbuf.mv_data = malloc(dbuf.mv_size);
if (!(mode & NOHDR))
readhdr();
2014-06-20 23:49:59 +08:00
envname = argv[optind];
rc = mdb_env_create(&env);
2014-09-17 04:54:07 +08:00
if (rc) {
fprintf(stderr, "mdb_env_create failed, error %d %s\n", rc, mdb_strerror(rc));
return EXIT_FAILURE;
}
2014-06-20 23:49:59 +08:00
2014-06-21 00:00:33 +08:00
mdb_env_set_maxdbs(env, 2);
2014-06-20 23:49:59 +08:00
2014-07-02 09:53:47 +08:00
if (info.me_maxreaders)
mdb_env_set_maxreaders(env, info.me_maxreaders);
if (info.me_mapsize)
mdb_env_set_mapsize(env, info.me_mapsize);
if (info.me_mapaddr)
envflags |= MDB_FIXEDMAP;
2014-06-20 23:49:59 +08:00
rc = mdb_env_open(env, envname, envflags, 0664);
if (rc) {
2014-07-01 20:11:43 +08:00
fprintf(stderr, "mdb_env_open failed, error %d %s\n", rc, mdb_strerror(rc));
2014-06-20 23:49:59 +08:00
goto env_close;
}
kbuf.mv_size = mdb_env_get_maxkeysize(env) * 2 + 2;
kbuf.mv_data = malloc(kbuf.mv_size * 2);
k0buf.mv_size = kbuf.mv_size;
k0buf.mv_data = (char *)kbuf.mv_data + kbuf.mv_size;
prevk.mv_data = k0buf.mv_data;
2014-06-20 23:49:59 +08:00
2014-07-03 22:49:22 +08:00
while(!Eof) {
2014-06-20 23:49:59 +08:00
MDB_val key, data;
int batch = 0;
flags = 0;
int appflag;
2014-06-20 23:49:59 +08:00
2014-07-02 09:53:47 +08:00
if (!dohdr) {
dohdr = 1;
} else if (!(mode & NOHDR))
2014-06-20 23:49:59 +08:00
readhdr();
rc = mdb_txn_begin(env, NULL, 0, &txn);
if (rc) {
2014-07-01 20:11:43 +08:00
fprintf(stderr, "mdb_txn_begin failed, error %d %s\n", rc, mdb_strerror(rc));
2014-06-20 23:49:59 +08:00
goto env_close;
}
2014-06-21 00:00:33 +08:00
rc = mdb_open(txn, subname, flags|MDB_CREATE, &dbi);
2014-06-20 23:49:59 +08:00
if (rc) {
2014-07-01 20:11:43 +08:00
fprintf(stderr, "mdb_open failed, error %d %s\n", rc, mdb_strerror(rc));
2014-06-20 23:49:59 +08:00
goto txn_abort;
}
prevk.mv_size = 0;
if (append) {
mdb_set_compare(txn, dbi, greater);
if (flags & MDB_DUPSORT)
mdb_set_dupsort(txn, dbi, greater);
}
2014-06-20 23:49:59 +08:00
rc = mdb_cursor_open(txn, dbi, &mc);
if (rc) {
2014-07-01 20:11:43 +08:00
fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc));
2014-06-20 23:49:59 +08:00
goto txn_abort;
}
while(1) {
rc = readline(&key, &kbuf);
if (rc) /* rc == EOF */
2014-06-20 23:49:59 +08:00
break;
rc = readline(&data, &dbuf);
if (rc) {
fprintf(stderr, "%s: line %" Z "d: failed to read key value\n", prog, lineno);
2014-06-20 23:49:59 +08:00
goto txn_abort;
}
if (append) {
appflag = MDB_APPEND;
if (flags & MDB_DUPSORT) {
if (prevk.mv_size == key.mv_size && !memcmp(prevk.mv_data, key.mv_data, key.mv_size))
appflag = MDB_CURRENT|MDB_APPENDDUP;
else {
memcpy(prevk.mv_data, key.mv_data, key.mv_size);
prevk.mv_size = key.mv_size;
}
}
} else {
appflag = 0;
}
rc = mdb_cursor_put(mc, &key, &data, putflags|appflag);
2014-06-20 23:49:59 +08:00
if (rc == MDB_KEYEXIST && putflags)
continue;
if (rc) {
fprintf(stderr, "mdb_cursor_put failed, error %d %s\n", rc, mdb_strerror(rc));
2014-06-20 23:49:59 +08:00
goto txn_abort;
}
2014-06-20 23:49:59 +08:00
batch++;
if (batch == 100) {
rc = mdb_txn_commit(txn);
if (rc) {
2014-07-03 22:49:22 +08:00
fprintf(stderr, "%s: line %" Z "d: txn_commit: %s\n",
2014-06-20 23:49:59 +08:00
prog, lineno, mdb_strerror(rc));
goto env_close;
}
rc = mdb_txn_begin(env, NULL, 0, &txn);
if (rc) {
2014-07-01 20:11:43 +08:00
fprintf(stderr, "mdb_txn_begin failed, error %d %s\n", rc, mdb_strerror(rc));
2014-06-20 23:49:59 +08:00
goto env_close;
}
rc = mdb_cursor_open(txn, dbi, &mc);
if (rc) {
2014-07-01 20:11:43 +08:00
fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc));
2014-06-20 23:49:59 +08:00
goto txn_abort;
}
if (appflag & MDB_APPENDDUP) {
MDB_val k, d;
mdb_cursor_get(mc, &k, &d, MDB_LAST);
}
2014-06-20 23:49:59 +08:00
batch = 0;
}
}
rc = mdb_txn_commit(txn);
txn = NULL;
if (rc) {
2014-07-03 22:49:22 +08:00
fprintf(stderr, "%s: line %" Z "d: txn_commit: %s\n",
2014-06-20 23:49:59 +08:00
prog, lineno, mdb_strerror(rc));
goto env_close;
}
mdb_dbi_close(env, dbi);
}
txn_abort:
mdb_txn_abort(txn);
env_close:
mdb_env_close(env);
return rc ? EXIT_FAILURE : EXIT_SUCCESS;
}