]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/dbif.c
0fdbb9ff56d1128f10bf69e9a4a15422acfa8fc9
[netatalk.git] / etc / cnid_dbd / dbif.c
1 /*
2  * $Id: dbif.c,v 1.17 2009-11-25 14:59: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(void)
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             LOG(log_debug, logtype_cnid, "Finished CNID database upgrade check");
320         }
321         
322         if ((fchdir(cwd)) != 0) {
323             LOG(log_error, logtype_cnid, "error chdiring back: %s", strerror(errno));        
324             return -1;
325         }
326     }
327
328     /* Now open databases ... */
329     for (i = 0; i != DBIF_DB_CNT; i++) {
330         if ((ret = db_create(&dbd->db_table[i].db, dbd->db_env, 0))) {
331             LOG(log_error, logtype_cnid, "error creating handle for database %s: %s",
332                 dbd->db_table[i].name, db_strerror(ret));
333             return -1;
334         }
335
336         if (dbd->db_table[i].flags) {
337             if ((ret = dbd->db_table[i].db->set_flags(dbd->db_table[i].db,
338                                                       dbd->db_table[i].flags))) {
339                 LOG(log_error, logtype_cnid, "error setting flags for database %s: %s",
340                     dbd->db_table[i].name, db_strerror(ret));
341                 return -1;
342             }
343         }
344
345         if (dbd->db_table[i].db->open(dbd->db_table[i].db,
346                                       dbd->db_txn,
347                                       dbd->db_filename,
348                                       dbd->db_table[i].name,
349                                       dbd->db_table[i].type,
350                                       dbd->db_table[i].openflags,
351                                       0664) < 0) {
352             LOG(log_error, logtype_cnid, "Cant open database");
353             return -1;
354         }
355         if (dbd->db_errlog != NULL)
356             dbd->db_table[i].db->set_errfile(dbd->db_table[i].db, dbd->db_errlog);
357
358         if (reindex && i > 0) {
359             LOG(log_info, logtype_cnid, "Truncating CNID index.");
360             if ((ret = dbd->db_table[i].db->truncate(dbd->db_table[i].db, NULL, &count, 0))) {
361                 LOG(log_error, logtype_cnid, "error truncating database %s: %s",
362                     dbd->db_table[i].name, db_strerror(ret));
363                 return -1;
364             }
365         }
366     }
367
368     /* TODO: Implement CNID DB versioning info on new databases. */
369
370     /* Associate the secondary with the primary. */
371     if (reindex)
372         LOG(log_info, logtype_cnid, "Reindexing did/name index...");
373     if ((ret = dbd->db_table[0].db->associate(dbd->db_table[DBIF_CNID].db,
374                                               dbd->db_txn,
375                                               dbd->db_table[DBIF_IDX_DIDNAME].db, 
376                                               didname,
377                                               (reindex) ? DB_CREATE : 0))
378          != 0) {
379         LOG(log_error, logtype_cnid, "Failed to associate didname database: %s",db_strerror(ret));
380         return -1;
381     }
382     if (reindex)
383         LOG(log_info, logtype_cnid, "... done.");
384
385     if (reindex)
386         LOG(log_info, logtype_cnid, "Reindexing dev/ino index...");
387     if ((ret = dbd->db_table[0].db->associate(dbd->db_table[0].db, 
388                                               dbd->db_txn,
389                                               dbd->db_table[DBIF_IDX_DEVINO].db, 
390                                               devino,
391                                               (reindex) ? DB_CREATE : 0))
392         != 0) {
393         LOG(log_error, logtype_cnid, "Failed to associate devino database: %s",db_strerror(ret));
394         return -1;
395     }
396     if (reindex)
397         LOG(log_info, logtype_cnid, "... done.");
398     
399     return 0;
400 }
401
402 /* ------------------------ */
403 static int dbif_closedb(DBD *dbd)
404 {
405     int i;
406     int ret;
407     int err = 0;
408
409     for (i = DBIF_DB_CNT -1; i >= 0; i--) {
410         if (dbd->db_table[i].db != NULL && (ret = dbd->db_table[i].db->close(dbd->db_table[i].db, 0))) {
411             LOG(log_error, logtype_cnid, "error closing database %s: %s", dbd->db_table[i].name, db_strerror(ret));
412             err++;
413         }
414     }
415     if (err)
416         return -1;
417     return 0;
418 }
419
420 /* ------------------------ */
421 int dbif_close(DBD *dbd)
422 {
423     int ret;
424     int err = 0;
425
426     if (dbif_closedb(dbd))
427         err++;
428
429     if (dbd->db_env != NULL && (ret = dbd->db_env->close(dbd->db_env, 0))) {
430         LOG(log_error, logtype_cnid, "error closing DB environment: %s", db_strerror(ret));
431         err++;
432     }
433     if (dbd->db_errlog != NULL && fclose(dbd->db_errlog) == EOF) {
434         LOG(log_error, logtype_cnid, "error closing DB logfile: %s", strerror(errno));
435         err++;
436     }
437
438     free(dbd->db_filename);
439     free(dbd);
440     dbd = NULL;
441
442     if (err)
443         return -1;
444     return 0;
445 }
446
447 /* 
448    In order to support silent database upgrades:
449    destroy env at cnid_dbd shutdown.
450  */
451 int dbif_prep_upgrade(const char *path)
452 {
453     int cwd, ret;
454     DBD *dbd;
455
456     LOG(log_debug, logtype_cnid, "Reopening BerkeleyDB environment");
457     
458     if (NULL == (dbd = dbif_init(path, "cnid2.db")))
459         return -1;
460
461     /* Remember cwd */
462     if ((cwd = open(".", O_RDONLY)) < 0) {
463         LOG(log_error, logtype_cnid, "error opening cwd: %s", strerror(errno));
464         return -1;
465     }
466
467     /* chdir to db_envhome. makes it easier checking for old db files and creating db_errlog file  */
468     if ((chdir(dbd->db_envhome)) != 0) {
469         LOG(log_error, logtype_cnid, "error chdiring to db_env '%s': %s", dbd->db_envhome, strerror(errno));        
470         return -1;
471     }
472
473     if ((dbd->db_errlog = fopen(DB_ERRLOGFILE, "a")) == NULL)
474         LOG(log_warning, logtype_cnid, "error creating/opening DB errlogfile: %s", strerror(errno));
475     
476     if ((fchdir(cwd)) != 0) {
477         LOG(log_error, logtype_cnid, "error chdiring back: %s", strerror(errno));        
478         return -1;
479     }
480
481     /* Get db_env handle */
482     if ((ret = db_env_create(&dbd->db_env, 0))) {
483         LOG(log_error, logtype_cnid, "error creating DB environment: %s", db_strerror(ret));
484         dbd->db_env = NULL;
485         return -1;
486     }
487
488     /* Set logfile */
489     if (dbd->db_errlog != NULL) {
490         dbd->db_env->set_errfile(dbd->db_env, dbd->db_errlog);
491         dbd->db_env->set_msgfile(dbd->db_env, dbd->db_errlog);
492         dbd->db_env->set_verbose(dbd->db_env, DB_VERB_RECOVERY, 1);
493     }
494
495     /* Open environment with recovery */
496     if ((ret = dbd->db_env->open(dbd->db_env, 
497                                  dbd->db_envhome,
498                                  DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN | DB_RECOVER | DB_PRIVATE,
499                                  0))) {
500         LOG(log_error, logtype_cnid, "error opening DB environment: %s",
501             db_strerror(ret));
502         dbd->db_env->close(dbd->db_env, 0);
503         dbd->db_env = NULL;
504         return -1;
505     }
506
507     if (dbd->db_errlog != NULL)
508         fflush(dbd->db_errlog);
509
510     /* Remove logfiles */
511     if ((ret = dbd->db_env->log_archive(dbd->db_env, NULL, DB_ARCH_REMOVE))) {
512          LOG(log_error, logtype_cnid, "error removing transaction logfiles: %s", db_strerror(ret));
513          return -1;
514     }
515
516     if ((ret = dbd->db_env->close(dbd->db_env, 0))) {
517         LOG(log_error, logtype_cnid, "error closing DB environment after recovery: %s", db_strerror(ret));
518         dbd->db_env = NULL;
519         return -1;
520     }
521
522     LOG(log_debug, logtype_cnid, "BerkeleyDB environment recovery done.");
523
524     /* Get a new db_env handle and then remove environment */
525     if ((ret = db_env_create(&dbd->db_env, 0))) {
526         LOG(log_error, logtype_cnid, "error acquiring db_end handle: %s", db_strerror(ret));
527         dbd->db_env = NULL;
528         return -1;
529     }
530     if ((ret = dbd->db_env->remove(dbd->db_env, dbd->db_envhome, 0))) {
531         LOG(log_error, logtype_cnid, "error removing BerkeleyDB environment: %s", db_strerror(ret));
532         return -1;
533     }
534
535     LOG(log_debug, logtype_cnid, "Removed BerkeleyDB environment.");
536
537     return 0;
538 }
539
540 /*
541  *  The following three functions are wrappers for DB->get(), DB->put() and DB->del().
542  *  All three return -1 on error. dbif_get()/dbif_del return 1 if the key was found and 0
543  *  otherwise. dbif_put() returns 0 if key/val was successfully updated and 1 if
544  *  the DB_NOOVERWRITE flag was specified and the key already exists.
545  *
546  *  All return codes other than DB_NOTFOUND and DB_KEYEXIST from the DB->()
547  *  functions are not expected and therefore error conditions.
548  */
549
550 int dbif_get(DBD *dbd, const int dbi, DBT *key, DBT *val, u_int32_t flags)
551 {
552     int ret;
553
554     ret = dbd->db_table[dbi].db->get(dbd->db_table[dbi].db,
555                                      dbd->db_txn,
556                                      key,
557                                      val,
558                                      flags);
559
560     if (ret == DB_NOTFOUND)
561         return 0;
562     if (ret) {
563         LOG(log_error, logtype_cnid, "error retrieving value from %s: %s",
564             dbd->db_table[dbi].name, db_strerror(errno));
565         return -1;
566     } else
567         return 1;
568 }
569
570 /* search by secondary return primary */
571 int dbif_pget(DBD *dbd, const int dbi, DBT *key, DBT *pkey, DBT *val, u_int32_t flags)
572 {
573     int ret;
574
575     ret = dbd->db_table[dbi].db->pget(dbd->db_table[dbi].db,
576                                       dbd->db_txn,
577                                       key,
578                                       pkey,
579                                       val,
580                                       flags);
581
582     if (ret == DB_NOTFOUND || ret == DB_SECONDARY_BAD) {
583         return 0;
584     }
585     if (ret) {
586         LOG(log_error, logtype_cnid, "error retrieving value from %s: %s",
587             dbd->db_table[dbi].name, db_strerror(errno));
588         return -1;
589    } else
590         return 1;
591 }
592
593 /* -------------------------- */
594 int dbif_put(DBD *dbd, const int dbi, DBT *key, DBT *val, u_int32_t flags)
595 {
596     int ret;
597
598     if (dbif_txn_begin(dbd) < 0) {
599         LOG(log_error, logtype_cnid, "error setting key/value in %s: %s",
600             dbd->db_table[dbi].name, db_strerror(errno));
601         return -1;
602     }
603
604     ret = dbd->db_table[dbi].db->put(dbd->db_table[dbi].db,
605                                      dbd->db_txn,
606                                      key,
607                                      val,
608                                      flags);
609     
610     if (ret) {
611         if ((flags & DB_NOOVERWRITE) && ret == DB_KEYEXIST) {
612             return 1;
613         } else {
614             LOG(log_error, logtype_cnid, "error setting key/value in %s: %s",
615                 dbd->db_table[dbi].name, db_strerror(errno));
616             return -1;
617         }
618     } else
619         return 0;
620 }
621
622 int dbif_del(DBD *dbd, const int dbi, DBT *key, u_int32_t flags)
623 {
624     int ret;
625
626     /* For cooperation with the dbd utility and its usage of a cursor */
627     if (dbd->db_cur) {
628         dbd->db_cur->close(dbd->db_cur);
629         dbd->db_cur = NULL;
630     }    
631
632     if (dbif_txn_begin(dbd) < 0) {
633         LOG(log_error, logtype_cnid, "error deleting key/value from %s: %s", 
634             dbd->db_table[dbi].name, db_strerror(errno));
635         return -1;
636     }
637
638     ret = dbd->db_table[dbi].db->del(dbd->db_table[dbi].db,
639                                      dbd->db_txn,
640                                      key,
641                                      flags);
642     
643     if (ret == DB_NOTFOUND || ret == DB_SECONDARY_BAD)
644         return 0;
645     if (ret) {
646         LOG(log_error, logtype_cnid, "error deleting key/value from %s: %s",
647             dbd->db_table[dbi].name, db_strerror(errno));
648         return -1;
649     } else
650         return 1;
651 }
652
653 int dbif_txn_begin(DBD *dbd)
654 {
655     int ret;
656
657     /* If we already have an active txn, just return */
658     if (dbd->db_txn)
659         return 0;
660
661     /* If our DBD has no env, just return (-> in memory db) */
662     if (dbd->db_env == NULL)
663         return 0;
664
665     ret = dbd->db_env->txn_begin(dbd->db_env, NULL, &dbd->db_txn, 0);
666
667     if (ret) {
668         LOG(log_error, logtype_cnid, "error starting transaction: %s", db_strerror(errno));
669         return -1;
670     } else
671         return 0;
672 }
673
674 int dbif_txn_commit(DBD *dbd)
675 {
676     int ret;
677
678     if (! dbd->db_txn)
679         return 0;
680
681     /* If our DBD has no env, just return (-> in memory db) */
682     if (dbd->db_env == NULL)
683         return 0;
684
685     ret = dbd->db_txn->commit(dbd->db_txn, 0);
686     dbd->db_txn = NULL;
687     
688     if (ret) {
689         LOG(log_error, logtype_cnid, "error committing transaction: %s", db_strerror(errno));
690         return -1;
691     } else
692         return 1;
693 }
694
695 int dbif_txn_abort(DBD *dbd)
696 {
697     int ret;
698
699     if (! dbd->db_txn)
700         return 0;
701
702     /* If our DBD has no env, just return (-> in memory db) */
703     if (dbd->db_env == NULL)
704         return 0;
705
706     ret = dbd->db_txn->abort(dbd->db_txn);
707     dbd->db_txn = NULL;
708     
709     if (ret) {
710         LOG(log_error, logtype_cnid, "error aborting transaction: %s", db_strerror(errno));
711         return -1;
712     } else
713         return 0;
714 }
715
716 /* 
717    ret = 1 -> commit txn
718    ret = 0 -> abort txn -> exit!
719    anything else -> exit!
720 */
721 void dbif_txn_close(DBD *dbd, int ret)
722 {
723     if (ret == 0) {
724         if (dbif_txn_abort(dbd) < 0) {
725             LOG( log_error, logtype_cnid, "Fatal error aborting transaction. Exiting!");
726             exit(EXIT_FAILURE);
727         }
728     } else if (ret == 1) {
729         ret = dbif_txn_commit(dbd);
730         if (  ret < 0) {
731             LOG( log_error, logtype_cnid, "Fatal error committing transaction. Exiting!");
732             exit(EXIT_FAILURE);
733         }
734     } else
735        exit(EXIT_FAILURE);
736 }
737
738 int dbif_txn_checkpoint(DBD *dbd, u_int32_t kbyte, u_int32_t min, u_int32_t flags)
739 {
740     int ret;
741     ret = dbd->db_env->txn_checkpoint(dbd->db_env, kbyte, min, flags);
742     if (ret) {
743         LOG(log_error, logtype_cnid, "error checkpointing transaction susystem: %s", db_strerror(errno));
744         return -1;
745     } else
746         return 0;
747 }
748
749 int dbif_count(DBD *dbd, const int dbi, u_int32_t *count)
750 {
751     int ret;
752     DB_BTREE_STAT *sp;
753     DB *db = dbd->db_table[dbi].db;
754
755     ret = db->stat(db, NULL, &sp, 0);
756
757     if (ret) {
758         LOG(log_error, logtype_cnid, "error getting stat infotmation on database: %s", db_strerror(errno));
759         return -1;
760     }
761
762     *count = sp->bt_ndata;
763     free(sp);
764
765     return 0;
766 }
767
768 int dbif_copy_rootinfokey(DBD *srcdbd, DBD *destdbd)
769 {
770     DBT key, data;
771     int rc;
772
773     memset(&key, 0, sizeof(key));
774     memset(&data, 0, sizeof(data));
775
776     key.data = ROOTINFO_KEY;
777     key.size = ROOTINFO_KEYLEN;
778
779     if ((rc = dbif_get(srcdbd, DBIF_CNID, &key, &data, 0)) <= 0) {
780         LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error getting rootinfo record");
781         return -1;
782     }
783
784     memset(&key, 0, sizeof(key));
785     key.data = ROOTINFO_KEY;
786     key.size = ROOTINFO_KEYLEN;
787
788     if ((rc = dbif_put(destdbd, DBIF_CNID, &key, &data, 0))) {
789         LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error writing rootinfo key");
790         return -1;
791     }
792
793     return 0;
794 }
795
796 int dbif_dump(DBD *dbd, int dumpindexes)
797 {
798     int rc;
799     uint32_t max = 0, count = 0, cnid, type, did, lastid;
800     uint64_t dev, ino;
801     time_t stamp;
802     DBC *cur;
803     DBT key = { 0 }, data = { 0 };
804     DB *db = dbd->db_table[DBIF_CNID].db;
805     char *typestring[2] = {"f", "d"};
806     char timebuf[64];
807
808     printf("CNID database dump:\n");
809
810     rc = db->cursor(db, NULL, &cur, 0);
811     if (rc) {
812         LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(errno));
813         return -1;
814     }
815
816     cur->c_get(cur, &key, &data, DB_FIRST);
817     while (rc == 0) {
818         /* Parse and print data */
819         memcpy(&cnid, key.data, 4);
820         cnid = ntohl(cnid);
821         if (cnid > max)
822             max = cnid;
823
824         /* Rootinfo node ? */
825         if (cnid == 0) {
826             memcpy(&stamp, (char *)data.data + 4, sizeof(time_t));
827             memcpy(&lastid, (char *)data.data + 20, sizeof(cnid_t));
828             lastid = ntohl(lastid);
829             strftime(timebuf, sizeof(timebuf), "%b %d %Y %H:%M:%S", localtime(&stamp));
830             printf("dbd stamp: 0x%08x (%s), next free CNID: %u\n", (unsigned int)stamp, timebuf, lastid + 1);
831         } else {
832             /* dev */
833             memcpy(&dev, (char *)data.data + CNID_DEV_OFS, 8);
834             dev = ntoh64(dev);
835             /* ino */
836             memcpy(&ino, (char *)data.data + CNID_INO_OFS, 8);
837             ino = ntoh64(ino);
838             /* type */
839             memcpy(&type, (char *)data.data + CNID_TYPE_OFS, 4);
840             type = ntohl(type);
841             /* did */
842             memcpy(&did, (char *)data.data + CNID_DID_OFS, 4);
843             did = ntohl(did);
844
845             count++;
846             printf("id: %10u, did: %10u, type: %s, dev: 0x%llx, ino: 0x%016llx, name: %s\n", 
847                    cnid, did, typestring[type],
848                    (long long unsigned int)dev, (long long unsigned int)ino, 
849                    (char *)data.data + CNID_NAME_OFS);
850
851         }
852
853         rc = cur->c_get(cur, &key, &data, DB_NEXT);
854     }
855
856     if (rc != DB_NOTFOUND) {
857         LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(errno));
858         return -1;
859     }
860
861     rc = cur->c_close(cur);
862     if (rc) {
863         LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(errno));
864         return -1;
865     }
866     printf("%u CNIDs in database. Max CNID: %u.\n", count, max);
867
868     /* Dump indexes too ? */
869     if (dumpindexes) {
870         /* DBIF_IDX_DEVINO */
871         printf("\ndev/inode index:\n");
872         count = 0;
873         db = dbd->db_table[DBIF_IDX_DEVINO].db;
874         rc = db->cursor(db, NULL, &cur, 0);
875         if (rc) {
876             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(errno));
877             return -1;
878         }
879         
880         cur->c_get(cur, &key, &data, DB_FIRST);
881         while (rc == 0) {
882             /* Parse and print data */
883
884             /* cnid */
885             memcpy(&cnid, data.data, CNID_LEN);
886             cnid = ntohl(cnid);
887             if (cnid == 0) {
888                 /* Rootinfo node */
889             } else {
890                 /* dev */
891                 memcpy(&dev, key.data, CNID_DEV_LEN);
892                 dev = ntoh64(dev);
893                 /* ino */
894                 memcpy(&ino, (char *)key.data + CNID_DEV_LEN, CNID_INO_LEN);
895                 ino = ntoh64(ino);
896                 
897                 printf("id: %10u <== dev: 0x%llx, ino: 0x%016llx\n", 
898                        cnid, (unsigned long long int)dev, (unsigned long long int)ino);
899                 count++;
900             }
901             rc = cur->c_get(cur, &key, &data, DB_NEXT);
902         }
903         if (rc != DB_NOTFOUND) {
904             LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(errno));
905             return -1;
906         }
907         
908         rc = cur->c_close(cur);
909         if (rc) {
910             LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(errno));
911             return -1;
912         }
913         printf("%u items\n", count);
914
915         /* Now dump DBIF_IDX_DIDNAME */
916         printf("\ndid/name index:\n");
917         count = 0;
918         db = dbd->db_table[DBIF_IDX_DIDNAME].db;
919         rc = db->cursor(db, NULL, &cur, 0);
920         if (rc) {
921             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(errno));
922             return -1;
923         }
924         
925         cur->c_get(cur, &key, &data, DB_FIRST);
926         while (rc == 0) {
927             /* Parse and print data */
928
929             /* cnid */
930             memcpy(&cnid, data.data, CNID_LEN);
931             cnid = ntohl(cnid);
932             if (cnid == 0) {
933                 /* Rootinfo node */
934             } else {
935                 /* did */
936                 memcpy(&did, key.data, CNID_LEN);
937                 did = ntohl(did);
938
939                 printf("id: %10u <== did: %10u, name: %s\n", cnid, did, (char *)key.data + CNID_LEN);
940                 count++;
941             }
942             rc = cur->c_get(cur, &key, &data, DB_NEXT);
943         }
944         if (rc != DB_NOTFOUND) {
945             LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(errno));
946             return -1;
947         }
948         
949         rc = cur->c_close(cur);
950         if (rc) {
951             LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(errno));
952             return -1;
953         }
954         printf("%u items\n", count);
955     }
956
957     return 0;
958 }
959
960 /* 
961    Iterates over dbd, returning cnids.
962    Uses in-value of cnid to seek to that cnid, then gets next and return that in cnid.
963    If close=1, close cursor.
964    Return -1 on error, 0 on EOD (end-of-database), 1 if returning cnid.
965 */
966 int dbif_idwalk(DBD *dbd, cnid_t *cnid, int close)
967 {
968     int rc;
969     int flag;
970     cnid_t id;
971
972     static DBT key = { 0 }, data = { 0 };
973     DB *db = dbd->db_table[DBIF_CNID].db;
974
975     if (close && dbd->db_cur) {
976         dbd->db_cur->close(dbd->db_cur);
977         dbd->db_cur = NULL;
978         return 0;
979     }
980
981     /* An dbif_del will have closed our cursor too */
982     if ( ! dbd->db_cur ) {
983         if (db->cursor(db, NULL, &dbd->db_cur, 0) != 0) {
984             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(errno));
985             return -1;
986         }
987         flag = DB_SET_RANGE;    /* This will seek to next cnid after the one just deleted */
988         id = htonl(*cnid);
989         key.data = &id;
990         key.size = sizeof(cnid_t);
991     } else
992         flag = DB_NEXT;
993
994     if ((rc = dbd->db_cur->get(dbd->db_cur, &key, &data, flag)) == 0) {
995         memcpy(cnid, key.data, sizeof(cnid_t));
996         *cnid = ntohl(*cnid);
997         return 1;
998     }
999
1000     if (rc != DB_NOTFOUND) {
1001         LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(errno));
1002         dbd->db_cur->close(dbd->db_cur);
1003         dbd->db_cur = NULL;
1004         return -1;
1005     }
1006
1007     if (dbd->db_cur) {
1008         dbd->db_cur->close(dbd->db_cur);
1009         dbd->db_cur = NULL;
1010     }    
1011
1012     return 0;
1013 }