Annotation of parser3/src/lib/sdbm/sdbm.c, revision 1.6
1.1 paf 1: /* ====================================================================
2: * The Apache Software License, Version 1.1
3: *
4: * Copyright (c) 2000-2002 The Apache Software Foundation. All rights
5: * reserved.
6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
10: *
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: *
14: * 2. Redistributions in binary form must reproduce the above copyright
15: * notice, this list of conditions and the following disclaimer in
16: * the documentation and/or other materials provided with the
17: * distribution.
18: *
19: * 3. The end-user documentation included with the redistribution,
20: * if any, must include the following acknowledgment:
21: * "This product includes software developed by the
22: * Apache Software Foundation (http://www.apache.org/)."
23: * Alternately, this acknowledgment may appear in the software itself,
24: * if and wherever such third-party acknowledgments normally appear.
25: *
26: * 4. The names "Apache" and "Apache Software Foundation" must
27: * not be used to endorse or promote products derived from this
28: * software without prior written permission. For written
29: * permission, please contact apache@apache.org.
30: *
31: * 5. Products derived from this software may not be called "Apache",
32: * nor may "Apache" appear in their name, without prior written
33: * permission of the Apache Software Foundation.
34: *
35: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46: * SUCH DAMAGE.
47: * ====================================================================
48: *
49: * This software consists of voluntary contributions made by many
50: * individuals on behalf of the Apache Software Foundation. For more
51: * information on the Apache Software Foundation, please see
52: * <http://www.apache.org/>.
53: */
54:
55: /*
56: * sdbm - ndbm work-alike hashed database library
57: * based on Per-Aake Larson's Dynamic Hashing algorithms. BIT 18 (1978).
58: * author: oz@nexus.yorku.ca
59: * ex-public domain, ported to APR for Apache 2
60: * core routines
61: */
62:
1.5 moko 63: #include "pa_apr.h"
64: #include "pa_file_io.h"
65: #include "pa_strings.h"
66: #include "pa_errno.h"
67: #include "pa_sdbm.h"
1.1 paf 68:
69: #include "sdbm_tune.h"
70: #include "sdbm_pair.h"
71: #include "sdbm_private.h"
72:
73: #include <string.h> /* for memset() */
1.4 misha 74: // #include <stdlib.h> /* for malloc() and free() */
1.1 paf 75:
76: /*
77: * forward
78: */
1.5 moko 79: static int getdbit (pa_sdbm_t *, long);
80: static pa_status_t setdbit(pa_sdbm_t *, long);
81: static pa_status_t getpage(pa_sdbm_t *db, long);
82: static pa_status_t getnext(pa_sdbm_datum_t *key, pa_sdbm_t *db);
83: static pa_status_t makroom(pa_sdbm_t *, long, int);
1.1 paf 84:
85: /*
86: * useful macros
87: */
88: #define bad(x) ((x).dptr == NULL || (x).dsize <= 0)
89: #define exhash(item) sdbm_hash((item).dptr, (item).dsize)
90:
91: /* ### Does anything need these externally? */
92: #define sdbm_dirfno(db) ((db)->dirf)
93: #define sdbm_pagfno(db) ((db)->pagf)
94:
1.5 moko 95: #define OFF_PAG(off) (pa_off_t) (off) * PBLKSIZ
96: #define OFF_DIR(off) (pa_off_t) (off) * DBLKSIZ
1.1 paf 97:
98: static long masks[] = {
99: 000000000000, 000000000001, 000000000003, 000000000007,
100: 000000000017, 000000000037, 000000000077, 000000000177,
101: 000000000377, 000000000777, 000000001777, 000000003777,
102: 000000007777, 000000017777, 000000037777, 000000077777,
103: 000000177777, 000000377777, 000000777777, 000001777777,
104: 000003777777, 000007777777, 000017777777, 000037777777,
105: 000077777777, 000177777777, 000377777777, 000777777777,
106: 001777777777, 003777777777, 007777777777, 017777777777
107: };
108:
1.5 moko 109: const pa_sdbm_datum_t sdbm_nullitem = { NULL, 0 };
1.1 paf 110:
1.5 moko 111: static pa_status_t database_cleanup(void *data)
1.1 paf 112: {
1.5 moko 113: pa_sdbm_t *db = data;
1.1 paf 114:
115: /*
1.5 moko 116: * Can't rely on pa_sdbm_unlock, since it will merely
1.1 paf 117: * decrement the refcnt if several locks are held.
118: */
119: if (db->flags & (SDBM_SHARED_LOCK | SDBM_EXCLUSIVE_LOCK))
1.5 moko 120: (void) pa_file_unlock(db->dirf);
121: (void) pa_file_close(db->dirf);
122: (void) pa_file_close(db->pagf);
1.4 misha 123: // free(db); // libcg will do it
1.1 paf 124:
1.5 moko 125: return PA_SUCCESS;
1.1 paf 126: }
127:
1.5 moko 128: static pa_status_t prep(pa_sdbm_t **pdb, const char *dirname, const char *pagname,
129: pa_int32_t flags, pa_fileperms_t perms, pa_pool_t *p)
1.1 paf 130: {
1.5 moko 131: pa_sdbm_t *db;
132: pa_status_t status;
1.1 paf 133:
134: *pdb = NULL;
135:
1.4 misha 136: // db = malloc(sizeof(*db));
137: // memset(db, 0, sizeof(*db));
1.6 ! moko 138: db = pa_sdbm_malloc(sizeof(*db));
1.1 paf 139:
140: db->pool = p;
141:
142: /*
143: * adjust user flags so that WRONLY becomes RDWR,
144: * as required by this package. Also set our internal
145: * flag for RDONLY if needed.
146: */
1.5 moko 147: if (!(flags & PA_WRITE)) {
1.1 paf 148: db->flags |= SDBM_RDONLY;
149: }
150:
151: /*
152: * adjust the file open flags so that we handle locking
153: * on our own (don't rely on any locking behavior within
1.5 moko 154: * an pa_file_t, in case it's ever introduced, and set
1.1 paf 155: * our own flag.
156: */
1.5 moko 157: if (flags & PA_SHARELOCK) {
1.1 paf 158: db->flags |= SDBM_SHARED;
1.5 moko 159: flags &= ~PA_SHARELOCK;
1.1 paf 160: }
161:
1.5 moko 162: flags |= PA_BINARY | PA_READ;
1.1 paf 163:
164: /*
165: * open the files in sequence, and stat the dirfile.
166: * If we fail anywhere, undo everything, return NULL.
167: */
168:
1.5 moko 169: if ((status = pa_file_open(&db->dirf, dirname, flags, perms, p))
170: != PA_SUCCESS)
1.1 paf 171: goto error;
172:
1.5 moko 173: if ((status = pa_file_open(&db->pagf, pagname, flags, perms, p))
174: != PA_SUCCESS)
1.1 paf 175: goto error;
176:
1.5 moko 177: if ((status = pa_sdbm_lock(db, (db->flags & SDBM_RDONLY)
178: ? PA_FLOCK_SHARED
179: : PA_FLOCK_EXCLUSIVE))
180: != PA_SUCCESS)
1.1 paf 181: goto error;
182:
1.5 moko 183: /* pa_pcalloc zeroed the buffers
184: * pa_sdbm_lock stated the dirf->size and invalidated the cache
1.1 paf 185: */
186:
187: /*
188: * if we are opened in SHARED mode, unlock ourself
189: */
190: if (db->flags & SDBM_SHARED)
1.5 moko 191: if ((status = pa_sdbm_unlock(db)) != PA_SUCCESS)
1.1 paf 192: goto error;
193:
194: /* make sure that we close the database at some point */
1.5 moko 195: //pa_pool_cleanup_register(p, db, database_cleanup, pa_pool_cleanup_null);
1.1 paf 196:
197: /* Done! */
198: *pdb = db;
1.5 moko 199: return PA_SUCCESS;
1.1 paf 200:
201: error:
202: if (db->dirf && db->pagf)
1.5 moko 203: (void) pa_sdbm_unlock(db);
1.1 paf 204: if (db->dirf != NULL)
1.5 moko 205: (void) pa_file_close(db->dirf);
1.1 paf 206: if (db->pagf != NULL) {
1.5 moko 207: (void) pa_file_close(db->pagf);
1.1 paf 208: }
1.4 misha 209: // free(db); // libcg will do it
1.1 paf 210: return status;
211: }
212:
1.5 moko 213: pa_status_t pa_sdbm_open(pa_sdbm_t **db, const char *file,
214: pa_int32_t flags,
215: pa_fileperms_t perms, pa_pool_t *p)
1.1 paf 216: {
1.5 moko 217: char *dirname = pa_pstrcat(p, file, PA_SDBM_DIRFEXT, NULL);
218: char *pagname = pa_pstrcat(p, file, PA_SDBM_PAGFEXT, NULL);
1.1 paf 219:
220: return prep(db, dirname, pagname, flags, perms, p);
221: }
222:
1.5 moko 223: pa_status_t pa_sdbm_close(pa_sdbm_t *db)
1.1 paf 224: {
1.5 moko 225: database_cleanup(db); return PA_SUCCESS;
226: //return pa_pool_cleanup_run(db->pool, db, database_cleanup);
1.1 paf 227: }
228:
1.5 moko 229: pa_status_t pa_sdbm_fetch(pa_sdbm_t *db, pa_sdbm_datum_t *val,
230: pa_sdbm_datum_t key)
1.1 paf 231: {
1.5 moko 232: pa_status_t status;
1.1 paf 233:
234: if (db == NULL || bad(key))
1.5 moko 235: return PA_EINVAL;
1.1 paf 236:
1.5 moko 237: if ((status = pa_sdbm_lock(db, PA_FLOCK_SHARED)) != PA_SUCCESS)
1.1 paf 238: return status;
239:
1.5 moko 240: if ((status = getpage(db, exhash(key))) == PA_SUCCESS) {
1.1 paf 241: *val = getpair(db->pagbuf, key);
242: /* ### do we want a not-found result? */
243: }
244:
1.5 moko 245: (void) pa_sdbm_unlock(db);
1.1 paf 246:
247: return status;
248: }
249:
1.5 moko 250: static pa_status_t write_page(pa_sdbm_t *db, const char *buf, long pagno)
1.1 paf 251: {
1.5 moko 252: pa_status_t status;
253: pa_off_t off = OFF_PAG(pagno);
1.1 paf 254:
1.5 moko 255: if ((status = pa_file_seek(db->pagf, PA_SET, &off)) == PA_SUCCESS)
256: status = pa_file_write_full(db->pagf, buf, PBLKSIZ, NULL);
1.1 paf 257:
258: return status;
259: }
260:
1.5 moko 261: pa_status_t pa_sdbm_delete(pa_sdbm_t *db,
262: const pa_sdbm_datum_t key)
1.1 paf 263: {
1.5 moko 264: pa_status_t status;
1.1 paf 265:
266: if (db == NULL || bad(key))
1.5 moko 267: return PA_EINVAL;
268: if (pa_sdbm_rdonly(db))
269: return PA_EINVAL;
1.1 paf 270:
1.5 moko 271: if ((status = pa_sdbm_lock(db, PA_FLOCK_EXCLUSIVE)) != PA_SUCCESS)
1.1 paf 272: return status;
273:
1.5 moko 274: if ((status = getpage(db, exhash(key))) == PA_SUCCESS) {
1.1 paf 275: if (!delpair(db->pagbuf, key))
276: /* ### should we define some APRUTIL codes? */
1.5 moko 277: status = PA_SUCCESS; /* PAF: were PA_EGENERAL, contradicting comment in .h file :( */
1.1 paf 278: else
279: status = write_page(db, db->pagbuf, db->pagbno);
280: }
281:
1.5 moko 282: (void) pa_sdbm_unlock(db);
1.1 paf 283:
284: return status;
285: }
286:
1.5 moko 287: pa_status_t pa_sdbm_store(pa_sdbm_t *db, pa_sdbm_datum_t key,
288: pa_sdbm_datum_t val, int flags)
1.1 paf 289: {
290: int need;
291: register long hash;
1.5 moko 292: pa_status_t status;
1.1 paf 293:
294: if (db == NULL || bad(key))
1.5 moko 295: return PA_EINVAL;
296: if (pa_sdbm_rdonly(db))
297: return PA_EINVAL;
1.1 paf 298: need = key.dsize + val.dsize;
299: /*
300: * is the pair too big (or too small) for this database ??
301: */
302: if (need < 0 || need > PAIRMAX)
1.5 moko 303: return PA_EINVAL;
1.1 paf 304:
1.5 moko 305: if ((status = pa_sdbm_lock(db, PA_FLOCK_EXCLUSIVE)) != PA_SUCCESS)
1.1 paf 306: return status;
307:
1.5 moko 308: if ((status = getpage(db, (hash = exhash(key)))) == PA_SUCCESS) {
1.1 paf 309:
310: /*
311: * if we need to replace, delete the key/data pair
312: * first. If it is not there, ignore.
313: */
1.5 moko 314: if (flags == PA_SDBM_REPLACE)
1.1 paf 315: (void) delpair(db->pagbuf, key);
1.5 moko 316: else if (!(flags & PA_SDBM_INSERTDUP) && duppair(db->pagbuf, key)) {
317: status = PA_EEXIST;
1.1 paf 318: goto error;
319: }
320: /*
321: * if we do not have enough room, we have to split.
322: */
323: if (!fitpair(db->pagbuf, need))
1.5 moko 324: if ((status = makroom(db, hash, need)) != PA_SUCCESS)
1.1 paf 325: goto error;
326: /*
327: * we have enough room or split is successful. insert the key,
328: * and update the page file.
329: */
330: (void) putpair(db->pagbuf, key, val);
331:
332: status = write_page(db, db->pagbuf, db->pagbno);
333: }
334:
335: error:
1.5 moko 336: (void) pa_sdbm_unlock(db);
1.1 paf 337:
338: return status;
339: }
340:
341: /*
342: * makroom - make room by splitting the overfull page
343: * this routine will attempt to make room for SPLTMAX times before
344: * giving up.
345: */
1.5 moko 346: static pa_status_t makroom(pa_sdbm_t *db, long hash, int need)
1.1 paf 347: {
348: long newp;
349: char twin[PBLKSIZ];
350: char *pag = db->pagbuf;
351: char *new = twin;
352: register int smax = SPLTMAX;
1.5 moko 353: pa_status_t status;
1.1 paf 354:
355: do {
356: /*
357: * split the current page
358: */
359: (void) splpage(pag, new, db->hmask + 1);
360: /*
361: * address of the new page
362: */
363: newp = (hash & db->hmask) | (db->hmask + 1);
364:
365: /*
366: * write delay, read avoidence/cache shuffle:
367: * select the page for incoming pair: if key is to go to the new page,
368: * write out the previous one, and copy the new one over, thus making
369: * it the current page. If not, simply write the new page, and we are
370: * still looking at the page of interest. current page is not updated
371: * here, as sdbm_store will do so, after it inserts the incoming pair.
372: */
373: if (hash & (db->hmask + 1)) {
374: if ((status = write_page(db, db->pagbuf, db->pagbno))
1.5 moko 375: != PA_SUCCESS)
1.1 paf 376: return status;
377:
378: db->pagbno = newp;
379: (void) memcpy(pag, new, PBLKSIZ);
380: }
381: else {
1.5 moko 382: if ((status = write_page(db, new, newp)) != PA_SUCCESS)
1.1 paf 383: return status;
384: }
385:
1.5 moko 386: if ((status = setdbit(db, db->curbit)) != PA_SUCCESS)
1.1 paf 387: return status;
388: /*
389: * see if we have enough room now
390: */
391: if (fitpair(pag, need))
1.5 moko 392: return PA_SUCCESS;
1.1 paf 393: /*
394: * try again... update curbit and hmask as getpage would have
395: * done. because of our update of the current page, we do not
396: * need to read in anything. BUT we have to write the current
397: * [deferred] page out, as the window of failure is too great.
398: */
399: db->curbit = 2 * db->curbit
400: + ((hash & (db->hmask + 1)) ? 2 : 1);
401: db->hmask |= db->hmask + 1;
402:
403: if ((status = write_page(db, db->pagbuf, db->pagbno))
1.5 moko 404: != PA_SUCCESS)
1.1 paf 405: return status;
406:
407: } while (--smax);
408:
409: /*
410: * if we are here, this is real bad news. After SPLTMAX splits,
411: * we still cannot fit the key. say goodnight.
412: */
413: #if 0
414: (void) write(2, "sdbm: cannot insert after SPLTMAX attempts.\n", 44);
415: #endif
416: /* ### ENOSPC not really appropriate but better than nothing */
1.5 moko 417: return PA_ENOSPC;
1.1 paf 418:
419: }
420:
421: /* Reads 'len' bytes from file 'f' at offset 'off' into buf.
422: * 'off' is given relative to the start of the file.
423: * If EOF is returned while reading, this is taken as success.
424: */
1.5 moko 425: static pa_status_t read_from(pa_file_t *f, void *buf,
426: pa_off_t off, pa_size_t len)
1.1 paf 427: {
1.5 moko 428: pa_status_t status;
1.1 paf 429:
1.5 moko 430: if ((status = pa_file_seek(f, PA_SET, &off)) != PA_SUCCESS ||
431: ((status = pa_file_read_full(f, buf, len, NULL)) != PA_SUCCESS)) {
1.1 paf 432: /* if EOF is reached, pretend we read all zero's */
1.5 moko 433: if (status == PA_EOF) {
1.1 paf 434: memset(buf, 0, len);
1.5 moko 435: status = PA_SUCCESS;
1.1 paf 436: }
437: }
438:
439: return status;
440: }
441:
442: /*
443: * the following two routines will break if
444: * deletions aren't taken into account. (ndbm bug)
445: */
1.5 moko 446: pa_status_t pa_sdbm_firstkey(pa_sdbm_t *db,
447: pa_sdbm_datum_t *key)
1.1 paf 448: {
1.5 moko 449: pa_status_t status;
1.1 paf 450:
1.5 moko 451: if ((status = pa_sdbm_lock(db, PA_FLOCK_SHARED)) != PA_SUCCESS)
1.1 paf 452: return status;
453:
454: /*
455: * start at page 0
456: */
457: if ((status = read_from(db->pagf, db->pagbuf, OFF_PAG(0), PBLKSIZ))
1.5 moko 458: == PA_SUCCESS) {
1.1 paf 459: db->pagbno = 0;
460: db->blkptr = 0;
461: db->keyptr = 0;
462: status = getnext(key, db);
463: }
464:
1.5 moko 465: (void) pa_sdbm_unlock(db);
1.1 paf 466:
467: return status;
468: }
469:
1.5 moko 470: pa_status_t pa_sdbm_nextkey(pa_sdbm_t *db,
471: pa_sdbm_datum_t *key)
1.1 paf 472: {
1.5 moko 473: pa_status_t status;
1.1 paf 474:
1.5 moko 475: if ((status = pa_sdbm_lock(db, PA_FLOCK_SHARED)) != PA_SUCCESS)
1.1 paf 476: return status;
477:
478: status = getnext(key, db);
479:
1.5 moko 480: (void) pa_sdbm_unlock(db);
1.1 paf 481:
482: return status;
483: }
484:
485: /*
486: * all important binary tree traversal
487: */
1.5 moko 488: static pa_status_t getpage(pa_sdbm_t *db, long hash)
1.1 paf 489: {
490: register int hbit;
491: register long dbit;
492: register long pagb;
1.5 moko 493: pa_status_t status;
1.1 paf 494:
495: dbit = 0;
496: hbit = 0;
497: while (dbit < db->maxbno && getdbit(db, dbit))
498: dbit = 2 * dbit + ((hash & (1 << hbit++)) ? 2 : 1);
499:
500: debug(("dbit: %d...", dbit));
501:
502: db->curbit = dbit;
503: db->hmask = masks[hbit];
504:
505: pagb = hash & db->hmask;
506: /*
507: * see if the block we need is already in memory.
508: * note: this lookaside cache has about 10% hit rate.
509: */
510: if (pagb != db->pagbno) {
511: /*
512: * note: here, we assume a "hole" is read as 0s.
513: * if not, must zero pagbuf first.
514: * ### joe: this assumption was surely never correct? but
515: * ### we make it so in read_from anyway.
516: */
517: if ((status = read_from(db->pagf, db->pagbuf, OFF_PAG(pagb), PBLKSIZ))
1.5 moko 518: != PA_SUCCESS)
1.1 paf 519: return status;
520:
521: if (!chkpage(db->pagbuf))
1.5 moko 522: return PA_ENOSPC; /* ### better error? */
1.1 paf 523: db->pagbno = pagb;
524:
525: debug(("pag read: %d\n", pagb));
526: }
1.5 moko 527: return PA_SUCCESS;
1.1 paf 528: }
529:
1.5 moko 530: static int getdbit(pa_sdbm_t *db, long dbit)
1.1 paf 531: {
532: register long c;
533: register long dirb;
534:
535: c = dbit / BYTESIZ;
536: dirb = c / DBLKSIZ;
537:
538: if (dirb != db->dirbno) {
539: if (read_from(db->dirf, db->dirbuf, OFF_DIR(dirb), DBLKSIZ)
1.5 moko 540: != PA_SUCCESS)
1.1 paf 541: return 0;
542:
543: db->dirbno = dirb;
544:
545: debug(("dir read: %d\n", dirb));
546: }
547:
548: return db->dirbuf[c % DBLKSIZ] & (1 << dbit % BYTESIZ);
549: }
550:
1.5 moko 551: static pa_status_t setdbit(pa_sdbm_t *db, long dbit)
1.1 paf 552: {
553: register long c;
554: register long dirb;
1.5 moko 555: pa_status_t status;
556: pa_off_t off;
1.1 paf 557:
558: c = dbit / BYTESIZ;
559: dirb = c / DBLKSIZ;
560:
561: if (dirb != db->dirbno) {
562: if ((status = read_from(db->dirf, db->dirbuf, OFF_DIR(dirb), DBLKSIZ))
1.5 moko 563: != PA_SUCCESS)
1.1 paf 564: return status;
565:
566: db->dirbno = dirb;
567:
568: debug(("dir read: %d\n", dirb));
569: }
570:
571: db->dirbuf[c % DBLKSIZ] |= (1 << dbit % BYTESIZ);
572:
573: if (dbit >= db->maxbno)
574: db->maxbno += DBLKSIZ * BYTESIZ;
575:
576: off = OFF_DIR(dirb);
1.5 moko 577: if ((status = pa_file_seek(db->dirf, PA_SET, &off)) == PA_SUCCESS)
578: status = pa_file_write_full(db->dirf, db->dirbuf, DBLKSIZ, NULL);
1.1 paf 579:
580: return status;
581: }
582:
583: /*
584: * getnext - get the next key in the page, and if done with
585: * the page, try the next page in sequence
586: */
1.5 moko 587: static pa_status_t getnext(pa_sdbm_datum_t *key, pa_sdbm_t *db)
1.1 paf 588: {
1.5 moko 589: pa_status_t status;
1.1 paf 590: for (;;) {
591: db->keyptr++;
592: *key = getnkey(db->pagbuf, db->keyptr);
593: if (key->dptr != NULL)
1.5 moko 594: return PA_SUCCESS;
1.1 paf 595: /*
596: * we either run out, or there is nothing on this page..
597: * try the next one... If we lost our position on the
598: * file, we will have to seek.
599: */
600: db->keyptr = 0;
601: if (db->pagbno != db->blkptr++) {
1.5 moko 602: pa_off_t off = OFF_PAG(db->blkptr);
603: if ((status = pa_file_seek(db->pagf, PA_SET, &off)
604: != PA_SUCCESS))
1.1 paf 605: return status;
606: }
607:
608: db->pagbno = db->blkptr;
609: /* ### EOF acceptable here too? */
1.5 moko 610: if ((status = pa_file_read_full(db->pagf, db->pagbuf, PBLKSIZ, NULL))
611: != PA_SUCCESS)
1.1 paf 612: return status;
613: if (!chkpage(db->pagbuf))
1.5 moko 614: return PA_EGENERAL; /* ### need better error */
1.1 paf 615: }
616:
617: /* NOTREACHED */
618: }
619:
620:
1.5 moko 621: int pa_sdbm_rdonly(pa_sdbm_t *db)
1.1 paf 622: {
623: /* ### Should we return true if the first lock is a share lock,
1.5 moko 624: * to reflect that pa_sdbm_store and pa_sdbm_delete will fail?
1.1 paf 625: */
626: return (db->flags & SDBM_RDONLY) != 0;
627: }
628:
E-mail: