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