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