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