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