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);
43362181Sdimstatic apr_status_t getpage(apr_sdbm_t *db, long, int, int);
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));
96362181Sdim    db->pagbno = -1L;
97251876Speter
98251876Speter    db->pool = p;
99251876Speter
100251876Speter    /*
101251876Speter     * adjust user flags so that WRONLY becomes RDWR,
102251876Speter     * as required by this package. Also set our internal
103251876Speter     * flag for RDONLY if needed.
104251876Speter     */
105251876Speter    if (!(flags & APR_FOPEN_WRITE)) {
106251876Speter        db->flags |= SDBM_RDONLY;
107251876Speter    }
108251876Speter
109251876Speter    /*
110251876Speter     * adjust the file open flags so that we handle locking
111251876Speter     * on our own (don't rely on any locking behavior within
112251876Speter     * an apr_file_t, in case it's ever introduced, and set
113251876Speter     * our own flag.
114251876Speter     */
115251876Speter    if (flags & APR_FOPEN_SHARELOCK) {
116251876Speter        db->flags |= SDBM_SHARED;
117251876Speter        flags &= ~APR_FOPEN_SHARELOCK;
118251876Speter    }
119251876Speter
120251876Speter    flags |= APR_FOPEN_BINARY | APR_FOPEN_READ;
121251876Speter
122251876Speter    /*
123251876Speter     * open the files in sequence, and stat the dirfile.
124251876Speter     * If we fail anywhere, undo everything, return NULL.
125251876Speter     */
126251876Speter
127251876Speter    if ((status = apr_file_open(&db->dirf, dirname, flags, perms, p))
128251876Speter                != APR_SUCCESS)
129251876Speter        goto error;
130251876Speter
131251876Speter    if ((status = apr_file_open(&db->pagf, pagname, flags, perms, p))
132251876Speter                != APR_SUCCESS)
133251876Speter        goto error;
134251876Speter
135251876Speter    if ((status = apr_sdbm_lock(db, (db->flags & SDBM_RDONLY)
136251876Speter                                        ? APR_FLOCK_SHARED
137251876Speter                                        : APR_FLOCK_EXCLUSIVE))
138251876Speter                != APR_SUCCESS)
139251876Speter        goto error;
140251876Speter
141251876Speter    /* apr_pcalloc zeroed the buffers
142251876Speter     * apr_sdbm_lock stated the dirf->size and invalidated the cache
143251876Speter     */
144251876Speter
145251876Speter    /*
146251876Speter     * if we are opened in SHARED mode, unlock ourself
147251876Speter     */
148251876Speter    if (db->flags & SDBM_SHARED)
149251876Speter        if ((status = apr_sdbm_unlock(db)) != APR_SUCCESS)
150251876Speter            goto error;
151251876Speter
152251876Speter    /* make sure that we close the database at some point */
153251876Speter    apr_pool_cleanup_register(p, db, database_cleanup, apr_pool_cleanup_null);
154251876Speter
155251876Speter    /* Done! */
156251876Speter    *pdb = db;
157251876Speter    return APR_SUCCESS;
158251876Speter
159251876Spetererror:
160251876Speter    if (db->dirf && db->pagf)
161251876Speter        (void) apr_sdbm_unlock(db);
162251876Speter    if (db->dirf != NULL)
163251876Speter        (void) apr_file_close(db->dirf);
164251876Speter    if (db->pagf != NULL) {
165251876Speter        (void) apr_file_close(db->pagf);
166251876Speter    }
167251876Speter    free(db);
168251876Speter    return status;
169251876Speter}
170251876Speter
171251876SpeterAPU_DECLARE(apr_status_t) apr_sdbm_open(apr_sdbm_t **db, const char *file,
172251876Speter                                        apr_int32_t flags,
173251876Speter                                        apr_fileperms_t perms, apr_pool_t *p)
174251876Speter{
175251876Speter    char *dirname = apr_pstrcat(p, file, APR_SDBM_DIRFEXT, NULL);
176251876Speter    char *pagname = apr_pstrcat(p, file, APR_SDBM_PAGFEXT, NULL);
177251876Speter
178251876Speter    return prep(db, dirname, pagname, flags, perms, p);
179251876Speter}
180251876Speter
181251876SpeterAPU_DECLARE(apr_status_t) apr_sdbm_close(apr_sdbm_t *db)
182251876Speter{
183251876Speter    return apr_pool_cleanup_run(db->pool, db, database_cleanup);
184251876Speter}
185251876Speter
186251876SpeterAPU_DECLARE(apr_status_t) apr_sdbm_fetch(apr_sdbm_t *db, apr_sdbm_datum_t *val,
187251876Speter                                         apr_sdbm_datum_t key)
188251876Speter{
189251876Speter    apr_status_t status;
190251876Speter
191251876Speter    if (db == NULL || bad(key))
192251876Speter        return APR_EINVAL;
193251876Speter
194251876Speter    if ((status = apr_sdbm_lock(db, APR_FLOCK_SHARED)) != APR_SUCCESS)
195251876Speter        return status;
196251876Speter
197362181Sdim    if ((status = getpage(db, exhash(key), 0, 1)) == APR_SUCCESS) {
198251876Speter        *val = getpair(db->pagbuf, key);
199251876Speter        /* ### do we want a not-found result? */
200251876Speter    }
201251876Speter
202251876Speter    (void) apr_sdbm_unlock(db);
203251876Speter
204251876Speter    return status;
205251876Speter}
206251876Speter
207251876Speterstatic apr_status_t write_page(apr_sdbm_t *db, const char *buf, long pagno)
208251876Speter{
209251876Speter    apr_status_t status;
210251876Speter    apr_off_t off = OFF_PAG(pagno);
211251876Speter
212251876Speter    if ((status = apr_file_seek(db->pagf, APR_SET, &off)) == APR_SUCCESS)
213251876Speter        status = apr_file_write_full(db->pagf, buf, PBLKSIZ, NULL);
214251876Speter
215251876Speter    return status;
216251876Speter}
217251876Speter
218251876SpeterAPU_DECLARE(apr_status_t) apr_sdbm_delete(apr_sdbm_t *db,
219251876Speter                                          const apr_sdbm_datum_t key)
220251876Speter{
221251876Speter    apr_status_t status;
222251876Speter
223251876Speter    if (db == NULL || bad(key))
224251876Speter        return APR_EINVAL;
225251876Speter    if (apr_sdbm_rdonly(db))
226251876Speter        return APR_EINVAL;
227251876Speter
228251876Speter    if ((status = apr_sdbm_lock(db, APR_FLOCK_EXCLUSIVE)) != APR_SUCCESS)
229251876Speter        return status;
230251876Speter
231362181Sdim    if ((status = getpage(db, exhash(key), 0, 1)) == APR_SUCCESS) {
232251876Speter        if (!delpair(db->pagbuf, key))
233251876Speter            /* ### should we define some APRUTIL codes? */
234251876Speter            status = APR_EGENERAL;
235251876Speter        else
236251876Speter            status = write_page(db, db->pagbuf, db->pagbno);
237251876Speter    }
238251876Speter
239251876Speter    (void) apr_sdbm_unlock(db);
240251876Speter
241251876Speter    return status;
242251876Speter}
243251876Speter
244251876SpeterAPU_DECLARE(apr_status_t) apr_sdbm_store(apr_sdbm_t *db, apr_sdbm_datum_t key,
245251876Speter                                         apr_sdbm_datum_t val, int flags)
246251876Speter{
247251876Speter    int need;
248251876Speter    register long hash;
249251876Speter    apr_status_t status;
250251876Speter
251251876Speter    if (db == NULL || bad(key))
252251876Speter        return APR_EINVAL;
253251876Speter    if (apr_sdbm_rdonly(db))
254251876Speter        return APR_EINVAL;
255251876Speter    need = key.dsize + val.dsize;
256251876Speter    /*
257251876Speter     * is the pair too big (or too small) for this database ??
258251876Speter     */
259251876Speter    if (need < 0 || need > PAIRMAX)
260251876Speter        return APR_EINVAL;
261251876Speter
262251876Speter    if ((status = apr_sdbm_lock(db, APR_FLOCK_EXCLUSIVE)) != APR_SUCCESS)
263251876Speter        return status;
264251876Speter
265362181Sdim    if ((status = getpage(db, (hash = exhash(key)), 0, 1)) == APR_SUCCESS) {
266251876Speter
267251876Speter        /*
268251876Speter         * if we need to replace, delete the key/data pair
269251876Speter         * first. If it is not there, ignore.
270251876Speter         */
271251876Speter        if (flags == APR_SDBM_REPLACE)
272251876Speter            (void) delpair(db->pagbuf, key);
273251876Speter        else if (!(flags & APR_SDBM_INSERTDUP) && duppair(db->pagbuf, key)) {
274251876Speter            status = APR_EEXIST;
275251876Speter            goto error;
276251876Speter        }
277251876Speter        /*
278251876Speter         * if we do not have enough room, we have to split.
279251876Speter         */
280251876Speter        if (!fitpair(db->pagbuf, need))
281251876Speter            if ((status = makroom(db, hash, need)) != APR_SUCCESS)
282251876Speter                goto error;
283251876Speter        /*
284251876Speter         * we have enough room or split is successful. insert the key,
285251876Speter         * and update the page file.
286251876Speter         */
287251876Speter        (void) putpair(db->pagbuf, key, val);
288251876Speter
289251876Speter        status = write_page(db, db->pagbuf, db->pagbno);
290251876Speter    }
291251876Speter
292251876Spetererror:
293251876Speter    (void) apr_sdbm_unlock(db);
294251876Speter
295251876Speter    return status;
296251876Speter}
297251876Speter
298251876Speter/*
299251876Speter * makroom - make room by splitting the overfull page
300251876Speter * this routine will attempt to make room for SPLTMAX times before
301251876Speter * giving up.
302251876Speter */
303251876Speterstatic apr_status_t makroom(apr_sdbm_t *db, long hash, int need)
304251876Speter{
305251876Speter    long newp;
306251876Speter    char twin[PBLKSIZ];
307251876Speter    char *pag = db->pagbuf;
308251876Speter    char *new = twin;
309251876Speter    register int smax = SPLTMAX;
310251876Speter    apr_status_t status;
311251876Speter
312251876Speter    do {
313251876Speter        /*
314251876Speter         * split the current page
315251876Speter         */
316251876Speter        (void) splpage(pag, new, db->hmask + 1);
317251876Speter        /*
318251876Speter         * address of the new page
319251876Speter         */
320251876Speter        newp = (hash & db->hmask) | (db->hmask + 1);
321251876Speter
322251876Speter        /*
323251876Speter         * write delay, read avoidence/cache shuffle:
324251876Speter         * select the page for incoming pair: if key is to go to the new page,
325251876Speter         * write out the previous one, and copy the new one over, thus making
326251876Speter         * it the current page. If not, simply write the new page, and we are
327251876Speter         * still looking at the page of interest. current page is not updated
328251876Speter         * here, as sdbm_store will do so, after it inserts the incoming pair.
329251876Speter         */
330251876Speter        if (hash & (db->hmask + 1)) {
331251876Speter            if ((status = write_page(db, db->pagbuf, db->pagbno))
332251876Speter                        != APR_SUCCESS)
333251876Speter                return status;
334251876Speter
335251876Speter            db->pagbno = newp;
336251876Speter            (void) memcpy(pag, new, PBLKSIZ);
337251876Speter        }
338251876Speter        else {
339251876Speter            if ((status = write_page(db, new, newp)) != APR_SUCCESS)
340251876Speter                return status;
341251876Speter        }
342251876Speter
343251876Speter        if ((status = setdbit(db, db->curbit)) != APR_SUCCESS)
344251876Speter            return status;
345251876Speter        /*
346251876Speter         * see if we have enough room now
347251876Speter         */
348251876Speter        if (fitpair(pag, need))
349251876Speter            return APR_SUCCESS;
350251876Speter        /*
351251876Speter         * try again... update curbit and hmask as getpage would have
352251876Speter         * done. because of our update of the current page, we do not
353251876Speter         * need to read in anything. BUT we have to write the current
354251876Speter         * [deferred] page out, as the window of failure is too great.
355251876Speter         */
356251876Speter        db->curbit = 2 * db->curbit
357251876Speter                   + ((hash & (db->hmask + 1)) ? 2 : 1);
358251876Speter        db->hmask |= db->hmask + 1;
359251876Speter
360251876Speter        if ((status = write_page(db, db->pagbuf, db->pagbno))
361251876Speter                    != APR_SUCCESS)
362251876Speter            return status;
363251876Speter
364251876Speter    } while (--smax);
365251876Speter
366251876Speter    /*
367251876Speter     * if we are here, this is real bad news. After SPLTMAX splits,
368251876Speter     * we still cannot fit the key. say goodnight.
369251876Speter     */
370251876Speter#if 0
371251876Speter    (void) write(2, "sdbm: cannot insert after SPLTMAX attempts.\n", 44);
372251876Speter#endif
373251876Speter    /* ### ENOSPC not really appropriate but better than nothing */
374251876Speter    return APR_ENOSPC;
375251876Speter
376251876Speter}
377251876Speter
378251876Speter/* Reads 'len' bytes from file 'f' at offset 'off' into buf.
379251876Speter * 'off' is given relative to the start of the file.
380362181Sdim * If 'create' is asked and EOF is returned while reading, this is taken
381362181Sdim * as success (i.e. a cleared buffer is returned).
382251876Speter */
383251876Speterstatic apr_status_t read_from(apr_file_t *f, void *buf,
384362181Sdim                              apr_off_t off, apr_size_t len,
385362181Sdim                              int create)
386251876Speter{
387251876Speter    apr_status_t status;
388251876Speter
389251876Speter    if ((status = apr_file_seek(f, APR_SET, &off)) != APR_SUCCESS ||
390251876Speter        ((status = apr_file_read_full(f, buf, len, NULL)) != APR_SUCCESS)) {
391251876Speter        /* if EOF is reached, pretend we read all zero's */
392362181Sdim        if (status == APR_EOF && create) {
393251876Speter            memset(buf, 0, len);
394251876Speter            status = APR_SUCCESS;
395251876Speter        }
396251876Speter    }
397251876Speter
398251876Speter    return status;
399251876Speter}
400251876Speter
401251876Speter/*
402251876Speter * the following two routines will break if
403251876Speter * deletions aren't taken into account. (ndbm bug)
404251876Speter */
405251876SpeterAPU_DECLARE(apr_status_t) apr_sdbm_firstkey(apr_sdbm_t *db,
406251876Speter                                            apr_sdbm_datum_t *key)
407251876Speter{
408251876Speter    apr_status_t status;
409251876Speter
410251876Speter    if ((status = apr_sdbm_lock(db, APR_FLOCK_SHARED)) != APR_SUCCESS)
411251876Speter        return status;
412251876Speter
413251876Speter    /*
414251876Speter     * start at page 0
415251876Speter     */
416362181Sdim    if ((status = getpage(db, 0, 1, 1)) == APR_SUCCESS) {
417251876Speter        db->blkptr = 0;
418251876Speter        db->keyptr = 0;
419251876Speter        status = getnext(key, db);
420251876Speter    }
421251876Speter
422251876Speter    (void) apr_sdbm_unlock(db);
423251876Speter
424251876Speter    return status;
425251876Speter}
426251876Speter
427251876SpeterAPU_DECLARE(apr_status_t) apr_sdbm_nextkey(apr_sdbm_t *db,
428251876Speter                                           apr_sdbm_datum_t *key)
429251876Speter{
430251876Speter    apr_status_t status;
431251876Speter
432251876Speter    if ((status = apr_sdbm_lock(db, APR_FLOCK_SHARED)) != APR_SUCCESS)
433251876Speter        return status;
434251876Speter
435251876Speter    status = getnext(key, db);
436251876Speter
437251876Speter    (void) apr_sdbm_unlock(db);
438251876Speter
439251876Speter    return status;
440251876Speter}
441251876Speter
442251876Speter/*
443251876Speter * all important binary tree traversal
444251876Speter */
445362181Sdimstatic apr_status_t getpage(apr_sdbm_t *db, long hash, int by_num, int create)
446251876Speter{
447362181Sdim    apr_status_t status;
448251876Speter    register long pagb;
449251876Speter
450362181Sdim    if (by_num) {
451362181Sdim        pagb = hash;
452362181Sdim    }
453362181Sdim    else {
454362181Sdim        register int hbit = 0;
455362181Sdim        register long dbit = 0;
456251876Speter
457362181Sdim        while (dbit < db->maxbno && getdbit(db, dbit))
458362181Sdim            dbit = 2 * dbit + ((hash & (1 << hbit++)) ? 2 : 1);
459362181Sdim        debug(("dbit: %d...", dbit));
460251876Speter
461362181Sdim        db->curbit = dbit;
462362181Sdim        db->hmask = masks[hbit];
463251876Speter
464362181Sdim        pagb = hash & db->hmask;
465362181Sdim    }
466362181Sdim
467251876Speter    /*
468251876Speter     * see if the block we need is already in memory.
469251876Speter     * note: this lookaside cache has about 10% hit rate.
470251876Speter     */
471251876Speter    if (pagb != db->pagbno) {
472251876Speter        /*
473251876Speter         * note: here, we assume a "hole" is read as 0s.
474251876Speter         * if not, must zero pagbuf first.
475251876Speter         * ### joe: this assumption was surely never correct? but
476251876Speter         * ### we make it so in read_from anyway.
477251876Speter         */
478362181Sdim        if ((status = read_from(db->pagf, db->pagbuf,
479362181Sdim                                OFF_PAG(pagb), PBLKSIZ,
480362181Sdim                                create)) != APR_SUCCESS)
481251876Speter            return status;
482251876Speter
483251876Speter        if (!chkpage(db->pagbuf))
484251876Speter            return APR_ENOSPC; /* ### better error? */
485362181Sdim
486251876Speter        db->pagbno = pagb;
487251876Speter
488251876Speter        debug(("pag read: %d\n", pagb));
489251876Speter    }
490251876Speter    return APR_SUCCESS;
491251876Speter}
492251876Speter
493251876Speterstatic int getdbit(apr_sdbm_t *db, long dbit)
494251876Speter{
495251876Speter    register long c;
496251876Speter    register long dirb;
497251876Speter
498251876Speter    c = dbit / BYTESIZ;
499251876Speter    dirb = c / DBLKSIZ;
500251876Speter
501251876Speter    if (dirb != db->dirbno) {
502362181Sdim        if (read_from(db->dirf, db->dirbuf,
503362181Sdim                      OFF_DIR(dirb), DBLKSIZ,
504362181Sdim                      1) != APR_SUCCESS)
505251876Speter            return 0;
506251876Speter
507251876Speter        db->dirbno = dirb;
508251876Speter
509251876Speter        debug(("dir read: %d\n", dirb));
510251876Speter    }
511251876Speter
512251876Speter    return db->dirbuf[c % DBLKSIZ] & (1 << dbit % BYTESIZ);
513251876Speter}
514251876Speter
515251876Speterstatic apr_status_t setdbit(apr_sdbm_t *db, long dbit)
516251876Speter{
517251876Speter    register long c;
518251876Speter    register long dirb;
519251876Speter    apr_status_t status;
520251876Speter    apr_off_t off;
521251876Speter
522251876Speter    c = dbit / BYTESIZ;
523251876Speter    dirb = c / DBLKSIZ;
524251876Speter
525251876Speter    if (dirb != db->dirbno) {
526362181Sdim        if ((status = read_from(db->dirf, db->dirbuf,
527362181Sdim                                OFF_DIR(dirb), DBLKSIZ,
528362181Sdim                                1)) != APR_SUCCESS)
529251876Speter            return status;
530251876Speter
531251876Speter        db->dirbno = dirb;
532251876Speter
533251876Speter        debug(("dir read: %d\n", dirb));
534251876Speter    }
535251876Speter
536251876Speter    db->dirbuf[c % DBLKSIZ] |= (1 << dbit % BYTESIZ);
537251876Speter
538251876Speter    if (dbit >= db->maxbno)
539251876Speter        db->maxbno += DBLKSIZ * BYTESIZ;
540251876Speter
541251876Speter    off = OFF_DIR(dirb);
542251876Speter    if ((status = apr_file_seek(db->dirf, APR_SET, &off)) == APR_SUCCESS)
543251876Speter        status = apr_file_write_full(db->dirf, db->dirbuf, DBLKSIZ, NULL);
544251876Speter
545251876Speter    return status;
546251876Speter}
547251876Speter
548251876Speter/*
549251876Speter* getnext - get the next key in the page, and if done with
550251876Speter* the page, try the next page in sequence
551251876Speter*/
552251876Speterstatic apr_status_t getnext(apr_sdbm_datum_t *key, apr_sdbm_t *db)
553251876Speter{
554251876Speter    apr_status_t status;
555251876Speter    for (;;) {
556251876Speter        db->keyptr++;
557251876Speter        *key = getnkey(db->pagbuf, db->keyptr);
558251876Speter        if (key->dptr != NULL)
559251876Speter            return APR_SUCCESS;
560251876Speter        /*
561251876Speter         * we either run out, or there is nothing on this page..
562251876Speter         * try the next one... If we lost our position on the
563251876Speter         * file, we will have to seek.
564251876Speter         */
565362181Sdim        db->blkptr++;
566251876Speter        db->keyptr = 0;
567251876Speter
568251876Speter        /* ### EOF acceptable here too? */
569362181Sdim        if ((status = getpage(db, db->blkptr, 1, 0)) != APR_SUCCESS)
570251876Speter            return status;
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