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