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