]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/dbif.c
Merge branch 'branch-netatalk-2-1' of ssh://netatalk.git.sourceforge.net/gitroot...
[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 static int dbif_openlog(DBD *dbd)
82 {
83     int ret = 0;
84     int cwd = -1;
85
86     if ( ! dbd->db_filename)
87         /* in memory db */
88         return 0;
89
90     /* Remember cwd */
91     if ((cwd = open(".", O_RDONLY)) < 0) {
92         LOG(log_error, logtype_cnid, "error opening cwd: %s", strerror(errno));
93         return -1;
94     }
95
96     /* chdir to db_envhome */
97     if ((chdir(dbd->db_envhome)) != 0) {
98         LOG(log_error, logtype_cnid, "error chdiring to db_env '%s': %s", dbd->db_envhome, strerror(errno));        
99         ret = -1;
100         goto exit;
101     }
102
103     if ((dbd->db_errlog = fopen(DB_ERRLOGFILE, "a")) == NULL)
104         LOG(log_warning, logtype_cnid, "error creating/opening DB errlogfile: %s", strerror(errno));
105
106     if (dbd->db_errlog != NULL) {
107         dbd->db_env->set_errfile(dbd->db_env, dbd->db_errlog);
108         dbd->db_env->set_msgfile(dbd->db_env, dbd->db_errlog);
109     }
110
111 exit:
112     if (cwd != -1) {
113         if ((fchdir(cwd)) != 0) {
114             LOG(log_error, logtype_cnid, "error chdiring back: %s", strerror(errno));        
115             ret = -1;
116         }
117         close(cwd);
118     }
119     return ret;
120 }
121
122 /* --------------- */
123 static int dbif_logautorem(DBD *dbd)
124 {
125     int ret = 0;
126     int cwd = -1;
127     char **logfiles = NULL;
128     char **file;
129
130     if ( ! dbd->db_filename)
131         /* in memory db */
132         return 0;
133
134     /* Remember cwd */
135     if ((cwd = open(".", O_RDONLY)) < 0) {
136         LOG(log_error, logtype_cnid, "error opening cwd: %s", strerror(errno));
137         return -1;
138     }
139
140     /* chdir to db_envhome */
141     if ((chdir(dbd->db_envhome)) != 0) {
142         LOG(log_error, logtype_cnid, "error chdiring to db_env '%s': %s", dbd->db_envhome, strerror(errno));        
143         ret = -1;
144         goto exit;
145     }
146
147     if ((ret = dbd->db_env->log_archive(dbd->db_env, &logfiles, 0)) != 0) {
148         LOG(log_error, logtype_cnid, "error getting list of stale logfiles: %s",
149             db_strerror(ret));
150         dbd->db_env->close(dbd->db_env, 0);
151         dbd->db_env = NULL;
152         ret = -1;
153         goto exit;
154     }
155
156     if (logfiles != NULL) {
157         for (file = logfiles; *file != NULL; file++) {
158             if (unlink(*file) < 0)
159                 LOG(log_warning, logtype_cnid, "Error removing stale logfile %s: %s", *file, strerror(errno));
160         }
161         free(logfiles);
162     }
163
164 exit:
165     if (cwd != -1) {
166         if ((fchdir(cwd)) != 0) {
167             LOG(log_error, logtype_cnid, "error chdiring back: %s", strerror(errno));        
168             ret = -1;
169         }
170         close(cwd);
171     }
172     return ret;
173 }
174
175 /* --------------- */
176 int dbif_stamp(DBD *dbd, void *buffer, int size)
177 {
178     struct stat st;
179     int         rc,cwd;
180
181     if (size < 8)
182         return -1;
183
184     /* Remember cwd */
185     if ((cwd = open(".", O_RDONLY)) < 0) {
186         LOG(log_error, logtype_cnid, "error opening cwd: %s", strerror(errno));
187         return -1;
188     }
189
190     /* chdir to db_envhome */
191     if ((chdir(dbd->db_envhome)) != 0) {
192         LOG(log_error, logtype_cnid, "error chdiring to db_env '%s': %s", dbd->db_envhome, strerror(errno));        
193         return -1;
194     }
195
196     if ((rc = stat(dbd->db_table[DBIF_CNID].name, &st)) < 0) {
197         LOG(log_error, logtype_cnid, "error stating database %s: %s", dbd->db_table[DBIF_CNID].name, db_strerror(errno));
198         return -1;
199     }
200     memset(buffer, 0, size);
201     memcpy(buffer, &st.st_ctime, sizeof(st.st_ctime));
202
203     if ((fchdir(cwd)) != 0) {
204         LOG(log_error, logtype_cnid, "error chdiring back: %s", strerror(errno));        
205         return -1;
206     }
207
208     return 0;
209 }
210
211 /* --------------- */
212 DBD *dbif_init(const char *envhome, const char *filename)
213 {
214     DBD *dbd;
215
216     if ( NULL == (dbd = calloc(sizeof(DBD), 1)) )
217         return NULL;
218
219     /* filename == NULL means in memory db */
220     if (filename) {
221         if (! envhome)
222             return NULL;
223
224         dbd->db_envhome = strdup(envhome);
225         if (NULL == dbd->db_envhome) {
226             free(dbd);
227             return NULL;
228         }
229
230         dbd->db_filename = strdup(filename);
231         if (NULL == dbd->db_filename) {
232             free(dbd->db_envhome);
233             free(dbd);
234             return NULL;
235         }
236     }
237     
238     dbd->db_table[DBIF_CNID].name        = "cnid2.db";
239     dbd->db_table[DBIF_IDX_DEVINO].name  = "devino.db";
240     dbd->db_table[DBIF_IDX_DIDNAME].name = "didname.db";
241
242     dbd->db_table[DBIF_CNID].type        = DB_BTREE;
243     dbd->db_table[DBIF_IDX_DEVINO].type  = DB_BTREE;
244     dbd->db_table[DBIF_IDX_DIDNAME].type = DB_BTREE;
245
246     dbd->db_table[DBIF_CNID].openflags        = DB_CREATE;
247     dbd->db_table[DBIF_IDX_DEVINO].openflags  = DB_CREATE;
248     dbd->db_table[DBIF_IDX_DIDNAME].openflags = DB_CREATE;
249
250     return dbd;
251 }
252
253 /* 
254    We must open the db_env with an absolute pathname, as `dbd` keeps chdir'ing, which
255    breaks e.g. bdb logfile-rotation with relative pathnames.
256    But still we use relative paths with upgrade_required() and the DB_ERRLOGFILE
257    in order to avoid creating absolute paths by copying. Both have no problem with
258    a relative path.
259 */
260 int dbif_env_open(DBD *dbd, struct db_param *dbp, uint32_t dbenv_oflags)
261 {
262     int ret;
263
264     /* Refuse to do anything if this is an old version of the CNID database */
265     if (upgrade_required(dbd)) {
266         LOG(log_error, logtype_cnid, "Found version 1 of the CNID database. Please upgrade to version 2");
267         return -1;
268     }
269
270     if ((ret = db_env_create(&dbd->db_env, 0))) {
271         LOG(log_error, logtype_cnid, "error creating DB environment: %s",
272             db_strerror(ret));
273         dbd->db_env = NULL;
274         return -1;
275     }
276
277     dbd->db_param = *dbp;
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 = 2 -> commit txn regardless of db_param.txn_frequency
795    ret = 1 -> commit txn if db_param.txn_frequency
796    ret = 0 -> abort txn db_param.txn_frequency -> exit!
797    anything else -> exit!
798
799    db_param of the db environment might specify txn_frequency > 1 in which case
800    we only close a txn every txn_frequency time. the `dbd` command uses this for the
801    temp rebuild db, cnid_dbd keeps it at 0. For increasing cnid_dbd throughput this
802    should be tuned and testes as well.
803 */
804 void dbif_txn_close(DBD *dbd, int ret)
805 {
806     if (ret == 0) {
807         if (dbif_txn_abort(dbd) < 0) {
808             LOG( log_error, logtype_cnid, "Fatal error aborting transaction. Exiting!");
809             exit(EXIT_FAILURE);
810         }
811     } else if (ret == 1 || ret == 2) {
812         static uint count;
813         if (ret != 2 && dbd->db_param.txn_frequency > 1) {
814             count++;
815             if ((count % dbd->db_param.txn_frequency) != 0)
816                 return;
817         }
818         ret = dbif_txn_commit(dbd);
819         if (  ret < 0) {
820             LOG( log_error, logtype_cnid, "Fatal error committing transaction. Exiting!");
821             exit(EXIT_FAILURE);
822         }
823     } else
824        exit(EXIT_FAILURE);
825 }
826
827 int dbif_txn_checkpoint(DBD *dbd, u_int32_t kbyte, u_int32_t min, u_int32_t flags)
828 {
829     int ret;
830     ret = dbd->db_env->txn_checkpoint(dbd->db_env, kbyte, min, flags);
831     if (ret) {
832         LOG(log_error, logtype_cnid, "error checkpointing transaction susystem: %s", db_strerror(ret));
833         return -1;
834     } else
835         return 0;
836 }
837
838 int dbif_count(DBD *dbd, const int dbi, u_int32_t *count)
839 {
840     int ret;
841     DB_BTREE_STAT *sp;
842     DB *db = dbd->db_table[dbi].db;
843
844     ret = db->stat(db, NULL, &sp, 0);
845
846     if (ret) {
847         LOG(log_error, logtype_cnid, "error getting stat infotmation on database: %s", db_strerror(ret));
848         return -1;
849     }
850
851     *count = sp->bt_ndata;
852     free(sp);
853
854     return 0;
855 }
856
857 int dbif_copy_rootinfokey(DBD *srcdbd, DBD *destdbd)
858 {
859     DBT key, data;
860     int rc;
861
862     memset(&key, 0, sizeof(key));
863     memset(&data, 0, sizeof(data));
864
865     key.data = ROOTINFO_KEY;
866     key.size = ROOTINFO_KEYLEN;
867
868     if ((rc = dbif_get(srcdbd, DBIF_CNID, &key, &data, 0)) <= 0) {
869         LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error getting rootinfo record");
870         return -1;
871     }
872
873     memset(&key, 0, sizeof(key));
874     key.data = ROOTINFO_KEY;
875     key.size = ROOTINFO_KEYLEN;
876
877     if ((rc = dbif_put(destdbd, DBIF_CNID, &key, &data, 0))) {
878         LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error writing rootinfo key");
879         return -1;
880     }
881
882     return 0;
883 }
884
885 int dbif_dump(DBD *dbd, int dumpindexes)
886 {
887     int rc;
888     uint32_t max = 0, count = 0, cnid, type, did, lastid;
889     uint64_t dev, ino;
890     time_t stamp;
891     DBC *cur;
892     DBT key = { 0 }, data = { 0 };
893     DB *db = dbd->db_table[DBIF_CNID].db;
894     char *typestring[2] = {"f", "d"};
895     char timebuf[64];
896
897     printf("CNID database dump:\n");
898
899     rc = db->cursor(db, NULL, &cur, 0);
900     if (rc) {
901         LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc));
902         return -1;
903     }
904
905     cur->c_get(cur, &key, &data, DB_FIRST);
906     while (rc == 0) {
907         /* Parse and print data */
908         memcpy(&cnid, key.data, 4);
909         cnid = ntohl(cnid);
910         if (cnid > max)
911             max = cnid;
912
913         /* Rootinfo node ? */
914         if (cnid == 0) {
915             memcpy(&stamp, (char *)data.data + 4, sizeof(time_t));
916             memcpy(&lastid, (char *)data.data + 20, sizeof(cnid_t));
917             lastid = ntohl(lastid);
918             strftime(timebuf, sizeof(timebuf), "%b %d %Y %H:%M:%S", localtime(&stamp));
919             printf("dbd stamp: 0x%08x (%s), next free CNID: %u\n", (unsigned int)stamp, timebuf, lastid + 1);
920         } else {
921             /* dev */
922             memcpy(&dev, (char *)data.data + CNID_DEV_OFS, 8);
923             dev = ntoh64(dev);
924             /* ino */
925             memcpy(&ino, (char *)data.data + CNID_INO_OFS, 8);
926             ino = ntoh64(ino);
927             /* type */
928             memcpy(&type, (char *)data.data + CNID_TYPE_OFS, 4);
929             type = ntohl(type);
930             /* did */
931             memcpy(&did, (char *)data.data + CNID_DID_OFS, 4);
932             did = ntohl(did);
933
934             count++;
935             printf("id: %10u, did: %10u, type: %s, dev: 0x%llx, ino: 0x%016llx, name: %s\n", 
936                    cnid, did, typestring[type],
937                    (long long unsigned int)dev, (long long unsigned int)ino, 
938                    (char *)data.data + CNID_NAME_OFS);
939
940         }
941
942         rc = cur->c_get(cur, &key, &data, DB_NEXT);
943     }
944
945     if (rc != DB_NOTFOUND) {
946         LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc));
947         return -1;
948     }
949
950     rc = cur->c_close(cur);
951     if (rc) {
952         LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(rc));
953         return -1;
954     }
955     printf("%u CNIDs in database. Max CNID: %u.\n", count, max);
956
957     /* Dump indexes too ? */
958     if (dumpindexes) {
959         /* DBIF_IDX_DEVINO */
960         printf("\ndev/inode index:\n");
961         count = 0;
962         db = dbd->db_table[DBIF_IDX_DEVINO].db;
963         rc = db->cursor(db, NULL, &cur, 0);
964         if (rc) {
965             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc));
966             return -1;
967         }
968         
969         cur->c_get(cur, &key, &data, DB_FIRST);
970         while (rc == 0) {
971             /* Parse and print data */
972
973             /* cnid */
974             memcpy(&cnid, data.data, CNID_LEN);
975             cnid = ntohl(cnid);
976             if (cnid == 0) {
977                 /* Rootinfo node */
978             } else {
979                 /* dev */
980                 memcpy(&dev, key.data, CNID_DEV_LEN);
981                 dev = ntoh64(dev);
982                 /* ino */
983                 memcpy(&ino, (char *)key.data + CNID_DEV_LEN, CNID_INO_LEN);
984                 ino = ntoh64(ino);
985                 
986                 printf("id: %10u <== dev: 0x%llx, ino: 0x%016llx\n", 
987                        cnid, (unsigned long long int)dev, (unsigned long long int)ino);
988                 count++;
989             }
990             rc = cur->c_get(cur, &key, &data, DB_NEXT);
991         }
992         if (rc != DB_NOTFOUND) {
993             LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc));
994             return -1;
995         }
996         
997         rc = cur->c_close(cur);
998         if (rc) {
999             LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(rc));
1000             return -1;
1001         }
1002         printf("%u items\n", count);
1003
1004         /* Now dump DBIF_IDX_DIDNAME */
1005         printf("\ndid/name index:\n");
1006         count = 0;
1007         db = dbd->db_table[DBIF_IDX_DIDNAME].db;
1008         rc = db->cursor(db, NULL, &cur, 0);
1009         if (rc) {
1010             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc));
1011             return -1;
1012         }
1013         
1014         cur->c_get(cur, &key, &data, DB_FIRST);
1015         while (rc == 0) {
1016             /* Parse and print data */
1017
1018             /* cnid */
1019             memcpy(&cnid, data.data, CNID_LEN);
1020             cnid = ntohl(cnid);
1021             if (cnid == 0) {
1022                 /* Rootinfo node */
1023             } else {
1024                 /* did */
1025                 memcpy(&did, key.data, CNID_LEN);
1026                 did = ntohl(did);
1027
1028                 printf("id: %10u <== did: %10u, name: %s\n", cnid, did, (char *)key.data + CNID_LEN);
1029                 count++;
1030             }
1031             rc = cur->c_get(cur, &key, &data, DB_NEXT);
1032         }
1033         if (rc != DB_NOTFOUND) {
1034             LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc));
1035             return -1;
1036         }
1037         
1038         rc = cur->c_close(cur);
1039         if (rc) {
1040             LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(rc));
1041             return -1;
1042         }
1043         printf("%u items\n", count);
1044     }
1045
1046     return 0;
1047 }
1048
1049 /* 
1050    Iterates over dbd, returning cnids.
1051    Uses in-value of cnid to seek to that cnid, then gets next and return that in cnid.
1052    If close=1, close cursor.
1053    Return -1 on error, 0 on EOD (end-of-database), 1 if returning cnid.
1054 */
1055 int dbif_idwalk(DBD *dbd, cnid_t *cnid, int close)
1056 {
1057     int rc;
1058     int flag;
1059     cnid_t id;
1060
1061     static DBT key = { 0 }, data = { 0 };
1062     DB *db = dbd->db_table[DBIF_CNID].db;
1063
1064     if (close) {
1065         if (dbd->db_cur) {
1066             dbd->db_cur->close(dbd->db_cur);
1067             dbd->db_cur = NULL;
1068         }
1069         return 0;
1070     }
1071
1072     /* An dbif_del will have closed our cursor too */
1073     if ( ! dbd->db_cur ) {
1074         if ((rc = db->cursor(db, NULL, &dbd->db_cur, 0)) != 0) {
1075             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc));
1076             return -1;
1077         }
1078         flag = DB_SET_RANGE;    /* This will seek to next cnid after the one just deleted */
1079         id = htonl(*cnid);
1080         key.data = &id;
1081         key.size = sizeof(cnid_t);
1082     } else
1083         flag = DB_NEXT;
1084
1085     if ((rc = dbd->db_cur->get(dbd->db_cur, &key, &data, flag)) == 0) {
1086         memcpy(cnid, key.data, sizeof(cnid_t));
1087         *cnid = ntohl(*cnid);
1088         return 1;
1089     }
1090
1091     if (rc != DB_NOTFOUND) {
1092         LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc));
1093         dbd->db_cur->close(dbd->db_cur);
1094         dbd->db_cur = NULL;
1095         return -1;
1096     }
1097
1098     if (dbd->db_cur) {
1099         dbd->db_cur->close(dbd->db_cur);
1100         dbd->db_cur = NULL;
1101     }    
1102
1103     return 0;
1104 }