]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/dbif.c
Update NEWS
[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 <sys/cdefs.h>
18 #include <unistd.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, "Trying to remove BerkeleyDB environment");
809
810     if (get_lock(LOCK_EXCL, path) != LOCK_EXCL) {
811         LOG(log_warning, logtype_cnid, "CNID db \"%s\" in use, can't remove BerkeleyDB environment", path);
812         return 0;
813     }
814     
815     if (NULL == (dbd = dbif_init(path, "cnid2.db")))
816         return -1;
817
818     /* Get db_env handle */
819     if ((ret = db_env_create(&dbd->db_env, 0))) {
820         LOG(log_error, logtype_cnid, "error creating DB environment: %s", db_strerror(ret));
821         dbd->db_env = NULL;
822         return -1;
823     }
824
825     if ((dbif_openlog(dbd)) != 0)
826         return -1;
827
828     /* Open environment with recovery */
829     if ((ret = dbd->db_env->open(dbd->db_env, 
830                                  dbd->db_envhome,
831                                  DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN | DB_RECOVER | DB_PRIVATE,
832                                  0))) {
833         LOG(log_error, logtype_cnid, "error opening DB environment: %s",
834             db_strerror(ret));
835         dbd->db_env->close(dbd->db_env, 0);
836         dbd->db_env = NULL;
837         return -1;
838     }
839
840     if (dbd->db_errlog != NULL)
841         fflush(dbd->db_errlog);
842
843     /* Remove logfiles */
844     if ((ret = dbd->db_env->log_archive(dbd->db_env, NULL, DB_ARCH_REMOVE))) {
845          LOG(log_error, logtype_cnid, "error removing transaction logfiles: %s", db_strerror(ret));
846          return -1;
847     }
848
849     if ((ret = dbd->db_env->close(dbd->db_env, 0))) {
850         LOG(log_error, logtype_cnid, "error closing DB environment after recovery: %s", db_strerror(ret));
851         dbd->db_env = NULL;
852         return -1;
853     }
854
855     LOG(log_debug, logtype_cnid, "BerkeleyDB environment recovery done.");
856
857     /* Get a new db_env handle and then remove environment */
858     if ((ret = db_env_create(&dbd->db_env, 0))) {
859         LOG(log_error, logtype_cnid, "error acquiring db_end handle: %s", db_strerror(ret));
860         dbd->db_env = NULL;
861         return -1;
862     }
863     if ((ret = dbd->db_env->remove(dbd->db_env, dbd->db_envhome, 0))) {
864         LOG(log_error, logtype_cnid, "error removing BerkeleyDB environment: %s", db_strerror(ret));
865         return -1;
866     }
867
868     LOG(log_debug, logtype_cnid, "Removed BerkeleyDB environment.");
869
870     return 0;
871 }
872
873 /*
874  *  The following three functions are wrappers for DB->get(), DB->put() and DB->del().
875  *  All three return -1 on error. dbif_get()/dbif_del return 1 if the key was found and 0
876  *  otherwise. dbif_put() returns 0 if key/val was successfully updated and 1 if
877  *  the DB_NOOVERWRITE flag was specified and the key already exists.
878  *
879  *  All return codes other than DB_NOTFOUND and DB_KEYEXIST from the DB->()
880  *  functions are not expected and therefore error conditions.
881  */
882
883 int dbif_get(DBD *dbd, const int dbi, DBT *key, DBT *val, u_int32_t flags)
884 {
885     int ret;
886
887     ret = dbd->db_table[dbi].db->get(dbd->db_table[dbi].db,
888                                      dbd->db_txn,
889                                      key,
890                                      val,
891                                      flags);
892
893     if (ret == DB_NOTFOUND)
894         return 0;
895     if (ret) {
896         LOG(log_error, logtype_cnid, "error retrieving value from %s: %s",
897             dbd->db_table[dbi].name, db_strerror(ret));
898         return -1;
899     } else
900         return 1;
901 }
902
903 /* search by secondary return primary */
904 int dbif_pget(DBD *dbd, const int dbi, DBT *key, DBT *pkey, DBT *val, u_int32_t flags)
905 {
906     int ret;
907
908     ret = dbd->db_table[dbi].db->pget(dbd->db_table[dbi].db,
909                                       dbd->db_txn,
910                                       key,
911                                       pkey,
912                                       val,
913                                       flags);
914
915     if (ret == DB_NOTFOUND || ret == DB_SECONDARY_BAD) {
916         return 0;
917     }
918     if (ret) {
919         LOG(log_error, logtype_cnid, "error retrieving value from %s: %s",
920             dbd->db_table[dbi].name, db_strerror(ret));
921         return -1;
922    } else
923         return 1;
924 }
925
926 /* -------------------------- */
927 int dbif_put(DBD *dbd, const int dbi, DBT *key, DBT *val, u_int32_t flags)
928 {
929     int ret;
930
931     if (dbif_txn_begin(dbd) < 0) {
932         LOG(log_error, logtype_cnid, "error setting key/value in %s", dbd->db_table[dbi].name);
933         return -1;
934     }
935
936     ret = dbd->db_table[dbi].db->put(dbd->db_table[dbi].db,
937                                      dbd->db_txn,
938                                      key,
939                                      val,
940                                      flags);
941
942     
943     if (ret) {
944         if ((flags & DB_NOOVERWRITE) && ret == DB_KEYEXIST) {
945             return 1;
946         } else {
947             LOG(log_error, logtype_cnid, "error setting key/value in %s: %s",
948                 dbd->db_table[dbi].name, db_strerror(ret));
949             return -1;
950         }
951     } else
952         return 0;
953 }
954
955 int dbif_del(DBD *dbd, const int dbi, DBT *key, u_int32_t flags)
956 {
957     int ret;
958
959     /* For cooperation with the dbd utility and its usage of a cursor */
960     if (dbd->db_cur) {
961         dbd->db_cur->close(dbd->db_cur);
962         dbd->db_cur = NULL;
963     }    
964
965     if (dbif_txn_begin(dbd) < 0) {
966         LOG(log_error, logtype_cnid, "error deleting key/value from %s", dbd->db_table[dbi].name);
967         return -1;
968     }
969
970     ret = dbd->db_table[dbi].db->del(dbd->db_table[dbi].db,
971                                      dbd->db_txn,
972                                      key,
973                                      flags);
974     
975     if (ret == DB_NOTFOUND) {
976         LOG(log_debug, logtype_cnid, "key not found");
977         return 0;
978     }
979     if (ret) {
980         LOG(log_error, logtype_cnid, "error deleting key/value from %s: %s",
981             dbd->db_table[dbi].name, db_strerror(ret));
982         return -1;
983     } else
984         return 1;
985 }
986
987 /*!
988  * Search the database by name
989  *
990  * @param resbuf    (w) buffer for search results CNIDs, maxsize is assumed to be
991  *                      DBD_MAX_SRCH_RSLTS * sizefof(cnid_t)
992  *
993  * @returns -1 on error, 0 when nothing found, else the number of matches
994  */
995 int dbif_search(DBD *dbd, DBT *key, char *resbuf)
996 {
997     int ret = 0;
998     int count = 0;
999     DBC *cursorp = NULL;
1000     DBT pkey, data;
1001     char *cnids = resbuf;
1002     cnid_t cnid;
1003     char *namebkp = key->data;
1004     int namelenbkp = key->size;
1005
1006     memset(&pkey, 0, sizeof(DBT));
1007     memset(&data, 0, sizeof(DBT));
1008
1009     /* Get a cursor */
1010     ret = dbd->db_table[DBIF_IDX_NAME].db->cursor(dbd->db_table[DBIF_IDX_NAME].db,
1011                                                   NULL,
1012                                                   &cursorp,
1013                                                   0);
1014     if (ret != 0) {
1015         LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(ret));
1016         ret = -1;
1017         goto exit;
1018     }
1019
1020     ret = cursorp->pget(cursorp, key, &pkey, &data, DB_SET_RANGE);
1021     while (count < DBD_MAX_SRCH_RSLTS && ret != DB_NOTFOUND) {
1022         if (!((namelenbkp <= key->size) && (strncmp(namebkp, key->data, namelenbkp) == 0)))
1023             break;
1024         count++;
1025         memcpy(cnids, pkey.data, sizeof(cnid_t));
1026         memcpy(&cnid, pkey.data, sizeof(cnid_t));
1027         cnids += sizeof(cnid_t);
1028         LOG(log_debug, logtype_cnid, "match: CNID %" PRIu32, ntohl(cnid));
1029
1030         ret = cursorp->pget(cursorp, key, &pkey, &data, DB_NEXT);
1031     }
1032
1033     ret = count;
1034
1035 exit:
1036     if (cursorp != NULL)
1037         cursorp->close(cursorp);
1038     return ret;
1039 }
1040
1041 int dbif_txn_begin(DBD *dbd)
1042 {
1043     int ret;
1044
1045     /* If we already have an active txn, just return */
1046     if (dbd->db_txn)
1047         return 0;
1048
1049     /* If our DBD has no env, just return (-> in memory db) */
1050     if (dbd->db_env == NULL)
1051         return 0;
1052
1053     ret = dbd->db_env->txn_begin(dbd->db_env, NULL, &dbd->db_txn, 0);
1054
1055     if (ret) {
1056         LOG(log_error, logtype_cnid, "error starting transaction: %s", db_strerror(ret));
1057         return -1;
1058     } else
1059         return 0;
1060 }
1061
1062 int dbif_txn_commit(DBD *dbd)
1063 {
1064     int ret;
1065
1066     if (! dbd->db_txn)
1067         return 0;
1068
1069     /* If our DBD has no env, just return (-> in memory db) */
1070     if (dbd->db_env == NULL)
1071         return 0;
1072
1073     ret = dbd->db_txn->commit(dbd->db_txn, 0);
1074     dbd->db_txn = NULL;
1075     
1076     if (ret) {
1077         LOG(log_error, logtype_cnid, "error committing transaction: %s", db_strerror(ret));
1078         return -1;
1079     } else
1080         return 1;
1081 }
1082
1083 int dbif_txn_abort(DBD *dbd)
1084 {
1085     int ret;
1086
1087     if (! dbd->db_txn)
1088         return 0;
1089
1090     /* If our DBD has no env, just return (-> in memory db) */
1091     if (dbd->db_env == NULL)
1092         return 0;
1093
1094     ret = dbd->db_txn->abort(dbd->db_txn);
1095     dbd->db_txn = NULL;
1096     
1097     if (ret) {
1098         LOG(log_error, logtype_cnid, "error aborting transaction: %s", db_strerror(ret));
1099         return -1;
1100     } else
1101         return 0;
1102 }
1103
1104 /* 
1105    ret = 1 -> commit txn if db_param.txn_frequency
1106    ret = 0 -> abort txn db_param.txn_frequency -> exit!
1107    anything else -> exit!
1108
1109    @returns 0 on success (abort or commit), -1 on error
1110 */
1111 int dbif_txn_close(DBD *dbd, int ret)
1112 {
1113     if (ret == 0) {
1114         if (dbif_txn_abort(dbd) < 0) {
1115             LOG( log_error, logtype_cnid, "Fatal error aborting transaction. Exiting!");
1116             return -1;
1117         }
1118     } else if (ret == 1) {
1119         ret = dbif_txn_commit(dbd);
1120         if (  ret < 0) {
1121             LOG( log_error, logtype_cnid, "Fatal error committing transaction. Exiting!");
1122             return -1;
1123         }
1124     } else {
1125         return -1;
1126     }
1127
1128     return 0;
1129 }
1130
1131 int dbif_txn_checkpoint(DBD *dbd, u_int32_t kbyte, u_int32_t min, u_int32_t flags)
1132 {
1133     int ret;
1134     ret = dbd->db_env->txn_checkpoint(dbd->db_env, kbyte, min, flags);
1135     if (ret) {
1136         LOG(log_error, logtype_cnid, "error checkpointing transaction susystem: %s", db_strerror(ret));
1137         return -1;
1138     } else
1139         return 0;
1140 }
1141
1142 int dbif_count(DBD *dbd, const int dbi, u_int32_t *count)
1143 {
1144     int ret;
1145     DB_BTREE_STAT *sp;
1146     DB *db = dbd->db_table[dbi].db;
1147
1148     ret = db->stat(db, NULL, &sp, 0);
1149
1150     if (ret) {
1151         LOG(log_error, logtype_cnid, "error getting stat infotmation on database: %s", db_strerror(ret));
1152         return -1;
1153     }
1154
1155     *count = sp->bt_ndata;
1156     free(sp);
1157
1158     return 0;
1159 }
1160
1161 int dbif_copy_rootinfokey(DBD *srcdbd, DBD *destdbd)
1162 {
1163     DBT key, data;
1164     int rc;
1165
1166     memset(&key, 0, sizeof(key));
1167     memset(&data, 0, sizeof(data));
1168
1169     key.data = ROOTINFO_KEY;
1170     key.size = ROOTINFO_KEYLEN;
1171
1172     if ((rc = dbif_get(srcdbd, DBIF_CNID, &key, &data, 0)) <= 0) {
1173         LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error getting rootinfo record");
1174         return -1;
1175     }
1176
1177     memset(&key, 0, sizeof(key));
1178     key.data = ROOTINFO_KEY;
1179     key.size = ROOTINFO_KEYLEN;
1180
1181     if ((rc = dbif_put(destdbd, DBIF_CNID, &key, &data, 0))) {
1182         LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error writing rootinfo key");
1183         return -1;
1184     }
1185
1186     return 0;
1187 }
1188
1189 int dbif_dump(DBD *dbd, int dumpindexes)
1190 {
1191     int rc;
1192     uint32_t max = 0, count = 0, cnid, type, did, lastid, version;
1193     uint64_t dev, ino;
1194     time_t stamp;
1195     DBC *cur;
1196     DBT key = { 0 }, data = { 0 };
1197     DB *db = dbd->db_table[DBIF_CNID].db;
1198     char *typestring[2] = {"f", "d"};
1199     char timebuf[64];
1200
1201     printf("CNID database dump:\n");
1202
1203     rc = db->cursor(db, NULL, &cur, 0);
1204     if (rc) {
1205         LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc));
1206         return -1;
1207     }
1208
1209     cur->c_get(cur, &key, &data, DB_FIRST);
1210     while (rc == 0) {
1211         /* Parse and print data */
1212         memcpy(&cnid, key.data, 4);
1213         cnid = ntohl(cnid);
1214         if (cnid > max)
1215             max = cnid;
1216
1217         /* Rootinfo node ? */
1218         if (cnid == 0) {
1219             memcpy(&stamp, (char *)data.data + CNID_DEV_OFS, sizeof(time_t));
1220             memcpy(&lastid, (char *)data.data + CNID_TYPE_OFS, CNID_TYPE_LEN);
1221             lastid = ntohl(lastid);
1222             memcpy(&version, (char *)data.data + CNID_DID_OFS, CNID_DID_LEN);
1223             version = ntohl(version);
1224
1225             strftime(timebuf, sizeof(timebuf), "%b %d %Y %H:%M:%S", localtime(&stamp));
1226             printf("CNID db version: %u, dbd stamp: 0x%08x (%s), next free CNID: %u\n",
1227                    version, (unsigned int)stamp, timebuf, lastid + 1);
1228         } else {
1229             /* dev */
1230             memcpy(&dev, (char *)data.data + CNID_DEV_OFS, 8);
1231             dev = ntoh64(dev);
1232             /* ino */
1233             memcpy(&ino, (char *)data.data + CNID_INO_OFS, 8);
1234             ino = ntoh64(ino);
1235             /* type */
1236             memcpy(&type, (char *)data.data + CNID_TYPE_OFS, 4);
1237             type = ntohl(type);
1238             /* did */
1239             memcpy(&did, (char *)data.data + CNID_DID_OFS, 4);
1240             did = ntohl(did);
1241
1242             count++;
1243             printf("id: %10u, did: %10u, type: %s, dev: 0x%llx, ino: 0x%016llx, name: %s\n", 
1244                    cnid, did, typestring[type],
1245                    (long long unsigned int)dev, (long long unsigned int)ino, 
1246                    (char *)data.data + CNID_NAME_OFS);
1247
1248         }
1249
1250         rc = cur->c_get(cur, &key, &data, DB_NEXT);
1251     }
1252
1253     if (rc != DB_NOTFOUND) {
1254         LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc));
1255         return -1;
1256     }
1257
1258     rc = cur->c_close(cur);
1259     if (rc) {
1260         LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(rc));
1261         return -1;
1262     }
1263     printf("%u CNIDs in database. Max CNID: %u.\n", count, max);
1264
1265     /* Dump indexes too ? */
1266     if (dumpindexes) {
1267         /* DBIF_IDX_DEVINO */
1268         printf("\ndev/inode index:\n");
1269         count = 0;
1270         db = dbd->db_table[DBIF_IDX_DEVINO].db;
1271         rc = db->cursor(db, NULL, &cur, 0);
1272         if (rc) {
1273             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc));
1274             return -1;
1275         }
1276         
1277         cur->c_get(cur, &key, &data, DB_FIRST);
1278         while (rc == 0) {
1279             /* Parse and print data */
1280
1281             /* cnid */
1282             memcpy(&cnid, data.data, CNID_LEN);
1283             cnid = ntohl(cnid);
1284             if (cnid == 0) {
1285                 /* Rootinfo node */
1286             } else {
1287                 /* dev */
1288                 memcpy(&dev, key.data, CNID_DEV_LEN);
1289                 dev = ntoh64(dev);
1290                 /* ino */
1291                 memcpy(&ino, (char *)key.data + CNID_DEV_LEN, CNID_INO_LEN);
1292                 ino = ntoh64(ino);
1293                 
1294                 printf("id: %10u <== dev: 0x%llx, ino: 0x%016llx\n", 
1295                        cnid, (unsigned long long int)dev, (unsigned long long int)ino);
1296                 count++;
1297             }
1298             rc = cur->c_get(cur, &key, &data, DB_NEXT);
1299         }
1300         if (rc != DB_NOTFOUND) {
1301             LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc));
1302             return -1;
1303         }
1304         
1305         rc = cur->c_close(cur);
1306         if (rc) {
1307             LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(rc));
1308             return -1;
1309         }
1310         printf("%u items\n", count);
1311
1312         /* Now dump DBIF_IDX_DIDNAME */
1313         printf("\ndid/name index:\n");
1314         count = 0;
1315         db = dbd->db_table[DBIF_IDX_DIDNAME].db;
1316         rc = db->cursor(db, NULL, &cur, 0);
1317         if (rc) {
1318             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc));
1319             return -1;
1320         }
1321         
1322         cur->c_get(cur, &key, &data, DB_FIRST);
1323         while (rc == 0) {
1324             /* Parse and print data */
1325
1326             /* cnid */
1327             memcpy(&cnid, data.data, CNID_LEN);
1328             cnid = ntohl(cnid);
1329             if (cnid == 0) {
1330                 /* Rootinfo node */
1331             } else {
1332                 /* did */
1333                 memcpy(&did, key.data, CNID_LEN);
1334                 did = ntohl(did);
1335
1336                 printf("id: %10u <== did: %10u, name: %s\n", cnid, did, (char *)key.data + CNID_LEN);
1337                 count++;
1338             }
1339             rc = cur->c_get(cur, &key, &data, DB_NEXT);
1340         }
1341         if (rc != DB_NOTFOUND) {
1342             LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc));
1343             return -1;
1344         }
1345         
1346         rc = cur->c_close(cur);
1347         if (rc) {
1348             LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(rc));
1349             return -1;
1350         }
1351         printf("%u items\n", count);
1352     }
1353
1354     return 0;
1355 }
1356
1357 /* 
1358    Iterates over dbd, returning cnids.
1359    Uses in-value of cnid to seek to that cnid, then gets next and return that in cnid.
1360    If close=1, close cursor.
1361    Return -1 on error, 0 on EOD (end-of-database), 1 if returning cnid.
1362 */
1363 int dbif_idwalk(DBD *dbd, cnid_t *cnid, int close)
1364 {
1365     int rc;
1366     int flag;
1367     cnid_t id;
1368
1369     static DBT key = { 0 }, data = { 0 };
1370     DB *db = dbd->db_table[DBIF_CNID].db;
1371
1372     if (close) {
1373         if (dbd->db_cur) {
1374             dbd->db_cur->close(dbd->db_cur);
1375             dbd->db_cur = NULL;
1376         }
1377         return 0;
1378     }
1379
1380     /* An dbif_del will have closed our cursor too */
1381     if ( ! dbd->db_cur ) {
1382         if ((rc = db->cursor(db, NULL, &dbd->db_cur, 0)) != 0) {
1383             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc));
1384             return -1;
1385         }
1386         flag = DB_SET_RANGE;    /* This will seek to next cnid after the one just deleted */
1387         id = htonl(*cnid);
1388         key.data = &id;
1389         key.size = sizeof(cnid_t);
1390     } else
1391         flag = DB_NEXT;
1392
1393     if ((rc = dbd->db_cur->get(dbd->db_cur, &key, &data, flag)) == 0) {
1394         memcpy(cnid, key.data, sizeof(cnid_t));
1395         *cnid = ntohl(*cnid);
1396         return 1;
1397     }
1398
1399     if (rc != DB_NOTFOUND) {
1400         LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc));
1401         dbd->db_cur->close(dbd->db_cur);
1402         dbd->db_cur = NULL;
1403         return -1;
1404     }
1405
1406     if (dbd->db_cur) {
1407         dbd->db_cur->close(dbd->db_cur);
1408         dbd->db_cur = NULL;
1409     }    
1410
1411     return 0;
1412 }