1 /* $NetBSD: mdb_load.c,v 1.1.1.1 2017/02/09 01:46:44 christos Exp $ */ 2 3 /* mdb_load.c - memory-mapped database load tool */ 4 /* 5 * Copyright 2011-2016 Howard Chu, Symas Corp. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted only as authorized by the OpenLDAP 10 * Public License. 11 * 12 * A copy of this license is available in the file LICENSE in the 13 * top-level directory of the distribution or, alternatively, at 14 * <http://www.OpenLDAP.org/license.html>. 15 */ 16 #include <stdio.h> 17 #include <stdlib.h> 18 #include <errno.h> 19 #include <string.h> 20 #include <ctype.h> 21 #include <unistd.h> 22 #include "lmdb.h" 23 24 #define PRINT 1 25 #define NOHDR 2 26 static int mode; 27 28 static char *subname = NULL; 29 30 static size_t lineno; 31 static int version; 32 33 static int flags; 34 35 static char *prog; 36 37 static int Eof; 38 39 static MDB_envinfo info; 40 41 static MDB_val kbuf, dbuf; 42 43 #ifdef _WIN32 44 #define Z "I" 45 #else 46 #define Z "z" 47 #endif 48 49 #define STRLENOF(s) (sizeof(s)-1) 50 51 typedef struct flagbit { 52 int bit; 53 char *name; 54 int len; 55 } flagbit; 56 57 #define S(s) s, STRLENOF(s) 58 59 flagbit dbflags[] = { 60 { MDB_REVERSEKEY, S("reversekey") }, 61 { MDB_DUPSORT, S("dupsort") }, 62 { MDB_INTEGERKEY, S("integerkey") }, 63 { MDB_DUPFIXED, S("dupfixed") }, 64 { MDB_INTEGERDUP, S("integerdup") }, 65 { MDB_REVERSEDUP, S("reversedup") }, 66 { 0, NULL, 0 } 67 }; 68 69 static void readhdr(void) 70 { 71 char *ptr; 72 73 while (fgets(dbuf.mv_data, dbuf.mv_size, stdin) != NULL) { 74 lineno++; 75 if (!strncmp(dbuf.mv_data, "VERSION=", STRLENOF("VERSION="))) { 76 version=atoi((char *)dbuf.mv_data+STRLENOF("VERSION=")); 77 if (version > 3) { 78 fprintf(stderr, "%s: line %" Z "d: unsupported VERSION %d\n", 79 prog, lineno, version); 80 exit(EXIT_FAILURE); 81 } 82 } else if (!strncmp(dbuf.mv_data, "HEADER=END", STRLENOF("HEADER=END"))) { 83 break; 84 } else if (!strncmp(dbuf.mv_data, "format=", STRLENOF("format="))) { 85 if (!strncmp((char *)dbuf.mv_data+STRLENOF("FORMAT="), "print", STRLENOF("print"))) 86 mode |= PRINT; 87 else if (strncmp((char *)dbuf.mv_data+STRLENOF("FORMAT="), "bytevalue", STRLENOF("bytevalue"))) { 88 fprintf(stderr, "%s: line %" Z "d: unsupported FORMAT %s\n", 89 prog, lineno, (char *)dbuf.mv_data+STRLENOF("FORMAT=")); 90 exit(EXIT_FAILURE); 91 } 92 } else if (!strncmp(dbuf.mv_data, "database=", STRLENOF("database="))) { 93 ptr = memchr(dbuf.mv_data, '\n', dbuf.mv_size); 94 if (ptr) *ptr = '\0'; 95 if (subname) free(subname); 96 subname = strdup((char *)dbuf.mv_data+STRLENOF("database=")); 97 } else if (!strncmp(dbuf.mv_data, "type=", STRLENOF("type="))) { 98 if (strncmp((char *)dbuf.mv_data+STRLENOF("type="), "btree", STRLENOF("btree"))) { 99 fprintf(stderr, "%s: line %" Z "d: unsupported type %s\n", 100 prog, lineno, (char *)dbuf.mv_data+STRLENOF("type=")); 101 exit(EXIT_FAILURE); 102 } 103 } else if (!strncmp(dbuf.mv_data, "mapaddr=", STRLENOF("mapaddr="))) { 104 int i; 105 ptr = memchr(dbuf.mv_data, '\n', dbuf.mv_size); 106 if (ptr) *ptr = '\0'; 107 i = sscanf((char *)dbuf.mv_data+STRLENOF("mapaddr="), "%p", &info.me_mapaddr); 108 if (i != 1) { 109 fprintf(stderr, "%s: line %" Z "d: invalid mapaddr %s\n", 110 prog, lineno, (char *)dbuf.mv_data+STRLENOF("mapaddr=")); 111 exit(EXIT_FAILURE); 112 } 113 } else if (!strncmp(dbuf.mv_data, "mapsize=", STRLENOF("mapsize="))) { 114 int i; 115 ptr = memchr(dbuf.mv_data, '\n', dbuf.mv_size); 116 if (ptr) *ptr = '\0'; 117 i = sscanf((char *)dbuf.mv_data+STRLENOF("mapsize="), "%" Z "u", &info.me_mapsize); 118 if (i != 1) { 119 fprintf(stderr, "%s: line %" Z "d: invalid mapsize %s\n", 120 prog, lineno, (char *)dbuf.mv_data+STRLENOF("mapsize=")); 121 exit(EXIT_FAILURE); 122 } 123 } else if (!strncmp(dbuf.mv_data, "maxreaders=", STRLENOF("maxreaders="))) { 124 int i; 125 ptr = memchr(dbuf.mv_data, '\n', dbuf.mv_size); 126 if (ptr) *ptr = '\0'; 127 i = sscanf((char *)dbuf.mv_data+STRLENOF("maxreaders="), "%u", &info.me_maxreaders); 128 if (i != 1) { 129 fprintf(stderr, "%s: line %" Z "d: invalid maxreaders %s\n", 130 prog, lineno, (char *)dbuf.mv_data+STRLENOF("maxreaders=")); 131 exit(EXIT_FAILURE); 132 } 133 } else { 134 int i; 135 for (i=0; dbflags[i].bit; i++) { 136 if (!strncmp(dbuf.mv_data, dbflags[i].name, dbflags[i].len) && 137 ((char *)dbuf.mv_data)[dbflags[i].len] == '=') { 138 flags |= dbflags[i].bit; 139 break; 140 } 141 } 142 if (!dbflags[i].bit) { 143 ptr = memchr(dbuf.mv_data, '=', dbuf.mv_size); 144 if (!ptr) { 145 fprintf(stderr, "%s: line %" Z "d: unexpected format\n", 146 prog, lineno); 147 exit(EXIT_FAILURE); 148 } else { 149 *ptr = '\0'; 150 fprintf(stderr, "%s: line %" Z "d: unrecognized keyword ignored: %s\n", 151 prog, lineno, (char *)dbuf.mv_data); 152 } 153 } 154 } 155 } 156 } 157 158 static void badend(void) 159 { 160 fprintf(stderr, "%s: line %" Z "d: unexpected end of input\n", 161 prog, lineno); 162 } 163 164 static int unhex(unsigned char *c2) 165 { 166 int x, c; 167 x = *c2++ & 0x4f; 168 if (x & 0x40) 169 x -= 55; 170 c = x << 4; 171 x = *c2 & 0x4f; 172 if (x & 0x40) 173 x -= 55; 174 c |= x; 175 return c; 176 } 177 178 static int readline(MDB_val *out, MDB_val *buf) 179 { 180 unsigned char *c1, *c2, *end; 181 size_t len, l2; 182 int c; 183 184 if (!(mode & NOHDR)) { 185 c = fgetc(stdin); 186 if (c == EOF) { 187 Eof = 1; 188 return EOF; 189 } 190 if (c != ' ') { 191 lineno++; 192 if (fgets(buf->mv_data, buf->mv_size, stdin) == NULL) { 193 badend: 194 Eof = 1; 195 badend(); 196 return EOF; 197 } 198 if (c == 'D' && !strncmp(buf->mv_data, "ATA=END", STRLENOF("ATA=END"))) 199 return EOF; 200 goto badend; 201 } 202 } 203 if (fgets(buf->mv_data, buf->mv_size, stdin) == NULL) { 204 Eof = 1; 205 return EOF; 206 } 207 lineno++; 208 209 c1 = buf->mv_data; 210 len = strlen((char *)c1); 211 l2 = len; 212 213 /* Is buffer too short? */ 214 while (c1[len-1] != '\n') { 215 buf->mv_data = realloc(buf->mv_data, buf->mv_size*2); 216 if (!buf->mv_data) { 217 Eof = 1; 218 fprintf(stderr, "%s: line %" Z "d: out of memory, line too long\n", 219 prog, lineno); 220 return EOF; 221 } 222 c1 = buf->mv_data; 223 c1 += l2; 224 if (fgets((char *)c1, buf->mv_size+1, stdin) == NULL) { 225 Eof = 1; 226 badend(); 227 return EOF; 228 } 229 buf->mv_size *= 2; 230 len = strlen((char *)c1); 231 l2 += len; 232 } 233 c1 = c2 = buf->mv_data; 234 len = l2; 235 c1[--len] = '\0'; 236 end = c1 + len; 237 238 if (mode & PRINT) { 239 while (c2 < end) { 240 if (*c2 == '\\') { 241 if (c2[1] == '\\') { 242 c1++; c2 += 2; 243 } else { 244 if (c2+3 > end || !isxdigit(c2[1]) || !isxdigit(c2[2])) { 245 Eof = 1; 246 badend(); 247 return EOF; 248 } 249 *c1++ = unhex(++c2); 250 c2 += 2; 251 } 252 } else { 253 c1++; c2++; 254 } 255 } 256 } else { 257 /* odd length not allowed */ 258 if (len & 1) { 259 Eof = 1; 260 badend(); 261 return EOF; 262 } 263 while (c2 < end) { 264 if (!isxdigit(*c2) || !isxdigit(c2[1])) { 265 Eof = 1; 266 badend(); 267 return EOF; 268 } 269 *c1++ = unhex(c2); 270 c2 += 2; 271 } 272 } 273 c2 = out->mv_data = buf->mv_data; 274 out->mv_size = c1 - c2; 275 276 return 0; 277 } 278 279 static void usage(void) 280 { 281 fprintf(stderr, "usage: %s [-V] [-f input] [-n] [-s name] [-N] [-T] dbpath\n", prog); 282 exit(EXIT_FAILURE); 283 } 284 285 int main(int argc, char *argv[]) 286 { 287 int i, rc; 288 MDB_env *env; 289 MDB_txn *txn; 290 MDB_cursor *mc; 291 MDB_dbi dbi; 292 char *envname; 293 int envflags = 0, putflags = 0; 294 int dohdr = 0; 295 296 prog = argv[0]; 297 298 if (argc < 2) { 299 usage(); 300 } 301 302 /* -f: load file instead of stdin 303 * -n: use NOSUBDIR flag on env_open 304 * -s: load into named subDB 305 * -N: use NOOVERWRITE on puts 306 * -T: read plaintext 307 * -V: print version and exit 308 */ 309 while ((i = getopt(argc, argv, "f:ns:NTV")) != EOF) { 310 switch(i) { 311 case 'V': 312 printf("%s\n", MDB_VERSION_STRING); 313 exit(0); 314 break; 315 case 'f': 316 if (freopen(optarg, "r", stdin) == NULL) { 317 fprintf(stderr, "%s: %s: reopen: %s\n", 318 prog, optarg, strerror(errno)); 319 exit(EXIT_FAILURE); 320 } 321 break; 322 case 'n': 323 envflags |= MDB_NOSUBDIR; 324 break; 325 case 's': 326 subname = strdup(optarg); 327 break; 328 case 'N': 329 putflags = MDB_NOOVERWRITE|MDB_NODUPDATA; 330 break; 331 case 'T': 332 mode |= NOHDR | PRINT; 333 break; 334 default: 335 usage(); 336 } 337 } 338 339 if (optind != argc - 1) 340 usage(); 341 342 dbuf.mv_size = 4096; 343 dbuf.mv_data = malloc(dbuf.mv_size); 344 345 if (!(mode & NOHDR)) 346 readhdr(); 347 348 envname = argv[optind]; 349 rc = mdb_env_create(&env); 350 if (rc) { 351 fprintf(stderr, "mdb_env_create failed, error %d %s\n", rc, mdb_strerror(rc)); 352 return EXIT_FAILURE; 353 } 354 355 mdb_env_set_maxdbs(env, 2); 356 357 if (info.me_maxreaders) 358 mdb_env_set_maxreaders(env, info.me_maxreaders); 359 360 if (info.me_mapsize) 361 mdb_env_set_mapsize(env, info.me_mapsize); 362 363 if (info.me_mapaddr) 364 envflags |= MDB_FIXEDMAP; 365 366 rc = mdb_env_open(env, envname, envflags, 0664); 367 if (rc) { 368 fprintf(stderr, "mdb_env_open failed, error %d %s\n", rc, mdb_strerror(rc)); 369 goto env_close; 370 } 371 372 kbuf.mv_size = mdb_env_get_maxkeysize(env) * 2 + 2; 373 kbuf.mv_data = malloc(kbuf.mv_size); 374 375 while(!Eof) { 376 MDB_val key, data; 377 int batch = 0; 378 flags = 0; 379 380 if (!dohdr) { 381 dohdr = 1; 382 } else if (!(mode & NOHDR)) 383 readhdr(); 384 385 rc = mdb_txn_begin(env, NULL, 0, &txn); 386 if (rc) { 387 fprintf(stderr, "mdb_txn_begin failed, error %d %s\n", rc, mdb_strerror(rc)); 388 goto env_close; 389 } 390 391 rc = mdb_open(txn, subname, flags|MDB_CREATE, &dbi); 392 if (rc) { 393 fprintf(stderr, "mdb_open failed, error %d %s\n", rc, mdb_strerror(rc)); 394 goto txn_abort; 395 } 396 397 rc = mdb_cursor_open(txn, dbi, &mc); 398 if (rc) { 399 fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc)); 400 goto txn_abort; 401 } 402 403 while(1) { 404 rc = readline(&key, &kbuf); 405 if (rc) /* rc == EOF */ 406 break; 407 408 rc = readline(&data, &dbuf); 409 if (rc) { 410 fprintf(stderr, "%s: line %" Z "d: failed to read key value\n", prog, lineno); 411 goto txn_abort; 412 } 413 414 rc = mdb_cursor_put(mc, &key, &data, putflags); 415 if (rc == MDB_KEYEXIST && putflags) 416 continue; 417 if (rc) { 418 fprintf(stderr, "mdb_cursor_put failed, error %d %s\n", rc, mdb_strerror(rc)); 419 goto txn_abort; 420 } 421 batch++; 422 if (batch == 100) { 423 rc = mdb_txn_commit(txn); 424 if (rc) { 425 fprintf(stderr, "%s: line %" Z "d: txn_commit: %s\n", 426 prog, lineno, mdb_strerror(rc)); 427 goto env_close; 428 } 429 rc = mdb_txn_begin(env, NULL, 0, &txn); 430 if (rc) { 431 fprintf(stderr, "mdb_txn_begin failed, error %d %s\n", rc, mdb_strerror(rc)); 432 goto env_close; 433 } 434 rc = mdb_cursor_open(txn, dbi, &mc); 435 if (rc) { 436 fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc)); 437 goto txn_abort; 438 } 439 batch = 0; 440 } 441 } 442 rc = mdb_txn_commit(txn); 443 txn = NULL; 444 if (rc) { 445 fprintf(stderr, "%s: line %" Z "d: txn_commit: %s\n", 446 prog, lineno, mdb_strerror(rc)); 447 goto env_close; 448 } 449 mdb_dbi_close(env, dbi); 450 } 451 452 txn_abort: 453 mdb_txn_abort(txn); 454 env_close: 455 mdb_env_close(env); 456 457 return rc ? EXIT_FAILURE : EXIT_SUCCESS; 458 } 459