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