apr_dbm_berkeleydb.c revision 251886
1172734Simp/* Licensed to the Apache Software Foundation (ASF) under one or more 2129198Scognet * contributor license agreements. See the NOTICE file distributed with 3139735Simp * this work for additional information regarding copyright ownership. 4129198Scognet * The ASF licenses this file to You under the Apache License, Version 2.0 5129198Scognet * (the "License"); you may not use this file except in compliance with 6129198Scognet * the License. You may obtain a copy of the License at 7129198Scognet * 8129198Scognet * http://www.apache.org/licenses/LICENSE-2.0 9129198Scognet * 10129198Scognet * Unless required by applicable law or agreed to in writing, software 11129198Scognet * distributed under the License is distributed on an "AS IS" BASIS, 12129198Scognet * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13129198Scognet * See the License for the specific language governing permissions and 14129198Scognet * limitations under the License. 15129198Scognet */ 16129198Scognet 17129198Scognet#include "apr_strings.h" 18129198Scognet#define APR_WANT_MEMFUNC 19129198Scognet#include "apr_want.h" 20129198Scognet 21129198Scognet#define APU_WANT_DB 22129198Scognet#include "apu_want.h" 23129198Scognet 24129198Scognet#if APR_HAVE_STDLIB_H 25129198Scognet#include <stdlib.h> /* for abort() */ 26129198Scognet#endif 27129198Scognet 28129198Scognet#include "apu_config.h" 29129198Scognet#include "apu.h" 30129198Scognet 31129198Scognet#if APU_HAVE_DB 32129198Scognet#include "apr_dbm_private.h" 33129198Scognet 34129198Scognet/* 35129198Scognet * We pick up all varieties of Berkeley DB through db.h (included through 36129198Scognet * apu_select_dbm.h). This code has been compiled/tested against DB1, 37129198Scognet * DB_185, DB2, DB3, and DB4. 38129198Scognet */ 39129198Scognet 40129198Scognet#if defined(DB_VERSION_MAJOR) && (DB_VERSION_MAJOR >= 4) 41129198Scognet/* We will treat anything greater than 4.1 as DB4. 42129198Scognet * We can treat 4.0 as DB3. 43172734Simp */ 44129198Scognet#if DB_VERSION_MAJOR > 4 || (defined(DB_VERSION_MINOR) && (DB_VERSION_MINOR >= 1)) 45129198Scognet#define DB_VER 4 46129198Scognet#elif DB_VERSION_MAJOR == 4 47129198Scognet#define DB_VER 3 48129198Scognet#endif 49129198Scognet#elif defined(DB_VERSION_MAJOR) && (DB_VERSION_MAJOR == 3) 50129198Scognet#define DB_VER 3 51129198Scognet#elif defined(DB_VERSION_MAJOR) && (DB_VERSION_MAJOR == 2) 52129198Scognet#define DB_VER 2 53129198Scognet#else 54129198Scognet#define DB_VER 1 55129198Scognet#endif 56129198Scognet 57129198Scognettypedef struct { 58129198Scognet DB *bdb; 59129198Scognet#if DB_VER != 1 60129198Scognet DBC *curs; 61129198Scognet#endif 62129198Scognet} real_file_t; 63129198Scognet 64129198Scognet 65129198Scognet#if DB_VER == 1 66129198Scognet#define TXN_ARG 67129198Scognet#else 68129198Scognet#define TXN_ARG NULL, 69172734Simp#endif 70129198Scognet 71129198Scognet#define GET_BDB(f) (((real_file_t *)(f))->bdb) 72129198Scognet 73129198Scognet#define do_fetch(bdb, k, v) ((*(bdb)->get)(bdb, TXN_ARG &(k), &(v), 0)) 74129198Scognet 75129198Scognet#if DB_VER == 1 76129198Scognet#include <sys/fcntl.h> 77129198Scognet#define APR_DBM_DBMODE_RO O_RDONLY 78129198Scognet#define APR_DBM_DBMODE_RW O_RDWR 79129198Scognet#define APR_DBM_DBMODE_RWCREATE (O_CREAT | O_RDWR) 80129198Scognet#define APR_DBM_DBMODE_RWTRUNC (O_CREAT | O_RDWR | O_TRUNC) 81129198Scognet#else 82129198Scognet#define APR_DBM_DBMODE_RO DB_RDONLY 83129198Scognet#define APR_DBM_DBMODE_RW 0 84129198Scognet#define APR_DBM_DBMODE_RWCREATE DB_CREATE 85129198Scognet#define APR_DBM_DBMODE_RWTRUNC DB_TRUNCATE 86129198Scognet#endif /* DBVER == 1 */ 87129198Scognet 88129198Scognet/* -------------------------------------------------------------------------- 89129198Scognet** 90129198Scognet** UTILITY FUNCTIONS 91129198Scognet*/ 92129198Scognet 93129198Scognet/* map a DB error to an apr_status_t */ 94172734Simpstatic apr_status_t db2s(int dberr) 95172734Simp{ 96239268Sgonzo if (dberr != 0) { 97129198Scognet /* ### need to fix this */ 98129198Scognet return APR_OS_START_USEERR + dberr; 99129198Scognet } 100129198Scognet 101129198Scognet return APR_SUCCESS; 102129198Scognet} 103129198Scognet 104129198Scognet 105129198Scognetstatic apr_status_t set_error(apr_dbm_t *dbm, apr_status_t dbm_said) 106129198Scognet{ 107129198Scognet apr_status_t rv = APR_SUCCESS; 108129198Scognet 109129198Scognet /* ### ignore whatever the DBM said (dbm_said); ask it explicitly */ 110129198Scognet 111129198Scognet if (dbm_said == APR_SUCCESS) { 112129198Scognet dbm->errcode = 0; 113129198Scognet dbm->errmsg = NULL; 114129198Scognet } 115129198Scognet else { 116129198Scognet /* ### need to fix. dberr was tossed in db2s(). */ 117129198Scognet /* ### use db_strerror() */ 118129198Scognet dbm->errcode = dbm_said; 119129198Scognet#if DB_VER == 1 || DB_VER == 2 120129198Scognet dbm->errmsg = NULL; 121129198Scognet#else 122129198Scognet dbm->errmsg = db_strerror(dbm_said - APR_OS_START_USEERR); 123129198Scognet#endif 124129198Scognet rv = dbm_said; 125129198Scognet } 126172734Simp 127129198Scognet return rv; 128129198Scognet} 129129198Scognet 130129198Scognet/* -------------------------------------------------------------------------- 131129198Scognet** 132129198Scognet** DEFINE THE VTABLE FUNCTIONS FOR BERKELEY DB 133129198Scognet** 134129198Scognet** ### we may need three sets of these: db1, db2, db3 135129198Scognet*/ 136129198Scognet 137152653Scognetstatic apr_status_t vt_db_open(apr_dbm_t **pdb, const char *pathname, 138129198Scognet apr_int32_t mode, apr_fileperms_t perm, 139172734Simp apr_pool_t *pool) 140129198Scognet{ 141129198Scognet real_file_t file; 142129198Scognet int dbmode; 143129198Scognet 144129198Scognet *pdb = NULL; 145129198Scognet 146172734Simp switch (mode) { 147172734Simp case APR_DBM_READONLY: 148172734Simp dbmode = APR_DBM_DBMODE_RO; 149244480Sgonzo break; 150256629Sbr case APR_DBM_READWRITE: 151253857Sganbold dbmode = APR_DBM_DBMODE_RW; 152239268Sgonzo break; 153239268Sgonzo case APR_DBM_RWCREATE: 154239268Sgonzo dbmode = APR_DBM_DBMODE_RWCREATE; 155239268Sgonzo break; 156239268Sgonzo case APR_DBM_RWTRUNC: 157249999Swkoszek dbmode = APR_DBM_DBMODE_RWTRUNC; 158252361Sray break; 159129198Scognet default: 160129198Scognet return APR_EINVAL; 161129198Scognet } 162183835Sraj 163239268Sgonzo { 164183835Sraj int dberr; 165239268Sgonzo 166239268Sgonzo#if DB_VER >= 3 167239268Sgonzo if ((dberr = db_create(&file.bdb, NULL, 0)) == 0) { 168239268Sgonzo if ((dberr = (*file.bdb->open)(file.bdb, 169239268Sgonzo#if DB_VER == 4 170239268Sgonzo NULL, 171239268Sgonzo#endif 172239268Sgonzo pathname, NULL, 173239268Sgonzo DB_HASH, dbmode, 174239268Sgonzo apr_posix_perms2mode(perm))) != 0) { 175239268Sgonzo /* close the DB handler */ 176239268Sgonzo (void) (*file.bdb->close)(file.bdb, 0); 177240486Sgber } 178239268Sgonzo } 179239268Sgonzo file.curs = NULL; 180239268Sgonzo#elif DB_VER == 2 181204121Skevlo dberr = db_open(pathname, DB_HASH, dbmode, apr_posix_perms2mode(perm), 182204121Skevlo NULL, NULL, &file.bdb); 183129198Scognet file.curs = NULL; 184129198Scognet#else 185129198Scognet file.bdb = dbopen(pathname, dbmode, apr_posix_perms2mode(perm), 186129198Scognet DB_HASH, NULL); 187129198Scognet if (file.bdb == NULL) 188129198Scognet return APR_EGENERAL; /* ### need a better error */ 189129198Scognet dberr = 0; 190129198Scognet#endif 191129198Scognet if (dberr != 0) 192129198Scognet return db2s(dberr); 193129198Scognet } 194172734Simp 195129198Scognet /* we have an open database... return it */ 196129198Scognet *pdb = apr_pcalloc(pool, sizeof(**pdb)); 197129198Scognet (*pdb)->pool = pool; 198129198Scognet (*pdb)->type = &apr_dbm_type_db; 199161592Scognet (*pdb)->file = apr_pmemdup(pool, &file, sizeof(file)); 200161592Scognet 201164080Scognet /* ### register a cleanup to close the DBM? */ 202186417Ssam 203129198Scognet return APR_SUCCESS; 204129198Scognet} 205129198Scognet 206186352Ssamstatic void vt_db_close(apr_dbm_t *dbm) 207186417Ssam{ 208129198Scognet (*GET_BDB(dbm->file)->close)(GET_BDB(dbm->file) 209129198Scognet#if DB_VER != 1 210129198Scognet , 0 211129198Scognet#endif 212129198Scognet ); 213129198Scognet} 214129198Scognet 215129198Scognetstatic apr_status_t vt_db_fetch(apr_dbm_t *dbm, apr_datum_t key, 216129198Scognet apr_datum_t * pvalue) 217129198Scognet{ 218129198Scognet DBT ckey = { 0 }; 219129198Scognet DBT rd = { 0 }; 220129198Scognet int dberr; 221239268Sgonzo 222239268Sgonzo ckey.data = key.dptr; 223239268Sgonzo ckey.size = key.dsize; 224239268Sgonzo 225239268Sgonzo dberr = do_fetch(GET_BDB(dbm->file), ckey, rd); 226239268Sgonzo 227239268Sgonzo /* "not found" is not an error. return zero'd value. */ 228239268Sgonzo if (dberr == 229239268Sgonzo#if DB_VER == 1 230239268Sgonzo RET_SPECIAL 231239268Sgonzo#else 232239268Sgonzo DB_NOTFOUND 233239268Sgonzo#endif 234239268Sgonzo ) { 235129198Scognet memset(&rd, 0, sizeof(rd)); 236129198Scognet dberr = 0; 237129198Scognet } 238129198Scognet 239129198Scognet pvalue->dptr = rd.data; 240129198Scognet pvalue->dsize = rd.size; 241129198Scognet 242129198Scognet /* store the error info into DBM, and return a status code. Also, note 243129198Scognet that *pvalue should have been cleared on error. */ 244129198Scognet return set_error(dbm, db2s(dberr)); 245129198Scognet} 246129198Scognet 247129198Scognetstatic apr_status_t vt_db_store(apr_dbm_t *dbm, apr_datum_t key, 248129198Scognet apr_datum_t value) 249129198Scognet{ 250129198Scognet apr_status_t rv; 251129198Scognet DBT ckey = { 0 }; 252129198Scognet DBT cvalue = { 0 }; 253129198Scognet 254129198Scognet ckey.data = key.dptr; 255129198Scognet ckey.size = key.dsize; 256129198Scognet 257129198Scognet cvalue.data = value.dptr; 258129198Scognet cvalue.size = value.dsize; 259129198Scognet 260129198Scognet rv = db2s((*GET_BDB(dbm->file)->put)(GET_BDB(dbm->file), 261129198Scognet TXN_ARG 262129198Scognet &ckey, 263129198Scognet &cvalue, 264129198Scognet 0)); 265129198Scognet 266129198Scognet /* store any error info into DBM, and return a status code. */ 267129198Scognet return set_error(dbm, rv); 268129198Scognet} 269129198Scognet 270129198Scognetstatic apr_status_t vt_db_del(apr_dbm_t *dbm, apr_datum_t key) 271129198Scognet{ 272129198Scognet apr_status_t rv; 273129198Scognet DBT ckey = { 0 }; 274129198Scognet 275129198Scognet ckey.data = key.dptr; 276129198Scognet ckey.size = key.dsize; 277129198Scognet 278129198Scognet rv = db2s((*GET_BDB(dbm->file)->del)(GET_BDB(dbm->file), 279129198Scognet TXN_ARG 280129198Scognet &ckey, 281129198Scognet 0)); 282129198Scognet 283129198Scognet /* store any error info into DBM, and return a status code. */ 284129198Scognet return set_error(dbm, rv); 285129198Scognet} 286129198Scognet 287129198Scognetstatic int vt_db_exists(apr_dbm_t *dbm, apr_datum_t key) 288244480Sgonzo{ 289244480Sgonzo DBT ckey = { 0 }; /* converted key */ 290239268Sgonzo DBT data = { 0 }; 291171630Scognet int dberr; 292250928Sgber 293129198Scognet ckey.data = key.dptr; 294129198Scognet ckey.size = key.dsize; 295129198Scognet 296244480Sgonzo dberr = do_fetch(GET_BDB(dbm->file), ckey, data); 297244480Sgonzo 298244480Sgonzo /* note: the result data is "loaned" to us; we don't need to free it */ 299244480Sgonzo 300244480Sgonzo /* DB returns DB_NOTFOUND if it doesn't exist. but we want to say 301244480Sgonzo that *any* error means it doesn't exist. */ 302244480Sgonzo return dberr == 0; 303244480Sgonzo} 304244480Sgonzo 305244480Sgonzostatic apr_status_t vt_db_firstkey(apr_dbm_t *dbm, apr_datum_t * pkey) 306244480Sgonzo{ 307244480Sgonzo real_file_t *f = dbm->file; 308244480Sgonzo DBT first = { 0 }; 309244480Sgonzo DBT data = { 0 }; 310244480Sgonzo int dberr; 311244480Sgonzo 312244480Sgonzo#if DB_VER == 1 313244480Sgonzo dberr = (*f->bdb->seq)(f->bdb, &first, &data, R_FIRST); 314244480Sgonzo#else 315244480Sgonzo if ((dberr = (*f->bdb->cursor)(f->bdb, NULL, &f->curs 316244480Sgonzo#if DB_VER >= 3 || ((DB_VERSION_MAJOR == 2) && (DB_VERSION_MINOR > 5)) 317244480Sgonzo , 0 318244480Sgonzo#endif 319129198Scognet )) == 0) { 320129198Scognet dberr = (*f->curs->c_get)(f->curs, &first, &data, DB_FIRST); 321129198Scognet if (dberr == DB_NOTFOUND) { 322171630Scognet memset(&first, 0, sizeof(first)); 323129198Scognet (*f->curs->c_close)(f->curs); 324129198Scognet f->curs = NULL; 325129198Scognet dberr = 0; 326129198Scognet } 327129198Scognet } 328171630Scognet#endif 329171630Scognet 330171630Scognet pkey->dptr = first.data; 331239268Sgonzo pkey->dsize = first.size; 332239268Sgonzo 333239268Sgonzo /* store any error info into DBM, and return a status code. */ 334239268Sgonzo return set_error(dbm, db2s(dberr)); 335239268Sgonzo} 336239268Sgonzo 337239268Sgonzostatic apr_status_t vt_db_nextkey(apr_dbm_t *dbm, apr_datum_t * pkey) 338239268Sgonzo{ 339239268Sgonzo real_file_t *f = dbm->file; 340239268Sgonzo DBT ckey = { 0 }; 341239268Sgonzo DBT data = { 0 }; 342183835Sraj int dberr; 343129198Scognet 344129198Scognet ckey.data = pkey->dptr; 345129198Scognet ckey.size = pkey->dsize; 346129198Scognet 347129198Scognet#if DB_VER == 1 348239268Sgonzo dberr = (*f->bdb->seq)(f->bdb, &ckey, &data, R_NEXT); 349129198Scognet if (dberr == RET_SPECIAL) { 350129198Scognet dberr = 0; 351129198Scognet ckey.data = NULL; 352129198Scognet ckey.size = 0; 353129198Scognet } 354129198Scognet#else 355129198Scognet if (f->curs == NULL) 356129198Scognet return APR_EINVAL; 357129198Scognet 358129198Scognet dberr = (*f->curs->c_get)(f->curs, &ckey, &data, DB_NEXT); 359129198Scognet if (dberr == DB_NOTFOUND) { 360129198Scognet (*f->curs->c_close)(f->curs); 361239268Sgonzo f->curs = NULL; 362239268Sgonzo dberr = 0; 363258780Seadler ckey.data = NULL; 364239268Sgonzo ckey.size = 0; 365239268Sgonzo } 366239268Sgonzo#endif 367239268Sgonzo 368239268Sgonzo pkey->dptr = ckey.data; 369239268Sgonzo pkey->dsize = ckey.size; 370239268Sgonzo 371239268Sgonzo /* store any error info into DBM, and return a status code. */ 372239268Sgonzo /* ### or use db2s(dberr) instead of APR_SUCCESS? */ 373239268Sgonzo return set_error(dbm, APR_SUCCESS); 374239268Sgonzo} 375239268Sgonzo 376239268Sgonzostatic void vt_db_freedatum(apr_dbm_t *dbm, apr_datum_t data) 377239268Sgonzo{ 378239268Sgonzo /* nothing to do */ 379239268Sgonzo} 380239268Sgonzo 381239268Sgonzostatic void vt_db_usednames(apr_pool_t *pool, const char *pathname, 382129198Scognet const char **used1, const char **used2) 383129198Scognet{ 384129198Scognet *used1 = apr_pstrdup(pool, pathname); 385129198Scognet *used2 = NULL; 386129198Scognet} 387129198Scognet 388129198Scognet 389129198ScognetAPU_MODULE_DECLARE_DATA const apr_dbm_type_t apr_dbm_type_db = { 390129198Scognet "db", 391129198Scognet 392129198Scognet vt_db_open, 393129198Scognet vt_db_close, 394129198Scognet vt_db_fetch, 395129198Scognet vt_db_store, 396129198Scognet vt_db_del, 397129198Scognet vt_db_exists, 398250928Sgber vt_db_firstkey, 399129198Scognet vt_db_nextkey, 400129198Scognet vt_db_freedatum, 401129198Scognet vt_db_usednames 402129198Scognet}; 403129198Scognet 404129198Scognet#endif /* APU_HAVE_DB */ 405129198Scognet