xref: /netbsd-src/external/bsd/openldap/dist/libraries/liblmdb/mdb_load.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
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