]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/dbif.c
Move stamp code to setversion, dbd fixes, ad fixes
[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 || ret == DB_SECONDARY_BAD)
800         return 0;
801     if (ret) {
802         LOG(log_error, logtype_cnid, "error deleting key/value from %s: %s",
803             dbd->db_table[dbi].name, db_strerror(ret));
804         return -1;
805     } else
806         return 1;
807 }
808
809 /*!
810  * Inititialize rootinfo key (which has CNID 0 as key)
811  *
812  * This also "stamps" the database, which means storing st.st_ctime of the
813  * "cnid2.db" file in the rootinfo data at the DEV offset
814  *
815  * @param dbd      (rw) database handle
816  * @param version  (r)  database version number
817  *
818  * @returns -1 on error, 0 on success
819  */
820 static int dbif_init_rootinfo(DBD *dbd, int version)
821 {
822     DBT key, data;
823     uint32_t v;
824     char buf[ROOTINFO_DATALEN];
825
826     LOG(log_debug, logtype_cnid, "Setting CNID database version to %u", version);
827
828     v = version;
829     v = htonl(v);
830
831     memset(&key, 0, sizeof(key));
832     memset(&data, 0, sizeof(data));
833     key.data = ROOTINFO_KEY;
834     key.size = ROOTINFO_KEYLEN;
835     data.data = buf;
836     data.size = ROOTINFO_DATALEN;
837
838     memcpy(buf, ROOTINFO_DATA, ROOTINFO_DATALEN);
839     memcpy(buf + CNID_DID_OFS, &v, sizeof(v));
840     if (dbif_stamp(dbd, buf + CNID_DEV_OFS, CNID_DEV_LEN) < 0)
841         return -1;
842
843     if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) < 0)
844         return -1;
845
846     return 0;
847 }
848
849 /*!
850  * Initialize or return CNID database version number
851  *
852  * Calls dbif_init_rootinfo if the rootinfo key does not exist yet
853  *
854  * @returns -1 on error, version number otherwise
855  */
856 int dbif_getversion(DBD *dbd)
857 {
858     DBT key, data;
859     uint32_t version;
860     int ret;
861
862     memset(&key, 0, sizeof(key));
863     memset(&data, 0, sizeof(data));
864     key.data = ROOTINFO_KEY;
865     key.size = ROOTINFO_KEYLEN;
866
867     switch (dbif_get(dbd, DBIF_CNID, &key, &data, 0)) {
868     case 1: /* found */
869         memcpy(&version, (char *)data.data + CNID_DID_OFS, sizeof(version));
870         version = ntohl(version);
871         LOG(log_debug, logtype_cnid, "CNID database version %u", version);
872         ret = version;
873         break;
874     case 0: /* not found */
875         if (dbif_init_rootinfo(dbd, CNID_VERSION) != 0)
876             return -1;
877         ret = CNID_VERSION;
878         break;
879     default:
880         return -1;
881     }
882     return ret;
883 }
884
885 /*!
886  * Set CNID database version number
887  *
888  * Initializes rootinfo key as neccessary, as does dbif_getversion
889  * @returns -1 on error, version number otherwise
890  */
891 int dbif_setversion(DBD *dbd, int version)
892 {
893     int ret;
894     DBT key, data;
895     uint32_t v;
896
897     LOG(log_debug, logtype_cnid, "Setting CNID database version to %u", version);
898
899     v = version;
900     v = htonl(v);
901
902     memset(&key, 0, sizeof(key));
903     memset(&data, 0, sizeof(data));
904     key.data = ROOTINFO_KEY;
905     key.size = ROOTINFO_KEYLEN;
906
907     if ((ret = dbif_get(dbd, DBIF_CNID, &key, &data, 0)) == -1)
908         return -1;
909     if (ret == 0) {
910         /* No rootinfo key yet, init it */
911         if (dbif_init_rootinfo(dbd, CNID_VERSION) != 0)
912             return -1;
913         /* Now try again */
914         if (dbif_get(dbd, DBIF_CNID, &key, &data, 0) == -1)
915             return -1;
916     }
917     memcpy((char *)data.data + CNID_DID_OFS, &v, sizeof(v));
918     data.size = ROOTINFO_DATALEN;
919     if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) < 0)
920         return -1;
921
922     return 0;
923 }
924
925 /*!
926  * Search the database by name
927  *
928  * @param resbuf    (w) buffer for search results CNIDs, maxsize is assumed to be
929  *                      DBD_MAX_SRCH_RSLTS * sizefof(cnid_t)
930  *
931  * @returns -1 on error, 0 when nothing found, else the number of matches
932  */
933 int dbif_search(DBD *dbd, DBT *key, char *resbuf)
934 {
935     int ret = 0;
936     int count = 0;
937     DBC *cursorp = NULL;
938     DBT pkey, data;
939     char *cnids = resbuf;
940     cnid_t cnid;
941     char *namebkp = key->data;
942     int namelenbkp = key->size;
943
944     memset(&pkey, 0, sizeof(DBT));
945     memset(&data, 0, sizeof(DBT));
946
947     /* Get a cursor */
948     ret = dbd->db_table[DBIF_IDX_NAME].db->cursor(dbd->db_table[DBIF_IDX_NAME].db,
949                                                   NULL,
950                                                   &cursorp,
951                                                   0);
952     if (ret != 0) {
953         LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(ret));
954         ret = -1;
955         goto exit;
956     }
957
958     ret = cursorp->pget(cursorp, key, &pkey, &data, DB_SET_RANGE);
959     while (count < DBD_MAX_SRCH_RSLTS && ret != DB_NOTFOUND) {
960         if (!((namelenbkp <= key->size) && (strncmp(namebkp, key->data, namelenbkp) == 0)))
961             break;
962         count++;
963         memcpy(cnids, pkey.data, sizeof(cnid_t));
964         memcpy(&cnid, pkey.data, sizeof(cnid_t));
965         cnids += sizeof(cnid_t);
966         LOG(log_debug, logtype_cnid, "match: CNID %" PRIu32, ntohl(cnid));
967
968         ret = cursorp->pget(cursorp, key, &pkey, &data, DB_NEXT);
969     }
970
971     ret = count;
972
973 exit:
974     if (cursorp != NULL)
975         cursorp->close(cursorp);
976     return ret;
977 }
978
979 int dbif_txn_begin(DBD *dbd)
980 {
981     int ret;
982
983     /* If we already have an active txn, just return */
984     if (dbd->db_txn)
985         return 0;
986
987     /* If our DBD has no env, just return (-> in memory db) */
988     if (dbd->db_env == NULL)
989         return 0;
990
991     ret = dbd->db_env->txn_begin(dbd->db_env, NULL, &dbd->db_txn, 0);
992
993     if (ret) {
994         LOG(log_error, logtype_cnid, "error starting transaction: %s", db_strerror(ret));
995         return -1;
996     } else
997         return 0;
998 }
999
1000 int dbif_txn_commit(DBD *dbd)
1001 {
1002     int ret;
1003
1004     if (! dbd->db_txn)
1005         return 0;
1006
1007     /* If our DBD has no env, just return (-> in memory db) */
1008     if (dbd->db_env == NULL)
1009         return 0;
1010
1011     ret = dbd->db_txn->commit(dbd->db_txn, 0);
1012     dbd->db_txn = NULL;
1013     
1014     if (ret) {
1015         LOG(log_error, logtype_cnid, "error committing transaction: %s", db_strerror(ret));
1016         return -1;
1017     } else
1018         return 1;
1019 }
1020
1021 int dbif_txn_abort(DBD *dbd)
1022 {
1023     int ret;
1024
1025     if (! dbd->db_txn)
1026         return 0;
1027
1028     /* If our DBD has no env, just return (-> in memory db) */
1029     if (dbd->db_env == NULL)
1030         return 0;
1031
1032     ret = dbd->db_txn->abort(dbd->db_txn);
1033     dbd->db_txn = NULL;
1034     
1035     if (ret) {
1036         LOG(log_error, logtype_cnid, "error aborting transaction: %s", db_strerror(ret));
1037         return -1;
1038     } else
1039         return 0;
1040 }
1041
1042 /* 
1043    ret = 1 -> commit txn
1044    ret = 0 -> abort txn -> exit!
1045    anything else -> exit!
1046 */
1047 void dbif_txn_close(DBD *dbd, int ret)
1048 {
1049     if (ret == 0) {
1050         if (dbif_txn_abort(dbd) < 0) {
1051             LOG( log_error, logtype_cnid, "Fatal error aborting transaction. Exiting!");
1052             exit(EXIT_FAILURE);
1053         }
1054     } else if (ret == 1) {
1055         ret = dbif_txn_commit(dbd);
1056         if (  ret < 0) {
1057             LOG( log_error, logtype_cnid, "Fatal error committing transaction. Exiting!");
1058             exit(EXIT_FAILURE);
1059         }
1060     } else
1061        exit(EXIT_FAILURE);
1062 }
1063
1064 int dbif_txn_checkpoint(DBD *dbd, u_int32_t kbyte, u_int32_t min, u_int32_t flags)
1065 {
1066     int ret;
1067     ret = dbd->db_env->txn_checkpoint(dbd->db_env, kbyte, min, flags);
1068     if (ret) {
1069         LOG(log_error, logtype_cnid, "error checkpointing transaction susystem: %s", db_strerror(ret));
1070         return -1;
1071     } else
1072         return 0;
1073 }
1074
1075 int dbif_count(DBD *dbd, const int dbi, u_int32_t *count)
1076 {
1077     int ret;
1078     DB_BTREE_STAT *sp;
1079     DB *db = dbd->db_table[dbi].db;
1080
1081     ret = db->stat(db, NULL, &sp, 0);
1082
1083     if (ret) {
1084         LOG(log_error, logtype_cnid, "error getting stat infotmation on database: %s", db_strerror(ret));
1085         return -1;
1086     }
1087
1088     *count = sp->bt_ndata;
1089     free(sp);
1090
1091     return 0;
1092 }
1093
1094 int dbif_copy_rootinfokey(DBD *srcdbd, DBD *destdbd)
1095 {
1096     DBT key, data;
1097     int rc;
1098
1099     memset(&key, 0, sizeof(key));
1100     memset(&data, 0, sizeof(data));
1101
1102     key.data = ROOTINFO_KEY;
1103     key.size = ROOTINFO_KEYLEN;
1104
1105     if ((rc = dbif_get(srcdbd, DBIF_CNID, &key, &data, 0)) <= 0) {
1106         LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error getting rootinfo record");
1107         return -1;
1108     }
1109
1110     memset(&key, 0, sizeof(key));
1111     key.data = ROOTINFO_KEY;
1112     key.size = ROOTINFO_KEYLEN;
1113
1114     if ((rc = dbif_put(destdbd, DBIF_CNID, &key, &data, 0))) {
1115         LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error writing rootinfo key");
1116         return -1;
1117     }
1118
1119     return 0;
1120 }
1121
1122 int dbif_dump(DBD *dbd, int dumpindexes)
1123 {
1124     int rc;
1125     uint32_t max = 0, count = 0, cnid, type, did, lastid, version;
1126     uint64_t dev, ino;
1127     time_t stamp;
1128     DBC *cur;
1129     DBT key = { 0 }, data = { 0 };
1130     DB *db = dbd->db_table[DBIF_CNID].db;
1131     char *typestring[2] = {"f", "d"};
1132     char timebuf[64];
1133
1134     printf("CNID database dump:\n");
1135
1136     rc = db->cursor(db, NULL, &cur, 0);
1137     if (rc) {
1138         LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc));
1139         return -1;
1140     }
1141
1142     cur->c_get(cur, &key, &data, DB_FIRST);
1143     while (rc == 0) {
1144         /* Parse and print data */
1145         memcpy(&cnid, key.data, 4);
1146         cnid = ntohl(cnid);
1147         if (cnid > max)
1148             max = cnid;
1149
1150         /* Rootinfo node ? */
1151         if (cnid == 0) {
1152             memcpy(&stamp, (char *)data.data + CNID_DEV_OFS, sizeof(time_t));
1153             memcpy(&lastid, (char *)data.data + CNID_TYPE_OFS, CNID_TYPE_LEN);
1154             lastid = ntohl(lastid);
1155             memcpy(&version, (char *)data.data + CNID_DID_OFS, CNID_DID_LEN);
1156             version = ntohl(version);
1157
1158             strftime(timebuf, sizeof(timebuf), "%b %d %Y %H:%M:%S", localtime(&stamp));
1159             printf("CNID db version: %u, dbd stamp: 0x%08x (%s), next free CNID: %u\n",
1160                    version, (unsigned int)stamp, timebuf, lastid + 1);
1161         } else {
1162             /* dev */
1163             memcpy(&dev, (char *)data.data + CNID_DEV_OFS, 8);
1164             dev = ntoh64(dev);
1165             /* ino */
1166             memcpy(&ino, (char *)data.data + CNID_INO_OFS, 8);
1167             ino = ntoh64(ino);
1168             /* type */
1169             memcpy(&type, (char *)data.data + CNID_TYPE_OFS, 4);
1170             type = ntohl(type);
1171             /* did */
1172             memcpy(&did, (char *)data.data + CNID_DID_OFS, 4);
1173             did = ntohl(did);
1174
1175             count++;
1176             printf("id: %10u, did: %10u, type: %s, dev: 0x%llx, ino: 0x%016llx, name: %s\n", 
1177                    cnid, did, typestring[type],
1178                    (long long unsigned int)dev, (long long unsigned int)ino, 
1179                    (char *)data.data + CNID_NAME_OFS);
1180
1181         }
1182
1183         rc = cur->c_get(cur, &key, &data, DB_NEXT);
1184     }
1185
1186     if (rc != DB_NOTFOUND) {
1187         LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc));
1188         return -1;
1189     }
1190
1191     rc = cur->c_close(cur);
1192     if (rc) {
1193         LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(rc));
1194         return -1;
1195     }
1196     printf("%u CNIDs in database. Max CNID: %u.\n", count, max);
1197
1198     /* Dump indexes too ? */
1199     if (dumpindexes) {
1200         /* DBIF_IDX_DEVINO */
1201         printf("\ndev/inode index:\n");
1202         count = 0;
1203         db = dbd->db_table[DBIF_IDX_DEVINO].db;
1204         rc = db->cursor(db, NULL, &cur, 0);
1205         if (rc) {
1206             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc));
1207             return -1;
1208         }
1209         
1210         cur->c_get(cur, &key, &data, DB_FIRST);
1211         while (rc == 0) {
1212             /* Parse and print data */
1213
1214             /* cnid */
1215             memcpy(&cnid, data.data, CNID_LEN);
1216             cnid = ntohl(cnid);
1217             if (cnid == 0) {
1218                 /* Rootinfo node */
1219             } else {
1220                 /* dev */
1221                 memcpy(&dev, key.data, CNID_DEV_LEN);
1222                 dev = ntoh64(dev);
1223                 /* ino */
1224                 memcpy(&ino, (char *)key.data + CNID_DEV_LEN, CNID_INO_LEN);
1225                 ino = ntoh64(ino);
1226                 
1227                 printf("id: %10u <== dev: 0x%llx, ino: 0x%016llx\n", 
1228                        cnid, (unsigned long long int)dev, (unsigned long long int)ino);
1229                 count++;
1230             }
1231             rc = cur->c_get(cur, &key, &data, DB_NEXT);
1232         }
1233         if (rc != DB_NOTFOUND) {
1234             LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc));
1235             return -1;
1236         }
1237         
1238         rc = cur->c_close(cur);
1239         if (rc) {
1240             LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(rc));
1241             return -1;
1242         }
1243         printf("%u items\n", count);
1244
1245         /* Now dump DBIF_IDX_DIDNAME */
1246         printf("\ndid/name index:\n");
1247         count = 0;
1248         db = dbd->db_table[DBIF_IDX_DIDNAME].db;
1249         rc = db->cursor(db, NULL, &cur, 0);
1250         if (rc) {
1251             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc));
1252             return -1;
1253         }
1254         
1255         cur->c_get(cur, &key, &data, DB_FIRST);
1256         while (rc == 0) {
1257             /* Parse and print data */
1258
1259             /* cnid */
1260             memcpy(&cnid, data.data, CNID_LEN);
1261             cnid = ntohl(cnid);
1262             if (cnid == 0) {
1263                 /* Rootinfo node */
1264             } else {
1265                 /* did */
1266                 memcpy(&did, key.data, CNID_LEN);
1267                 did = ntohl(did);
1268
1269                 printf("id: %10u <== did: %10u, name: %s\n", cnid, did, (char *)key.data + CNID_LEN);
1270                 count++;
1271             }
1272             rc = cur->c_get(cur, &key, &data, DB_NEXT);
1273         }
1274         if (rc != DB_NOTFOUND) {
1275             LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc));
1276             return -1;
1277         }
1278         
1279         rc = cur->c_close(cur);
1280         if (rc) {
1281             LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(rc));
1282             return -1;
1283         }
1284         printf("%u items\n", count);
1285     }
1286
1287     return 0;
1288 }
1289
1290 /* 
1291    Iterates over dbd, returning cnids.
1292    Uses in-value of cnid to seek to that cnid, then gets next and return that in cnid.
1293    If close=1, close cursor.
1294    Return -1 on error, 0 on EOD (end-of-database), 1 if returning cnid.
1295 */
1296 int dbif_idwalk(DBD *dbd, cnid_t *cnid, int close)
1297 {
1298     int rc;
1299     int flag;
1300     cnid_t id;
1301
1302     static DBT key = { 0 }, data = { 0 };
1303     DB *db = dbd->db_table[DBIF_CNID].db;
1304
1305     if (close) {
1306         if (dbd->db_cur) {
1307             dbd->db_cur->close(dbd->db_cur);
1308             dbd->db_cur = NULL;
1309         }
1310         return 0;
1311     }
1312
1313     /* An dbif_del will have closed our cursor too */
1314     if ( ! dbd->db_cur ) {
1315         if ((rc = db->cursor(db, NULL, &dbd->db_cur, 0)) != 0) {
1316             LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc));
1317             return -1;
1318         }
1319         flag = DB_SET_RANGE;    /* This will seek to next cnid after the one just deleted */
1320         id = htonl(*cnid);
1321         key.data = &id;
1322         key.size = sizeof(cnid_t);
1323     } else
1324         flag = DB_NEXT;
1325
1326     if ((rc = dbd->db_cur->get(dbd->db_cur, &key, &data, flag)) == 0) {
1327         memcpy(cnid, key.data, sizeof(cnid_t));
1328         *cnid = ntohl(*cnid);
1329         return 1;
1330     }
1331
1332     if (rc != DB_NOTFOUND) {
1333         LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc));
1334         dbd->db_cur->close(dbd->db_cur);
1335         dbd->db_cur = NULL;
1336         return -1;
1337     }
1338
1339     if (dbd->db_cur) {
1340         dbd->db_cur->close(dbd->db_cur);
1341         dbd->db_cur = NULL;
1342     }    
1343
1344     return 0;
1345 }