]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/dbif.c
database versioning
[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->open(dbd->db_env, dbd->db_envhome, dbenv_oflags, 0))) {
360         LOG(log_error, logtype_cnid, "error opening DB environment after recovery: %s",
361             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_flags(dbd->db_env, DB_AUTO_COMMIT, 1))) {
368         LOG(log_error, logtype_cnid, "error setting DB_AUTO_COMMIT flag: %s",
369             db_strerror(ret));
370         dbd->db_env->close(dbd->db_env, 0);
371         dbd->db_env = NULL;
372         return -1;
373     }
374
375     if (dbp->logfile_autoremove) {
376         if ((dbif_logautorem(dbd)) != 0)
377             return -1;
378
379 #if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 7)
380         if ((ret = dbd->db_env->log_set_config(dbd->db_env, DB_LOG_AUTO_REMOVE, 1))) {
381             LOG(log_error, logtype_cnid, "error setting DB_LOG_AUTO_REMOVE flag: %s",
382             db_strerror(ret));
383             dbd->db_env->close(dbd->db_env, 0);
384             dbd->db_env = NULL;
385             return -1;
386         }
387 #else
388         if ((ret = dbd->db_env->set_flags(dbd->db_env, DB_LOG_AUTOREMOVE, 1))) {
389             LOG(log_error, logtype_cnid, "error setting DB_LOG_AUTOREMOVE flag: %s",
390                 db_strerror(ret));
391             dbd->db_env->close(dbd->db_env, 0);
392             dbd->db_env = NULL;
393             return -1;
394         }
395 #endif
396     }
397
398     return 0;
399 }
400
401 /* --------------- */
402 int dbif_open(DBD *dbd, struct db_param *dbp, int reindex)
403 {
404     int ret, i, cwd;
405     u_int32_t count;
406     struct stat st;
407     DB *upgrade_db;
408
409     /* Try to upgrade if it's a normal on-disk database */
410     if (dbd->db_envhome) {
411         /* Remember cwd */
412         if ((cwd = open(".", O_RDONLY)) < 0) {
413             LOG(log_error, logtype_cnid, "error opening cwd: %s", strerror(errno));
414             return -1;
415         }
416         
417         /* chdir to db_envhome. makes it easier checking for old db files and creating db_errlog file  */
418         if ((chdir(dbd->db_envhome)) != 0) {
419             LOG(log_error, logtype_cnid, "error chdiring to db_env '%s': %s", dbd->db_envhome, strerror(errno));        
420             return -1;
421         }
422         
423         if ((stat(dbd->db_filename, &st)) == 0) {
424             LOG(log_debug, logtype_cnid, "See if we can upgrade the CNID database...");
425             if ((ret = db_create(&upgrade_db, dbd->db_env, 0))) {
426                 LOG(log_error, logtype_cnid, "error creating handle for database: %s", db_strerror(ret));
427                 return -1;
428             }
429             if ((ret = upgrade_db->upgrade(upgrade_db, dbd->db_filename, 0))) {
430                 LOG(log_error, logtype_cnid, "error upgarding database: %s", db_strerror(ret));
431                 return -1;
432             }
433             if ((ret = upgrade_db->close(upgrade_db, 0))) {
434                 LOG(log_error, logtype_cnid, "error closing database: %s", db_strerror(ret));
435                 return -1;
436             }
437             if ((ret = dbd->db_env->txn_checkpoint(dbd->db_env, 0, 0, DB_FORCE))) {
438                 LOG(log_error, logtype_cnid, "error forcing checkpoint: %s", db_strerror(ret));
439                 return -1;
440             }
441             LOG(log_debug, logtype_cnid, "Finished BerkeleyBD upgrade check");
442         }
443         
444         if ((fchdir(cwd)) != 0) {
445             LOG(log_error, logtype_cnid, "error chdiring back: %s", strerror(errno));        
446             return -1;
447         }
448     }
449
450     /* Now open databases ... */
451     for (i = 0; i != DBIF_DB_CNT; i++) {
452         if ((ret = db_create(&dbd->db_table[i].db, dbd->db_env, 0))) {
453             LOG(log_error, logtype_cnid, "error creating handle for database %s: %s",
454                 dbd->db_table[i].name, db_strerror(ret));
455             return -1;
456         }
457
458         if (dbd->db_table[i].flags) {
459             if ((ret = dbd->db_table[i].db->set_flags(dbd->db_table[i].db,
460                                                       dbd->db_table[i].flags))) {
461                 LOG(log_error, logtype_cnid, "error setting flags for database %s: %s",
462                     dbd->db_table[i].name, db_strerror(ret));
463                 return -1;
464             }
465         }
466
467         if ( ! dbd->db_env) {   /* In memory db */
468             if ((ret = dbd->db_table[i].db->set_cachesize(dbd->db_table[i].db,
469                                                           0,
470                                                           dbp->cachesize,
471                                                           4)) /* split in 4 memory chunks */
472                 < 0)  {
473                 LOG(log_error, logtype_cnid, "error setting cachesize %u KB for database %s: %s",
474                     dbp->cachesize / 1024, dbd->db_table[i].name, db_strerror(ret));
475                 return -1;
476             }
477         }
478
479         if (dbd->db_table[i].db->open(dbd->db_table[i].db,
480                                       dbd->db_txn,
481                                       dbd->db_filename,
482                                       dbd->db_table[i].name,
483                                       dbd->db_table[i].type,
484                                       dbd->db_table[i].openflags,
485                                       0664) < 0) {
486             LOG(log_error, logtype_cnid, "Cant open database");
487             return -1;
488         }
489
490         if (reindex && i > 0) {
491             LOG(log_info, logtype_cnid, "Truncating CNID index.");
492             if ((ret = dbd->db_table[i].db->truncate(dbd->db_table[i].db, NULL, &count, 0))) {
493                 LOG(log_error, logtype_cnid, "error truncating database %s: %s",
494                     dbd->db_table[i].name, db_strerror(ret));
495                 return -1;
496             }
497         }
498     }
499
500     /* TODO: Implement CNID DB versioning info on new databases. */
501
502     /* Associate the secondary with the primary. */
503     if (reindex)
504         LOG(log_info, logtype_cnid, "Reindexing did/name index...");
505     if ((ret = dbd->db_table[0].db->associate(dbd->db_table[DBIF_CNID].db,
506                                               dbd->db_txn,
507                                               dbd->db_table[DBIF_IDX_DIDNAME].db, 
508                                               didname,
509                                               (reindex) ? DB_CREATE : 0))
510          != 0) {
511         LOG(log_error, logtype_cnid, "Failed to associate didname database: %s",db_strerror(ret));
512         return -1;
513     }
514     if (reindex)
515         LOG(log_info, logtype_cnid, "... done.");
516
517     if (reindex)
518         LOG(log_info, logtype_cnid, "Reindexing dev/ino index...");
519     if ((ret = dbd->db_table[0].db->associate(dbd->db_table[0].db, 
520                                               dbd->db_txn,
521                                               dbd->db_table[DBIF_IDX_DEVINO].db, 
522                                               devino,
523                                               (reindex) ? DB_CREATE : 0))
524         != 0) {
525         LOG(log_error, logtype_cnid, "Failed to associate devino database: %s",db_strerror(ret));
526         return -1;
527     }
528     if (reindex)
529         LOG(log_info, logtype_cnid, "... done.");
530
531     if (reindex)
532         LOG(log_info, logtype_cnid, "Reindexing name index...");
533     if ((ret = dbd->db_table[0].db->associate(dbd->db_table[0].db, 
534                                               dbd->db_txn,
535                                               dbd->db_table[DBIF_IDX_NAME].db, 
536                                               idxname,
537                                               (reindex
538                                                || 
539                                                ((CNID_VERSION == CNID_VERSION_1) && (dbif_getversion(dbd) == CNID_VERSION_0)))
540                                               ? DB_CREATE : 0)) != 0) {
541         LOG(log_error, logtype_cnid, "Failed to associate name index: %s", db_strerror(ret));
542         return -1;
543     }
544     if (reindex)
545         LOG(log_info, logtype_cnid, "... done.");
546
547     if ((ret = dbif_upgrade(dbd)) != 0) {
548         LOG(log_error, logtype_cnid, "Error upgrading CNID database to version %d", CNID_VERSION);
549         return -1;
550     }
551     
552     return 0;
553 }
554
555 /* ------------------------ */
556 static int dbif_closedb(DBD *dbd)
557 {
558     int i;
559     int ret;
560     int err = 0;
561
562     for (i = DBIF_DB_CNT -1; i >= 0; i--) {
563         if (dbd->db_table[i].db != NULL && (ret = dbd->db_table[i].db->close(dbd->db_table[i].db, 0))) {
564             LOG(log_error, logtype_cnid, "error closing database %s: %s", dbd->db_table[i].name, db_strerror(ret));
565             err++;
566         }
567     }
568     if (err)
569         return -1;
570     return 0;
571 }
572
573 /* ------------------------ */
574 int dbif_close(DBD *dbd)
575 {
576     int ret;
577     int err = 0;
578
579     if (dbif_closedb(dbd))
580         err++;
581
582     if (dbd->db_env != NULL && (ret = dbd->db_env->close(dbd->db_env, 0))) {
583         LOG(log_error, logtype_cnid, "error closing DB environment: %s", db_strerror(ret));
584         err++;
585     }
586     if (dbd->db_errlog != NULL && fclose(dbd->db_errlog) == EOF) {
587         LOG(log_error, logtype_cnid, "error closing DB logfile: %s", strerror(errno));
588         err++;
589     }
590
591     free(dbd->db_filename);
592     free(dbd);
593     dbd = NULL;
594
595     if (err)
596         return -1;
597     return 0;
598 }
599
600 /* 
601    In order to support silent database upgrades:
602    destroy env at cnid_dbd shutdown.
603  */
604 int dbif_env_remove(const char *path)
605 {
606     int ret;
607     DBD *dbd;
608
609     LOG(log_debug, logtype_cnid, "Reopening BerkeleyDB environment");
610     
611     if (NULL == (dbd = dbif_init(path, "cnid2.db")))
612         return -1;
613
614     /* Get db_env handle */
615     if ((ret = db_env_create(&dbd->db_env, 0))) {
616         LOG(log_error, logtype_cnid, "error creating DB environment: %s", db_strerror(ret));
617         dbd->db_env = NULL;
618         return -1;
619     }
620
621     if ((dbif_openlog(dbd)) != 0)
622         return -1;
623
624     /* Open environment with recovery */
625     if ((ret = dbd->db_env->open(dbd->db_env, 
626                                  dbd->db_envhome,
627                                  DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN | DB_RECOVER | DB_PRIVATE,
628                                  0))) {
629         LOG(log_error, logtype_cnid, "error opening DB environment: %s",
630             db_strerror(ret));
631         dbd->db_env->close(dbd->db_env, 0);
632         dbd->db_env = NULL;
633         return -1;
634     }
635
636     if (dbd->db_errlog != NULL)
637         fflush(dbd->db_errlog);
638
639     /* Remove logfiles */
640     if ((ret = dbd->db_env->log_archive(dbd->db_env, NULL, DB_ARCH_REMOVE))) {
641          LOG(log_error, logtype_cnid, "error removing transaction logfiles: %s", db_strerror(ret));
642          return -1;
643     }
644
645     if ((ret = dbd->db_env->close(dbd->db_env, 0))) {
646         LOG(log_error, logtype_cnid, "error closing DB environment after recovery: %s", db_strerror(ret));
647         dbd->db_env = NULL;
648         return -1;
649     }
650
651     LOG(log_debug, logtype_cnid, "BerkeleyDB environment recovery done.");
652
653     /* Get a new db_env handle and then remove environment */
654     if ((ret = db_env_create(&dbd->db_env, 0))) {
655         LOG(log_error, logtype_cnid, "error acquiring db_end handle: %s", db_strerror(ret));
656         dbd->db_env = NULL;
657         return -1;
658     }
659     if ((ret = dbd->db_env->remove(dbd->db_env, dbd->db_envhome, 0))) {
660         LOG(log_error, logtype_cnid, "error removing BerkeleyDB environment: %s", db_strerror(ret));
661         return -1;
662     }
663
664     LOG(log_debug, logtype_cnid, "Removed BerkeleyDB environment.");
665
666     return 0;
667 }
668
669 /*
670  *  The following three functions are wrappers for DB->get(), DB->put() and DB->del().
671  *  All three return -1 on error. dbif_get()/dbif_del return 1 if the key was found and 0
672  *  otherwise. dbif_put() returns 0 if key/val was successfully updated and 1 if
673  *  the DB_NOOVERWRITE flag was specified and the key already exists.
674  *
675  *  All return codes other than DB_NOTFOUND and DB_KEYEXIST from the DB->()
676  *  functions are not expected and therefore error conditions.
677  */
678
679 int dbif_get(DBD *dbd, const int dbi, DBT *key, DBT *val, u_int32_t flags)
680 {
681     int ret;
682
683     ret = dbd->db_table[dbi].db->get(dbd->db_table[dbi].db,
684                                      dbd->db_txn,
685                                      key,
686                                      val,
687                                      flags);
688
689     if (ret == DB_NOTFOUND)
690         return 0;
691     if (ret) {
692         LOG(log_error, logtype_cnid, "error retrieving value from %s: %s",
693             dbd->db_table[dbi].name, db_strerror(ret));
694         return -1;
695     } else
696         return 1;
697 }
698
699 /* search by secondary return primary */
700 int dbif_pget(DBD *dbd, const int dbi, DBT *key, DBT *pkey, DBT *val, u_int32_t flags)
701 {
702     int ret;
703
704     ret = dbd->db_table[dbi].db->pget(dbd->db_table[dbi].db,
705                                       dbd->db_txn,
706                                       key,
707                                       pkey,
708                                       val,
709                                       flags);
710
711     if (ret == DB_NOTFOUND || ret == DB_SECONDARY_BAD) {
712         return 0;
713     }
714     if (ret) {
715         LOG(log_error, logtype_cnid, "error retrieving value from %s: %s",
716             dbd->db_table[dbi].name, db_strerror(ret));
717         return -1;
718    } else
719         return 1;
720 }
721
722 /* -------------------------- */
723 int dbif_put(DBD *dbd, const int dbi, DBT *key, DBT *val, u_int32_t flags)
724 {
725     int ret;
726
727     if (dbif_txn_begin(dbd) < 0) {
728         LOG(log_error, logtype_cnid, "error setting key/value in %s", dbd->db_table[dbi].name);
729         return -1;
730     }
731
732     ret = dbd->db_table[dbi].db->put(dbd->db_table[dbi].db,
733                                      dbd->db_txn,
734                                      key,
735                                      val,
736                                      flags);
737
738     
739     if (ret) {
740         if ((flags & DB_NOOVERWRITE) && ret == DB_KEYEXIST) {
741             return 1;
742         } else {
743             LOG(log_error, logtype_cnid, "error setting key/value in %s: %s",
744                 dbd->db_table[dbi].name, db_strerror(ret));
745             return -1;
746         }
747     } else
748         return 0;
749 }
750
751 int dbif_del(DBD *dbd, const int dbi, DBT *key, u_int32_t flags)
752 {
753     int ret;
754
755     /* For cooperation with the dbd utility and its usage of a cursor */
756     if (dbd->db_cur) {
757         dbd->db_cur->close(dbd->db_cur);
758         dbd->db_cur = NULL;
759     }    
760
761     if (dbif_txn_begin(dbd) < 0) {
762         LOG(log_error, logtype_cnid, "error deleting key/value from %s", dbd->db_table[dbi].name);
763         return -1;
764     }
765
766     ret = dbd->db_table[dbi].db->del(dbd->db_table[dbi].db,
767                                      dbd->db_txn,
768                                      key,
769                                      flags);
770     
771     if (ret == DB_NOTFOUND || ret == DB_SECONDARY_BAD)
772         return 0;
773     if (ret) {
774         LOG(log_error, logtype_cnid, "error deleting key/value from %s: %s",
775             dbd->db_table[dbi].name, db_strerror(ret));
776         return -1;
777     } else
778         return 1;
779 }
780
781 /*!
782  * Return CNID database version number
783  * @returns -1 on error, version number otherwise
784  */
785 int dbif_getversion(DBD *dbd)
786 {
787     DBT key, data;
788     uint32_t version;
789     int ret;
790
791     memset(&key, 0, sizeof(key));
792     memset(&data, 0, sizeof(data));
793     key.data = ROOTINFO_KEY;
794     key.size = ROOTINFO_KEYLEN;
795
796     switch (dbif_get(dbd, DBIF_CNID, &key, &data, 0)) {
797     case 1:
798         memcpy(&version, (char *)data.data + CNID_DID_OFS, sizeof(version));
799         version = ntohl(version);
800         LOG(log_debug, logtype_cnid, "CNID database version %u", version);
801         ret = version;
802         break;
803     default:
804         ret = -1;
805         break;
806     }
807     return ret;
808 }
809
810 /*!
811  * Return CNID database version number
812  * @returns -1 on error, version number otherwise
813  */
814 int dbif_setversion(DBD *dbd, int version)
815 {
816     DBT key, data;
817     uint32_t v;
818
819     v = version;
820     v = htonl(v);
821
822     memset(&key, 0, sizeof(key));
823     memset(&data, 0, sizeof(data));
824     key.data = ROOTINFO_KEY;
825     key.size = ROOTINFO_KEYLEN;
826
827     switch (dbif_get(dbd, DBIF_CNID, &key, &data, 0)) {
828     case 1:
829         break;
830     default:
831         return -1;
832     }
833
834     memcpy((char *)data.data + CNID_DID_OFS, &v, sizeof(v));
835     data.size = ROOTINFO_DATALEN;
836     if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) < 0)
837         return -1;
838
839     return 0;
840 }
841
842 /*!
843  * Search the database by name
844  *
845  * @param resbuf    (w) buffer for search results CNIDs, maxsize is assumed to be
846  *                      DBD_MAX_SRCH_RSLTS * sizefof(cnid_t)
847  *
848  * @returns -1 on error, 0 when nothing found, else the number of matches
849  */
850 int dbif_search(DBD *dbd, DBT *key, char *resbuf)
851 {
852     int ret = 0;
853     int count = 0;
854     DBC *cursorp = NULL;
855     DBT pkey, data;
856     char *cnids = resbuf;
857     cnid_t cnid;
858     char *namebkp = key->data;
859     int namelenbkp = key->size;
860
861     memset(&pkey, 0, sizeof(DBT));
862     memset(&data, 0, sizeof(DBT));
863
864     /* Get a cursor */
865     ret = dbd->db_table[DBIF_IDX_NAME].db->cursor(dbd->db_table[DBIF_IDX_NAME].db,
866                                                   NULL,
867                                                   &cursorp,
868                                                   0);
869     if (ret != 0) {
870         LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(ret));
871         ret = -1;
872         goto exit;
873     }
874
875     ret = cursorp->pget(cursorp, key, &pkey, &data, DB_SET_RANGE);
876     while (count < DBD_MAX_SRCH_RSLTS && ret != DB_NOTFOUND) {
877         if (namelenbkp <= key->size && strncmp(namebkp, key->data, namelenbkp) == 0) {
878             count++;
879             memcpy(cnids, pkey.data, sizeof(cnid_t));
880             memcpy(&cnid, pkey.data, sizeof(cnid_t));
881             cnids += sizeof(cnid_t);
882             LOG(log_error, logtype_cnid, "match: CNID %" PRIu32, ntohl(cnid));
883         } else {
884             LOG(log_error, logtype_cnid, "ignoring: CNID %" PRIu32, ntohl(cnid));
885         }
886         ret = cursorp->pget(cursorp, key, &pkey, &data, DB_NEXT);
887     }
888
889     ret = count;
890
891 exit:
892     if (cursorp != NULL)
893         cursorp->close(cursorp);
894     return ret;
895 }
896
897 int dbif_txn_begin(DBD *dbd)
898 {
899     int ret;
900
901     /* If we already have an active txn, just return */
902     if (dbd->db_txn)
903         return 0;
904
905     /* If our DBD has no env, just return (-> in memory db) */
906     if (dbd->db_env == NULL)
907         return 0;
908
909     ret = dbd->db_env->txn_begin(dbd->db_env, NULL, &dbd->db_txn, 0);
910
911     if (ret) {
912         LOG(log_error, logtype_cnid, "error starting transaction: %s", db_strerror(ret));
913         return -1;
914     } else
915         return 0;
916 }
917
918 int dbif_txn_commit(DBD *dbd)
919 {
920     int ret;
921
922     if (! dbd->db_txn)
923         return 0;
924
925     /* If our DBD has no env, just return (-> in memory db) */
926     if (dbd->db_env == NULL)
927         return 0;
928
929     ret = dbd->db_txn->commit(dbd->db_txn, 0);
930     dbd->db_txn = NULL;
931     
932     if (ret) {
933         LOG(log_error, logtype_cnid, "error committing transaction: %s", db_strerror(ret));
934         return -1;
935     } else
936         return 1;
937 }
938
939 int dbif_txn_abort(DBD *dbd)
940 {
941     int ret;
942
943     if (! dbd->db_txn)
944         return 0;
945
946     /* If our DBD has no env, just return (-> in memory db) */
947     if (dbd->db_env == NULL)
948         return 0;
949
950     ret = dbd->db_txn->abort(dbd->db_txn);
951     dbd->db_txn = NULL;
952     
953     if (ret) {
954         LOG(log_error, logtype_cnid, "error aborting transaction: %s", db_strerror(ret));
955         return -1;
956     } else
957         return 0;
958 }
959
960 /* 
961    ret = 1 -> commit txn
962    ret = 0 -> abort txn -> exit!
963    anything else -> exit!
964 */
965 void dbif_txn_close(DBD *dbd, int ret)
966 {
967     if (ret == 0) {
968         if (dbif_txn_abort(dbd) < 0) {
969             LOG( log_error, logtype_cnid, "Fatal error aborting transaction. Exiting!");
970             exit(EXIT_FAILURE);
971         }
972     } else if (ret == 1) {
973         ret = dbif_txn_commit(dbd);
974         if (  ret < 0) {
975             LOG( log_error, logtype_cnid, "Fatal error committing transaction. Exiting!");
976             exit(EXIT_FAILURE);
977         }
978     } else
979        exit(EXIT_FAILURE);
980 }
981
982 int dbif_txn_checkpoint(DBD *dbd, u_int32_t kbyte, u_int32_t min, u_int32_t flags)
983 {
984     int ret;
985     ret = dbd->db_env->txn_checkpoint(dbd->db_env, kbyte, min, flags);
986     if (ret) {
987         LOG(log_error, logtype_cnid, "error checkpointing transaction susystem: %s", db_strerror(ret));
988         return -1;
989     } else
990         return 0;
991 }
992
993 int dbif_count(DBD *dbd, const int dbi, u_int32_t *count)
994 {
995     int ret;
996     DB_BTREE_STAT *sp;
997     DB *db = dbd->db_table[dbi].db;
998
999     ret = db->stat(db, NULL, &sp, 0);
1000
1001     if (ret) {
1002         LOG(log_error, logtype_cnid, "error getting stat infotmation on database: %s", db_strerror(ret));
1003         return -1;
1004     }
1005
1006     *count = sp->bt_ndata;
1007     free(sp);
1008
1009     return 0;
1010 }
1011
1012 int dbif_copy_rootinfokey(DBD *srcdbd, DBD *destdbd)
1013 {
1014     DBT key, data;
1015     int rc;
1016
1017     memset(&key, 0, sizeof(key));
1018     memset(&data, 0, sizeof(data));
1019
1020     key.data = ROOTINFO_KEY;
1021     key.size = ROOTINFO_KEYLEN;
1022
1023     if ((rc = dbif_get(srcdbd, DBIF_CNID, &key, &data, 0)) <= 0) {
1024         LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error getting rootinfo record");
1025         return -1;
1026     }
1027
1028     memset(&key, 0, sizeof(key));
1029     key.data = ROOTINFO_KEY;
1030     key.size = ROOTINFO_KEYLEN;
1031
1032     if ((rc = dbif_put(destdbd, DBIF_CNID, &key, &data, 0))) {
1033         LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error writing rootinfo key");
1034         return -1;
1035     }
1036
1037     return 0;
1038 }
1039
1040 int dbif_dump(DBD *dbd, int dumpindexes)
1041 {
1042     int rc;
1043     uint32_t max = 0, count = 0, cnid, type, did, lastid;
1044     uint64_t dev, ino;
1045     time_t stamp;
1046     DBC *cur;
1047     DBT key = { 0 }, data = { 0 };
1048     DB *db = dbd->db_table[DBIF_CNID].db;
1049     char *typestring[2] = {"f", "d"};
1050     char timebuf[64];
1051
1052     printf("CNID database dump:\n");
1053
1054     rc = db->cursor(db, NULL, &cur, 0);
1055     if (rc) {
1056         LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc));
1057         return -1;
1058     }
1059
1060     cur->c_get(cur, &key, &data, DB_FIRST);
1061     while (rc == 0) {
1062         /* Parse and print data */
1063         memcpy(&cnid, key.data, 4);
1064         cnid = ntohl(cnid);
1065         if (cnid > max)
1066             max = cnid;
1067
1068         /* Rootinfo node ? */
1069         if (cnid == 0) {
1070             memcpy(&stamp, (char *)data.data + 4, sizeof(time_t));
1071             memcpy(&lastid, (char *)data.data + 20, sizeof(cnid_t));
1072             lastid = ntohl(lastid);
1073             strftime(timebuf, sizeof(timebuf), "%b %d %Y %H:%M:%S", localtime(&stamp));
1074             printf("dbd stamp: 0x%08x (%s), next free CNID: %u\n", (unsigned int)stamp, timebuf, lastid + 1);
1075         } else {
1076             /* dev */
1077             memcpy(&dev, (char *)data.data + CNID_DEV_OFS, 8);
1078             dev = ntoh64(dev);
1079             /* ino */
1080             memcpy(&ino, (char *)data.data + CNID_INO_OFS, 8);
1081             ino = ntoh64(ino);
1082             /* type */
1083             memcpy(&type, (char *)data.data + CNID_TYPE_OFS, 4);
1084             type = ntohl(type);
1085             /* did */
1086             memcpy(&did, (char *)data.data + CNID_DID_OFS, 4);
1087             did = ntohl(did);
1088
1089             count++;
1090             printf("id: %10u, did: %10u, type: %s, dev: 0x%llx, ino: 0x%016llx, name: %s\n", 
1091                    cnid, did, typestring[type],
1092                    (long long unsigned int)dev, (long long unsigned int)ino, 
1093                    (char *)data.data + CNID_NAME_OFS);
1094
1095         }
1096
1097         rc = cur->c_get(cur, &key, &data, DB_NEXT);
1098     }
1099
1100     if (rc != DB_NOTFOUND) {
1101         LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc));
1102         return -1;
1103     }
1104
1105     rc = cur->c_close(cur);
1106     if (rc) {
1107         LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(rc));
1108         return -1;
1109     }
1110     printf("%u CNIDs in database. Max CNID: %u.\n", count, max);
1111
1112     /* Dump indexes too ? */
1113     if (dumpindexes) {
1114         /* DBIF_IDX_DEVINO */
1115         printf("\ndev/inode index:\n");
1116         count = 0;
1117         db = dbd->db_table[DBIF_IDX_DEVINO].db;
1118         rc = db->cursor(db, NULL, &cur, 0);
1119         if (rc) {
1120             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc));
1121             return -1;
1122         }
1123         
1124         cur->c_get(cur, &key, &data, DB_FIRST);
1125         while (rc == 0) {
1126             /* Parse and print data */
1127
1128             /* cnid */
1129             memcpy(&cnid, data.data, CNID_LEN);
1130             cnid = ntohl(cnid);
1131             if (cnid == 0) {
1132                 /* Rootinfo node */
1133             } else {
1134                 /* dev */
1135                 memcpy(&dev, key.data, CNID_DEV_LEN);
1136                 dev = ntoh64(dev);
1137                 /* ino */
1138                 memcpy(&ino, (char *)key.data + CNID_DEV_LEN, CNID_INO_LEN);
1139                 ino = ntoh64(ino);
1140                 
1141                 printf("id: %10u <== dev: 0x%llx, ino: 0x%016llx\n", 
1142                        cnid, (unsigned long long int)dev, (unsigned long long int)ino);
1143                 count++;
1144             }
1145             rc = cur->c_get(cur, &key, &data, DB_NEXT);
1146         }
1147         if (rc != DB_NOTFOUND) {
1148             LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc));
1149             return -1;
1150         }
1151         
1152         rc = cur->c_close(cur);
1153         if (rc) {
1154             LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(rc));
1155             return -1;
1156         }
1157         printf("%u items\n", count);
1158
1159         /* Now dump DBIF_IDX_DIDNAME */
1160         printf("\ndid/name index:\n");
1161         count = 0;
1162         db = dbd->db_table[DBIF_IDX_DIDNAME].db;
1163         rc = db->cursor(db, NULL, &cur, 0);
1164         if (rc) {
1165             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc));
1166             return -1;
1167         }
1168         
1169         cur->c_get(cur, &key, &data, DB_FIRST);
1170         while (rc == 0) {
1171             /* Parse and print data */
1172
1173             /* cnid */
1174             memcpy(&cnid, data.data, CNID_LEN);
1175             cnid = ntohl(cnid);
1176             if (cnid == 0) {
1177                 /* Rootinfo node */
1178             } else {
1179                 /* did */
1180                 memcpy(&did, key.data, CNID_LEN);
1181                 did = ntohl(did);
1182
1183                 printf("id: %10u <== did: %10u, name: %s\n", cnid, did, (char *)key.data + CNID_LEN);
1184                 count++;
1185             }
1186             rc = cur->c_get(cur, &key, &data, DB_NEXT);
1187         }
1188         if (rc != DB_NOTFOUND) {
1189             LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc));
1190             return -1;
1191         }
1192         
1193         rc = cur->c_close(cur);
1194         if (rc) {
1195             LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(rc));
1196             return -1;
1197         }
1198         printf("%u items\n", count);
1199     }
1200
1201     return 0;
1202 }
1203
1204 /* 
1205    Iterates over dbd, returning cnids.
1206    Uses in-value of cnid to seek to that cnid, then gets next and return that in cnid.
1207    If close=1, close cursor.
1208    Return -1 on error, 0 on EOD (end-of-database), 1 if returning cnid.
1209 */
1210 int dbif_idwalk(DBD *dbd, cnid_t *cnid, int close)
1211 {
1212     int rc;
1213     int flag;
1214     cnid_t id;
1215
1216     static DBT key = { 0 }, data = { 0 };
1217     DB *db = dbd->db_table[DBIF_CNID].db;
1218
1219     if (close) {
1220         if (dbd->db_cur) {
1221             dbd->db_cur->close(dbd->db_cur);
1222             dbd->db_cur = NULL;
1223         }
1224         return 0;
1225     }
1226
1227     /* An dbif_del will have closed our cursor too */
1228     if ( ! dbd->db_cur ) {
1229         if ((rc = db->cursor(db, NULL, &dbd->db_cur, 0)) != 0) {
1230             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc));
1231             return -1;
1232         }
1233         flag = DB_SET_RANGE;    /* This will seek to next cnid after the one just deleted */
1234         id = htonl(*cnid);
1235         key.data = &id;
1236         key.size = sizeof(cnid_t);
1237     } else
1238         flag = DB_NEXT;
1239
1240     if ((rc = dbd->db_cur->get(dbd->db_cur, &key, &data, flag)) == 0) {
1241         memcpy(cnid, key.data, sizeof(cnid_t));
1242         *cnid = ntohl(*cnid);
1243         return 1;
1244     }
1245
1246     if (rc != DB_NOTFOUND) {
1247         LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc));
1248         dbd->db_cur->close(dbd->db_cur);
1249         dbd->db_cur = NULL;
1250         return -1;
1251     }
1252
1253     if (dbd->db_cur) {
1254         dbd->db_cur->close(dbd->db_cur);
1255         dbd->db_cur = NULL;
1256     }    
1257
1258     return 0;
1259 }