Annotation of parser3/src/lib/sdbm/sdbm.c, revision 1.7
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: /*
74: * forward
75: */
1.5 moko 76: static int getdbit (pa_sdbm_t *, long);
77: static pa_status_t setdbit(pa_sdbm_t *, long);
78: static pa_status_t getpage(pa_sdbm_t *db, long);
79: static pa_status_t getnext(pa_sdbm_datum_t *key, pa_sdbm_t *db);
80: static pa_status_t makroom(pa_sdbm_t *, long, int);
1.1 paf 81:
82: /*
83: * useful macros
84: */
85: #define bad(x) ((x).dptr == NULL || (x).dsize <= 0)
86: #define exhash(item) sdbm_hash((item).dptr, (item).dsize)
87:
88: /* ### Does anything need these externally? */
89: #define sdbm_dirfno(db) ((db)->dirf)
90: #define sdbm_pagfno(db) ((db)->pagf)
91:
1.5 moko 92: #define OFF_PAG(off) (pa_off_t) (off) * PBLKSIZ
93: #define OFF_DIR(off) (pa_off_t) (off) * DBLKSIZ
1.1 paf 94:
95: static long masks[] = {
96: 000000000000, 000000000001, 000000000003, 000000000007,
97: 000000000017, 000000000037, 000000000077, 000000000177,
98: 000000000377, 000000000777, 000000001777, 000000003777,
99: 000000007777, 000000017777, 000000037777, 000000077777,
100: 000000177777, 000000377777, 000000777777, 000001777777,
101: 000003777777, 000007777777, 000017777777, 000037777777,
102: 000077777777, 000177777777, 000377777777, 000777777777,
103: 001777777777, 003777777777, 007777777777, 017777777777
104: };
105:
1.5 moko 106: const pa_sdbm_datum_t sdbm_nullitem = { NULL, 0 };
1.1 paf 107:
1.5 moko 108: static pa_status_t database_cleanup(void *data)
1.1 paf 109: {
1.5 moko 110: pa_sdbm_t *db = data;
1.1 paf 111:
112: /*
1.5 moko 113: * Can't rely on pa_sdbm_unlock, since it will merely
1.1 paf 114: * decrement the refcnt if several locks are held.
115: */
116: if (db->flags & (SDBM_SHARED_LOCK | SDBM_EXCLUSIVE_LOCK))
1.5 moko 117: (void) pa_file_unlock(db->dirf);
118: (void) pa_file_close(db->dirf);
119: (void) pa_file_close(db->pagf);
1.4 misha 120: // free(db); // libcg will do it
1.1 paf 121:
1.5 moko 122: return PA_SUCCESS;
1.1 paf 123: }
124:
1.5 moko 125: static pa_status_t prep(pa_sdbm_t **pdb, const char *dirname, const char *pagname,
126: pa_int32_t flags, pa_fileperms_t perms, pa_pool_t *p)
1.1 paf 127: {
1.5 moko 128: pa_sdbm_t *db;
129: pa_status_t status;
1.1 paf 130:
131: *pdb = NULL;
132:
1.4 misha 133: // db = malloc(sizeof(*db));
134: // memset(db, 0, sizeof(*db));
1.6 moko 135: db = pa_sdbm_malloc(sizeof(*db));
1.1 paf 136:
137: db->pool = p;
138:
139: /*
140: * adjust user flags so that WRONLY becomes RDWR,
141: * as required by this package. Also set our internal
142: * flag for RDONLY if needed.
143: */
1.5 moko 144: if (!(flags & PA_WRITE)) {
1.1 paf 145: db->flags |= SDBM_RDONLY;
146: }
147:
148: /*
149: * adjust the file open flags so that we handle locking
150: * on our own (don't rely on any locking behavior within
1.5 moko 151: * an pa_file_t, in case it's ever introduced, and set
1.1 paf 152: * our own flag.
153: */
1.5 moko 154: if (flags & PA_SHARELOCK) {
1.1 paf 155: db->flags |= SDBM_SHARED;
1.5 moko 156: flags &= ~PA_SHARELOCK;
1.1 paf 157: }
158:
1.5 moko 159: flags |= PA_BINARY | PA_READ;
1.1 paf 160:
161: /*
162: * open the files in sequence, and stat the dirfile.
163: * If we fail anywhere, undo everything, return NULL.
164: */
165:
1.5 moko 166: if ((status = pa_file_open(&db->dirf, dirname, flags, perms, p))
167: != PA_SUCCESS)
1.1 paf 168: goto error;
169:
1.5 moko 170: if ((status = pa_file_open(&db->pagf, pagname, flags, perms, p))
171: != PA_SUCCESS)
1.1 paf 172: goto error;
173:
1.5 moko 174: if ((status = pa_sdbm_lock(db, (db->flags & SDBM_RDONLY)
175: ? PA_FLOCK_SHARED
176: : PA_FLOCK_EXCLUSIVE))
177: != PA_SUCCESS)
1.1 paf 178: goto error;
179:
1.5 moko 180: /* pa_pcalloc zeroed the buffers
181: * pa_sdbm_lock stated the dirf->size and invalidated the cache
1.1 paf 182: */
183:
184: /*
185: * if we are opened in SHARED mode, unlock ourself
186: */
187: if (db->flags & SDBM_SHARED)
1.5 moko 188: if ((status = pa_sdbm_unlock(db)) != PA_SUCCESS)
1.1 paf 189: goto error;
190:
191: /* make sure that we close the database at some point */
1.5 moko 192: //pa_pool_cleanup_register(p, db, database_cleanup, pa_pool_cleanup_null);
1.1 paf 193:
194: /* Done! */
195: *pdb = db;
1.5 moko 196: return PA_SUCCESS;
1.1 paf 197:
198: error:
199: if (db->dirf && db->pagf)
1.5 moko 200: (void) pa_sdbm_unlock(db);
1.1 paf 201: if (db->dirf != NULL)
1.5 moko 202: (void) pa_file_close(db->dirf);
1.1 paf 203: if (db->pagf != NULL) {
1.5 moko 204: (void) pa_file_close(db->pagf);
1.1 paf 205: }
1.4 misha 206: // free(db); // libcg will do it
1.1 paf 207: return status;
208: }
209:
1.5 moko 210: pa_status_t pa_sdbm_open(pa_sdbm_t **db, const char *file,
211: pa_int32_t flags,
212: pa_fileperms_t perms, pa_pool_t *p)
1.1 paf 213: {
1.5 moko 214: char *dirname = pa_pstrcat(p, file, PA_SDBM_DIRFEXT, NULL);
215: char *pagname = pa_pstrcat(p, file, PA_SDBM_PAGFEXT, NULL);
1.1 paf 216:
217: return prep(db, dirname, pagname, flags, perms, p);
218: }
219:
1.5 moko 220: pa_status_t pa_sdbm_close(pa_sdbm_t *db)
1.1 paf 221: {
1.5 moko 222: database_cleanup(db); return PA_SUCCESS;
223: //return pa_pool_cleanup_run(db->pool, db, database_cleanup);
1.1 paf 224: }
225:
1.5 moko 226: pa_status_t pa_sdbm_fetch(pa_sdbm_t *db, pa_sdbm_datum_t *val,
227: pa_sdbm_datum_t key)
1.1 paf 228: {
1.5 moko 229: pa_status_t status;
1.1 paf 230:
231: if (db == NULL || bad(key))
1.5 moko 232: return PA_EINVAL;
1.1 paf 233:
1.5 moko 234: if ((status = pa_sdbm_lock(db, PA_FLOCK_SHARED)) != PA_SUCCESS)
1.1 paf 235: return status;
236:
1.5 moko 237: if ((status = getpage(db, exhash(key))) == PA_SUCCESS) {
1.1 paf 238: *val = getpair(db->pagbuf, key);
239: /* ### do we want a not-found result? */
240: }
241:
1.5 moko 242: (void) pa_sdbm_unlock(db);
1.1 paf 243:
244: return status;
245: }
246:
1.5 moko 247: static pa_status_t write_page(pa_sdbm_t *db, const char *buf, long pagno)
1.1 paf 248: {
1.5 moko 249: pa_status_t status;
250: pa_off_t off = OFF_PAG(pagno);
1.1 paf 251:
1.5 moko 252: if ((status = pa_file_seek(db->pagf, PA_SET, &off)) == PA_SUCCESS)
253: status = pa_file_write_full(db->pagf, buf, PBLKSIZ, NULL);
1.1 paf 254:
255: return status;
256: }
257:
1.5 moko 258: pa_status_t pa_sdbm_delete(pa_sdbm_t *db,
259: const pa_sdbm_datum_t key)
1.1 paf 260: {
1.5 moko 261: pa_status_t status;
1.1 paf 262:
263: if (db == NULL || bad(key))
1.5 moko 264: return PA_EINVAL;
265: if (pa_sdbm_rdonly(db))
266: return PA_EINVAL;
1.1 paf 267:
1.5 moko 268: if ((status = pa_sdbm_lock(db, PA_FLOCK_EXCLUSIVE)) != PA_SUCCESS)
1.1 paf 269: return status;
270:
1.5 moko 271: if ((status = getpage(db, exhash(key))) == PA_SUCCESS) {
1.1 paf 272: if (!delpair(db->pagbuf, key))
273: /* ### should we define some APRUTIL codes? */
1.5 moko 274: status = PA_SUCCESS; /* PAF: were PA_EGENERAL, contradicting comment in .h file :( */
1.1 paf 275: else
276: status = write_page(db, db->pagbuf, db->pagbno);
277: }
278:
1.5 moko 279: (void) pa_sdbm_unlock(db);
1.1 paf 280:
281: return status;
282: }
283:
1.5 moko 284: pa_status_t pa_sdbm_store(pa_sdbm_t *db, pa_sdbm_datum_t key,
285: pa_sdbm_datum_t val, int flags)
1.1 paf 286: {
287: int need;
288: register long hash;
1.5 moko 289: pa_status_t status;
1.1 paf 290:
291: if (db == NULL || bad(key))
1.5 moko 292: return PA_EINVAL;
293: if (pa_sdbm_rdonly(db))
294: return PA_EINVAL;
1.1 paf 295: need = key.dsize + val.dsize;
296: /*
297: * is the pair too big (or too small) for this database ??
298: */
299: if (need < 0 || need > PAIRMAX)
1.5 moko 300: return PA_EINVAL;
1.1 paf 301:
1.5 moko 302: if ((status = pa_sdbm_lock(db, PA_FLOCK_EXCLUSIVE)) != PA_SUCCESS)
1.1 paf 303: return status;
304:
1.5 moko 305: if ((status = getpage(db, (hash = exhash(key)))) == PA_SUCCESS) {
1.1 paf 306:
307: /*
308: * if we need to replace, delete the key/data pair
309: * first. If it is not there, ignore.
310: */
1.5 moko 311: if (flags == PA_SDBM_REPLACE)
1.1 paf 312: (void) delpair(db->pagbuf, key);
1.5 moko 313: else if (!(flags & PA_SDBM_INSERTDUP) && duppair(db->pagbuf, key)) {
314: status = PA_EEXIST;
1.1 paf 315: goto error;
316: }
317: /*
318: * if we do not have enough room, we have to split.
319: */
320: if (!fitpair(db->pagbuf, need))
1.5 moko 321: if ((status = makroom(db, hash, need)) != PA_SUCCESS)
1.1 paf 322: goto error;
323: /*
324: * we have enough room or split is successful. insert the key,
325: * and update the page file.
326: */
327: (void) putpair(db->pagbuf, key, val);
328:
329: status = write_page(db, db->pagbuf, db->pagbno);
330: }
331:
332: error:
1.5 moko 333: (void) pa_sdbm_unlock(db);
1.1 paf 334:
335: return status;
336: }
337:
338: /*
339: * makroom - make room by splitting the overfull page
340: * this routine will attempt to make room for SPLTMAX times before
341: * giving up.
342: */
1.5 moko 343: static pa_status_t makroom(pa_sdbm_t *db, long hash, int need)
1.1 paf 344: {
345: long newp;
346: char twin[PBLKSIZ];
347: char *pag = db->pagbuf;
348: char *new = twin;
349: register int smax = SPLTMAX;
1.5 moko 350: pa_status_t status;
1.1 paf 351:
352: do {
353: /*
354: * split the current page
355: */
356: (void) splpage(pag, new, db->hmask + 1);
357: /*
358: * address of the new page
359: */
360: newp = (hash & db->hmask) | (db->hmask + 1);
361:
362: /*
363: * write delay, read avoidence/cache shuffle:
364: * select the page for incoming pair: if key is to go to the new page,
365: * write out the previous one, and copy the new one over, thus making
366: * it the current page. If not, simply write the new page, and we are
367: * still looking at the page of interest. current page is not updated
368: * here, as sdbm_store will do so, after it inserts the incoming pair.
369: */
370: if (hash & (db->hmask + 1)) {
371: if ((status = write_page(db, db->pagbuf, db->pagbno))
1.5 moko 372: != PA_SUCCESS)
1.1 paf 373: return status;
374:
375: db->pagbno = newp;
376: (void) memcpy(pag, new, PBLKSIZ);
377: }
378: else {
1.5 moko 379: if ((status = write_page(db, new, newp)) != PA_SUCCESS)
1.1 paf 380: return status;
381: }
382:
1.5 moko 383: if ((status = setdbit(db, db->curbit)) != PA_SUCCESS)
1.1 paf 384: return status;
385: /*
386: * see if we have enough room now
387: */
388: if (fitpair(pag, need))
1.5 moko 389: return PA_SUCCESS;
1.1 paf 390: /*
391: * try again... update curbit and hmask as getpage would have
392: * done. because of our update of the current page, we do not
393: * need to read in anything. BUT we have to write the current
394: * [deferred] page out, as the window of failure is too great.
395: */
396: db->curbit = 2 * db->curbit
397: + ((hash & (db->hmask + 1)) ? 2 : 1);
398: db->hmask |= db->hmask + 1;
399:
400: if ((status = write_page(db, db->pagbuf, db->pagbno))
1.5 moko 401: != PA_SUCCESS)
1.1 paf 402: return status;
403:
404: } while (--smax);
405:
406: /*
407: * if we are here, this is real bad news. After SPLTMAX splits,
408: * we still cannot fit the key. say goodnight.
409: */
410: #if 0
411: (void) write(2, "sdbm: cannot insert after SPLTMAX attempts.\n", 44);
412: #endif
413: /* ### ENOSPC not really appropriate but better than nothing */
1.5 moko 414: return PA_ENOSPC;
1.1 paf 415:
416: }
417:
418: /* Reads 'len' bytes from file 'f' at offset 'off' into buf.
419: * 'off' is given relative to the start of the file.
420: * If EOF is returned while reading, this is taken as success.
421: */
1.5 moko 422: static pa_status_t read_from(pa_file_t *f, void *buf,
423: pa_off_t off, pa_size_t len)
1.1 paf 424: {
1.5 moko 425: pa_status_t status;
1.1 paf 426:
1.5 moko 427: if ((status = pa_file_seek(f, PA_SET, &off)) != PA_SUCCESS ||
428: ((status = pa_file_read_full(f, buf, len, NULL)) != PA_SUCCESS)) {
1.1 paf 429: /* if EOF is reached, pretend we read all zero's */
1.5 moko 430: if (status == PA_EOF) {
1.1 paf 431: memset(buf, 0, len);
1.5 moko 432: status = PA_SUCCESS;
1.1 paf 433: }
434: }
435:
436: return status;
437: }
438:
439: /*
440: * the following two routines will break if
441: * deletions aren't taken into account. (ndbm bug)
442: */
1.5 moko 443: pa_status_t pa_sdbm_firstkey(pa_sdbm_t *db,
444: pa_sdbm_datum_t *key)
1.1 paf 445: {
1.5 moko 446: pa_status_t status;
1.1 paf 447:
1.5 moko 448: if ((status = pa_sdbm_lock(db, PA_FLOCK_SHARED)) != PA_SUCCESS)
1.1 paf 449: return status;
450:
451: /*
452: * start at page 0
453: */
454: if ((status = read_from(db->pagf, db->pagbuf, OFF_PAG(0), PBLKSIZ))
1.5 moko 455: == PA_SUCCESS) {
1.1 paf 456: db->pagbno = 0;
457: db->blkptr = 0;
458: db->keyptr = 0;
459: status = getnext(key, db);
460: }
461:
1.5 moko 462: (void) pa_sdbm_unlock(db);
1.1 paf 463:
464: return status;
465: }
466:
1.5 moko 467: pa_status_t pa_sdbm_nextkey(pa_sdbm_t *db,
468: pa_sdbm_datum_t *key)
1.1 paf 469: {
1.5 moko 470: pa_status_t status;
1.1 paf 471:
1.5 moko 472: if ((status = pa_sdbm_lock(db, PA_FLOCK_SHARED)) != PA_SUCCESS)
1.1 paf 473: return status;
474:
475: status = getnext(key, db);
476:
1.5 moko 477: (void) pa_sdbm_unlock(db);
1.1 paf 478:
479: return status;
480: }
481:
482: /*
483: * all important binary tree traversal
484: */
1.5 moko 485: static pa_status_t getpage(pa_sdbm_t *db, long hash)
1.1 paf 486: {
487: register int hbit;
488: register long dbit;
489: register long pagb;
1.5 moko 490: pa_status_t status;
1.1 paf 491:
492: dbit = 0;
493: hbit = 0;
494: while (dbit < db->maxbno && getdbit(db, dbit))
495: dbit = 2 * dbit + ((hash & (1 << hbit++)) ? 2 : 1);
496:
497: debug(("dbit: %d...", dbit));
498:
499: db->curbit = dbit;
500: db->hmask = masks[hbit];
501:
502: pagb = hash & db->hmask;
503: /*
504: * see if the block we need is already in memory.
505: * note: this lookaside cache has about 10% hit rate.
506: */
507: if (pagb != db->pagbno) {
508: /*
509: * note: here, we assume a "hole" is read as 0s.
510: * if not, must zero pagbuf first.
511: * ### joe: this assumption was surely never correct? but
512: * ### we make it so in read_from anyway.
513: */
514: if ((status = read_from(db->pagf, db->pagbuf, OFF_PAG(pagb), PBLKSIZ))
1.5 moko 515: != PA_SUCCESS)
1.1 paf 516: return status;
517:
518: if (!chkpage(db->pagbuf))
1.5 moko 519: return PA_ENOSPC; /* ### better error? */
1.1 paf 520: db->pagbno = pagb;
521:
522: debug(("pag read: %d\n", pagb));
523: }
1.5 moko 524: return PA_SUCCESS;
1.1 paf 525: }
526:
1.5 moko 527: static int getdbit(pa_sdbm_t *db, long dbit)
1.1 paf 528: {
529: register long c;
530: register long dirb;
531:
532: c = dbit / BYTESIZ;
533: dirb = c / DBLKSIZ;
534:
535: if (dirb != db->dirbno) {
536: if (read_from(db->dirf, db->dirbuf, OFF_DIR(dirb), DBLKSIZ)
1.5 moko 537: != PA_SUCCESS)
1.1 paf 538: return 0;
539:
540: db->dirbno = dirb;
541:
542: debug(("dir read: %d\n", dirb));
543: }
544:
545: return db->dirbuf[c % DBLKSIZ] & (1 << dbit % BYTESIZ);
546: }
547:
1.5 moko 548: static pa_status_t setdbit(pa_sdbm_t *db, long dbit)
1.1 paf 549: {
550: register long c;
551: register long dirb;
1.5 moko 552: pa_status_t status;
553: pa_off_t off;
1.1 paf 554:
555: c = dbit / BYTESIZ;
556: dirb = c / DBLKSIZ;
557:
558: if (dirb != db->dirbno) {
559: if ((status = read_from(db->dirf, db->dirbuf, OFF_DIR(dirb), DBLKSIZ))
1.5 moko 560: != PA_SUCCESS)
1.1 paf 561: return status;
562:
563: db->dirbno = dirb;
564:
565: debug(("dir read: %d\n", dirb));
566: }
567:
568: db->dirbuf[c % DBLKSIZ] |= (1 << dbit % BYTESIZ);
569:
570: if (dbit >= db->maxbno)
571: db->maxbno += DBLKSIZ * BYTESIZ;
572:
573: off = OFF_DIR(dirb);
1.5 moko 574: if ((status = pa_file_seek(db->dirf, PA_SET, &off)) == PA_SUCCESS)
575: status = pa_file_write_full(db->dirf, db->dirbuf, DBLKSIZ, NULL);
1.1 paf 576:
577: return status;
578: }
579:
580: /*
581: * getnext - get the next key in the page, and if done with
582: * the page, try the next page in sequence
583: */
1.5 moko 584: static pa_status_t getnext(pa_sdbm_datum_t *key, pa_sdbm_t *db)
1.1 paf 585: {
1.5 moko 586: pa_status_t status;
1.1 paf 587: for (;;) {
588: db->keyptr++;
589: *key = getnkey(db->pagbuf, db->keyptr);
590: if (key->dptr != NULL)
1.5 moko 591: return PA_SUCCESS;
1.1 paf 592: /*
593: * we either run out, or there is nothing on this page..
594: * try the next one... If we lost our position on the
595: * file, we will have to seek.
596: */
597: db->keyptr = 0;
598: if (db->pagbno != db->blkptr++) {
1.5 moko 599: pa_off_t off = OFF_PAG(db->blkptr);
600: if ((status = pa_file_seek(db->pagf, PA_SET, &off)
601: != PA_SUCCESS))
1.1 paf 602: return status;
603: }
604:
605: db->pagbno = db->blkptr;
606: /* ### EOF acceptable here too? */
1.5 moko 607: if ((status = pa_file_read_full(db->pagf, db->pagbuf, PBLKSIZ, NULL))
608: != PA_SUCCESS)
1.1 paf 609: return status;
610: if (!chkpage(db->pagbuf))
1.5 moko 611: return PA_EGENERAL; /* ### need better error */
1.1 paf 612: }
613:
614: /* NOTREACHED */
615: }
616:
617:
1.5 moko 618: int pa_sdbm_rdonly(pa_sdbm_t *db)
1.1 paf 619: {
620: /* ### Should we return true if the first lock is a share lock,
1.5 moko 621: * to reflect that pa_sdbm_store and pa_sdbm_delete will fail?
1.1 paf 622: */
623: return (db->flags & SDBM_RDONLY) != 0;
624: }
625:
E-mail: