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