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