]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/dbif.c
Merge master
[netatalk.git] / etc / cnid_dbd / dbif.c
1 /*
2  * Copyright (C) Joerg Lenneis 2003
3  * Copyright (C) Frank Lahm 2009
4  * All Rights Reserved.  See COPYING.
5  */
6
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif /* HAVE_CONFIG_H */
10
11 #include <stdio.h>
12 #include <errno.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
18 #include <arpa/inet.h>
19
20 #include <db.h>
21
22 #include <atalk/logger.h>
23 #include <atalk/util.h>
24 #include <atalk/errchk.h>
25
26 #include "db_param.h"
27 #include "dbif.h"
28 #include "pack.h"
29
30 #define DB_ERRLOGFILE "db_errlog"
31
32 /*!
33  * Get the db stamp which is the st_ctime of the file "cnid2.db" and store it in buffer
34  */
35 static int dbif_stamp(DBD *dbd, void *buffer, int size)
36 {
37     EC_INIT;
38     struct stat st;
39     int cwd = -1;
40
41     if (size < 8)
42         EC_FAIL;
43
44     /* Remember cwd */
45     if ((cwd = open(".", O_RDONLY)) < 0) {
46         LOG(log_error, logtype_cnid, "error opening cwd: %s", strerror(errno));
47         EC_FAIL;
48     }
49
50     /* chdir to db_envhome */
51     if ((chdir(dbd->db_envhome)) != 0) {
52         LOG(log_error, logtype_cnid, "error chdiring to db_env '%s': %s", dbd->db_envhome, strerror(errno));        
53         EC_FAIL;
54     }
55
56     if (stat(dbd->db_table[DBIF_CNID].name, &st) < 0) {
57         LOG(log_error, logtype_cnid, "error stating database %s: %s", dbd->db_table[DBIF_CNID].name, db_strerror(errno));
58         EC_FAIL;
59     }
60
61     LOG(log_maxdebug, logtype_cnid,"stamp: %s", asctime(localtime(&st.st_ctime)));
62
63     memset(buffer, 0, size);
64     memcpy(buffer, &st.st_ctime, sizeof(st.st_ctime));
65
66 EC_CLEANUP:
67     if (cwd != -1) {
68         if (fchdir(cwd) != 0) {
69             LOG(log_error, logtype_cnid, "error chdiring back: %s", strerror(errno));        
70             EC_STATUS(-1);
71         }
72         close(cwd);
73     }
74     EC_EXIT;
75 }
76
77 /*!
78  * Inititialize rootinfo key (which has CNID 0 as key)
79  *
80  * This also "stamps" the database, which means storing st.st_ctime of the
81  * "cnid2.db" file in the rootinfo data at the DEV offset
82  *
83  * @param dbd      (rw) database handle
84  * @param version  (r)  database version number
85  *
86  * @returns -1 on error, 0 on success
87  */
88 static int dbif_init_rootinfo(DBD *dbd, int version)
89 {
90     DBT key, data;
91     uint32_t v;
92     char buf[ROOTINFO_DATALEN];
93
94     LOG(log_debug, logtype_cnid, "Setting CNID database version to %u", version);
95
96     v = version;
97     v = htonl(v);
98
99     memset(&key, 0, sizeof(key));
100     memset(&data, 0, sizeof(data));
101     key.data = ROOTINFO_KEY;
102     key.size = ROOTINFO_KEYLEN;
103     data.data = buf;
104     data.size = ROOTINFO_DATALEN;
105
106     memcpy(buf, ROOTINFO_DATA, ROOTINFO_DATALEN);
107     memcpy(buf + CNID_DID_OFS, &v, sizeof(v));
108     if (dbif_stamp(dbd, buf + CNID_DEV_OFS, CNID_DEV_LEN) < 0)
109         return -1;
110
111     if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) < 0)
112         return -1;
113     if (dbif_txn_commit(dbd) != 1) {
114         LOG(log_error, logtype_cnid, "dbif_init_rootinfo: cant commit txn");
115         return -1;
116     }
117
118     return 0;
119 }
120
121 /*!
122  * Return CNID database version number
123  *
124  * Returns version in *version
125  *
126  * @returns -1 on error, 0 if theres no rootinfo key yet, 1 if *version is returned
127  */
128 static int dbif_getversion(DBD *dbd, uint32_t *version)
129 {
130     DBT key, data;
131     int ret;
132
133     LOG(log_maxdebug, logtype_cnid, "dbif_getversion: reading version info");
134
135     *version = -1;
136     memset(&key, 0, sizeof(key));
137     memset(&data, 0, sizeof(data));
138     key.data = ROOTINFO_KEY;
139     key.size = ROOTINFO_KEYLEN;
140
141     switch (dbif_get(dbd, DBIF_CNID, &key, &data, 0)) {
142     case 1: /* found */
143         memcpy(version, (char *)data.data + CNID_DID_OFS, sizeof(uint32_t));
144         *version = ntohl(*version);
145         LOG(log_debug, logtype_cnid, "CNID database version %u", *version);
146         ret = 1;
147         break;
148     case 0: /* not found */
149         LOG(log_debug, logtype_cnid, "dbif_getversion: no version info found");
150         ret = 0;
151         break;
152     default:
153         LOG(log_error, logtype_cnid, "dbif_getversion: database error");
154         ret = -1;
155         break;
156     }
157
158     return ret;
159 }
160
161 /*!
162  * Set CNID database version number
163  *
164  * Initializes rootinfo key as neccessary
165  * @returns -1 on error, 0 on success
166  */
167 static int dbif_setversion(DBD *dbd, uint32_t version)
168 {
169     int ret;
170     DBT key, data;
171     uint32_t v;
172
173     LOG(log_debug, logtype_cnid, "Setting CNID database version to %u", version);
174
175     v = version;
176     v = htonl(v);
177
178     memset(&key, 0, sizeof(key));
179     memset(&data, 0, sizeof(data));
180     key.data = ROOTINFO_KEY;
181     key.size = ROOTINFO_KEYLEN;
182
183     if ((ret = dbif_get(dbd, DBIF_CNID, &key, &data, 0)) == -1)
184         return -1;
185     if (ret == 0) {
186         /* No rootinfo key yet, init it */
187         if (dbif_init_rootinfo(dbd, CNID_VERSION) != 0)
188             return -1;
189         /* Now try again */
190         if (dbif_get(dbd, DBIF_CNID, &key, &data, 0) == -1)
191             return -1;
192     }
193     memcpy((char *)data.data + CNID_DID_OFS, &v, sizeof(v));
194     data.size = ROOTINFO_DATALEN;
195     if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) < 0)
196         return -1;
197
198     return 0;
199 }
200
201 /*!
202  * Upgrade CNID database versions, initialize rootinfo key as as necessary in dbif_setversion()
203  *
204  * For now this does nothing, as upgrading from ver. 0 to 1 is done in dbif_open
205  */
206 #define UNINTIALIZED_DB UINT32_MAX
207 static int dbif_upgrade(DBD *dbd)
208 {
209     uint32_t version = CNID_VERSION_UNINTIALIZED_DB;
210
211     if (dbif_getversion(dbd, &version) == -1)
212         return -1;
213     if (version == CNID_VERSION_UNINTIALIZED_DB) {
214         version = CNID_VERSION;
215         if (dbif_setversion(dbd, CNID_VERSION) != 0)
216             return -1;
217     }
218
219     /* 
220      * Do upgrade stuff ...
221      */
222
223     /* Write current version to database */
224     if (version != CNID_VERSION) {
225         if (dbif_setversion(dbd, CNID_VERSION) != 0)
226             return -1;
227     }
228
229     LOG(log_debug, logtype_cnid, "Finished CNID database version upgrade check");
230
231     return 0;
232 }
233
234 /* --------------- */
235 static int dbif_openlog(DBD *dbd)
236 {
237     int ret = 0;
238     int cwd = -1;
239
240     if ( ! dbd->db_filename)
241         /* in memory db */
242         return 0;
243
244     /* Remember cwd */
245     if ((cwd = open(".", O_RDONLY)) < 0) {
246         LOG(log_error, logtype_cnid, "error opening cwd: %s", strerror(errno));
247         return -1;
248     }
249
250     /* chdir to db_envhome */
251     if ((chdir(dbd->db_envhome)) != 0) {
252         LOG(log_error, logtype_cnid, "error chdiring to db_env '%s': %s", dbd->db_envhome, strerror(errno));        
253         ret = -1;
254         goto exit;
255     }
256
257     if ((dbd->db_errlog = fopen(DB_ERRLOGFILE, "a")) == NULL)
258         LOG(log_warning, logtype_cnid, "error creating/opening DB errlogfile: %s", strerror(errno));
259
260     if (dbd->db_errlog != NULL) {
261         dbd->db_env->set_errfile(dbd->db_env, dbd->db_errlog);
262         dbd->db_env->set_msgfile(dbd->db_env, dbd->db_errlog);
263     }
264
265 exit:
266     if (cwd != -1) {
267         if ((fchdir(cwd)) != 0) {
268             LOG(log_error, logtype_cnid, "error chdiring back: %s", strerror(errno));        
269             ret = -1;
270         }
271         close(cwd);
272     }
273     return ret;
274 }
275
276 /* --------------- */
277 static int dbif_logautorem(DBD *dbd)
278 {
279     int ret = 0;
280     int cwd = -1;
281     char **logfiles = NULL;
282     char **file;
283
284     if ( ! dbd->db_filename)
285         /* in memory db */
286         return 0;
287
288     /* Remember cwd */
289     if ((cwd = open(".", O_RDONLY)) < 0) {
290         LOG(log_error, logtype_cnid, "error opening cwd: %s", strerror(errno));
291         return -1;
292     }
293
294     /* chdir to db_envhome */
295     if ((chdir(dbd->db_envhome)) != 0) {
296         LOG(log_error, logtype_cnid, "error chdiring to db_env '%s': %s", dbd->db_envhome, strerror(errno));        
297         ret = -1;
298         goto exit;
299     }
300
301     if ((ret = dbd->db_env->log_archive(dbd->db_env, &logfiles, 0)) != 0) {
302         LOG(log_error, logtype_cnid, "error getting list of stale logfiles: %s",
303             db_strerror(ret));
304         dbd->db_env->close(dbd->db_env, 0);
305         dbd->db_env = NULL;
306         ret = -1;
307         goto exit;
308     }
309
310     if (logfiles != NULL) {
311         for (file = logfiles; *file != NULL; file++) {
312             if (unlink(*file) < 0)
313                 LOG(log_warning, logtype_cnid, "Error removing stale logfile %s: %s", *file, strerror(errno));
314         }
315         free(logfiles);
316     }
317
318 exit:
319     if (cwd != -1) {
320         if ((fchdir(cwd)) != 0) {
321             LOG(log_error, logtype_cnid, "error chdiring back: %s", strerror(errno));        
322             ret = -1;
323         }
324         close(cwd);
325     }
326     return ret;
327 }
328
329 /*!
330  * Get lock on db lock file
331  *
332  * @args cmd       (r) lock command:
333  *                     LOCK_FREE:   close lockfd
334  *                     LOCK_UNLOCK: unlock lockm keep lockfd open
335  *                     LOCK_EXCL:   F_WRLCK on lockfd
336  *                     LOCK_SHRD:   F_RDLCK on lockfd
337  * @args dbpath    (r) path to lockfile, only used on first call,
338  *                     later the stored fd is used
339  * @returns            LOCK_FREE/LOCK_UNLOCK return 0 on success, -1 on error
340  *                     LOCK_EXCL/LOCK_SHRD return LOCK_EXCL or LOCK_SHRD respectively on
341  *                     success, 0 if the lock couldn't be acquired, -1 on other errors
342  */
343 int get_lock(int cmd, const char *dbpath)
344 {
345     static int lockfd = -1;
346     int ret;
347     char lockpath[PATH_MAX];
348     struct stat st;
349
350     switch (cmd) {
351     case LOCK_FREE:
352         if (lockfd == -1)
353             return -1;
354         close(lockfd);
355         lockfd = -1;
356         return 0;
357
358     case LOCK_UNLOCK:
359         if (lockfd == -1)
360             return -1;
361         return unlock(lockfd, 0, SEEK_SET, 0);
362
363     case LOCK_EXCL:
364     case LOCK_SHRD:
365         if (lockfd == -1) {
366             if ( (strlen(dbpath) + strlen(LOCKFILENAME+1)) > (PATH_MAX - 1) ) {
367                 LOG(log_error, logtype_cnid, ".AppleDB pathname too long");
368                 return -1;
369             }
370             strncpy(lockpath, dbpath, PATH_MAX - 1);
371             strcat(lockpath, "/");
372             strcat(lockpath, LOCKFILENAME);
373
374             if ((lockfd = open(lockpath, O_RDWR | O_CREAT, 0644)) < 0) {
375                 LOG(log_error, logtype_cnid, "Error opening lockfile: %s", strerror(errno));
376                 return -1;
377             }
378
379             if ((stat(dbpath, &st)) != 0) {
380                 LOG(log_error, logtype_cnid, "Error statting lockfile: %s", strerror(errno));
381                 return -1;
382             }
383
384             if ((chown(lockpath, st.st_uid, st.st_gid)) != 0) {
385                 LOG(log_error, logtype_cnid, "Error inheriting lockfile permissions: %s",
386                          strerror(errno));
387                 return -1;
388             }
389         }
390     
391         if (cmd == LOCK_EXCL)
392             ret = write_lock(lockfd, 0, SEEK_SET, 0);
393         else
394             ret = read_lock(lockfd, 0, SEEK_SET, 0);
395
396         if (ret != 0) {
397             if (cmd == LOCK_SHRD)
398                 LOG(log_error, logtype_cnid, "Volume CNID db is locked, try again...");
399             return 0; 
400         }
401
402         LOG(log_debug, logtype_cnid, "get_lock: got %s lock",
403             cmd == LOCK_EXCL ? "LOCK_EXCL" : "LOCK_SHRD");    
404         return cmd;
405
406     default:
407         return -1;
408     } /* switch(cmd) */
409
410     /* deadc0de, never get here */
411     return -1;
412 }
413
414 /* --------------- */
415 DBD *dbif_init(const char *envhome, const char *filename)
416 {
417     DBD *dbd;
418
419     if ( NULL == (dbd = calloc(sizeof(DBD), 1)) )
420         return NULL;
421
422     /* filename == NULL means in memory db */
423     if (filename) {
424         if (! envhome)
425             return NULL;
426
427         dbd->db_envhome = strdup(envhome);
428         if (NULL == dbd->db_envhome) {
429             free(dbd);
430             return NULL;
431         }
432
433         dbd->db_filename = strdup(filename);
434         if (NULL == dbd->db_filename) {
435             free(dbd->db_envhome);
436             free(dbd);
437             return NULL;
438         }
439     }
440     
441     dbd->db_table[DBIF_CNID].name        = "cnid2.db";
442     dbd->db_table[DBIF_IDX_DEVINO].name  = "devino.db";
443     dbd->db_table[DBIF_IDX_DIDNAME].name = "didname.db";
444     dbd->db_table[DBIF_IDX_NAME].name    = "name.db";
445
446     dbd->db_table[DBIF_CNID].type        = DB_BTREE;
447     dbd->db_table[DBIF_IDX_DEVINO].type  = DB_BTREE;
448     dbd->db_table[DBIF_IDX_DIDNAME].type = DB_BTREE;
449     dbd->db_table[DBIF_IDX_NAME].type    = DB_BTREE;
450
451     dbd->db_table[DBIF_CNID].openflags        = DB_CREATE;
452     dbd->db_table[DBIF_IDX_DEVINO].openflags  = DB_CREATE;
453     dbd->db_table[DBIF_IDX_DIDNAME].openflags = DB_CREATE;
454     dbd->db_table[DBIF_IDX_NAME].openflags    = DB_CREATE;
455
456     dbd->db_table[DBIF_IDX_NAME].flags = DB_DUPSORT;
457
458     return dbd;
459 }
460
461 /* 
462    We must open the db_env with an absolute pathname, as `dbd` keeps chdir'ing, which
463    breaks e.g. bdb logfile-rotation with relative pathnames.
464    But still we use relative paths with DB_ERRLOGFILE
465    in order to avoid creating absolute paths by copying. Both have no problem with
466    a relative path.
467 */
468 int dbif_env_open(DBD *dbd, struct db_param *dbp, uint32_t dbenv_oflags)
469 {
470     int ret;
471
472     if ((ret = db_env_create(&dbd->db_env, 0))) {
473         LOG(log_error, logtype_cnid, "error creating DB environment: %s",
474             db_strerror(ret));
475         dbd->db_env = NULL;
476         return -1;
477     }
478
479     dbd->db_param = *dbp;
480
481     if ((dbif_openlog(dbd)) != 0)
482         return -1;
483
484     if (dbenv_oflags & DB_RECOVER) {
485
486         LOG(log_debug, logtype_cnid, "Running recovery");
487
488         dbd->db_env->set_verbose(dbd->db_env, DB_VERB_RECOVERY, 1);
489         /* Open the database for recovery using DB_PRIVATE option which is faster */
490         if ((ret = dbd->db_env->open(dbd->db_env, dbd->db_envhome, dbenv_oflags | DB_PRIVATE, 0))) {
491             LOG(log_error, logtype_cnid, "error opening DB environment: %s",
492                 db_strerror(ret));
493             dbd->db_env->close(dbd->db_env, 0);
494             dbd->db_env = NULL;
495             return -1;
496         }
497         dbenv_oflags = (dbenv_oflags & ~DB_RECOVER);
498
499         if (dbd->db_errlog != NULL)
500             fflush(dbd->db_errlog);
501
502         if ((ret = dbd->db_env->close(dbd->db_env, 0))) {
503             LOG(log_error, logtype_cnid, "error closing DB environment after recovery: %s",
504                 db_strerror(ret));
505             dbd->db_env = NULL;
506             return -1;
507         }
508         dbd->db_errlog = NULL;        
509
510         if ((ret = db_env_create(&dbd->db_env, 0))) {
511             LOG(log_error, logtype_cnid, "error creating DB environment after recovery: %s",
512                 db_strerror(ret));
513             dbd->db_env = NULL;
514             return -1;
515         }
516
517         if ((dbif_openlog(dbd)) != 0)
518             return -1;
519
520         LOG(log_debug, logtype_cnid, "Finished recovery.");
521     }
522
523     if ((ret = dbd->db_env->set_cachesize(dbd->db_env, 0, 1024 * dbp->cachesize, 0))) {
524         LOG(log_error, logtype_cnid, "error setting DB environment cachesize to %i: %s",
525             dbp->cachesize, db_strerror(ret));
526         dbd->db_env->close(dbd->db_env, 0);
527         dbd->db_env = NULL;
528         return -1;
529     }
530
531     if ((ret = dbd->db_env->set_lk_max_locks(dbd->db_env, dbp->maxlocks))) {
532         LOG(log_error, logtype_cnid, "error setting DB environment maxlocks to %i: %s",
533             10000, db_strerror(ret));
534         dbd->db_env->close(dbd->db_env, 0);
535         dbd->db_env = NULL;
536         return -1;
537     }
538
539     if ((ret = dbd->db_env->set_lk_max_objects(dbd->db_env, dbp->maxlockobjs))) {
540         LOG(log_error, logtype_cnid, "error setting DB environment max lockobjects to %i: %s",
541             10000, db_strerror(ret));
542         dbd->db_env->close(dbd->db_env, 0);
543         dbd->db_env = NULL;
544         return -1;
545     }
546
547     if ((ret = dbd->db_env->open(dbd->db_env, dbd->db_envhome, dbenv_oflags, 0))) {
548         LOG(log_error, logtype_cnid, "error opening DB environment after recovery: %s",
549             db_strerror(ret));
550         dbd->db_env->close(dbd->db_env, 0);
551         dbd->db_env = NULL;
552         return -1;
553     }
554
555     if ((ret = dbd->db_env->set_flags(dbd->db_env, DB_AUTO_COMMIT, 1))) {
556         LOG(log_error, logtype_cnid, "error setting DB_AUTO_COMMIT flag: %s",
557             db_strerror(ret));
558         dbd->db_env->close(dbd->db_env, 0);
559         dbd->db_env = NULL;
560         return -1;
561     }
562
563     if (dbp->logfile_autoremove) {
564         if ((dbif_logautorem(dbd)) != 0)
565             return -1;
566
567 #if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 7)
568         if ((ret = dbd->db_env->log_set_config(dbd->db_env, DB_LOG_AUTO_REMOVE, 1))) {
569             LOG(log_error, logtype_cnid, "error setting DB_LOG_AUTO_REMOVE flag: %s",
570             db_strerror(ret));
571             dbd->db_env->close(dbd->db_env, 0);
572             dbd->db_env = NULL;
573             return -1;
574         }
575 #else
576         if ((ret = dbd->db_env->set_flags(dbd->db_env, DB_LOG_AUTOREMOVE, 1))) {
577             LOG(log_error, logtype_cnid, "error setting DB_LOG_AUTOREMOVE flag: %s",
578                 db_strerror(ret));
579             dbd->db_env->close(dbd->db_env, 0);
580             dbd->db_env = NULL;
581             return -1;
582         }
583 #endif
584     }
585
586     return 0;
587 }
588
589 /* --------------- */
590 int dbif_open(DBD *dbd, struct db_param *dbp, int reindex)
591 {
592     int ret, i, cwd;
593     u_int32_t count;
594     struct stat st;
595     DB *upgrade_db;
596
597     /* Try to upgrade if it's a normal on-disk database */
598     if (dbd->db_envhome) {
599         /* Remember cwd */
600         if ((cwd = open(".", O_RDONLY)) < 0) {
601             LOG(log_error, logtype_cnid, "error opening cwd: %s", strerror(errno));
602             return -1;
603         }
604         
605         /* chdir to db_envhome. makes it easier checking for old db files and creating db_errlog file  */
606         if ((chdir(dbd->db_envhome)) != 0) {
607             LOG(log_error, logtype_cnid, "error chdiring to db_env '%s': %s", dbd->db_envhome, strerror(errno));        
608             return -1;
609         }
610         
611         if ((stat(dbd->db_filename, &st)) == 0) {
612             LOG(log_debug, logtype_cnid, "See if we can upgrade the CNID database...");
613             if ((ret = db_create(&upgrade_db, dbd->db_env, 0))) {
614                 LOG(log_error, logtype_cnid, "error creating handle for database: %s", db_strerror(ret));
615                 return -1;
616             }
617             if ((ret = upgrade_db->upgrade(upgrade_db, dbd->db_filename, 0))) {
618                 LOG(log_error, logtype_cnid, "error upgarding database: %s", db_strerror(ret));
619                 return -1;
620             }
621             if ((ret = upgrade_db->close(upgrade_db, 0))) {
622                 LOG(log_error, logtype_cnid, "error closing database: %s", db_strerror(ret));
623                 return -1;
624             }
625             if ((ret = dbd->db_env->txn_checkpoint(dbd->db_env, 0, 0, DB_FORCE))) {
626                 LOG(log_error, logtype_cnid, "error forcing checkpoint: %s", db_strerror(ret));
627                 return -1;
628             }
629             LOG(log_debug, logtype_cnid, "Finished BerkeleyBD upgrade check");
630         }
631         
632         if ((fchdir(cwd)) != 0) {
633             LOG(log_error, logtype_cnid, "error chdiring back: %s", strerror(errno));        
634             return -1;
635         }
636     }
637
638     /* Now open databases ... */
639     for (i = 0; i != DBIF_DB_CNT; i++) {
640         if ((ret = db_create(&dbd->db_table[i].db, dbd->db_env, 0))) {
641             LOG(log_error, logtype_cnid, "error creating handle for database %s: %s",
642                 dbd->db_table[i].name, db_strerror(ret));
643             return -1;
644         }
645
646         if (dbd->db_table[i].flags) {
647             if ((ret = dbd->db_table[i].db->set_flags(dbd->db_table[i].db,
648                                                       dbd->db_table[i].flags))) {
649                 LOG(log_error, logtype_cnid, "error setting flags for database %s: %s",
650                     dbd->db_table[i].name, db_strerror(ret));
651                 return -1;
652             }
653         }
654
655         if ( ! dbd->db_env) {   /* In memory db */
656             if ((ret = dbd->db_table[i].db->set_cachesize(dbd->db_table[i].db,
657                                                           0,
658                                                           dbp->cachesize,
659                                                           4)) /* split in 4 memory chunks */
660                 < 0)  {
661                 LOG(log_error, logtype_cnid, "error setting cachesize %u KB for database %s: %s",
662                     dbp->cachesize / 1024, dbd->db_table[i].name, db_strerror(ret));
663                 return -1;
664             }
665         }
666
667         if (dbd->db_table[i].db->open(dbd->db_table[i].db,
668                                       dbd->db_txn,
669                                       dbd->db_filename,
670                                       dbd->db_table[i].name,
671                                       dbd->db_table[i].type,
672                                       dbd->db_table[i].openflags,
673                                       0664) < 0) {
674             LOG(log_error, logtype_cnid, "Cant open database");
675             return -1;
676         }
677
678         if (reindex && i > 0) {
679             LOG(log_info, logtype_cnid, "Truncating CNID index.");
680             if ((ret = dbd->db_table[i].db->truncate(dbd->db_table[i].db, NULL, &count, 0))) {
681                 LOG(log_error, logtype_cnid, "error truncating database %s: %s",
682                     dbd->db_table[i].name, db_strerror(ret));
683                 return -1;
684             }
685         }
686     }
687
688     /* TODO: Implement CNID DB versioning info on new databases. */
689
690     /* Associate the secondary with the primary. */
691     if (reindex)
692         LOG(log_info, logtype_cnid, "Reindexing did/name index...");
693     if ((ret = dbd->db_table[0].db->associate(dbd->db_table[DBIF_CNID].db,
694                                               dbd->db_txn,
695                                               dbd->db_table[DBIF_IDX_DIDNAME].db, 
696                                               didname,
697                                               (reindex) ? DB_CREATE : 0))
698          != 0) {
699         LOG(log_error, logtype_cnid, "Failed to associate didname database: %s",db_strerror(ret));
700         return -1;
701     }
702     if (reindex)
703         LOG(log_info, logtype_cnid, "... done.");
704
705     if (reindex)
706         LOG(log_info, logtype_cnid, "Reindexing dev/ino index...");
707     if ((ret = dbd->db_table[0].db->associate(dbd->db_table[0].db, 
708                                               dbd->db_txn,
709                                               dbd->db_table[DBIF_IDX_DEVINO].db, 
710                                               devino,
711                                               (reindex) ? DB_CREATE : 0))
712         != 0) {
713         LOG(log_error, logtype_cnid, "Failed to associate devino database: %s",db_strerror(ret));
714         return -1;
715     }
716     if (reindex)
717         LOG(log_info, logtype_cnid, "... done.");
718
719     if (reindex)
720         LOG(log_info, logtype_cnid, "Reindexing name index...");
721
722     /*
723      * Upgrading from version 0 to 1 requires adding the name index below which
724      * must be done by specifying the DB_CREATE flag
725      */
726     uint32_t version = CNID_VERSION;
727     if (dbd->db_envhome && !reindex) {
728         if (dbif_getversion(dbd, &version) == -1)
729             return -1;
730     }
731
732     if ((ret = dbd->db_table[0].db->associate(dbd->db_table[0].db, 
733                                               dbd->db_txn,
734                                               dbd->db_table[DBIF_IDX_NAME].db, 
735                                               idxname,
736                                               (reindex
737                                                || 
738                                                ((CNID_VERSION == CNID_VERSION_1) && (version == CNID_VERSION_0)))
739                                               ? DB_CREATE : 0)) != 0) {
740         LOG(log_error, logtype_cnid, "Failed to associate name index: %s", db_strerror(ret));
741         return -1;
742     }
743     if (reindex)
744         LOG(log_info, logtype_cnid, "... done.");
745
746     if ((dbd->db_envhome) && ((ret = dbif_upgrade(dbd)) != 0)) {
747         LOG(log_error, logtype_cnid, "Error upgrading CNID database to version %d", CNID_VERSION);
748         return -1;
749     }
750     
751     return 0;
752 }
753
754 /* ------------------------ */
755 static int dbif_closedb(DBD *dbd)
756 {
757     int i;
758     int ret;
759     int err = 0;
760
761     for (i = DBIF_DB_CNT -1; i >= 0; i--) {
762         if (dbd->db_table[i].db != NULL && (ret = dbd->db_table[i].db->close(dbd->db_table[i].db, 0))) {
763             LOG(log_error, logtype_cnid, "error closing database %s: %s", dbd->db_table[i].name, db_strerror(ret));
764             err++;
765         }
766     }
767     if (err)
768         return -1;
769     return 0;
770 }
771
772 /* ------------------------ */
773 int dbif_close(DBD *dbd)
774 {
775     int ret;
776     int err = 0;
777
778     if (dbif_closedb(dbd))
779         err++;
780
781     if (dbd->db_env != NULL && (ret = dbd->db_env->close(dbd->db_env, 0))) {
782         LOG(log_error, logtype_cnid, "error closing DB environment: %s", db_strerror(ret));
783         err++;
784     }
785     if (dbd->db_errlog != NULL && fclose(dbd->db_errlog) == EOF) {
786         LOG(log_error, logtype_cnid, "error closing DB logfile: %s", strerror(errno));
787         err++;
788     }
789
790     free(dbd->db_filename);
791     free(dbd);
792     dbd = NULL;
793
794     if (err)
795         return -1;
796     return 0;
797 }
798
799 /* 
800    In order to support silent database upgrades:
801    destroy env at cnid_dbd shutdown.
802  */
803 int dbif_env_remove(const char *path)
804 {
805     int ret;
806     DBD *dbd;
807
808     LOG(log_debug, logtype_cnid, "Reopening BerkeleyDB environment");
809     
810     if (NULL == (dbd = dbif_init(path, "cnid2.db")))
811         return -1;
812
813     /* Get db_env handle */
814     if ((ret = db_env_create(&dbd->db_env, 0))) {
815         LOG(log_error, logtype_cnid, "error creating DB environment: %s", db_strerror(ret));
816         dbd->db_env = NULL;
817         return -1;
818     }
819
820     if ((dbif_openlog(dbd)) != 0)
821         return -1;
822
823     /* Open environment with recovery */
824     if ((ret = dbd->db_env->open(dbd->db_env, 
825                                  dbd->db_envhome,
826                                  DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN | DB_RECOVER | DB_PRIVATE,
827                                  0))) {
828         LOG(log_error, logtype_cnid, "error opening DB environment: %s",
829             db_strerror(ret));
830         dbd->db_env->close(dbd->db_env, 0);
831         dbd->db_env = NULL;
832         return -1;
833     }
834
835     if (dbd->db_errlog != NULL)
836         fflush(dbd->db_errlog);
837
838     /* Remove logfiles */
839     if ((ret = dbd->db_env->log_archive(dbd->db_env, NULL, DB_ARCH_REMOVE))) {
840          LOG(log_error, logtype_cnid, "error removing transaction logfiles: %s", db_strerror(ret));
841          return -1;
842     }
843
844     if ((ret = dbd->db_env->close(dbd->db_env, 0))) {
845         LOG(log_error, logtype_cnid, "error closing DB environment after recovery: %s", db_strerror(ret));
846         dbd->db_env = NULL;
847         return -1;
848     }
849
850     LOG(log_debug, logtype_cnid, "BerkeleyDB environment recovery done.");
851
852     /* Get a new db_env handle and then remove environment */
853     if ((ret = db_env_create(&dbd->db_env, 0))) {
854         LOG(log_error, logtype_cnid, "error acquiring db_end handle: %s", db_strerror(ret));
855         dbd->db_env = NULL;
856         return -1;
857     }
858     if ((ret = dbd->db_env->remove(dbd->db_env, dbd->db_envhome, 0))) {
859         LOG(log_error, logtype_cnid, "error removing BerkeleyDB environment: %s", db_strerror(ret));
860         return -1;
861     }
862
863     LOG(log_debug, logtype_cnid, "Removed BerkeleyDB environment.");
864
865     return 0;
866 }
867
868 /*
869  *  The following three functions are wrappers for DB->get(), DB->put() and DB->del().
870  *  All three return -1 on error. dbif_get()/dbif_del return 1 if the key was found and 0
871  *  otherwise. dbif_put() returns 0 if key/val was successfully updated and 1 if
872  *  the DB_NOOVERWRITE flag was specified and the key already exists.
873  *
874  *  All return codes other than DB_NOTFOUND and DB_KEYEXIST from the DB->()
875  *  functions are not expected and therefore error conditions.
876  */
877
878 int dbif_get(DBD *dbd, const int dbi, DBT *key, DBT *val, u_int32_t flags)
879 {
880     int ret;
881
882     ret = dbd->db_table[dbi].db->get(dbd->db_table[dbi].db,
883                                      dbd->db_txn,
884                                      key,
885                                      val,
886                                      flags);
887
888     if (ret == DB_NOTFOUND)
889         return 0;
890     if (ret) {
891         LOG(log_error, logtype_cnid, "error retrieving value from %s: %s",
892             dbd->db_table[dbi].name, db_strerror(ret));
893         return -1;
894     } else
895         return 1;
896 }
897
898 /* search by secondary return primary */
899 int dbif_pget(DBD *dbd, const int dbi, DBT *key, DBT *pkey, DBT *val, u_int32_t flags)
900 {
901     int ret;
902
903     ret = dbd->db_table[dbi].db->pget(dbd->db_table[dbi].db,
904                                       dbd->db_txn,
905                                       key,
906                                       pkey,
907                                       val,
908                                       flags);
909
910     if (ret == DB_NOTFOUND || ret == DB_SECONDARY_BAD) {
911         return 0;
912     }
913     if (ret) {
914         LOG(log_error, logtype_cnid, "error retrieving value from %s: %s",
915             dbd->db_table[dbi].name, db_strerror(ret));
916         return -1;
917    } else
918         return 1;
919 }
920
921 /* -------------------------- */
922 int dbif_put(DBD *dbd, const int dbi, DBT *key, DBT *val, u_int32_t flags)
923 {
924     int ret;
925
926     if (dbif_txn_begin(dbd) < 0) {
927         LOG(log_error, logtype_cnid, "error setting key/value in %s", dbd->db_table[dbi].name);
928         return -1;
929     }
930
931     ret = dbd->db_table[dbi].db->put(dbd->db_table[dbi].db,
932                                      dbd->db_txn,
933                                      key,
934                                      val,
935                                      flags);
936
937     
938     if (ret) {
939         if ((flags & DB_NOOVERWRITE) && ret == DB_KEYEXIST) {
940             return 1;
941         } else {
942             LOG(log_error, logtype_cnid, "error setting key/value in %s: %s",
943                 dbd->db_table[dbi].name, db_strerror(ret));
944             return -1;
945         }
946     } else
947         return 0;
948 }
949
950 int dbif_del(DBD *dbd, const int dbi, DBT *key, u_int32_t flags)
951 {
952     int ret;
953
954     /* For cooperation with the dbd utility and its usage of a cursor */
955     if (dbd->db_cur) {
956         dbd->db_cur->close(dbd->db_cur);
957         dbd->db_cur = NULL;
958     }    
959
960     if (dbif_txn_begin(dbd) < 0) {
961         LOG(log_error, logtype_cnid, "error deleting key/value from %s", dbd->db_table[dbi].name);
962         return -1;
963     }
964
965     ret = dbd->db_table[dbi].db->del(dbd->db_table[dbi].db,
966                                      dbd->db_txn,
967                                      key,
968                                      flags);
969     
970     if (ret == DB_NOTFOUND) {
971         LOG(log_debug, logtype_cnid, "key not found");
972         return 0;
973     }
974     if (ret) {
975         LOG(log_error, logtype_cnid, "error deleting key/value from %s: %s",
976             dbd->db_table[dbi].name, db_strerror(ret));
977         return -1;
978     } else
979         return 1;
980 }
981
982 /*!
983  * Search the database by name
984  *
985  * @param resbuf    (w) buffer for search results CNIDs, maxsize is assumed to be
986  *                      DBD_MAX_SRCH_RSLTS * sizefof(cnid_t)
987  *
988  * @returns -1 on error, 0 when nothing found, else the number of matches
989  */
990 int dbif_search(DBD *dbd, DBT *key, char *resbuf)
991 {
992     int ret = 0;
993     int count = 0;
994     DBC *cursorp = NULL;
995     DBT pkey, data;
996     char *cnids = resbuf;
997     cnid_t cnid;
998     char *namebkp = key->data;
999     int namelenbkp = key->size;
1000
1001     memset(&pkey, 0, sizeof(DBT));
1002     memset(&data, 0, sizeof(DBT));
1003
1004     /* Get a cursor */
1005     ret = dbd->db_table[DBIF_IDX_NAME].db->cursor(dbd->db_table[DBIF_IDX_NAME].db,
1006                                                   NULL,
1007                                                   &cursorp,
1008                                                   0);
1009     if (ret != 0) {
1010         LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(ret));
1011         ret = -1;
1012         goto exit;
1013     }
1014
1015     ret = cursorp->pget(cursorp, key, &pkey, &data, DB_SET_RANGE);
1016     while (count < DBD_MAX_SRCH_RSLTS && ret != DB_NOTFOUND) {
1017         if (!((namelenbkp <= key->size) && (strncmp(namebkp, key->data, namelenbkp) == 0)))
1018             break;
1019         count++;
1020         memcpy(cnids, pkey.data, sizeof(cnid_t));
1021         memcpy(&cnid, pkey.data, sizeof(cnid_t));
1022         cnids += sizeof(cnid_t);
1023         LOG(log_debug, logtype_cnid, "match: CNID %" PRIu32, ntohl(cnid));
1024
1025         ret = cursorp->pget(cursorp, key, &pkey, &data, DB_NEXT);
1026     }
1027
1028     ret = count;
1029
1030 exit:
1031     if (cursorp != NULL)
1032         cursorp->close(cursorp);
1033     return ret;
1034 }
1035
1036 int dbif_txn_begin(DBD *dbd)
1037 {
1038     int ret;
1039
1040     /* If we already have an active txn, just return */
1041     if (dbd->db_txn)
1042         return 0;
1043
1044     /* If our DBD has no env, just return (-> in memory db) */
1045     if (dbd->db_env == NULL)
1046         return 0;
1047
1048     ret = dbd->db_env->txn_begin(dbd->db_env, NULL, &dbd->db_txn, 0);
1049
1050     if (ret) {
1051         LOG(log_error, logtype_cnid, "error starting transaction: %s", db_strerror(ret));
1052         return -1;
1053     } else
1054         return 0;
1055 }
1056
1057 int dbif_txn_commit(DBD *dbd)
1058 {
1059     int ret;
1060
1061     if (! dbd->db_txn)
1062         return 0;
1063
1064     /* If our DBD has no env, just return (-> in memory db) */
1065     if (dbd->db_env == NULL)
1066         return 0;
1067
1068     ret = dbd->db_txn->commit(dbd->db_txn, 0);
1069     dbd->db_txn = NULL;
1070     
1071     if (ret) {
1072         LOG(log_error, logtype_cnid, "error committing transaction: %s", db_strerror(ret));
1073         return -1;
1074     } else
1075         return 1;
1076 }
1077
1078 int dbif_txn_abort(DBD *dbd)
1079 {
1080     int ret;
1081
1082     if (! dbd->db_txn)
1083         return 0;
1084
1085     /* If our DBD has no env, just return (-> in memory db) */
1086     if (dbd->db_env == NULL)
1087         return 0;
1088
1089     ret = dbd->db_txn->abort(dbd->db_txn);
1090     dbd->db_txn = NULL;
1091     
1092     if (ret) {
1093         LOG(log_error, logtype_cnid, "error aborting transaction: %s", db_strerror(ret));
1094         return -1;
1095     } else
1096         return 0;
1097 }
1098
1099 /* 
1100    ret = 1 -> commit txn if db_param.txn_frequency
1101    ret = 0 -> abort txn db_param.txn_frequency -> exit!
1102    anything else -> exit!
1103
1104    @returns 0 on success (abort or commit), -1 on error
1105 */
1106 int dbif_txn_close(DBD *dbd, int ret)
1107 {
1108     if (ret == 0) {
1109         if (dbif_txn_abort(dbd) < 0) {
1110             LOG( log_error, logtype_cnid, "Fatal error aborting transaction. Exiting!");
1111             return -1;
1112         }
1113     } else if (ret == 1) {
1114         ret = dbif_txn_commit(dbd);
1115         if (  ret < 0) {
1116             LOG( log_error, logtype_cnid, "Fatal error committing transaction. Exiting!");
1117             return -1;
1118         }
1119     } else {
1120         return -1;
1121     }
1122
1123     return 0;
1124 }
1125
1126 int dbif_txn_checkpoint(DBD *dbd, u_int32_t kbyte, u_int32_t min, u_int32_t flags)
1127 {
1128     int ret;
1129     ret = dbd->db_env->txn_checkpoint(dbd->db_env, kbyte, min, flags);
1130     if (ret) {
1131         LOG(log_error, logtype_cnid, "error checkpointing transaction susystem: %s", db_strerror(ret));
1132         return -1;
1133     } else
1134         return 0;
1135 }
1136
1137 int dbif_count(DBD *dbd, const int dbi, u_int32_t *count)
1138 {
1139     int ret;
1140     DB_BTREE_STAT *sp;
1141     DB *db = dbd->db_table[dbi].db;
1142
1143     ret = db->stat(db, NULL, &sp, 0);
1144
1145     if (ret) {
1146         LOG(log_error, logtype_cnid, "error getting stat infotmation on database: %s", db_strerror(ret));
1147         return -1;
1148     }
1149
1150     *count = sp->bt_ndata;
1151     free(sp);
1152
1153     return 0;
1154 }
1155
1156 int dbif_copy_rootinfokey(DBD *srcdbd, DBD *destdbd)
1157 {
1158     DBT key, data;
1159     int rc;
1160
1161     memset(&key, 0, sizeof(key));
1162     memset(&data, 0, sizeof(data));
1163
1164     key.data = ROOTINFO_KEY;
1165     key.size = ROOTINFO_KEYLEN;
1166
1167     if ((rc = dbif_get(srcdbd, DBIF_CNID, &key, &data, 0)) <= 0) {
1168         LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error getting rootinfo record");
1169         return -1;
1170     }
1171
1172     memset(&key, 0, sizeof(key));
1173     key.data = ROOTINFO_KEY;
1174     key.size = ROOTINFO_KEYLEN;
1175
1176     if ((rc = dbif_put(destdbd, DBIF_CNID, &key, &data, 0))) {
1177         LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error writing rootinfo key");
1178         return -1;
1179     }
1180
1181     return 0;
1182 }
1183
1184 int dbif_dump(DBD *dbd, int dumpindexes)
1185 {
1186     int rc;
1187     uint32_t max = 0, count = 0, cnid, type, did, lastid, version;
1188     uint64_t dev, ino;
1189     time_t stamp;
1190     DBC *cur;
1191     DBT key = { 0 }, data = { 0 };
1192     DB *db = dbd->db_table[DBIF_CNID].db;
1193     char *typestring[2] = {"f", "d"};
1194     char timebuf[64];
1195
1196     printf("CNID database dump:\n");
1197
1198     rc = db->cursor(db, NULL, &cur, 0);
1199     if (rc) {
1200         LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc));
1201         return -1;
1202     }
1203
1204     cur->c_get(cur, &key, &data, DB_FIRST);
1205     while (rc == 0) {
1206         /* Parse and print data */
1207         memcpy(&cnid, key.data, 4);
1208         cnid = ntohl(cnid);
1209         if (cnid > max)
1210             max = cnid;
1211
1212         /* Rootinfo node ? */
1213         if (cnid == 0) {
1214             memcpy(&stamp, (char *)data.data + CNID_DEV_OFS, sizeof(time_t));
1215             memcpy(&lastid, (char *)data.data + CNID_TYPE_OFS, CNID_TYPE_LEN);
1216             lastid = ntohl(lastid);
1217             memcpy(&version, (char *)data.data + CNID_DID_OFS, CNID_DID_LEN);
1218             version = ntohl(version);
1219
1220             strftime(timebuf, sizeof(timebuf), "%b %d %Y %H:%M:%S", localtime(&stamp));
1221             printf("CNID db version: %u, dbd stamp: 0x%08x (%s), next free CNID: %u\n",
1222                    version, (unsigned int)stamp, timebuf, lastid + 1);
1223         } else {
1224             /* dev */
1225             memcpy(&dev, (char *)data.data + CNID_DEV_OFS, 8);
1226             dev = ntoh64(dev);
1227             /* ino */
1228             memcpy(&ino, (char *)data.data + CNID_INO_OFS, 8);
1229             ino = ntoh64(ino);
1230             /* type */
1231             memcpy(&type, (char *)data.data + CNID_TYPE_OFS, 4);
1232             type = ntohl(type);
1233             /* did */
1234             memcpy(&did, (char *)data.data + CNID_DID_OFS, 4);
1235             did = ntohl(did);
1236
1237             count++;
1238             printf("id: %10u, did: %10u, type: %s, dev: 0x%llx, ino: 0x%016llx, name: %s\n", 
1239                    cnid, did, typestring[type],
1240                    (long long unsigned int)dev, (long long unsigned int)ino, 
1241                    (char *)data.data + CNID_NAME_OFS);
1242
1243         }
1244
1245         rc = cur->c_get(cur, &key, &data, DB_NEXT);
1246     }
1247
1248     if (rc != DB_NOTFOUND) {
1249         LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc));
1250         return -1;
1251     }
1252
1253     rc = cur->c_close(cur);
1254     if (rc) {
1255         LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(rc));
1256         return -1;
1257     }
1258     printf("%u CNIDs in database. Max CNID: %u.\n", count, max);
1259
1260     /* Dump indexes too ? */
1261     if (dumpindexes) {
1262         /* DBIF_IDX_DEVINO */
1263         printf("\ndev/inode index:\n");
1264         count = 0;
1265         db = dbd->db_table[DBIF_IDX_DEVINO].db;
1266         rc = db->cursor(db, NULL, &cur, 0);
1267         if (rc) {
1268             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc));
1269             return -1;
1270         }
1271         
1272         cur->c_get(cur, &key, &data, DB_FIRST);
1273         while (rc == 0) {
1274             /* Parse and print data */
1275
1276             /* cnid */
1277             memcpy(&cnid, data.data, CNID_LEN);
1278             cnid = ntohl(cnid);
1279             if (cnid == 0) {
1280                 /* Rootinfo node */
1281             } else {
1282                 /* dev */
1283                 memcpy(&dev, key.data, CNID_DEV_LEN);
1284                 dev = ntoh64(dev);
1285                 /* ino */
1286                 memcpy(&ino, (char *)key.data + CNID_DEV_LEN, CNID_INO_LEN);
1287                 ino = ntoh64(ino);
1288                 
1289                 printf("id: %10u <== dev: 0x%llx, ino: 0x%016llx\n", 
1290                        cnid, (unsigned long long int)dev, (unsigned long long int)ino);
1291                 count++;
1292             }
1293             rc = cur->c_get(cur, &key, &data, DB_NEXT);
1294         }
1295         if (rc != DB_NOTFOUND) {
1296             LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc));
1297             return -1;
1298         }
1299         
1300         rc = cur->c_close(cur);
1301         if (rc) {
1302             LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(rc));
1303             return -1;
1304         }
1305         printf("%u items\n", count);
1306
1307         /* Now dump DBIF_IDX_DIDNAME */
1308         printf("\ndid/name index:\n");
1309         count = 0;
1310         db = dbd->db_table[DBIF_IDX_DIDNAME].db;
1311         rc = db->cursor(db, NULL, &cur, 0);
1312         if (rc) {
1313             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc));
1314             return -1;
1315         }
1316         
1317         cur->c_get(cur, &key, &data, DB_FIRST);
1318         while (rc == 0) {
1319             /* Parse and print data */
1320
1321             /* cnid */
1322             memcpy(&cnid, data.data, CNID_LEN);
1323             cnid = ntohl(cnid);
1324             if (cnid == 0) {
1325                 /* Rootinfo node */
1326             } else {
1327                 /* did */
1328                 memcpy(&did, key.data, CNID_LEN);
1329                 did = ntohl(did);
1330
1331                 printf("id: %10u <== did: %10u, name: %s\n", cnid, did, (char *)key.data + CNID_LEN);
1332                 count++;
1333             }
1334             rc = cur->c_get(cur, &key, &data, DB_NEXT);
1335         }
1336         if (rc != DB_NOTFOUND) {
1337             LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc));
1338             return -1;
1339         }
1340         
1341         rc = cur->c_close(cur);
1342         if (rc) {
1343             LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(rc));
1344             return -1;
1345         }
1346         printf("%u items\n", count);
1347     }
1348
1349     return 0;
1350 }
1351
1352 /* 
1353    Iterates over dbd, returning cnids.
1354    Uses in-value of cnid to seek to that cnid, then gets next and return that in cnid.
1355    If close=1, close cursor.
1356    Return -1 on error, 0 on EOD (end-of-database), 1 if returning cnid.
1357 */
1358 int dbif_idwalk(DBD *dbd, cnid_t *cnid, int close)
1359 {
1360     int rc;
1361     int flag;
1362     cnid_t id;
1363
1364     static DBT key = { 0 }, data = { 0 };
1365     DB *db = dbd->db_table[DBIF_CNID].db;
1366
1367     if (close) {
1368         if (dbd->db_cur) {
1369             dbd->db_cur->close(dbd->db_cur);
1370             dbd->db_cur = NULL;
1371         }
1372         return 0;
1373     }
1374
1375     /* An dbif_del will have closed our cursor too */
1376     if ( ! dbd->db_cur ) {
1377         if ((rc = db->cursor(db, NULL, &dbd->db_cur, 0)) != 0) {
1378             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc));
1379             return -1;
1380         }
1381         flag = DB_SET_RANGE;    /* This will seek to next cnid after the one just deleted */
1382         id = htonl(*cnid);
1383         key.data = &id;
1384         key.size = sizeof(cnid_t);
1385     } else
1386         flag = DB_NEXT;
1387
1388     if ((rc = dbd->db_cur->get(dbd->db_cur, &key, &data, flag)) == 0) {
1389         memcpy(cnid, key.data, sizeof(cnid_t));
1390         *cnid = ntohl(*cnid);
1391         return 1;
1392     }
1393
1394     if (rc != DB_NOTFOUND) {
1395         LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc));
1396         dbd->db_cur->close(dbd->db_cur);
1397         dbd->db_cur = NULL;
1398         return -1;
1399     }
1400
1401     if (dbd->db_cur) {
1402         dbd->db_cur->close(dbd->db_cur);
1403         dbd->db_cur = NULL;
1404     }    
1405
1406     return 0;
1407 }