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