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