]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/dbif.c
6b5f455aed70d1e4e035f15888671ea02381fb31
[netatalk.git] / etc / cnid_dbd / dbif.c
1 /*
2  * $Id: dbif.c,v 1.9 2009-05-10 08:08:28 franklahm Exp $
3  *
4  * Copyright (C) Joerg Lenneis 2003
5  * Copyright (C) Frank Lahm 2009
6  * All Rights Reserved.  See COPYING.
7  */
8
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif /* HAVE_CONFIG_H */
12
13 #include <stdio.h>
14 #include <errno.h>
15 #include <stdlib.h>
16 #ifdef HAVE_SYS_TYPES_H
17 #include <sys/types.h>
18 #endif /* HAVE_SYS_TYPES_H */
19 #include <string.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <sys/cdefs.h>
23 #include <unistd.h>
24 #include <atalk/logger.h>
25 #include <db.h>
26 #include "db_param.h"
27 #include "dbif.h"
28 #include "pack.h"
29
30 #define DB_ERRLOGFILE "db_errlog"
31
32 static char *old_dbfiles[] = {"cnid.db", NULL};
33
34 /* --------------- */
35 static int upgrade_required()
36 {
37     int i;
38     int found = 0;
39     struct stat st;
40
41     for (i = 0; old_dbfiles[i] != NULL; i++) {
42         if ( !(stat(old_dbfiles[i], &st) < 0) ) {
43             found++;
44             continue;
45         }
46         if (errno != ENOENT) {
47             LOG(log_error, logtype_cnid, "cnid_open: Checking %s gave %s", old_dbfiles[i], strerror(errno));
48             found++;
49         }
50     }
51     return found;
52 }
53
54 /* --------------- */
55 int dbif_stamp(DBD *dbd, void *buffer, int size)
56 {
57     struct stat st;
58     int         rc;
59
60     if (size < 8)
61         return -1;
62
63     if ((rc = stat(dbd->db_table[DBIF_CNID].name, &st)) < 0) {
64         LOG(log_error, logtype_cnid, "error stating database %s: %s", dbd->db_table[DBIF_CNID].name, db_strerror(rc));
65         return -1;
66     }
67     memset(buffer, 0, size);
68     memcpy(buffer, &st.st_ctime, sizeof(st.st_ctime));
69
70     return 0;
71 }
72
73 /* --------------- */
74 DBD *dbif_init(const char *filename)
75 {
76     DBD *dbd;
77
78     if ( NULL == (dbd = calloc(sizeof(DBD), 1)) )
79         return NULL;
80
81     /* filename == NULL means in memory db */
82     if (filename) {
83         dbd->db_filename = strdup(filename);
84         if (NULL == dbd->db_filename) {
85             free(dbd);
86             return NULL;
87         }
88     }
89     
90     dbd->db_table[DBIF_CNID].name        = "cnid2.db";
91     dbd->db_table[DBIF_IDX_DEVINO].name  = "devino.db";
92     dbd->db_table[DBIF_IDX_DIDNAME].name = "didname.db";
93
94     dbd->db_table[DBIF_CNID].type        = DB_BTREE;
95     dbd->db_table[DBIF_IDX_DEVINO].type  = DB_BTREE;
96     dbd->db_table[DBIF_IDX_DIDNAME].type = DB_BTREE;
97
98     dbd->db_table[DBIF_CNID].openflags        = DB_CREATE;
99     dbd->db_table[DBIF_IDX_DEVINO].openflags  = DB_CREATE;
100     dbd->db_table[DBIF_IDX_DIDNAME].openflags = DB_CREATE;
101
102     return dbd;
103 }
104
105 /* --------------- */
106 /*
107  *  We assume our current directory is already the BDB homedir. Otherwise
108  *  opening the databases will not work as expected.
109  */
110 int dbif_env_open(DBD *dbd, struct db_param *dbp, uint32_t dbenv_oflags)
111 {
112     int ret;
113     char **logfiles = NULL;
114     char **file;
115
116     /* Refuse to do anything if this is an old version of the CNID database */
117     if (upgrade_required()) {
118         LOG(log_error, logtype_cnid, "Found version 1 of the CNID database. Please upgrade to version 2");
119         return -1;
120     }
121
122     if ((dbd->db_errlog = fopen(DB_ERRLOGFILE, "a")) == NULL)
123         LOG(log_warning, logtype_cnid, "error creating/opening DB errlogfile: %s", strerror(errno));
124
125     if ((ret = db_env_create(&dbd->db_env, 0))) {
126         LOG(log_error, logtype_cnid, "error creating DB environment: %s",
127             db_strerror(ret));
128         dbd->db_env = NULL;
129         return -1;
130     }
131
132     if (dbd->db_errlog != NULL) {
133         dbd->db_env->set_errfile(dbd->db_env, dbd->db_errlog);
134         dbd->db_env->set_msgfile(dbd->db_env, dbd->db_errlog);
135     }
136
137     dbd->db_env->set_verbose(dbd->db_env, DB_VERB_RECOVERY, 1);
138
139     if (dbenv_oflags & DB_RECOVER) {
140         /* Open the database for recovery using DB_PRIVATE option which is faster */
141         if ((ret = dbd->db_env->open(dbd->db_env, ".", dbenv_oflags | DB_PRIVATE, 0))) {
142             LOG(log_error, logtype_cnid, "error opening DB environment: %s",
143                 db_strerror(ret));
144             dbd->db_env->close(dbd->db_env, 0);
145             dbd->db_env = NULL;
146             return -1;
147         }
148         dbenv_oflags = (dbenv_oflags & ~DB_RECOVER);
149
150         if (dbd->db_errlog != NULL)
151             fflush(dbd->db_errlog);
152
153         if ((ret = dbd->db_env->close(dbd->db_env, 0))) {
154             LOG(log_error, logtype_cnid, "error closing DB environment after recovery: %s",
155                 db_strerror(ret));
156             dbd->db_env = NULL;
157             return -1;
158         }
159
160         if ((ret = db_env_create(&dbd->db_env, 0))) {
161             LOG(log_error, logtype_cnid, "error creating DB environment after recovery: %s",
162                 db_strerror(ret));
163             dbd->db_env = NULL;
164             return -1;
165         }
166     }
167
168     if ((ret = dbd->db_env->set_cachesize(dbd->db_env, 0, 1024 * dbp->cachesize, 0))) {
169         LOG(log_error, logtype_cnid, "error setting DB environment cachesize to %i: %s",
170             dbp->cachesize, db_strerror(ret));
171         dbd->db_env->close(dbd->db_env, 0);
172         dbd->db_env = NULL;
173         return -1;
174     }
175
176     if (dbd->db_errlog != NULL) {
177         dbd->db_env->set_errfile(dbd->db_env, dbd->db_errlog);
178         dbd->db_env->set_msgfile(dbd->db_env, dbd->db_errlog);
179     }
180     if ((ret = dbd->db_env->open(dbd->db_env, ".", dbenv_oflags, 0))) {
181         LOG(log_error, logtype_cnid, "error opening DB environment after recovery: %s",
182             db_strerror(ret));
183         dbd->db_env->close(dbd->db_env, 0);
184         dbd->db_env = NULL;
185         return -1;
186     }
187
188     if ((ret = dbd->db_env->set_flags(dbd->db_env, DB_AUTO_COMMIT, 1))) {
189         LOG(log_error, logtype_cnid, "error setting DB_AUTO_COMMIT flag: %s",
190             db_strerror(ret));
191         dbd->db_env->close(dbd->db_env, 0);
192         dbd->db_env = NULL;
193         return -1;
194     }
195
196     if (dbp->logfile_autoremove) {
197         if (dbd->db_env->log_archive(dbd->db_env, &logfiles, 0)) {
198             LOG(log_error, logtype_cnid, "error getting list of stale logfiles: %s",
199                 db_strerror(ret));
200             dbd->db_env->close(dbd->db_env, 0);
201             dbd->db_env = NULL;
202             return -1;
203         }
204         if (logfiles != NULL) {
205             for (file = logfiles; *file != NULL; file++) {
206                 if (unlink(*file) < 0)
207                     LOG(log_warning, logtype_cnid, "Error removing stale logfile %s: %s", *file, strerror(errno));
208             }
209             free(logfiles);
210         }
211
212 #if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 7)
213         if ((ret = dbd->db_env->log_set_config(dbd->db_env, DB_LOG_AUTO_REMOVE, 1))) {
214             LOG(log_error, logtype_cnid, "error setting DB_LOG_AUTO_REMOVE flag: %s",
215             db_strerror(ret));
216             dbd->db_env->close(dbd->db_env, 0);
217             dbd->db_env = NULL;
218             return -1;
219         }
220 #else
221         if ((ret = dbd->db_env->set_flags(dbd->db_env, DB_LOG_AUTOREMOVE, 1))) {
222             LOG(log_error, logtype_cnid, "error setting DB_LOG_AUTOREMOVE flag: %s",
223                 db_strerror(ret));
224             dbd->db_env->close(dbd->db_env, 0);
225             dbd->db_env = NULL;
226             return -1;
227         }
228 #endif
229     }
230
231     return 0;
232 }
233
234 /* --------------- */
235 int dbif_open(DBD *dbd, struct db_param *dbp _U_, int do_truncate)
236 {
237     int ret;
238     int i;
239     u_int32_t count;
240
241     for (i = 0; i != DBIF_DB_CNT; i++) {
242         if ((ret = db_create(&dbd->db_table[i].db, dbd->db_env, 0))) {
243             LOG(log_error, logtype_cnid, "error creating handle for database %s: %s",
244                 dbd->db_table[i].name, db_strerror(ret));
245             return -1;
246         }
247
248         if (dbd->db_table[i].flags) {
249             if ((ret = dbd->db_table[i].db->set_flags(dbd->db_table[i].db,
250                                                       dbd->db_table[i].flags))) {
251                 LOG(log_error, logtype_cnid, "error setting flags for database %s: %s",
252                     dbd->db_table[i].name, db_strerror(ret));
253                 return -1;
254             }
255         }
256
257         if (dbd->db_table[i].db->open(dbd->db_table[i].db,
258                                       dbd->db_txn,
259                                       dbd->db_filename,
260                                       dbd->db_table[i].name,
261                                       dbd->db_table[i].type,
262                                       dbd->db_table[i].openflags,
263                                       0664) < 0) {
264             LOG(log_error, logtype_cnid, "Cant open database");
265             return -1;
266         }
267         if (dbd->db_errlog != NULL)
268             dbd->db_table[i].db->set_errfile(dbd->db_table[i].db, dbd->db_errlog);
269
270         if (do_truncate && i > 0) {
271             LOG(log_info, logtype_cnid, "Truncating CNID index.");
272             if ((ret = dbd->db_table[i].db->truncate(dbd->db_table[i].db, NULL, &count, 0))) {
273                 LOG(log_error, logtype_cnid, "error truncating database %s: %s",
274                     dbd->db_table[i].name, db_strerror(ret));
275                 return -1;
276             }
277         }
278     }
279
280     /* TODO: Implement CNID DB versioning info on new databases. */
281
282     /* Associate the secondary with the primary. */
283     if (do_truncate)
284         LOG(log_info, logtype_cnid, "Reindexing did/name index...");
285     if ((ret = dbd->db_table[0].db->associate(dbd->db_table[DBIF_CNID].db,
286                                               dbd->db_txn,
287                                               dbd->db_table[DBIF_IDX_DIDNAME].db, 
288                                               didname,
289                                               (do_truncate) ? DB_CREATE : 0))
290          != 0) {
291         LOG(log_error, logtype_cnid, "Failed to associate didname database: %s",db_strerror(ret));
292         return -1;
293     }
294     if (do_truncate)
295         LOG(log_info, logtype_cnid, "... done.");
296
297     if (do_truncate)
298         LOG(log_info, logtype_cnid, "Reindexing dev/ino index...");
299     if ((ret = dbd->db_table[0].db->associate(dbd->db_table[0].db, 
300                                               dbd->db_txn,
301                                               dbd->db_table[DBIF_IDX_DEVINO].db, 
302                                               devino,
303                                               (do_truncate) ? DB_CREATE : 0))
304         != 0) {
305         LOG(log_error, logtype_cnid, "Failed to associate devino database: %s",db_strerror(ret));
306         return -1;
307     }
308     if (do_truncate)
309         LOG(log_info, logtype_cnid, "... done.");
310     
311     return 0;
312 }
313
314 /* ------------------------ */
315 int dbif_closedb(DBD *dbd)
316 {
317     int i;
318     int ret;
319     int err = 0;
320
321     for (i = DBIF_DB_CNT -1; i >= 0; i--) {
322         if (dbd->db_table[i].db != NULL && (ret = dbd->db_table[i].db->close(dbd->db_table[i].db, 0))) {
323             LOG(log_error, logtype_cnid, "error closing database %s: %s", dbd->db_table[i].name, db_strerror(ret));
324             err++;
325         }
326     }
327     if (err)
328         return -1;
329     return 0;
330 }
331
332 /* ------------------------ */
333 int dbif_close(DBD *dbd)
334 {
335     int ret;
336     int err = 0;
337
338     if (dbif_closedb(dbd))
339         err++;
340
341     if (dbd->db_env != NULL && (ret = dbd->db_env->close(dbd->db_env, 0))) {
342         LOG(log_error, logtype_cnid, "error closing DB environment: %s", db_strerror(ret));
343         err++;
344     }
345     if (dbd->db_errlog != NULL && fclose(dbd->db_errlog) == EOF) {
346         LOG(log_error, logtype_cnid, "error closing DB logfile: %s", strerror(errno));
347         err++;
348     }
349
350     free(dbd->db_filename);
351     free(dbd);
352     dbd = NULL;
353
354     if (err)
355         return -1;
356     return 0;
357 }
358
359 /*
360  *  The following three functions are wrappers for DB->get(), DB->put() and DB->del().
361  *  All three return -1 on error. dbif_get()/dbif_del return 1 if the key was found and 0
362  *  otherwise. dbif_put() returns 0 if key/val was successfully updated and 1 if
363  *  the DB_NOOVERWRITE flag was specified and the key already exists.
364  *
365  *  All return codes other than DB_NOTFOUND and DB_KEYEXIST from the DB->()
366  *  functions are not expected and therefore error conditions.
367  */
368
369 int dbif_get(DBD *dbd, const int dbi, DBT *key, DBT *val, u_int32_t flags)
370 {
371     int ret;
372
373     ret = dbd->db_table[dbi].db->get(dbd->db_table[dbi].db,
374                                      dbd->db_txn,
375                                      key,
376                                      val,
377                                      flags);
378
379     if (ret == DB_NOTFOUND)
380         return 0;
381     if (ret) {
382         LOG(log_error, logtype_cnid, "error retrieving value from %s: %s",
383             dbd->db_table[dbi].name, db_strerror(errno));
384         return -1;
385     } else
386         return 1;
387 }
388
389 /* search by secondary return primary */
390 int dbif_pget(DBD *dbd, const int dbi, DBT *key, DBT *pkey, DBT *val, u_int32_t flags)
391 {
392     int ret;
393
394     ret = dbd->db_table[dbi].db->pget(dbd->db_table[dbi].db,
395                                       dbd->db_txn,
396                                       key,
397                                       pkey,
398                                       val,
399                                       flags);
400
401     if (ret == DB_NOTFOUND || ret == DB_SECONDARY_BAD) {
402         return 0;
403     }
404     if (ret) {
405         LOG(log_error, logtype_cnid, "error retrieving value from %s: %s",
406             dbd->db_table[dbi].name, db_strerror(errno));
407         return -1;
408    } else
409         return 1;
410 }
411
412 /* -------------------------- */
413 int dbif_put(DBD *dbd, const int dbi, DBT *key, DBT *val, u_int32_t flags)
414 {
415     int ret;
416
417     if (dbif_txn_begin(dbd) < 0) {
418         LOG(log_error, logtype_cnid, "error setting key/value in %s: %s",
419             dbd->db_table[dbi].name, db_strerror(errno));
420         return -1;
421     }
422
423     ret = dbd->db_table[dbi].db->put(dbd->db_table[dbi].db,
424                                      dbd->db_txn,
425                                      key,
426                                      val,
427                                      flags);
428     
429     if (ret) {
430         if ((flags & DB_NOOVERWRITE) && ret == DB_KEYEXIST) {
431             return 1;
432         } else {
433             LOG(log_error, logtype_cnid, "error setting key/value in %s: %s",
434                 dbd->db_table[dbi].name, db_strerror(errno));
435             return -1;
436         }
437     } else
438         return 0;
439 }
440
441 int dbif_del(DBD *dbd, const int dbi, DBT *key, u_int32_t flags)
442 {
443     int ret;
444
445     if (dbif_txn_begin(dbd) < 0) {
446         LOG(log_error, logtype_cnid, "error deleting key/value from %s: %s", 
447             dbd->db_table[dbi].name, db_strerror(errno));
448         return -1;
449     }
450
451     ret = dbd->db_table[dbi].db->del(dbd->db_table[dbi].db,
452                                      dbd->db_txn,
453                                      key,
454                                      flags);
455     
456     if (ret == DB_NOTFOUND || ret == DB_SECONDARY_BAD)
457         return 0;
458     if (ret) {
459         LOG(log_error, logtype_cnid, "error deleting key/value from %s: %s",
460             dbd->db_table[dbi].name, db_strerror(errno));
461         return -1;
462     } else
463         return 1;
464 }
465
466 int dbif_txn_begin(DBD *dbd)
467 {
468     int ret;
469
470     /* If we already have an active txn, just return */
471     if (dbd->db_txn)
472         return 0;
473
474     /* If our DBD has no env, just return (-> in memory db) */
475     if (dbd->db_env == NULL)
476         return 0;
477
478     ret = dbd->db_env->txn_begin(dbd->db_env, NULL, &dbd->db_txn, 0);
479
480     if (ret) {
481         LOG(log_error, logtype_cnid, "error starting transaction: %s", db_strerror(errno));
482         return -1;
483     } else
484         return 0;
485 }
486
487 int dbif_txn_commit(DBD *dbd)
488 {
489     int ret;
490
491     if (! dbd->db_txn)
492         return 0;
493
494     /* If our DBD has no env, just return (-> in memory db) */
495     if (dbd->db_env == NULL)
496         return 0;
497
498     ret = dbd->db_txn->commit(dbd->db_txn, 0);
499     dbd->db_txn = NULL;
500     
501     if (ret) {
502         LOG(log_error, logtype_cnid, "error committing transaction: %s", db_strerror(errno));
503         return -1;
504     } else
505         return 1;
506 }
507
508 int dbif_txn_abort(DBD *dbd)
509 {
510     int ret;
511
512     if (! dbd->db_txn)
513         return 0;
514
515     /* If our DBD has no env, just return (-> in memory db) */
516     if (dbd->db_env == NULL)
517         return 0;
518
519     ret = dbd->db_txn->abort(dbd->db_txn);
520     dbd->db_txn = NULL;
521     
522     if (ret) {
523         LOG(log_error, logtype_cnid, "error aborting transaction: %s", db_strerror(errno));
524         return -1;
525     } else
526         return 0;
527 }
528
529 int dbif_txn_checkpoint(DBD *dbd, u_int32_t kbyte, u_int32_t min, u_int32_t flags)
530 {
531     int ret;
532     ret = dbd->db_env->txn_checkpoint(dbd->db_env, kbyte, min, flags);
533     if (ret) {
534         LOG(log_error, logtype_cnid, "error checkpointing transaction susystem: %s", db_strerror(errno));
535         return -1;
536     } else
537         return 0;
538 }
539
540 int dbif_count(DBD *dbd, const int dbi, u_int32_t *count)
541 {
542     int ret;
543     DB_BTREE_STAT *sp;
544     DB *db = dbd->db_table[dbi].db;
545
546     ret = db->stat(db, NULL, &sp, 0);
547
548     if (ret) {
549         LOG(log_error, logtype_cnid, "error getting stat infotmation on database: %s", db_strerror(errno));
550         return -1;
551     }
552
553     *count = sp->bt_ndata;
554     free(sp);
555
556     return 0;
557 }
558
559 int dbif_copy_rootinfokey(DBD *srcdbd, DBD *destdbd)
560 {
561     DBT key, data;
562     int rc;
563
564     memset(&key, 0, sizeof(key));
565     memset(&data, 0, sizeof(data));
566
567     key.data = ROOTINFO_KEY;
568     key.size = ROOTINFO_KEYLEN;
569
570     if ((rc = dbif_get(srcdbd, DBIF_CNID, &key, &data, 0)) <= 0) {
571         LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error getting rootinfo record");
572         return -1;
573     }
574
575     memset(&key, 0, sizeof(key));
576     key.data = ROOTINFO_KEY;
577     key.size = ROOTINFO_KEYLEN;
578
579     if ((rc = dbif_put(destdbd, DBIF_CNID, &key, &data, 0))) {
580         LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error writing rootinfo key");
581         return -1;
582     }
583
584     return 0;
585 }
586
587 int dbif_dump(DBD *dbd, int dumpindexes)
588 {
589     int rc;
590     uint32_t max = 0, count = 0, cnid, type, did, lastid;
591     uint64_t dev, ino;
592     time_t stamp;
593     DBC *cur;
594     DBT key = { 0 }, data = { 0 };
595     DB *db = dbd->db_table[DBIF_CNID].db;
596     char *typestring[2] = {"f", "d"};
597     char timebuf[64];
598
599     printf("CNID database dump:\n");
600
601     rc = db->cursor(db, NULL, &cur, 0);
602     if (rc) {
603         LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(errno));
604         return -1;
605     }
606
607     cur->c_get(cur, &key, &data, DB_FIRST);
608     while (rc == 0) {
609         /* Parse and print data */
610         memcpy(&cnid, key.data, 4);
611         cnid = ntohl(cnid);
612         if (cnid > max)
613             max = cnid;
614
615         /* Rootinfo node ? */
616         if (cnid == 0) {
617             memcpy(&stamp, data.data + 4, sizeof(time_t));
618             memcpy(&lastid, data.data + 20, sizeof(cnid_t));
619             lastid = ntohl(lastid);
620             strftime(timebuf, sizeof(timebuf), "%b %d %Y %H:%M:%S", localtime(&stamp));
621             printf("dbd stamp: 0x%08x (%s), next free CNID: %u\n", (unsigned int)stamp, timebuf, lastid + 1);
622         } else {
623             /* dev */
624             memcpy(&dev, data.data + CNID_DEV_OFS, 8);
625             dev = ntoh64(dev);
626             /* ino */
627             memcpy(&ino, data.data + CNID_INO_OFS, 8);
628             ino = ntoh64(ino);
629             /* type */
630             memcpy(&type, data.data + CNID_TYPE_OFS, 4);
631             type = ntohl(type);
632             /* did */
633             memcpy(&did, data.data + CNID_DID_OFS, 4);
634             did = ntohl(did);
635
636             count++;
637             printf("id: %10u, did: %10u, type: %s, dev: 0x%llx, ino: 0x%016llx, name: %s\n", 
638                    cnid, did, typestring[type],
639                    (long long unsigned int)dev, (long long unsigned int)ino, 
640                    (char *)data.data + CNID_NAME_OFS);
641
642         }
643
644         rc = cur->c_get(cur, &key, &data, DB_NEXT);
645     }
646
647     if (rc != DB_NOTFOUND) {
648         LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(errno));
649         return -1;
650     }
651
652     rc = cur->c_close(cur);
653     if (rc) {
654         LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(errno));
655         return -1;
656     }
657     printf("%u CNIDs in database. Max CNID: %u.\n", count, max);
658
659     /* Dump indexes too ? */
660     if (dumpindexes) {
661         /* DBIF_IDX_DEVINO */
662         printf("\ndev/inode index:\n");
663         count = 0;
664         db = dbd->db_table[DBIF_IDX_DEVINO].db;
665         rc = db->cursor(db, NULL, &cur, 0);
666         if (rc) {
667             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(errno));
668             return -1;
669         }
670         
671         cur->c_get(cur, &key, &data, DB_FIRST);
672         while (rc == 0) {
673             /* Parse and print data */
674
675             /* cnid */
676             memcpy(&cnid, data.data, CNID_LEN);
677             cnid = ntohl(cnid);
678             if (cnid == 0) {
679                 /* Rootinfo node */
680             } else {
681                 /* dev */
682                 memcpy(&dev, key.data, CNID_DEV_LEN);
683                 dev = ntoh64(dev);
684                 /* ino */
685                 memcpy(&ino, key.data + CNID_DEV_LEN, CNID_INO_LEN);
686                 ino = ntoh64(ino);
687                 
688                 printf("id: %10u <== dev: 0x%llx, ino: 0x%016llx\n", 
689                        cnid, (unsigned long long int)dev, (unsigned long long int)ino);
690                 count++;
691             }
692             rc = cur->c_get(cur, &key, &data, DB_NEXT);
693         }
694         if (rc != DB_NOTFOUND) {
695             LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(errno));
696             return -1;
697         }
698         
699         rc = cur->c_close(cur);
700         if (rc) {
701             LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(errno));
702             return -1;
703         }
704         printf("%u items\n", count);
705
706         /* Now dump DBIF_IDX_DIDNAME */
707         printf("\ndid/name index:\n");
708         count = 0;
709         db = dbd->db_table[DBIF_IDX_DIDNAME].db;
710         rc = db->cursor(db, NULL, &cur, 0);
711         if (rc) {
712             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(errno));
713             return -1;
714         }
715         
716         cur->c_get(cur, &key, &data, DB_FIRST);
717         while (rc == 0) {
718             /* Parse and print data */
719
720             /* cnid */
721             memcpy(&cnid, data.data, CNID_LEN);
722             cnid = ntohl(cnid);
723             if (cnid == 0) {
724                 /* Rootinfo node */
725             } else {
726                 /* did */
727                 memcpy(&did, key.data, CNID_LEN);
728                 did = ntohl(did);
729
730                 printf("id: %10u <== did: %10u, name: %s\n", cnid, did, (char *)key.data + CNID_LEN);
731                 count++;
732             }
733             rc = cur->c_get(cur, &key, &data, DB_NEXT);
734         }
735         if (rc != DB_NOTFOUND) {
736             LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(errno));
737             return -1;
738         }
739         
740         rc = cur->c_close(cur);
741         if (rc) {
742             LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(errno));
743             return -1;
744         }
745         printf("%u items\n", count);
746     }
747
748     return 0;
749 }
750