1251876Speter/* Licensed to the Apache Software Foundation (ASF) under one or more
2251876Speter * contributor license agreements.  See the NOTICE file distributed with
3251876Speter * this work for additional information regarding copyright ownership.
4251876Speter * The ASF licenses this file to You under the Apache License, Version 2.0
5251876Speter * (the "License"); you may not use this file except in compliance with
6251876Speter * the License.  You may obtain a copy of the License at
7251876Speter *
8251876Speter *     http://www.apache.org/licenses/LICENSE-2.0
9251876Speter *
10251876Speter * Unless required by applicable law or agreed to in writing, software
11251876Speter * distributed under the License is distributed on an "AS IS" BASIS,
12251876Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13251876Speter * See the License for the specific language governing permissions and
14251876Speter * limitations under the License.
15251876Speter */
16251876Speter
17251876Speter/*
18251876Speter * sdbm - ndbm work-alike hashed database library
19251876Speter * based on Per-Aake Larson's Dynamic Hashing algorithms. BIT 18 (1978).
20251876Speter * author: oz@nexus.yorku.ca
21251876Speter * ex-public domain, ported to APR for Apache 2
22251876Speter * core routines
23251876Speter */
24251876Speter
25251876Speter#include "apr.h"
26251876Speter#include "apr_file_io.h"
27251876Speter#include "apr_strings.h"
28251876Speter#include "apr_errno.h"
29251876Speter#include "apr_sdbm.h"
30251876Speter
31251876Speter#include "sdbm_tune.h"
32251876Speter#include "sdbm_pair.h"
33251876Speter#include "sdbm_private.h"
34251876Speter
35251876Speter#include <string.h>     /* for memset() */
36251876Speter#include <stdlib.h>     /* for malloc() and free() */
37251876Speter
38251876Speter/*
39251876Speter * forward
40251876Speter */
41251876Speterstatic int getdbit (apr_sdbm_t *, long);
42251876Speterstatic apr_status_t setdbit(apr_sdbm_t *, long);
43251876Speterstatic apr_status_t getpage(apr_sdbm_t *db, long);
44251876Speterstatic apr_status_t getnext(apr_sdbm_datum_t *key, apr_sdbm_t *db);
45251876Speterstatic apr_status_t makroom(apr_sdbm_t *, long, int);
46251876Speter
47251876Speter/*
48251876Speter * useful macros
49251876Speter */
50251876Speter#define bad(x)		((x).dptr == NULL || (x).dsize <= 0)
51251876Speter#define exhash(item)	sdbm_hash((item).dptr, (item).dsize)
52251876Speter
53251876Speter#define OFF_PAG(off)	(apr_off_t) (off) * PBLKSIZ
54251876Speter#define OFF_DIR(off)	(apr_off_t) (off) * DBLKSIZ
55251876Speter
56251876Speterstatic const long masks[] = {
57251876Speter        000000000000, 000000000001, 000000000003, 000000000007,
58251876Speter        000000000017, 000000000037, 000000000077, 000000000177,
59251876Speter        000000000377, 000000000777, 000000001777, 000000003777,
60251876Speter        000000007777, 000000017777, 000000037777, 000000077777,
61251876Speter        000000177777, 000000377777, 000000777777, 000001777777,
62251876Speter        000003777777, 000007777777, 000017777777, 000037777777,
63251876Speter        000077777777, 000177777777, 000377777777, 000777777777,
64251876Speter        001777777777, 003777777777, 007777777777, 017777777777
65251876Speter};
66251876Speter
67251876Speterconst apr_sdbm_datum_t sdbm_nullitem = { NULL, 0 };
68251876Speter
69251876Speterstatic apr_status_t database_cleanup(void *data)
70251876Speter{
71251876Speter    apr_sdbm_t *db = data;
72251876Speter
73251876Speter    /*
74251876Speter     * Can't rely on apr_sdbm_unlock, since it will merely
75251876Speter     * decrement the refcnt if several locks are held.
76251876Speter     */
77251876Speter    if (db->flags & (SDBM_SHARED_LOCK | SDBM_EXCLUSIVE_LOCK))
78251876Speter        (void) apr_file_unlock(db->dirf);
79251876Speter    (void) apr_file_close(db->dirf);
80251876Speter    (void) apr_file_close(db->pagf);
81251876Speter    free(db);
82251876Speter
83251876Speter    return APR_SUCCESS;
84251876Speter}
85251876Speter
86251876Speterstatic apr_status_t prep(apr_sdbm_t **pdb, const char *dirname, const char *pagname,
87251876Speter                         apr_int32_t flags, apr_fileperms_t perms, apr_pool_t *p)
88251876Speter{
89251876Speter    apr_sdbm_t *db;
90251876Speter    apr_status_t status;
91251876Speter
92251876Speter    *pdb = NULL;
93251876Speter
94251876Speter    db = malloc(sizeof(*db));
95251876Speter    memset(db, 0, sizeof(*db));
96251876Speter
97251876Speter    db->pool = p;
98251876Speter
99251876Speter    /*
100251876Speter     * adjust user flags so that WRONLY becomes RDWR,
101251876Speter     * as required by this package. Also set our internal
102251876Speter     * flag for RDONLY if needed.
103251876Speter     */
104251876Speter    if (!(flags & APR_FOPEN_WRITE)) {
105251876Speter        db->flags |= SDBM_RDONLY;
106251876Speter    }
107251876Speter
108251876Speter    /*
109251876Speter     * adjust the file open flags so that we handle locking
110251876Speter     * on our own (don't rely on any locking behavior within
111251876Speter     * an apr_file_t, in case it's ever introduced, and set
112251876Speter     * our own flag.
113251876Speter     */
114251876Speter    if (flags & APR_FOPEN_SHARELOCK) {
115251876Speter        db->flags |= SDBM_SHARED;
116251876Speter        flags &= ~APR_FOPEN_SHARELOCK;
117251876Speter    }
118251876Speter
119251876Speter    flags |= APR_FOPEN_BINARY | APR_FOPEN_READ;
120251876Speter
121251876Speter    /*
122251876Speter     * open the files in sequence, and stat the dirfile.
123251876Speter     * If we fail anywhere, undo everything, return NULL.
124251876Speter     */
125251876Speter
126251876Speter    if ((status = apr_file_open(&db->dirf, dirname, flags, perms, p))
127251876Speter                != APR_SUCCESS)
128251876Speter        goto error;
129251876Speter
130251876Speter    if ((status = apr_file_open(&db->pagf, pagname, flags, perms, p))
131251876Speter                != APR_SUCCESS)
132251876Speter        goto error;
133251876Speter
134251876Speter    if ((status = apr_sdbm_lock(db, (db->flags & SDBM_RDONLY)
135251876Speter                                        ? APR_FLOCK_SHARED
136251876Speter                                        : APR_FLOCK_EXCLUSIVE))
137251876Speter                != APR_SUCCESS)
138251876Speter        goto error;
139251876Speter
140251876Speter    /* apr_pcalloc zeroed the buffers
141251876Speter     * apr_sdbm_lock stated the dirf->size and invalidated the cache
142251876Speter     */
143251876Speter
144251876Speter    /*
145251876Speter     * if we are opened in SHARED mode, unlock ourself
146251876Speter     */
147251876Speter    if (db->flags & SDBM_SHARED)
148251876Speter        if ((status = apr_sdbm_unlock(db)) != APR_SUCCESS)
149251876Speter            goto error;
150251876Speter
151251876Speter    /* make sure that we close the database at some point */
152251876Speter    apr_pool_cleanup_register(p, db, database_cleanup, apr_pool_cleanup_null);
153251876Speter
154251876Speter    /* Done! */
155251876Speter    *pdb = db;
156251876Speter    return APR_SUCCESS;
157251876Speter
158251876Spetererror:
159251876Speter    if (db->dirf && db->pagf)
160251876Speter        (void) apr_sdbm_unlock(db);
161251876Speter    if (db->dirf != NULL)
162251876Speter        (void) apr_file_close(db->dirf);
163251876Speter    if (db->pagf != NULL) {
164251876Speter        (void) apr_file_close(db->pagf);
165251876Speter    }
166251876Speter    free(db);
167251876Speter    return status;
168251876Speter}
169251876Speter
170251876SpeterAPU_DECLARE(apr_status_t) apr_sdbm_open(apr_sdbm_t **db, const char *file,
171251876Speter                                        apr_int32_t flags,
172251876Speter                                        apr_fileperms_t perms, apr_pool_t *p)
173251876Speter{
174251876Speter    char *dirname = apr_pstrcat(p, file, APR_SDBM_DIRFEXT, NULL);
175251876Speter    char *pagname = apr_pstrcat(p, file, APR_SDBM_PAGFEXT, NULL);
176251876Speter
177251876Speter    return prep(db, dirname, pagname, flags, perms, p);
178251876Speter}
179251876Speter
180251876SpeterAPU_DECLARE(apr_status_t) apr_sdbm_close(apr_sdbm_t *db)
181251876Speter{
182251876Speter    return apr_pool_cleanup_run(db->pool, db, database_cleanup);
183251876Speter}
184251876Speter
185251876SpeterAPU_DECLARE(apr_status_t) apr_sdbm_fetch(apr_sdbm_t *db, apr_sdbm_datum_t *val,
186251876Speter                                         apr_sdbm_datum_t key)
187251876Speter{
188251876Speter    apr_status_t status;
189251876Speter
190251876Speter    if (db == NULL || bad(key))
191251876Speter        return APR_EINVAL;
192251876Speter
193251876Speter    if ((status = apr_sdbm_lock(db, APR_FLOCK_SHARED)) != APR_SUCCESS)
194251876Speter        return status;
195251876Speter
196251876Speter    if ((status = getpage(db, exhash(key))) == APR_SUCCESS) {
197251876Speter        *val = getpair(db->pagbuf, key);
198251876Speter        /* ### do we want a not-found result? */
199251876Speter    }
200251876Speter
201251876Speter    (void) apr_sdbm_unlock(db);
202251876Speter
203251876Speter    return status;
204251876Speter}
205251876Speter
206251876Speterstatic apr_status_t write_page(apr_sdbm_t *db, const char *buf, long pagno)
207251876Speter{
208251876Speter    apr_status_t status;
209251876Speter    apr_off_t off = OFF_PAG(pagno);
210251876Speter
211251876Speter    if ((status = apr_file_seek(db->pagf, APR_SET, &off)) == APR_SUCCESS)
212251876Speter        status = apr_file_write_full(db->pagf, buf, PBLKSIZ, NULL);
213251876Speter
214251876Speter    return status;
215251876Speter}
216251876Speter
217251876SpeterAPU_DECLARE(apr_status_t) apr_sdbm_delete(apr_sdbm_t *db,
218251876Speter                                          const apr_sdbm_datum_t key)
219251876Speter{
220251876Speter    apr_status_t status;
221251876Speter
222251876Speter    if (db == NULL || bad(key))
223251876Speter        return APR_EINVAL;
224251876Speter    if (apr_sdbm_rdonly(db))
225251876Speter        return APR_EINVAL;
226251876Speter
227251876Speter    if ((status = apr_sdbm_lock(db, APR_FLOCK_EXCLUSIVE)) != APR_SUCCESS)
228251876Speter        return status;
229251876Speter
230251876Speter    if ((status = getpage(db, exhash(key))) == APR_SUCCESS) {
231251876Speter        if (!delpair(db->pagbuf, key))
232251876Speter            /* ### should we define some APRUTIL codes? */
233251876Speter            status = APR_EGENERAL;
234251876Speter        else
235251876Speter            status = write_page(db, db->pagbuf, db->pagbno);
236251876Speter    }
237251876Speter
238251876Speter    (void) apr_sdbm_unlock(db);
239251876Speter
240251876Speter    return status;
241251876Speter}
242251876Speter
243251876SpeterAPU_DECLARE(apr_status_t) apr_sdbm_store(apr_sdbm_t *db, apr_sdbm_datum_t key,
244251876Speter                                         apr_sdbm_datum_t val, int flags)
245251876Speter{
246251876Speter    int need;
247251876Speter    register long hash;
248251876Speter    apr_status_t status;
249251876Speter
250251876Speter    if (db == NULL || bad(key))
251251876Speter        return APR_EINVAL;
252251876Speter    if (apr_sdbm_rdonly(db))
253251876Speter        return APR_EINVAL;
254251876Speter    need = key.dsize + val.dsize;
255251876Speter    /*
256251876Speter     * is the pair too big (or too small) for this database ??
257251876Speter     */
258251876Speter    if (need < 0 || need > PAIRMAX)
259251876Speter        return APR_EINVAL;
260251876Speter
261251876Speter    if ((status = apr_sdbm_lock(db, APR_FLOCK_EXCLUSIVE)) != APR_SUCCESS)
262251876Speter        return status;
263251876Speter
264251876Speter    if ((status = getpage(db, (hash = exhash(key)))) == APR_SUCCESS) {
265251876Speter
266251876Speter        /*
267251876Speter         * if we need to replace, delete the key/data pair
268251876Speter         * first. If it is not there, ignore.
269251876Speter         */
270251876Speter        if (flags == APR_SDBM_REPLACE)
271251876Speter            (void) delpair(db->pagbuf, key);
272251876Speter        else if (!(flags & APR_SDBM_INSERTDUP) && duppair(db->pagbuf, key)) {
273251876Speter            status = APR_EEXIST;
274251876Speter            goto error;
275251876Speter        }
276251876Speter        /*
277251876Speter         * if we do not have enough room, we have to split.
278251876Speter         */
279251876Speter        if (!fitpair(db->pagbuf, need))
280251876Speter            if ((status = makroom(db, hash, need)) != APR_SUCCESS)
281251876Speter                goto error;
282251876Speter        /*
283251876Speter         * we have enough room or split is successful. insert the key,
284251876Speter         * and update the page file.
285251876Speter         */
286251876Speter        (void) putpair(db->pagbuf, key, val);
287251876Speter
288251876Speter        status = write_page(db, db->pagbuf, db->pagbno);
289251876Speter    }
290251876Speter
291251876Spetererror:
292251876Speter    (void) apr_sdbm_unlock(db);
293251876Speter
294251876Speter    return status;
295251876Speter}
296251876Speter
297251876Speter/*
298251876Speter * makroom - make room by splitting the overfull page
299251876Speter * this routine will attempt to make room for SPLTMAX times before
300251876Speter * giving up.
301251876Speter */
302251876Speterstatic apr_status_t makroom(apr_sdbm_t *db, long hash, int need)
303251876Speter{
304251876Speter    long newp;
305251876Speter    char twin[PBLKSIZ];
306251876Speter    char *pag = db->pagbuf;
307251876Speter    char *new = twin;
308251876Speter    register int smax = SPLTMAX;
309251876Speter    apr_status_t status;
310251876Speter
311251876Speter    do {
312251876Speter        /*
313251876Speter         * split the current page
314251876Speter         */
315251876Speter        (void) splpage(pag, new, db->hmask + 1);
316251876Speter        /*
317251876Speter         * address of the new page
318251876Speter         */
319251876Speter        newp = (hash & db->hmask) | (db->hmask + 1);
320251876Speter
321251876Speter        /*
322251876Speter         * write delay, read avoidence/cache shuffle:
323251876Speter         * select the page for incoming pair: if key is to go to the new page,
324251876Speter         * write out the previous one, and copy the new one over, thus making
325251876Speter         * it the current page. If not, simply write the new page, and we are
326251876Speter         * still looking at the page of interest. current page is not updated
327251876Speter         * here, as sdbm_store will do so, after it inserts the incoming pair.
328251876Speter         */
329251876Speter        if (hash & (db->hmask + 1)) {
330251876Speter            if ((status = write_page(db, db->pagbuf, db->pagbno))
331251876Speter                        != APR_SUCCESS)
332251876Speter                return status;
333251876Speter
334251876Speter            db->pagbno = newp;
335251876Speter            (void) memcpy(pag, new, PBLKSIZ);
336251876Speter        }
337251876Speter        else {
338251876Speter            if ((status = write_page(db, new, newp)) != APR_SUCCESS)
339251876Speter                return status;
340251876Speter        }
341251876Speter
342251876Speter        if ((status = setdbit(db, db->curbit)) != APR_SUCCESS)
343251876Speter            return status;
344251876Speter        /*
345251876Speter         * see if we have enough room now
346251876Speter         */
347251876Speter        if (fitpair(pag, need))
348251876Speter            return APR_SUCCESS;
349251876Speter        /*
350251876Speter         * try again... update curbit and hmask as getpage would have
351251876Speter         * done. because of our update of the current page, we do not
352251876Speter         * need to read in anything. BUT we have to write the current
353251876Speter         * [deferred] page out, as the window of failure is too great.
354251876Speter         */
355251876Speter        db->curbit = 2 * db->curbit
356251876Speter                   + ((hash & (db->hmask + 1)) ? 2 : 1);
357251876Speter        db->hmask |= db->hmask + 1;
358251876Speter
359251876Speter        if ((status = write_page(db, db->pagbuf, db->pagbno))
360251876Speter                    != APR_SUCCESS)
361251876Speter            return status;
362251876Speter
363251876Speter    } while (--smax);
364251876Speter
365251876Speter    /*
366251876Speter     * if we are here, this is real bad news. After SPLTMAX splits,
367251876Speter     * we still cannot fit the key. say goodnight.
368251876Speter     */
369251876Speter#if 0
370251876Speter    (void) write(2, "sdbm: cannot insert after SPLTMAX attempts.\n", 44);
371251876Speter#endif
372251876Speter    /* ### ENOSPC not really appropriate but better than nothing */
373251876Speter    return APR_ENOSPC;
374251876Speter
375251876Speter}
376251876Speter
377251876Speter/* Reads 'len' bytes from file 'f' at offset 'off' into buf.
378251876Speter * 'off' is given relative to the start of the file.
379251876Speter * If EOF is returned while reading, this is taken as success.
380251876Speter */
381251876Speterstatic apr_status_t read_from(apr_file_t *f, void *buf,
382251876Speter             apr_off_t off, apr_size_t len)
383251876Speter{
384251876Speter    apr_status_t status;
385251876Speter
386251876Speter    if ((status = apr_file_seek(f, APR_SET, &off)) != APR_SUCCESS ||
387251876Speter        ((status = apr_file_read_full(f, buf, len, NULL)) != APR_SUCCESS)) {
388251876Speter        /* if EOF is reached, pretend we read all zero's */
389251876Speter        if (status == APR_EOF) {
390251876Speter            memset(buf, 0, len);
391251876Speter            status = APR_SUCCESS;
392251876Speter        }
393251876Speter    }
394251876Speter
395251876Speter    return status;
396251876Speter}
397251876Speter
398251876Speter/*
399251876Speter * the following two routines will break if
400251876Speter * deletions aren't taken into account. (ndbm bug)
401251876Speter */
402251876SpeterAPU_DECLARE(apr_status_t) apr_sdbm_firstkey(apr_sdbm_t *db,
403251876Speter                                            apr_sdbm_datum_t *key)
404251876Speter{
405251876Speter    apr_status_t status;
406251876Speter
407251876Speter    if ((status = apr_sdbm_lock(db, APR_FLOCK_SHARED)) != APR_SUCCESS)
408251876Speter        return status;
409251876Speter
410251876Speter    /*
411251876Speter     * start at page 0
412251876Speter     */
413251876Speter    if ((status = read_from(db->pagf, db->pagbuf, OFF_PAG(0), PBLKSIZ))
414251876Speter                == APR_SUCCESS) {
415251876Speter        db->pagbno = 0;
416251876Speter        db->blkptr = 0;
417251876Speter        db->keyptr = 0;
418251876Speter        status = getnext(key, db);
419251876Speter    }
420251876Speter
421251876Speter    (void) apr_sdbm_unlock(db);
422251876Speter
423251876Speter    return status;
424251876Speter}
425251876Speter
426251876SpeterAPU_DECLARE(apr_status_t) apr_sdbm_nextkey(apr_sdbm_t *db,
427251876Speter                                           apr_sdbm_datum_t *key)
428251876Speter{
429251876Speter    apr_status_t status;
430251876Speter
431251876Speter    if ((status = apr_sdbm_lock(db, APR_FLOCK_SHARED)) != APR_SUCCESS)
432251876Speter        return status;
433251876Speter
434251876Speter    status = getnext(key, db);
435251876Speter
436251876Speter    (void) apr_sdbm_unlock(db);
437251876Speter
438251876Speter    return status;
439251876Speter}
440251876Speter
441251876Speter/*
442251876Speter * all important binary tree traversal
443251876Speter */
444251876Speterstatic apr_status_t getpage(apr_sdbm_t *db, long hash)
445251876Speter{
446251876Speter    register int hbit;
447251876Speter    register long dbit;
448251876Speter    register long pagb;
449251876Speter    apr_status_t status;
450251876Speter
451251876Speter    dbit = 0;
452251876Speter    hbit = 0;
453251876Speter    while (dbit < db->maxbno && getdbit(db, dbit))
454251876Speter    dbit = 2 * dbit + ((hash & (1 << hbit++)) ? 2 : 1);
455251876Speter
456251876Speter    debug(("dbit: %d...", dbit));
457251876Speter
458251876Speter    db->curbit = dbit;
459251876Speter    db->hmask = masks[hbit];
460251876Speter
461251876Speter    pagb = hash & db->hmask;
462251876Speter    /*
463251876Speter     * see if the block we need is already in memory.
464251876Speter     * note: this lookaside cache has about 10% hit rate.
465251876Speter     */
466251876Speter    if (pagb != db->pagbno) {
467251876Speter        /*
468251876Speter         * note: here, we assume a "hole" is read as 0s.
469251876Speter         * if not, must zero pagbuf first.
470251876Speter         * ### joe: this assumption was surely never correct? but
471251876Speter         * ### we make it so in read_from anyway.
472251876Speter         */
473251876Speter        if ((status = read_from(db->pagf, db->pagbuf, OFF_PAG(pagb), PBLKSIZ))
474251876Speter                    != APR_SUCCESS)
475251876Speter            return status;
476251876Speter
477251876Speter        if (!chkpage(db->pagbuf))
478251876Speter            return APR_ENOSPC; /* ### better error? */
479251876Speter        db->pagbno = pagb;
480251876Speter
481251876Speter        debug(("pag read: %d\n", pagb));
482251876Speter    }
483251876Speter    return APR_SUCCESS;
484251876Speter}
485251876Speter
486251876Speterstatic int getdbit(apr_sdbm_t *db, long dbit)
487251876Speter{
488251876Speter    register long c;
489251876Speter    register long dirb;
490251876Speter
491251876Speter    c = dbit / BYTESIZ;
492251876Speter    dirb = c / DBLKSIZ;
493251876Speter
494251876Speter    if (dirb != db->dirbno) {
495251876Speter        if (read_from(db->dirf, db->dirbuf, OFF_DIR(dirb), DBLKSIZ)
496251876Speter                    != APR_SUCCESS)
497251876Speter            return 0;
498251876Speter
499251876Speter        db->dirbno = dirb;
500251876Speter
501251876Speter        debug(("dir read: %d\n", dirb));
502251876Speter    }
503251876Speter
504251876Speter    return db->dirbuf[c % DBLKSIZ] & (1 << dbit % BYTESIZ);
505251876Speter}
506251876Speter
507251876Speterstatic apr_status_t setdbit(apr_sdbm_t *db, long dbit)
508251876Speter{
509251876Speter    register long c;
510251876Speter    register long dirb;
511251876Speter    apr_status_t status;
512251876Speter    apr_off_t off;
513251876Speter
514251876Speter    c = dbit / BYTESIZ;
515251876Speter    dirb = c / DBLKSIZ;
516251876Speter
517251876Speter    if (dirb != db->dirbno) {
518251876Speter        if ((status = read_from(db->dirf, db->dirbuf, OFF_DIR(dirb), DBLKSIZ))
519251876Speter                    != APR_SUCCESS)
520251876Speter            return status;
521251876Speter
522251876Speter        db->dirbno = dirb;
523251876Speter
524251876Speter        debug(("dir read: %d\n", dirb));
525251876Speter    }
526251876Speter
527251876Speter    db->dirbuf[c % DBLKSIZ] |= (1 << dbit % BYTESIZ);
528251876Speter
529251876Speter    if (dbit >= db->maxbno)
530251876Speter        db->maxbno += DBLKSIZ * BYTESIZ;
531251876Speter
532251876Speter    off = OFF_DIR(dirb);
533251876Speter    if ((status = apr_file_seek(db->dirf, APR_SET, &off)) == APR_SUCCESS)
534251876Speter        status = apr_file_write_full(db->dirf, db->dirbuf, DBLKSIZ, NULL);
535251876Speter
536251876Speter    return status;
537251876Speter}
538251876Speter
539251876Speter/*
540251876Speter* getnext - get the next key in the page, and if done with
541251876Speter* the page, try the next page in sequence
542251876Speter*/
543251876Speterstatic apr_status_t getnext(apr_sdbm_datum_t *key, apr_sdbm_t *db)
544251876Speter{
545251876Speter    apr_status_t status;
546251876Speter    for (;;) {
547251876Speter        db->keyptr++;
548251876Speter        *key = getnkey(db->pagbuf, db->keyptr);
549251876Speter        if (key->dptr != NULL)
550251876Speter            return APR_SUCCESS;
551251876Speter        /*
552251876Speter         * we either run out, or there is nothing on this page..
553251876Speter         * try the next one... If we lost our position on the
554251876Speter         * file, we will have to seek.
555251876Speter         */
556251876Speter        db->keyptr = 0;
557251876Speter        if (db->pagbno != db->blkptr++) {
558251876Speter            apr_off_t off = OFF_PAG(db->blkptr);
559251876Speter            if ((status = apr_file_seek(db->pagf, APR_SET, &off))
560251876Speter                        != APR_SUCCESS)
561251876Speter                return status;
562251876Speter        }
563251876Speter
564251876Speter        db->pagbno = db->blkptr;
565251876Speter        /* ### EOF acceptable here too? */
566251876Speter        if ((status = apr_file_read_full(db->pagf, db->pagbuf, PBLKSIZ, NULL))
567251876Speter                    != APR_SUCCESS)
568251876Speter            return status;
569251876Speter        if (!chkpage(db->pagbuf))
570251876Speter            return APR_EGENERAL;     /* ### need better error */
571251876Speter    }
572251876Speter
573251876Speter    /* NOTREACHED */
574251876Speter}
575251876Speter
576251876Speter
577251876SpeterAPU_DECLARE(int) apr_sdbm_rdonly(apr_sdbm_t *db)
578251876Speter{
579251876Speter    /* ### Should we return true if the first lock is a share lock,
580251876Speter     *     to reflect that apr_sdbm_store and apr_sdbm_delete will fail?
581251876Speter     */
582251876Speter    return (db->flags & SDBM_RDONLY) != 0;
583251876Speter}
584251876Speter
585