]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/dbif.c
Increase maxlocks and maxlockobjs and make them configurable
[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             count++;
905             memcpy(cnids, pkey.data, sizeof(cnid_t));
906             memcpy(&cnid, pkey.data, sizeof(cnid_t));
907             cnids += sizeof(cnid_t);
908             LOG(log_error, logtype_cnid, "match: CNID %" PRIu32, ntohl(cnid));
909         } else {
910             LOG(log_error, logtype_cnid, "ignoring: CNID %" PRIu32, ntohl(cnid));
911         }
912         ret = cursorp->pget(cursorp, key, &pkey, &data, DB_NEXT);
913     }
914
915     ret = count;
916
917 exit:
918     if (cursorp != NULL)
919         cursorp->close(cursorp);
920     return ret;
921 }
922
923 int dbif_txn_begin(DBD *dbd)
924 {
925     int ret;
926
927     /* If we already have an active txn, just return */
928     if (dbd->db_txn)
929         return 0;
930
931     /* If our DBD has no env, just return (-> in memory db) */
932     if (dbd->db_env == NULL)
933         return 0;
934
935     ret = dbd->db_env->txn_begin(dbd->db_env, NULL, &dbd->db_txn, 0);
936
937     if (ret) {
938         LOG(log_error, logtype_cnid, "error starting transaction: %s", db_strerror(ret));
939         return -1;
940     } else
941         return 0;
942 }
943
944 int dbif_txn_commit(DBD *dbd)
945 {
946     int ret;
947
948     if (! dbd->db_txn)
949         return 0;
950
951     /* If our DBD has no env, just return (-> in memory db) */
952     if (dbd->db_env == NULL)
953         return 0;
954
955     ret = dbd->db_txn->commit(dbd->db_txn, 0);
956     dbd->db_txn = NULL;
957     
958     if (ret) {
959         LOG(log_error, logtype_cnid, "error committing transaction: %s", db_strerror(ret));
960         return -1;
961     } else
962         return 1;
963 }
964
965 int dbif_txn_abort(DBD *dbd)
966 {
967     int ret;
968
969     if (! dbd->db_txn)
970         return 0;
971
972     /* If our DBD has no env, just return (-> in memory db) */
973     if (dbd->db_env == NULL)
974         return 0;
975
976     ret = dbd->db_txn->abort(dbd->db_txn);
977     dbd->db_txn = NULL;
978     
979     if (ret) {
980         LOG(log_error, logtype_cnid, "error aborting transaction: %s", db_strerror(ret));
981         return -1;
982     } else
983         return 0;
984 }
985
986 /* 
987    ret = 1 -> commit txn
988    ret = 0 -> abort txn -> exit!
989    anything else -> exit!
990 */
991 void dbif_txn_close(DBD *dbd, int ret)
992 {
993     if (ret == 0) {
994         if (dbif_txn_abort(dbd) < 0) {
995             LOG( log_error, logtype_cnid, "Fatal error aborting transaction. Exiting!");
996             exit(EXIT_FAILURE);
997         }
998     } else if (ret == 1) {
999         ret = dbif_txn_commit(dbd);
1000         if (  ret < 0) {
1001             LOG( log_error, logtype_cnid, "Fatal error committing transaction. Exiting!");
1002             exit(EXIT_FAILURE);
1003         }
1004     } else
1005        exit(EXIT_FAILURE);
1006 }
1007
1008 int dbif_txn_checkpoint(DBD *dbd, u_int32_t kbyte, u_int32_t min, u_int32_t flags)
1009 {
1010     int ret;
1011     ret = dbd->db_env->txn_checkpoint(dbd->db_env, kbyte, min, flags);
1012     if (ret) {
1013         LOG(log_error, logtype_cnid, "error checkpointing transaction susystem: %s", db_strerror(ret));
1014         return -1;
1015     } else
1016         return 0;
1017 }
1018
1019 int dbif_count(DBD *dbd, const int dbi, u_int32_t *count)
1020 {
1021     int ret;
1022     DB_BTREE_STAT *sp;
1023     DB *db = dbd->db_table[dbi].db;
1024
1025     ret = db->stat(db, NULL, &sp, 0);
1026
1027     if (ret) {
1028         LOG(log_error, logtype_cnid, "error getting stat infotmation on database: %s", db_strerror(ret));
1029         return -1;
1030     }
1031
1032     *count = sp->bt_ndata;
1033     free(sp);
1034
1035     return 0;
1036 }
1037
1038 int dbif_copy_rootinfokey(DBD *srcdbd, DBD *destdbd)
1039 {
1040     DBT key, data;
1041     int rc;
1042
1043     memset(&key, 0, sizeof(key));
1044     memset(&data, 0, sizeof(data));
1045
1046     key.data = ROOTINFO_KEY;
1047     key.size = ROOTINFO_KEYLEN;
1048
1049     if ((rc = dbif_get(srcdbd, DBIF_CNID, &key, &data, 0)) <= 0) {
1050         LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error getting rootinfo record");
1051         return -1;
1052     }
1053
1054     memset(&key, 0, sizeof(key));
1055     key.data = ROOTINFO_KEY;
1056     key.size = ROOTINFO_KEYLEN;
1057
1058     if ((rc = dbif_put(destdbd, DBIF_CNID, &key, &data, 0))) {
1059         LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error writing rootinfo key");
1060         return -1;
1061     }
1062
1063     return 0;
1064 }
1065
1066 int dbif_dump(DBD *dbd, int dumpindexes)
1067 {
1068     int rc;
1069     uint32_t max = 0, count = 0, cnid, type, did, lastid;
1070     uint64_t dev, ino;
1071     time_t stamp;
1072     DBC *cur;
1073     DBT key = { 0 }, data = { 0 };
1074     DB *db = dbd->db_table[DBIF_CNID].db;
1075     char *typestring[2] = {"f", "d"};
1076     char timebuf[64];
1077
1078     printf("CNID database dump:\n");
1079
1080     rc = db->cursor(db, NULL, &cur, 0);
1081     if (rc) {
1082         LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc));
1083         return -1;
1084     }
1085
1086     cur->c_get(cur, &key, &data, DB_FIRST);
1087     while (rc == 0) {
1088         /* Parse and print data */
1089         memcpy(&cnid, key.data, 4);
1090         cnid = ntohl(cnid);
1091         if (cnid > max)
1092             max = cnid;
1093
1094         /* Rootinfo node ? */
1095         if (cnid == 0) {
1096             memcpy(&stamp, (char *)data.data + 4, sizeof(time_t));
1097             memcpy(&lastid, (char *)data.data + 20, sizeof(cnid_t));
1098             lastid = ntohl(lastid);
1099             strftime(timebuf, sizeof(timebuf), "%b %d %Y %H:%M:%S", localtime(&stamp));
1100             printf("dbd stamp: 0x%08x (%s), next free CNID: %u\n", (unsigned int)stamp, timebuf, lastid + 1);
1101         } else {
1102             /* dev */
1103             memcpy(&dev, (char *)data.data + CNID_DEV_OFS, 8);
1104             dev = ntoh64(dev);
1105             /* ino */
1106             memcpy(&ino, (char *)data.data + CNID_INO_OFS, 8);
1107             ino = ntoh64(ino);
1108             /* type */
1109             memcpy(&type, (char *)data.data + CNID_TYPE_OFS, 4);
1110             type = ntohl(type);
1111             /* did */
1112             memcpy(&did, (char *)data.data + CNID_DID_OFS, 4);
1113             did = ntohl(did);
1114
1115             count++;
1116             printf("id: %10u, did: %10u, type: %s, dev: 0x%llx, ino: 0x%016llx, name: %s\n", 
1117                    cnid, did, typestring[type],
1118                    (long long unsigned int)dev, (long long unsigned int)ino, 
1119                    (char *)data.data + CNID_NAME_OFS);
1120
1121         }
1122
1123         rc = cur->c_get(cur, &key, &data, DB_NEXT);
1124     }
1125
1126     if (rc != DB_NOTFOUND) {
1127         LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc));
1128         return -1;
1129     }
1130
1131     rc = cur->c_close(cur);
1132     if (rc) {
1133         LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(rc));
1134         return -1;
1135     }
1136     printf("%u CNIDs in database. Max CNID: %u.\n", count, max);
1137
1138     /* Dump indexes too ? */
1139     if (dumpindexes) {
1140         /* DBIF_IDX_DEVINO */
1141         printf("\ndev/inode index:\n");
1142         count = 0;
1143         db = dbd->db_table[DBIF_IDX_DEVINO].db;
1144         rc = db->cursor(db, NULL, &cur, 0);
1145         if (rc) {
1146             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc));
1147             return -1;
1148         }
1149         
1150         cur->c_get(cur, &key, &data, DB_FIRST);
1151         while (rc == 0) {
1152             /* Parse and print data */
1153
1154             /* cnid */
1155             memcpy(&cnid, data.data, CNID_LEN);
1156             cnid = ntohl(cnid);
1157             if (cnid == 0) {
1158                 /* Rootinfo node */
1159             } else {
1160                 /* dev */
1161                 memcpy(&dev, key.data, CNID_DEV_LEN);
1162                 dev = ntoh64(dev);
1163                 /* ino */
1164                 memcpy(&ino, (char *)key.data + CNID_DEV_LEN, CNID_INO_LEN);
1165                 ino = ntoh64(ino);
1166                 
1167                 printf("id: %10u <== dev: 0x%llx, ino: 0x%016llx\n", 
1168                        cnid, (unsigned long long int)dev, (unsigned long long int)ino);
1169                 count++;
1170             }
1171             rc = cur->c_get(cur, &key, &data, DB_NEXT);
1172         }
1173         if (rc != DB_NOTFOUND) {
1174             LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc));
1175             return -1;
1176         }
1177         
1178         rc = cur->c_close(cur);
1179         if (rc) {
1180             LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(rc));
1181             return -1;
1182         }
1183         printf("%u items\n", count);
1184
1185         /* Now dump DBIF_IDX_DIDNAME */
1186         printf("\ndid/name index:\n");
1187         count = 0;
1188         db = dbd->db_table[DBIF_IDX_DIDNAME].db;
1189         rc = db->cursor(db, NULL, &cur, 0);
1190         if (rc) {
1191             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc));
1192             return -1;
1193         }
1194         
1195         cur->c_get(cur, &key, &data, DB_FIRST);
1196         while (rc == 0) {
1197             /* Parse and print data */
1198
1199             /* cnid */
1200             memcpy(&cnid, data.data, CNID_LEN);
1201             cnid = ntohl(cnid);
1202             if (cnid == 0) {
1203                 /* Rootinfo node */
1204             } else {
1205                 /* did */
1206                 memcpy(&did, key.data, CNID_LEN);
1207                 did = ntohl(did);
1208
1209                 printf("id: %10u <== did: %10u, name: %s\n", cnid, did, (char *)key.data + CNID_LEN);
1210                 count++;
1211             }
1212             rc = cur->c_get(cur, &key, &data, DB_NEXT);
1213         }
1214         if (rc != DB_NOTFOUND) {
1215             LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc));
1216             return -1;
1217         }
1218         
1219         rc = cur->c_close(cur);
1220         if (rc) {
1221             LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(rc));
1222             return -1;
1223         }
1224         printf("%u items\n", count);
1225     }
1226
1227     return 0;
1228 }
1229
1230 /* 
1231    Iterates over dbd, returning cnids.
1232    Uses in-value of cnid to seek to that cnid, then gets next and return that in cnid.
1233    If close=1, close cursor.
1234    Return -1 on error, 0 on EOD (end-of-database), 1 if returning cnid.
1235 */
1236 int dbif_idwalk(DBD *dbd, cnid_t *cnid, int close)
1237 {
1238     int rc;
1239     int flag;
1240     cnid_t id;
1241
1242     static DBT key = { 0 }, data = { 0 };
1243     DB *db = dbd->db_table[DBIF_CNID].db;
1244
1245     if (close) {
1246         if (dbd->db_cur) {
1247             dbd->db_cur->close(dbd->db_cur);
1248             dbd->db_cur = NULL;
1249         }
1250         return 0;
1251     }
1252
1253     /* An dbif_del will have closed our cursor too */
1254     if ( ! dbd->db_cur ) {
1255         if ((rc = db->cursor(db, NULL, &dbd->db_cur, 0)) != 0) {
1256             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc));
1257             return -1;
1258         }
1259         flag = DB_SET_RANGE;    /* This will seek to next cnid after the one just deleted */
1260         id = htonl(*cnid);
1261         key.data = &id;
1262         key.size = sizeof(cnid_t);
1263     } else
1264         flag = DB_NEXT;
1265
1266     if ((rc = dbd->db_cur->get(dbd->db_cur, &key, &data, flag)) == 0) {
1267         memcpy(cnid, key.data, sizeof(cnid_t));
1268         *cnid = ntohl(*cnid);
1269         return 1;
1270     }
1271
1272     if (rc != DB_NOTFOUND) {
1273         LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc));
1274         dbd->db_cur->close(dbd->db_cur);
1275         dbd->db_cur = NULL;
1276         return -1;
1277     }
1278
1279     if (dbd->db_cur) {
1280         dbd->db_cur->close(dbd->db_cur);
1281         dbd->db_cur = NULL;
1282     }    
1283
1284     return 0;
1285 }