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 */
35static 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
66EC_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 */
88static 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 */
128static 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 */
167static 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
207static 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/* --------------- */
235static 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
265exit:
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/* --------------- */
277static 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
318exit:
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 */
343int 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/* --------------- */
415DBD *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*/
468int 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/* --------------- */
590int 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/* ------------------------ */
755static 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/* ------------------------ */
773int 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 */
803int 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_debug, 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
883int 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 */
904int 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/* -------------------------- */
927int 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
955int 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 */
995int 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
1035exit:
1036    if (cursorp != NULL)
1037        cursorp->close(cursorp);
1038    return ret;
1039}
1040
1041int 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
1062int 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
1083int 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*/
1111int 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
1131int 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
1142int 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
1161int 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
1189int 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*/
1363int 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}
1413