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