]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/dbif.c
Finished dbd
[netatalk.git] / etc / cnid_dbd / dbif.c
1 /*
2  * $Id: dbif.c,v 1.12 2009-05-22 20:48:44 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,cwd;
59
60     if (size < 8)
61         return -1;
62
63     /* Remember cwd */
64     if ((cwd = open(".", O_RDONLY)) < 0) {
65         LOG(log_error, logtype_cnid, "error opening cwd: %s", strerror(errno));
66         return -1;
67     }
68
69     /* chdir to db_envhome */
70     if ((chdir(dbd->db_envhome)) != 0) {
71         LOG(log_error, logtype_cnid, "error chdiring to db_env '%s': %s", dbd->db_envhome, strerror(errno));        
72         return -1;
73     }
74
75     if ((rc = stat(dbd->db_table[DBIF_CNID].name, &st)) < 0) {
76         LOG(log_error, logtype_cnid, "error stating database %s: %s", dbd->db_table[DBIF_CNID].name, db_strerror(rc));
77         return -1;
78     }
79     memset(buffer, 0, size);
80     memcpy(buffer, &st.st_ctime, sizeof(st.st_ctime));
81
82     if ((fchdir(cwd)) != 0) {
83         LOG(log_error, logtype_cnid, "error chdiring back: %s", strerror(errno));        
84         return -1;
85     }
86
87     return 0;
88 }
89
90 /* --------------- */
91 DBD *dbif_init(const char *envhome, const char *filename)
92 {
93     DBD *dbd;
94
95     if ( NULL == (dbd = calloc(sizeof(DBD), 1)) )
96         return NULL;
97
98     /* filename == NULL means in memory db */
99     if (filename) {
100         if (! envhome)
101             return NULL;
102
103         dbd->db_envhome = strdup(envhome);
104         if (NULL == dbd->db_envhome) {
105             free(dbd);
106             return NULL;
107         }
108
109         dbd->db_filename = strdup(filename);
110         if (NULL == dbd->db_filename) {
111             free(dbd->db_envhome);
112             free(dbd);
113             return NULL;
114         }
115     }
116     
117     dbd->db_table[DBIF_CNID].name        = "cnid2.db";
118     dbd->db_table[DBIF_IDX_DEVINO].name  = "devino.db";
119     dbd->db_table[DBIF_IDX_DIDNAME].name = "didname.db";
120
121     dbd->db_table[DBIF_CNID].type        = DB_BTREE;
122     dbd->db_table[DBIF_IDX_DEVINO].type  = DB_BTREE;
123     dbd->db_table[DBIF_IDX_DIDNAME].type = DB_BTREE;
124
125     dbd->db_table[DBIF_CNID].openflags        = DB_CREATE;
126     dbd->db_table[DBIF_IDX_DEVINO].openflags  = DB_CREATE;
127     dbd->db_table[DBIF_IDX_DIDNAME].openflags = DB_CREATE;
128
129     return dbd;
130 }
131
132 /* 
133    We must open the db_env with an absolute pathname, as `dbd` keeps chdir'ing, which
134    breaks e.g. bdb logfile-rotation with relative pathnames.
135    But still we use relative paths with upgrade_required() and the DB_ERRLOGFILE
136    in order to avoid creating absolute paths by copying. Both have no problem with
137    a relative path.
138 */
139 int dbif_env_open(DBD *dbd, struct db_param *dbp, uint32_t dbenv_oflags)
140 {
141     int ret, cwd;
142     char **logfiles = NULL;
143     char **file;
144
145     /* Remember cwd */
146     if ((cwd = open(".", O_RDONLY)) < 0) {
147         LOG(log_error, logtype_cnid, "error opening cwd: %s", strerror(errno));
148         return -1;
149     }
150
151     /* chdir to db_envhome. makes it easier checking for old db files and creating db_errlog file  */
152     if ((chdir(dbd->db_envhome)) != 0) {
153         LOG(log_error, logtype_cnid, "error chdiring to db_env '%s': %s", dbd->db_envhome, strerror(errno));        
154         return -1;
155     }
156
157     /* Refuse to do anything if this is an old version of the CNID database */
158     if (upgrade_required()) {
159         LOG(log_error, logtype_cnid, "Found version 1 of the CNID database. Please upgrade to version 2");
160         return -1;
161     }
162
163     if ((dbd->db_errlog = fopen(DB_ERRLOGFILE, "a")) == NULL)
164         LOG(log_warning, logtype_cnid, "error creating/opening DB errlogfile: %s", strerror(errno));
165
166     if ((fchdir(cwd)) != 0) {
167         LOG(log_error, logtype_cnid, "error chdiring back: %s", strerror(errno));        
168         return -1;
169     }
170
171     if ((ret = db_env_create(&dbd->db_env, 0))) {
172         LOG(log_error, logtype_cnid, "error creating DB environment: %s",
173             db_strerror(ret));
174         dbd->db_env = NULL;
175         return -1;
176     }
177
178     if (dbd->db_errlog != NULL) {
179         dbd->db_env->set_errfile(dbd->db_env, dbd->db_errlog);
180         dbd->db_env->set_msgfile(dbd->db_env, dbd->db_errlog);
181     }
182
183     if (dbenv_oflags & DB_RECOVER) {
184         dbd->db_env->set_verbose(dbd->db_env, DB_VERB_RECOVERY, 1);
185         /* Open the database for recovery using DB_PRIVATE option which is faster */
186         if ((ret = dbd->db_env->open(dbd->db_env, dbd->db_envhome, dbenv_oflags | DB_PRIVATE, 0))) {
187             LOG(log_error, logtype_cnid, "error opening DB environment: %s",
188                 db_strerror(ret));
189             dbd->db_env->close(dbd->db_env, 0);
190             dbd->db_env = NULL;
191             return -1;
192         }
193         dbenv_oflags = (dbenv_oflags & ~DB_RECOVER);
194
195         if (dbd->db_errlog != NULL)
196             fflush(dbd->db_errlog);
197
198         if ((ret = dbd->db_env->close(dbd->db_env, 0))) {
199             LOG(log_error, logtype_cnid, "error closing DB environment after recovery: %s",
200                 db_strerror(ret));
201             dbd->db_env = NULL;
202             return -1;
203         }
204
205         if ((ret = db_env_create(&dbd->db_env, 0))) {
206             LOG(log_error, logtype_cnid, "error creating DB environment after recovery: %s",
207                 db_strerror(ret));
208             dbd->db_env = NULL;
209             return -1;
210         }
211     }
212
213     if ((ret = dbd->db_env->set_cachesize(dbd->db_env, 0, 1024 * dbp->cachesize, 0))) {
214         LOG(log_error, logtype_cnid, "error setting DB environment cachesize to %i: %s",
215             dbp->cachesize, db_strerror(ret));
216         dbd->db_env->close(dbd->db_env, 0);
217         dbd->db_env = NULL;
218         return -1;
219     }
220
221     if (dbd->db_errlog != NULL) {
222         dbd->db_env->set_errfile(dbd->db_env, dbd->db_errlog);
223         dbd->db_env->set_msgfile(dbd->db_env, dbd->db_errlog);
224     }
225     if ((ret = dbd->db_env->open(dbd->db_env, dbd->db_envhome, dbenv_oflags, 0))) {
226         LOG(log_error, logtype_cnid, "error opening DB environment after recovery: %s",
227             db_strerror(ret));
228         dbd->db_env->close(dbd->db_env, 0);
229         dbd->db_env = NULL;
230         return -1;
231     }
232
233     if ((ret = dbd->db_env->set_flags(dbd->db_env, DB_AUTO_COMMIT, 1))) {
234         LOG(log_error, logtype_cnid, "error setting DB_AUTO_COMMIT flag: %s",
235             db_strerror(ret));
236         dbd->db_env->close(dbd->db_env, 0);
237         dbd->db_env = NULL;
238         return -1;
239     }
240
241     if (dbp->logfile_autoremove) {
242         if (dbd->db_env->log_archive(dbd->db_env, &logfiles, 0)) {
243             LOG(log_error, logtype_cnid, "error getting list of stale logfiles: %s",
244                 db_strerror(ret));
245             dbd->db_env->close(dbd->db_env, 0);
246             dbd->db_env = NULL;
247             return -1;
248         }
249         if (logfiles != NULL) {
250             for (file = logfiles; *file != NULL; file++) {
251                 if (unlink(*file) < 0)
252                     LOG(log_warning, logtype_cnid, "Error removing stale logfile %s: %s", *file, strerror(errno));
253             }
254             free(logfiles);
255         }
256
257 #if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 7)
258         if ((ret = dbd->db_env->log_set_config(dbd->db_env, DB_LOG_AUTO_REMOVE, 1))) {
259             LOG(log_error, logtype_cnid, "error setting DB_LOG_AUTO_REMOVE flag: %s",
260             db_strerror(ret));
261             dbd->db_env->close(dbd->db_env, 0);
262             dbd->db_env = NULL;
263             return -1;
264         }
265 #else
266         if ((ret = dbd->db_env->set_flags(dbd->db_env, DB_LOG_AUTOREMOVE, 1))) {
267             LOG(log_error, logtype_cnid, "error setting DB_LOG_AUTOREMOVE flag: %s",
268                 db_strerror(ret));
269             dbd->db_env->close(dbd->db_env, 0);
270             dbd->db_env = NULL;
271             return -1;
272         }
273 #endif
274     }
275
276     return 0;
277 }
278
279 /* --------------- */
280 int dbif_open(DBD *dbd, struct db_param *dbp _U_, int reindex)
281 {
282     int ret;
283     int i;
284     u_int32_t count;
285
286     for (i = 0; i != DBIF_DB_CNT; i++) {
287         if ((ret = db_create(&dbd->db_table[i].db, dbd->db_env, 0))) {
288             LOG(log_error, logtype_cnid, "error creating handle for database %s: %s",
289                 dbd->db_table[i].name, db_strerror(ret));
290             return -1;
291         }
292
293         if (dbd->db_table[i].flags) {
294             if ((ret = dbd->db_table[i].db->set_flags(dbd->db_table[i].db,
295                                                       dbd->db_table[i].flags))) {
296                 LOG(log_error, logtype_cnid, "error setting flags for database %s: %s",
297                     dbd->db_table[i].name, db_strerror(ret));
298                 return -1;
299             }
300         }
301
302         if (dbd->db_table[i].db->open(dbd->db_table[i].db,
303                                       dbd->db_txn,
304                                       dbd->db_filename,
305                                       dbd->db_table[i].name,
306                                       dbd->db_table[i].type,
307                                       dbd->db_table[i].openflags,
308                                       0664) < 0) {
309             LOG(log_error, logtype_cnid, "Cant open database");
310             return -1;
311         }
312         if (dbd->db_errlog != NULL)
313             dbd->db_table[i].db->set_errfile(dbd->db_table[i].db, dbd->db_errlog);
314
315         if (reindex && i > 0) {
316             LOG(log_info, logtype_cnid, "Truncating CNID index.");
317             if ((ret = dbd->db_table[i].db->truncate(dbd->db_table[i].db, NULL, &count, 0))) {
318                 LOG(log_error, logtype_cnid, "error truncating database %s: %s",
319                     dbd->db_table[i].name, db_strerror(ret));
320                 return -1;
321             }
322         }
323     }
324
325     /* TODO: Implement CNID DB versioning info on new databases. */
326
327     /* Associate the secondary with the primary. */
328     if (reindex)
329         LOG(log_info, logtype_cnid, "Reindexing did/name index...");
330     if ((ret = dbd->db_table[0].db->associate(dbd->db_table[DBIF_CNID].db,
331                                               dbd->db_txn,
332                                               dbd->db_table[DBIF_IDX_DIDNAME].db, 
333                                               didname,
334                                               (reindex) ? DB_CREATE : 0))
335          != 0) {
336         LOG(log_error, logtype_cnid, "Failed to associate didname database: %s",db_strerror(ret));
337         return -1;
338     }
339     if (reindex)
340         LOG(log_info, logtype_cnid, "... done.");
341
342     if (reindex)
343         LOG(log_info, logtype_cnid, "Reindexing dev/ino index...");
344     if ((ret = dbd->db_table[0].db->associate(dbd->db_table[0].db, 
345                                               dbd->db_txn,
346                                               dbd->db_table[DBIF_IDX_DEVINO].db, 
347                                               devino,
348                                               (reindex) ? DB_CREATE : 0))
349         != 0) {
350         LOG(log_error, logtype_cnid, "Failed to associate devino database: %s",db_strerror(ret));
351         return -1;
352     }
353     if (reindex)
354         LOG(log_info, logtype_cnid, "... done.");
355     
356     return 0;
357 }
358
359 /* ------------------------ */
360 int dbif_closedb(DBD *dbd)
361 {
362     int i;
363     int ret;
364     int err = 0;
365
366     for (i = DBIF_DB_CNT -1; i >= 0; i--) {
367         if (dbd->db_table[i].db != NULL && (ret = dbd->db_table[i].db->close(dbd->db_table[i].db, 0))) {
368             LOG(log_error, logtype_cnid, "error closing database %s: %s", dbd->db_table[i].name, db_strerror(ret));
369             err++;
370         }
371     }
372     if (err)
373         return -1;
374     return 0;
375 }
376
377 /* ------------------------ */
378 int dbif_close(DBD *dbd)
379 {
380     int ret;
381     int err = 0;
382
383     if (dbif_closedb(dbd))
384         err++;
385
386     if (dbd->db_env != NULL && (ret = dbd->db_env->close(dbd->db_env, 0))) {
387         LOG(log_error, logtype_cnid, "error closing DB environment: %s", db_strerror(ret));
388         err++;
389     }
390     if (dbd->db_errlog != NULL && fclose(dbd->db_errlog) == EOF) {
391         LOG(log_error, logtype_cnid, "error closing DB logfile: %s", strerror(errno));
392         err++;
393     }
394
395     free(dbd->db_filename);
396     free(dbd);
397     dbd = NULL;
398
399     if (err)
400         return -1;
401     return 0;
402 }
403
404 /*
405  *  The following three functions are wrappers for DB->get(), DB->put() and DB->del().
406  *  All three return -1 on error. dbif_get()/dbif_del return 1 if the key was found and 0
407  *  otherwise. dbif_put() returns 0 if key/val was successfully updated and 1 if
408  *  the DB_NOOVERWRITE flag was specified and the key already exists.
409  *
410  *  All return codes other than DB_NOTFOUND and DB_KEYEXIST from the DB->()
411  *  functions are not expected and therefore error conditions.
412  */
413
414 int dbif_get(DBD *dbd, const int dbi, DBT *key, DBT *val, u_int32_t flags)
415 {
416     int ret;
417
418     ret = dbd->db_table[dbi].db->get(dbd->db_table[dbi].db,
419                                      dbd->db_txn,
420                                      key,
421                                      val,
422                                      flags);
423
424     if (ret == DB_NOTFOUND)
425         return 0;
426     if (ret) {
427         LOG(log_error, logtype_cnid, "error retrieving value from %s: %s",
428             dbd->db_table[dbi].name, db_strerror(errno));
429         return -1;
430     } else
431         return 1;
432 }
433
434 /* search by secondary return primary */
435 int dbif_pget(DBD *dbd, const int dbi, DBT *key, DBT *pkey, DBT *val, u_int32_t flags)
436 {
437     int ret;
438
439     ret = dbd->db_table[dbi].db->pget(dbd->db_table[dbi].db,
440                                       dbd->db_txn,
441                                       key,
442                                       pkey,
443                                       val,
444                                       flags);
445
446     if (ret == DB_NOTFOUND || ret == DB_SECONDARY_BAD) {
447         return 0;
448     }
449     if (ret) {
450         LOG(log_error, logtype_cnid, "error retrieving value from %s: %s",
451             dbd->db_table[dbi].name, db_strerror(errno));
452         return -1;
453    } else
454         return 1;
455 }
456
457 /* -------------------------- */
458 int dbif_put(DBD *dbd, const int dbi, DBT *key, DBT *val, u_int32_t flags)
459 {
460     int ret;
461
462     if (dbif_txn_begin(dbd) < 0) {
463         LOG(log_error, logtype_cnid, "error setting key/value in %s: %s",
464             dbd->db_table[dbi].name, db_strerror(errno));
465         return -1;
466     }
467
468     ret = dbd->db_table[dbi].db->put(dbd->db_table[dbi].db,
469                                      dbd->db_txn,
470                                      key,
471                                      val,
472                                      flags);
473     
474     if (ret) {
475         if ((flags & DB_NOOVERWRITE) && ret == DB_KEYEXIST) {
476             return 1;
477         } else {
478             LOG(log_error, logtype_cnid, "error setting key/value in %s: %s",
479                 dbd->db_table[dbi].name, db_strerror(errno));
480             return -1;
481         }
482     } else
483         return 0;
484 }
485
486 int dbif_del(DBD *dbd, const int dbi, DBT *key, u_int32_t flags)
487 {
488     int ret;
489
490     /* For cooperation with the dbd utility and its usage of a cursor */
491     if (dbd->db_cur) {
492         dbd->db_cur->close(dbd->db_cur);
493         dbd->db_cur = NULL;
494     }    
495
496     if (dbif_txn_begin(dbd) < 0) {
497         LOG(log_error, logtype_cnid, "error deleting key/value from %s: %s", 
498             dbd->db_table[dbi].name, db_strerror(errno));
499         return -1;
500     }
501
502     ret = dbd->db_table[dbi].db->del(dbd->db_table[dbi].db,
503                                      dbd->db_txn,
504                                      key,
505                                      flags);
506     
507     if (ret == DB_NOTFOUND || ret == DB_SECONDARY_BAD)
508         return 0;
509     if (ret) {
510         LOG(log_error, logtype_cnid, "error deleting key/value from %s: %s",
511             dbd->db_table[dbi].name, db_strerror(errno));
512         return -1;
513     } else
514         return 1;
515 }
516
517 int dbif_txn_begin(DBD *dbd)
518 {
519     int ret;
520
521     /* If we already have an active txn, just return */
522     if (dbd->db_txn)
523         return 0;
524
525     /* If our DBD has no env, just return (-> in memory db) */
526     if (dbd->db_env == NULL)
527         return 0;
528
529     ret = dbd->db_env->txn_begin(dbd->db_env, NULL, &dbd->db_txn, 0);
530
531     if (ret) {
532         LOG(log_error, logtype_cnid, "error starting transaction: %s", db_strerror(errno));
533         return -1;
534     } else
535         return 0;
536 }
537
538 int dbif_txn_commit(DBD *dbd)
539 {
540     int ret;
541
542     if (! dbd->db_txn)
543         return 0;
544
545     /* If our DBD has no env, just return (-> in memory db) */
546     if (dbd->db_env == NULL)
547         return 0;
548
549     ret = dbd->db_txn->commit(dbd->db_txn, 0);
550     dbd->db_txn = NULL;
551     
552     if (ret) {
553         LOG(log_error, logtype_cnid, "error committing transaction: %s", db_strerror(errno));
554         return -1;
555     } else
556         return 1;
557 }
558
559 int dbif_txn_abort(DBD *dbd)
560 {
561     int ret;
562
563     if (! dbd->db_txn)
564         return 0;
565
566     /* If our DBD has no env, just return (-> in memory db) */
567     if (dbd->db_env == NULL)
568         return 0;
569
570     ret = dbd->db_txn->abort(dbd->db_txn);
571     dbd->db_txn = NULL;
572     
573     if (ret) {
574         LOG(log_error, logtype_cnid, "error aborting transaction: %s", db_strerror(errno));
575         return -1;
576     } else
577         return 0;
578 }
579
580 /* 
581    ret = 1 -> commit txn
582    ret = 0 -> abort txn -> exit!
583    anything else -> exit!
584 */
585 void dbif_txn_close(DBD *dbd, int ret)
586 {
587     if (ret == 0) {
588         if (dbif_txn_abort(dbd) < 0) {
589             LOG( log_error, logtype_cnid, "Fatal error aborting transaction. Exiting!");
590             exit(EXIT_FAILURE);
591         }
592     } else if (ret == 1) {
593         ret = dbif_txn_commit(dbd);
594         if (  ret < 0) {
595             LOG( log_error, logtype_cnid, "Fatal error committing transaction. Exiting!");
596             exit(EXIT_FAILURE);
597         }
598     } else
599        exit(EXIT_FAILURE);
600 }
601
602 int dbif_txn_checkpoint(DBD *dbd, u_int32_t kbyte, u_int32_t min, u_int32_t flags)
603 {
604     int ret;
605     ret = dbd->db_env->txn_checkpoint(dbd->db_env, kbyte, min, flags);
606     if (ret) {
607         LOG(log_error, logtype_cnid, "error checkpointing transaction susystem: %s", db_strerror(errno));
608         return -1;
609     } else
610         return 0;
611 }
612
613 int dbif_count(DBD *dbd, const int dbi, u_int32_t *count)
614 {
615     int ret;
616     DB_BTREE_STAT *sp;
617     DB *db = dbd->db_table[dbi].db;
618
619     ret = db->stat(db, NULL, &sp, 0);
620
621     if (ret) {
622         LOG(log_error, logtype_cnid, "error getting stat infotmation on database: %s", db_strerror(errno));
623         return -1;
624     }
625
626     *count = sp->bt_ndata;
627     free(sp);
628
629     return 0;
630 }
631
632 int dbif_copy_rootinfokey(DBD *srcdbd, DBD *destdbd)
633 {
634     DBT key, data;
635     int rc;
636
637     memset(&key, 0, sizeof(key));
638     memset(&data, 0, sizeof(data));
639
640     key.data = ROOTINFO_KEY;
641     key.size = ROOTINFO_KEYLEN;
642
643     if ((rc = dbif_get(srcdbd, DBIF_CNID, &key, &data, 0)) <= 0) {
644         LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error getting rootinfo record");
645         return -1;
646     }
647
648     memset(&key, 0, sizeof(key));
649     key.data = ROOTINFO_KEY;
650     key.size = ROOTINFO_KEYLEN;
651
652     if ((rc = dbif_put(destdbd, DBIF_CNID, &key, &data, 0))) {
653         LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error writing rootinfo key");
654         return -1;
655     }
656
657     return 0;
658 }
659
660 int dbif_dump(DBD *dbd, int dumpindexes)
661 {
662     int rc;
663     uint32_t max = 0, count = 0, cnid, type, did, lastid;
664     uint64_t dev, ino;
665     time_t stamp;
666     DBC *cur;
667     DBT key = { 0 }, data = { 0 };
668     DB *db = dbd->db_table[DBIF_CNID].db;
669     char *typestring[2] = {"f", "d"};
670     char timebuf[64];
671
672     printf("CNID database dump:\n");
673
674     rc = db->cursor(db, NULL, &cur, 0);
675     if (rc) {
676         LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(errno));
677         return -1;
678     }
679
680     cur->c_get(cur, &key, &data, DB_FIRST);
681     while (rc == 0) {
682         /* Parse and print data */
683         memcpy(&cnid, key.data, 4);
684         cnid = ntohl(cnid);
685         if (cnid > max)
686             max = cnid;
687
688         /* Rootinfo node ? */
689         if (cnid == 0) {
690             memcpy(&stamp, data.data + 4, sizeof(time_t));
691             memcpy(&lastid, data.data + 20, sizeof(cnid_t));
692             lastid = ntohl(lastid);
693             strftime(timebuf, sizeof(timebuf), "%b %d %Y %H:%M:%S", localtime(&stamp));
694             printf("dbd stamp: 0x%08x (%s), next free CNID: %u\n", (unsigned int)stamp, timebuf, lastid + 1);
695         } else {
696             /* dev */
697             memcpy(&dev, data.data + CNID_DEV_OFS, 8);
698             dev = ntoh64(dev);
699             /* ino */
700             memcpy(&ino, data.data + CNID_INO_OFS, 8);
701             ino = ntoh64(ino);
702             /* type */
703             memcpy(&type, data.data + CNID_TYPE_OFS, 4);
704             type = ntohl(type);
705             /* did */
706             memcpy(&did, data.data + CNID_DID_OFS, 4);
707             did = ntohl(did);
708
709             count++;
710             printf("id: %10u, did: %10u, type: %s, dev: 0x%llx, ino: 0x%016llx, name: %s\n", 
711                    cnid, did, typestring[type],
712                    (long long unsigned int)dev, (long long unsigned int)ino, 
713                    (char *)data.data + CNID_NAME_OFS);
714
715         }
716
717         rc = cur->c_get(cur, &key, &data, DB_NEXT);
718     }
719
720     if (rc != DB_NOTFOUND) {
721         LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(errno));
722         return -1;
723     }
724
725     rc = cur->c_close(cur);
726     if (rc) {
727         LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(errno));
728         return -1;
729     }
730     printf("%u CNIDs in database. Max CNID: %u.\n", count, max);
731
732     /* Dump indexes too ? */
733     if (dumpindexes) {
734         /* DBIF_IDX_DEVINO */
735         printf("\ndev/inode index:\n");
736         count = 0;
737         db = dbd->db_table[DBIF_IDX_DEVINO].db;
738         rc = db->cursor(db, NULL, &cur, 0);
739         if (rc) {
740             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(errno));
741             return -1;
742         }
743         
744         cur->c_get(cur, &key, &data, DB_FIRST);
745         while (rc == 0) {
746             /* Parse and print data */
747
748             /* cnid */
749             memcpy(&cnid, data.data, CNID_LEN);
750             cnid = ntohl(cnid);
751             if (cnid == 0) {
752                 /* Rootinfo node */
753             } else {
754                 /* dev */
755                 memcpy(&dev, key.data, CNID_DEV_LEN);
756                 dev = ntoh64(dev);
757                 /* ino */
758                 memcpy(&ino, key.data + CNID_DEV_LEN, CNID_INO_LEN);
759                 ino = ntoh64(ino);
760                 
761                 printf("id: %10u <== dev: 0x%llx, ino: 0x%016llx\n", 
762                        cnid, (unsigned long long int)dev, (unsigned long long int)ino);
763                 count++;
764             }
765             rc = cur->c_get(cur, &key, &data, DB_NEXT);
766         }
767         if (rc != DB_NOTFOUND) {
768             LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(errno));
769             return -1;
770         }
771         
772         rc = cur->c_close(cur);
773         if (rc) {
774             LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(errno));
775             return -1;
776         }
777         printf("%u items\n", count);
778
779         /* Now dump DBIF_IDX_DIDNAME */
780         printf("\ndid/name index:\n");
781         count = 0;
782         db = dbd->db_table[DBIF_IDX_DIDNAME].db;
783         rc = db->cursor(db, NULL, &cur, 0);
784         if (rc) {
785             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(errno));
786             return -1;
787         }
788         
789         cur->c_get(cur, &key, &data, DB_FIRST);
790         while (rc == 0) {
791             /* Parse and print data */
792
793             /* cnid */
794             memcpy(&cnid, data.data, CNID_LEN);
795             cnid = ntohl(cnid);
796             if (cnid == 0) {
797                 /* Rootinfo node */
798             } else {
799                 /* did */
800                 memcpy(&did, key.data, CNID_LEN);
801                 did = ntohl(did);
802
803                 printf("id: %10u <== did: %10u, name: %s\n", cnid, did, (char *)key.data + CNID_LEN);
804                 count++;
805             }
806             rc = cur->c_get(cur, &key, &data, DB_NEXT);
807         }
808         if (rc != DB_NOTFOUND) {
809             LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(errno));
810             return -1;
811         }
812         
813         rc = cur->c_close(cur);
814         if (rc) {
815             LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(errno));
816             return -1;
817         }
818         printf("%u items\n", count);
819     }
820
821     return 0;
822 }
823
824 /* 
825    Iterates over dbd, returning cnids.
826    Uses in-value of cnid to seek to that cnid, then gets next and return that in cnid.
827    If close=1, close cursor.
828    Return -1 on error, 0 on EOD (end-of-database), 1 if returning cnid.
829 */
830 int dbif_idwalk(DBD *dbd, cnid_t *cnid, int close)
831 {
832     int rc;
833     int flag;
834     cnid_t id;
835
836     static DBT key = { 0 }, data = { 0 };
837     DB *db = dbd->db_table[DBIF_CNID].db;
838
839     if (close && dbd->db_cur) {
840         dbd->db_cur->close(dbd->db_cur);
841         dbd->db_cur = NULL;
842         return 0;
843     }
844
845     /* An dbif_del will have closed our cursor too */
846     if ( ! dbd->db_cur ) {
847         if (db->cursor(db, NULL, &dbd->db_cur, 0) != 0) {
848             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(errno));
849             return -1;
850         }
851         flag = DB_SET_RANGE;    /* This will seek to next cnid after the one just deleted */
852         id = htonl(*cnid);
853         key.data = &id;
854         key.size = sizeof(cnid_t);
855     } else
856         flag = DB_NEXT;
857
858     if ((rc = dbd->db_cur->get(dbd->db_cur, &key, &data, flag)) == 0) {
859         memcpy(cnid, key.data, sizeof(cnid_t));
860         *cnid = ntohl(*cnid);
861         return 1;
862     }
863
864     if (rc != DB_NOTFOUND) {
865         LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(errno));
866         dbd->db_cur->close(dbd->db_cur);
867         dbd->db_cur = NULL;
868         return -1;
869     }
870
871     if (dbd->db_cur) {
872         dbd->db_cur->close(dbd->db_cur);
873         dbd->db_cur = NULL;
874     }    
875
876     return 0;
877 }