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