]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/dbif.c
remove DB_VERB_CHKPOINT, it's gone. From Adam Goode
[netatalk.git] / etc / cnid_dbd / dbif.c
1 /*
2  * $Id: dbif.c,v 1.4 2008-08-07 07:39:14 didg Exp $
3  *
4  * Copyright (C) Joerg Lenneis 2003
5  * All Rights Reserved.  See COPYING.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif /* HAVE_CONFIG_H */
11
12 #include <stdio.h>
13 #include <errno.h>
14 #include <stdlib.h>
15 #ifdef HAVE_SYS_TYPES_H
16 #include <sys/types.h>
17 #endif /* HAVE_SYS_TYPES_H */
18 #include <string.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <sys/cdefs.h>
22 #include <unistd.h>
23 #include <atalk/logger.h>
24 #include <db.h>
25 #include "db_param.h"
26 #include "dbif.h"
27
28 #define DB_ERRLOGFILE "db_errlog"
29
30
31 static DB_ENV *db_env = NULL;
32 static DB_TXN *db_txn = NULL;
33 static FILE   *db_errlog = NULL;
34
35 #ifdef CNID_BACKEND_DBD_TXN
36 #define DBOPTIONS    (DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN)
37 #else
38 #define DBOPTIONS    (DB_CREATE | DB_INIT_CDB | DB_INIT_MPOOL | DB_PRIVATE) 
39 #endif
40
41 static struct db_table {
42      char            *name;
43      DB              *db;
44      u_int32_t       general_flags;
45      DBTYPE          type;
46 } db_table[] =
47 {
48      { "cnid2.db",       NULL,      0, DB_BTREE},
49      { "devino.db",      NULL,      0, DB_BTREE},
50      { "didname.db",     NULL,      0, DB_BTREE},
51 };
52
53 static char *old_dbfiles[] = {"cnid.db", NULL};
54
55 extern int didname(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey);
56 extern int devino(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey);
57
58 /* --------------- */
59 static int  db_compat_associate (DB *p, DB *s,
60                    int (*callback)(DB *, const DBT *,const DBT *, DBT *),
61                    u_int32_t flags)
62 {
63 #if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1)
64     return p->associate(p, db_txn, s, callback, flags);
65 #else
66 #if (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR == 0)
67     return p->associate(p,       s, callback, flags);
68 #else
69     return 0;
70 #endif
71 #endif
72 }
73
74 /* --------------- */
75 static int db_compat_open(DB *db, char *file, char *name, DBTYPE type, int mode)
76 {
77     int ret;
78
79 #if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1)
80     ret = db->open(db, db_txn, file, name, type, DB_CREATE, mode); 
81 #else
82     ret = db->open(db,       file, name, type, DB_CREATE, mode); 
83 #endif
84
85     if (ret) {
86         LOG(log_error, logtype_cnid, "error opening database %s: %s", name, db_strerror(ret));
87         return -1;
88     } else {
89         return 0;
90     }
91 }
92
93 /* --------------- */
94 static int upgrade_required()
95 {
96     int i;
97     int found = 0;
98     struct stat st;
99     
100     for (i = 0; old_dbfiles[i] != NULL; i++) {
101         if ( !(stat(old_dbfiles[i], &st) < 0) ) {
102             found++;
103             continue;
104         }
105         if (errno != ENOENT) {
106             LOG(log_error, logtype_cnid, "cnid_open: Checking %s gave %s", old_dbfiles[i], strerror(errno));
107             found++;
108         }
109     }
110     return found;
111 }
112
113 /* --------------- */
114 int dbif_stamp(void *buffer, int size)
115 {
116     struct stat st;
117     int         rc;
118
119     if (size < 8)
120         return -1;
121
122     if ((rc = stat(db_table[0].name, &st)) < 0) {
123         LOG(log_error, logtype_cnid, "error stating database %s: %s", db_table[0].name, db_strerror(rc));
124         return -1;
125     }
126     memset(buffer, 0, size);
127     memcpy(buffer, &st.st_ctime, sizeof(st.st_ctime));
128
129     return 0;
130 }
131
132 /* --------------- */
133 /*
134  *  We assume our current directory is already the BDB homedir. Otherwise
135  *  opening the databases will not work as expected. If we use transactions,
136  *  dbif_env_init(), dbif_close() and dbif_stamp() are the only interface
137  *  functions that can be called without a valid transaction handle in db_txn.
138  */
139 int dbif_env_init(struct db_param *dbp)
140 {
141     int ret;
142 #ifdef CNID_BACKEND_DBD_TXN
143     char **logfiles = NULL;
144     char **file;
145 #endif
146
147     /* Refuse to do anything if this is an old version of the CNID database */
148     if (upgrade_required()) {
149         LOG(log_error, logtype_cnid, "Found version 1 of the CNID database. Please upgrade to version 2");
150         return -1;
151     }
152
153     if ((db_errlog = fopen(DB_ERRLOGFILE, "a")) == NULL)
154         LOG(log_warning, logtype_cnid, "error creating/opening DB errlogfile: %s", strerror(errno));
155
156 #ifdef CNID_BACKEND_DBD_TXN
157     if ((ret = db_env_create(&db_env, 0))) {
158         LOG(log_error, logtype_cnid, "error creating DB environment: %s", 
159             db_strerror(ret));
160         db_env = NULL;
161         return -1;
162     }    
163     if (db_errlog != NULL)
164         db_env->set_errfile(db_env, db_errlog); 
165     db_env->set_verbose(db_env, DB_VERB_RECOVERY, 1);
166     if ((ret = db_env->open(db_env, ".", DBOPTIONS | DB_PRIVATE | DB_RECOVER, 0))) {
167         LOG(log_error, logtype_cnid, "error opening DB environment: %s", 
168             db_strerror(ret));
169         db_env->close(db_env, 0);
170         db_env = NULL;
171         return -1;
172     }
173
174     if (db_errlog != NULL)
175         fflush(db_errlog);
176
177     if ((ret = db_env->close(db_env, 0))) {
178         LOG(log_error, logtype_cnid, "error closing DB environment after recovery: %s", 
179             db_strerror(ret));
180         db_env = NULL;
181         return -1;
182     }
183 #endif
184     if ((ret = db_env_create(&db_env, 0))) {
185         LOG(log_error, logtype_cnid, "error creating DB environment after recovery: %s",
186             db_strerror(ret));
187         db_env = NULL;
188         return -1;
189     }
190     if ((ret = db_env->set_cachesize(db_env, 0, 1024 * dbp->cachesize, 0))) {
191         LOG(log_error, logtype_cnid, "error setting DB environment cachesize to %i: %s",
192             dbp->cachesize, db_strerror(ret));
193         db_env->close(db_env, 0);
194         db_env = NULL;
195         return -1;
196     }
197     
198     if (db_errlog != NULL)
199         db_env->set_errfile(db_env, db_errlog);
200     if ((ret = db_env->open(db_env, ".", DBOPTIONS , 0))) {
201         LOG(log_error, logtype_cnid, "error opening DB environment after recovery: %s",
202             db_strerror(ret));
203         db_env->close(db_env, 0);
204         db_env = NULL;
205         return -1;      
206     }
207
208 #ifdef CNID_BACKEND_DBD_TXN
209     if (dbp->nosync && (ret = db_env->set_flags(db_env, DB_TXN_NOSYNC, 1))) {
210         LOG(log_error, logtype_cnid, "error setting TXN_NOSYNC flag: %s",
211             db_strerror(ret));
212         db_env->close(db_env, 0);
213         db_env = NULL;
214         return -1;
215     }
216     if (dbp->logfile_autoremove && db_env->log_archive(db_env, &logfiles, 0)) {
217         LOG(log_error, logtype_cnid, "error getting list of stale logfiles: %s",
218             db_strerror(ret));
219         db_env->close(db_env, 0);
220         db_env = NULL;
221         return -1;
222     }
223     if (logfiles != NULL) {
224         for (file = logfiles; *file != NULL; file++) {
225             if (unlink(*file) < 0)
226                 LOG(log_warning, logtype_cnid, "Error removing stale logfile %s: %s", *file, strerror(errno));
227         }
228         free(logfiles);
229     }
230 #endif
231     return 0;
232 }
233
234 /* --------------- */
235 int dbif_open(struct db_param *dbp _U_, int do_truncate)
236 {
237     int ret;
238     int i;
239     u_int32_t count;
240
241     for (i = 0; i != DBIF_DB_CNT; i++) {
242         if ((ret = db_create(&(db_table[i].db), db_env, 0))) {
243             LOG(log_error, logtype_cnid, "error creating handle for database %s: %s", 
244                 db_table[i].name, db_strerror(ret));
245             return -1;
246         }
247
248         if (db_table[i].general_flags) { 
249             if ((ret = db_table[i].db->set_flags(db_table[i].db, db_table[i].general_flags))) {
250                 LOG(log_error, logtype_cnid, "error setting flags for database %s: %s", 
251                     db_table[i].name, db_strerror(ret));
252                 return -1;
253             }
254         }
255
256 #if 0
257 #ifndef CNID_BACKEND_DBD_TXN
258         if ((ret = db_table[i].db->set_cachesize(db_table[i].db, 0, 1024 * dbp->cachesize, 0))) {
259             LOG(log_error, logtype_cnid, "error setting DB cachesize to %i for database %s: %s",
260                 dbp->cachesize, db_table[i].name, db_strerror(ret));
261             return -1;
262         }
263 #endif /* CNID_BACKEND_DBD_TXN */
264 #endif
265         if (db_compat_open(db_table[i].db, db_table[0].name, db_table[i].name, db_table[i].type, 0664) < 0)
266             return -1;
267         if (db_errlog != NULL)
268             db_table[i].db->set_errfile(db_table[i].db, db_errlog);
269
270         if (do_truncate && i > 0) {
271             if ((ret = db_table[i].db->truncate(db_table[i].db, db_txn, &count, 0))) {
272                 LOG(log_error, logtype_cnid, "error truncating database %s: %s", 
273                     db_table[i].name, db_strerror(ret));
274                 return -1;
275             }
276         }
277     }
278
279     /* TODO: Implement CNID DB versioning info on new databases. */
280     /* TODO: Make transaction support a runtime option. */
281     /* Associate the secondary with the primary. */
282     if ((ret = db_compat_associate(db_table[0].db, db_table[DBIF_IDX_DIDNAME].db, didname, (do_truncate)?DB_CREATE:0)) != 0) {
283         LOG(log_error, logtype_cnid, "Failed to associate didname database: %s",db_strerror(ret));
284         return -1;
285     }
286  
287     if ((ret = db_compat_associate(db_table[0].db, db_table[DBIF_IDX_DEVINO].db, devino, (do_truncate)?DB_CREATE:0)) != 0) {
288         LOG(log_error, logtype_cnid, "Failed to associate devino database: %s",db_strerror(ret));
289         return -1;
290     }
291
292     return 0;
293 }
294
295 /* ------------------------ */
296 int dbif_closedb()
297 {
298     int i;
299     int ret;
300     int err = 0;
301
302     for (i = DBIF_DB_CNT -1; i >= 0; i--) {
303         if (db_table[i].db != NULL && (ret = db_table[i].db->close(db_table[i].db, 0))) {
304             LOG(log_error, logtype_cnid, "error closing database %s: %s", db_table[i].name, db_strerror(ret));
305             err++;
306         }
307     }
308     if (err)
309         return -1;
310     return 0;
311 }
312
313 /* ------------------------ */
314 int dbif_close()
315 {
316     int ret;
317     int err = 0;
318     
319     if (dbif_closedb()) 
320         err++;
321      
322     if (db_env != NULL && (ret = db_env->close(db_env, 0))) { 
323         LOG(log_error, logtype_cnid, "error closing DB environment: %s", db_strerror(ret));
324         err++;
325     }
326     if (db_errlog != NULL && fclose(db_errlog) == EOF) {
327         LOG(log_error, logtype_cnid, "error closing DB logfile: %s", strerror(errno));
328         err++;
329     }
330     if (err)
331         return -1;
332     return 0;
333 }
334
335 /*
336  *  The following three functions are wrappers for DB->get(), DB->put() and
337  *  DB->del(). We define them here because we want access to the db_txn
338  *  transaction handle and the database handles limited to the functions in this
339  *  file. A consequence is that there is always only one transaction in
340  *  progress. For nontransactional access db_txn is NULL. All three return -1 on
341  *  error. dbif_get()/dbif_del return 1 if the key was found and 0
342  *  otherwise. dbif_put() returns 0 if key/val was successfully updated and 1 if
343  *  the DB_NOOVERWRITE flag was specified and the key already exists.
344  *  
345  *  All return codes other than DB_NOTFOUND and DB_KEYEXIST from the DB->()
346  *  functions are not expected and therefore error conditions.
347  */
348
349 int dbif_get(const int dbi, DBT *key, DBT *val, u_int32_t flags)
350 {
351     int ret;
352     DB *db = db_table[dbi].db;
353
354     ret = db->get(db, db_txn, key, val, flags);
355      
356     if (ret == DB_NOTFOUND)
357         return 0;
358     if (ret) {
359         LOG(log_error, logtype_cnid, "error retrieving value from %s: %s", db_table[dbi].name, db_strerror(errno));
360         return -1;
361     } else 
362         return 1;
363 }
364
365 /* search by secondary return primary */
366 int dbif_pget(const int dbi, DBT *key, DBT *pkey, DBT *val, u_int32_t flags)
367 {
368     int ret;
369     DB *db = db_table[dbi].db;
370
371     ret = db->pget(db, db_txn, key, pkey, val, flags);
372
373 #if DB_VERSION_MAJOR >= 4
374     if (ret == DB_NOTFOUND || ret == DB_SECONDARY_BAD) {
375 #else
376     if (ret == DB_NOTFOUND) {
377 #endif     
378         return 0;
379     }
380     if (ret) {
381         LOG(log_error, logtype_cnid, "error retrieving value from %s: %s", db_table[dbi].name, db_strerror(errno));
382         return -1;
383     } else 
384         return 1;
385 }
386
387 /* -------------------------- */
388 int dbif_put(const int dbi, DBT *key, DBT *val, u_int32_t flags)
389 {
390     int ret;
391     DB *db = db_table[dbi].db;
392
393     ret = db->put(db, db_txn, key, val, flags);
394      
395     if (ret) {
396         if ((flags & DB_NOOVERWRITE) && ret == DB_KEYEXIST) {
397             return 1;
398         } else {
399             LOG(log_error, logtype_cnid, "error setting key/value in %s: %s", db_table[dbi].name, db_strerror(errno));
400             return -1;
401         }
402     } else
403         return 0;
404 }
405
406 int dbif_del(const int dbi, DBT *key, u_int32_t flags)
407 {
408     int ret;
409     DB *db = db_table[dbi].db;
410
411     ret = db->del(db, db_txn, key, flags);
412
413 #if DB_VERSION_MAJOR > 3
414     if (ret == DB_NOTFOUND || ret == DB_SECONDARY_BAD)
415 #else
416     if (ret == DB_NOTFOUND)
417 #endif
418         return 0;
419     if (ret) {
420         LOG(log_error, logtype_cnid, "error deleting key/value from %s: %s", db_table[dbi].name, db_strerror(errno));
421         return -1;
422     } else
423         return 1;
424 }
425
426 #ifdef CNID_BACKEND_DBD_TXN
427
428 int dbif_txn_begin()
429 {
430     int ret;
431 #if DB_VERSION_MAJOR >= 4
432     ret = db_env->txn_begin(db_env, NULL, &db_txn, 0);
433 #else     
434     ret = txn_begin(db_env, NULL, &db_txn, 0);
435 #endif
436     if (ret) {
437         LOG(log_error, logtype_cnid, "error starting transaction: %s", db_strerror(errno));
438         return -1;
439     } else 
440         return 0;
441 }
442
443 int dbif_txn_commit()
444 {
445     int ret;
446 #if DB_VERSION_MAJOR >= 4
447     ret = db_txn->commit(db_txn, 0);
448 #else
449     ret = txn_commit(db_txn, 0);
450 #endif
451     if (ret) {
452         LOG(log_error, logtype_cnid, "error committing transaction: %s", db_strerror(errno));
453         return -1;
454     } else 
455         return 0;
456 }
457
458 int dbif_txn_abort()
459 {
460     int ret;
461 #if DB_VERSION_MAJOR >= 4
462     ret = db_txn->abort(db_txn);
463 #else
464     ret = txn_abort(db_txn);
465 #endif
466     if (ret) {
467         LOG(log_error, logtype_cnid, "error aborting transaction: %s", db_strerror(errno));
468         return -1;
469     } else
470         return 0;
471 }
472
473 int dbif_txn_checkpoint(u_int32_t kbyte, u_int32_t min, u_int32_t flags)
474 {
475     int ret;
476 #if DB_VERSION_MAJOR >= 4
477     ret = db_env->txn_checkpoint(db_env, kbyte, min, flags);
478 #else 
479     ret = txn_checkpoint(db_env, kbyte, min, flags);
480 #endif
481     if (ret) {
482         LOG(log_error, logtype_cnid, "error checkpointing transaction susystem: %s", db_strerror(errno));
483         return -1;
484     } else 
485         return 0;
486 }
487
488 #else
489
490 int dbif_sync()
491 {
492     int i;
493     int ret;
494     int err = 0;
495      
496     for (i = 0; i != /* DBIF_DB_CNT*/ 1; i++) {
497         if ((ret = db_table[i].db->sync(db_table[i].db, 0))) {
498             LOG(log_error, logtype_cnid, "error syncing database %s: %s", db_table[i].name, db_strerror(ret));
499             err++;
500         }
501     }
502  
503     if (err)
504         return -1;
505     else
506         return 0;
507 }
508
509
510 int dbif_count(const int dbi, u_int32_t *count) 
511 {
512     int ret;
513     DB_BTREE_STAT *sp;
514     DB *db = db_table[dbi].db;
515
516 #if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3)
517     ret = db->stat(db, db_txn, &sp, 0);
518 #else
519     ret = db->stat(db, &sp, 0);
520 #endif
521
522     if (ret) {
523         LOG(log_error, logtype_cnid, "error getting stat infotmation on database: %s", db_strerror(errno));
524         return -1;
525     }
526
527     *count = sp->bt_ndata;
528     free(sp);
529
530     return 0;
531 }
532 #endif /* CNID_BACKEND_DBD_TXN */
533