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