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