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