1 /* $NetBSD: mdb_load.c,v 1.1.1.2 2018/02/06 01:53:08 christos Exp $ */ 2 3 /* mdb_load.c - memory-mapped database load tool */ 4 /* 5 * Copyright 2011-2017 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 /* copies are redundant when no escapes were used */ 254 *c1++ = *c2++; 255 } 256 } 257 } else { 258 /* odd length not allowed */ 259 if (len & 1) { 260 Eof = 1; 261 badend(); 262 return EOF; 263 } 264 while (c2 < end) { 265 if (!isxdigit(*c2) || !isxdigit(c2[1])) { 266 Eof = 1; 267 badend(); 268 return EOF; 269 } 270 *c1++ = unhex(c2); 271 c2 += 2; 272 } 273 } 274 c2 = out->mv_data = buf->mv_data; 275 out->mv_size = c1 - c2; 276 277 return 0; 278 } 279 280 static void usage(void) 281 { 282 fprintf(stderr, "usage: %s [-V] [-f input] [-n] [-s name] [-N] [-T] dbpath\n", prog); 283 exit(EXIT_FAILURE); 284 } 285 286 int main(int argc, char *argv[]) 287 { 288 int i, rc; 289 MDB_env *env; 290 MDB_txn *txn; 291 MDB_cursor *mc; 292 MDB_dbi dbi; 293 char *envname; 294 int envflags = 0, putflags = 0; 295 int dohdr = 0; 296 297 prog = argv[0]; 298 299 if (argc < 2) { 300 usage(); 301 } 302 303 /* -f: load file instead of stdin 304 * -n: use NOSUBDIR flag on env_open 305 * -s: load into named subDB 306 * -N: use NOOVERWRITE on puts 307 * -T: read plaintext 308 * -V: print version and exit 309 */ 310 while ((i = getopt(argc, argv, "f:ns:NTV")) != EOF) { 311 switch(i) { 312 case 'V': 313 printf("%s\n", MDB_VERSION_STRING); 314 exit(0); 315 break; 316 case 'f': 317 if (freopen(optarg, "r", stdin) == NULL) { 318 fprintf(stderr, "%s: %s: reopen: %s\n", 319 prog, optarg, strerror(errno)); 320 exit(EXIT_FAILURE); 321 } 322 break; 323 case 'n': 324 envflags |= MDB_NOSUBDIR; 325 break; 326 case 's': 327 subname = strdup(optarg); 328 break; 329 case 'N': 330 putflags = MDB_NOOVERWRITE|MDB_NODUPDATA; 331 break; 332 case 'T': 333 mode |= NOHDR | PRINT; 334 break; 335 default: 336 usage(); 337 } 338 } 339 340 if (optind != argc - 1) 341 usage(); 342 343 dbuf.mv_size = 4096; 344 dbuf.mv_data = malloc(dbuf.mv_size); 345 346 if (!(mode & NOHDR)) 347 readhdr(); 348 349 envname = argv[optind]; 350 rc = mdb_env_create(&env); 351 if (rc) { 352 fprintf(stderr, "mdb_env_create failed, error %d %s\n", rc, mdb_strerror(rc)); 353 return EXIT_FAILURE; 354 } 355 356 mdb_env_set_maxdbs(env, 2); 357 358 if (info.me_maxreaders) 359 mdb_env_set_maxreaders(env, info.me_maxreaders); 360 361 if (info.me_mapsize) 362 mdb_env_set_mapsize(env, info.me_mapsize); 363 364 if (info.me_mapaddr) 365 envflags |= MDB_FIXEDMAP; 366 367 rc = mdb_env_open(env, envname, envflags, 0664); 368 if (rc) { 369 fprintf(stderr, "mdb_env_open failed, error %d %s\n", rc, mdb_strerror(rc)); 370 goto env_close; 371 } 372 373 kbuf.mv_size = mdb_env_get_maxkeysize(env) * 2 + 2; 374 kbuf.mv_data = malloc(kbuf.mv_size); 375 376 while(!Eof) { 377 MDB_val key, data; 378 int batch = 0; 379 flags = 0; 380 381 if (!dohdr) { 382 dohdr = 1; 383 } else if (!(mode & NOHDR)) 384 readhdr(); 385 386 rc = mdb_txn_begin(env, NULL, 0, &txn); 387 if (rc) { 388 fprintf(stderr, "mdb_txn_begin failed, error %d %s\n", rc, mdb_strerror(rc)); 389 goto env_close; 390 } 391 392 rc = mdb_open(txn, subname, flags|MDB_CREATE, &dbi); 393 if (rc) { 394 fprintf(stderr, "mdb_open failed, error %d %s\n", rc, mdb_strerror(rc)); 395 goto txn_abort; 396 } 397 398 rc = mdb_cursor_open(txn, dbi, &mc); 399 if (rc) { 400 fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc)); 401 goto txn_abort; 402 } 403 404 while(1) { 405 rc = readline(&key, &kbuf); 406 if (rc) /* rc == EOF */ 407 break; 408 409 rc = readline(&data, &dbuf); 410 if (rc) { 411 fprintf(stderr, "%s: line %" Z "d: failed to read key value\n", prog, lineno); 412 goto txn_abort; 413 } 414 415 rc = mdb_cursor_put(mc, &key, &data, putflags); 416 if (rc == MDB_KEYEXIST && putflags) 417 continue; 418 if (rc) { 419 fprintf(stderr, "mdb_cursor_put failed, error %d %s\n", rc, mdb_strerror(rc)); 420 goto txn_abort; 421 } 422 batch++; 423 if (batch == 100) { 424 rc = mdb_txn_commit(txn); 425 if (rc) { 426 fprintf(stderr, "%s: line %" Z "d: txn_commit: %s\n", 427 prog, lineno, mdb_strerror(rc)); 428 goto env_close; 429 } 430 rc = mdb_txn_begin(env, NULL, 0, &txn); 431 if (rc) { 432 fprintf(stderr, "mdb_txn_begin failed, error %d %s\n", rc, mdb_strerror(rc)); 433 goto env_close; 434 } 435 rc = mdb_cursor_open(txn, dbi, &mc); 436 if (rc) { 437 fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc)); 438 goto txn_abort; 439 } 440 batch = 0; 441 } 442 } 443 rc = mdb_txn_commit(txn); 444 txn = NULL; 445 if (rc) { 446 fprintf(stderr, "%s: line %" Z "d: txn_commit: %s\n", 447 prog, lineno, mdb_strerror(rc)); 448 goto env_close; 449 } 450 mdb_dbi_close(env, dbi); 451 } 452 453 txn_abort: 454 mdb_txn_abort(txn); 455 env_close: 456 mdb_env_close(env); 457 458 return rc ? EXIT_FAILURE : EXIT_SUCCESS; 459 } 460