xref: /illumos-gate/usr/src/lib/libc/port/gen/ndbm.c (revision dd72704bd9e794056c558153663c739e2012d721)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  * Copyright (c) 2016 by Delphix. All rights reserved.
26  */
27 
28 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29 /*	  All Rights Reserved	*/
30 
31 /*
32  * University Copyright- Copyright (c) 1982, 1986, 1988
33  * The Regents of the University of California
34  * All Rights Reserved
35  *
36  * University Acknowledgment- Portions of this document are derived from
37  * software developed by the University of California, Berkeley, and its
38  * contributors.
39  */
40 
41 #include "lint.h"
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <fcntl.h>
45 #include <sys/file.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <errno.h>
49 #include <ndbm.h>
50 #include <unistd.h>
51 #include <string.h>
52 #include <pthread.h>
53 
54 /* add support for batched writing for NIS */
55 
56 #define	_DBM_DEFWRITE 0x4
57 #define	_DBM_DIRTY 0x8
58 #define	_DBM_DIRDIRTY 0x10
59 #define	dbm_dirty(db) ((db)->dbm_flags & _DBM_DIRTY)
60 #define	dbm_dirdirty(db) ((db)->dbm_flags & _DBM_DIRDIRTY)
61 #define	dbm_defwrite(db) ((db)->dbm_flags & _DBM_DEFWRITE)
62 #define	dbm_setdirty(db) (db)->dbm_flags |= _DBM_DIRTY
63 #define	dbm_clrdirty(db) (db)->dbm_flags &= ~_DBM_DIRTY
64 #define	dbm_setdirdirty(db) (db)->dbm_flags |= _DBM_DIRDIRTY
65 #define	dbm_clrdirdirty(db) (db)->dbm_flags &= ~_DBM_DIRDIRTY
66 
67 /*
68  * forward declarations
69  */
70 static datum makdatum(char *, int);
71 static unsigned long dcalchash(datum);
72 static void dbm_access(DBM *, unsigned long);
73 static int finddatum(char *, datum);
74 static int delitem(char *, int);
75 static int additem(char *, datum, datum);
76 static int cmpdatum(datum, datum);
77 static int setbit(DBM *);
78 static int getbit(DBM *);
79 static int dbm_flushdir(DBM *);
80 static int dbm_flushpag(DBM *db);
81 
82 /* the following three are required by mapfile-vers */
83 datum dbm_do_nextkey(DBM *, datum);
84 int dbm_close_status(DBM *);
85 
86 /* used to make a dbm file all at once instead of incrementally */
87 void
88 dbm_setdefwrite(DBM *db)
89 {
90 	db->dbm_flags |= _DBM_DEFWRITE;
91 }
92 
93 #undef	dbm_error
94 
95 int
96 dbm_error(DBM *db)
97 {
98 	return (db->dbm_flags & _DBM_IOERR);
99 }
100 
101 #undef	dbm_clearerr
102 
103 int
104 dbm_clearerr(DBM *db)
105 {
106 	return (db->dbm_flags &= ~_DBM_IOERR);
107 }
108 
109 int
110 dbm_flush(DBM *db)
111 {
112 	int ok = 0;
113 	if (dbm_flushpag(db) < 0)
114 		ok = -1;
115 	if (dbm_flushdir(db) < 0)
116 		ok = -1;
117 	return (ok);
118 }
119 
120 static int
121 dbm_flushpag(DBM *db)
122 {
123 	int ok = 0;
124 	off64_t where;
125 
126 	if (dbm_dirty(db)) { /* must page out the page */
127 		where = (((off64_t)db->dbm_pagbno) * PBLKSIZ);
128 		if ((lseek64(db->dbm_pagf, where, L_SET) != where) ||
129 		    (write(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) != PBLKSIZ)) {
130 			db->dbm_flags |= _DBM_IOERR;
131 			ok = -1;
132 		}
133 		dbm_clrdirty(db);
134 	}
135 	return (ok);
136 }
137 
138 static int
139 dbm_flushdir(DBM *db)
140 {
141 	int ok = 0;
142 	off64_t where;
143 	if (dbm_dirdirty(db)) { /* must page out the dir */
144 		where = (((off64_t)db->dbm_dirbno) * DBLKSIZ);
145 		if ((lseek64(db->dbm_dirf, where, L_SET) != where) ||
146 		    (write(db->dbm_dirf, db->dbm_dirbuf, DBLKSIZ) != DBLKSIZ)) {
147 			ok = -1;
148 		}
149 		dbm_clrdirdirty(db);
150 	}
151 	return (ok);
152 }
153 
154 #define	BYTESIZ 8
155 #undef setbit
156 
157 DBM *
158 dbm_open(const char *file, int flags, mode_t mode)
159 {
160 	struct stat64 statb;
161 	DBM *db;
162 	int	serrno;
163 
164 	if ((db = (DBM *)malloc(sizeof (*db))) == 0) {
165 		errno = ENOMEM;
166 		return ((DBM *)0);
167 	}
168 	db->dbm_flags = (flags & 03) == O_RDONLY ? _DBM_RDONLY : 0;
169 	if ((flags & 03) == O_WRONLY)
170 		flags = (flags & ~03) | O_RDWR;
171 	if (strlcpy(db->dbm_pagbuf, file, sizeof (db->dbm_pagbuf)) >=
172 	    sizeof (db->dbm_pagbuf) ||
173 	    strlcat(db->dbm_pagbuf, ".pag", sizeof (db->dbm_pagbuf)) >=
174 	    sizeof (db->dbm_pagbuf)) {
175 		/*
176 		 * file.pag does not fit into dbm_pagbuf.
177 		 * fail with ENAMETOOLONG.
178 		 */
179 		serrno = ENAMETOOLONG;
180 		goto bad;
181 	}
182 	db->dbm_pagf = open64(db->dbm_pagbuf, flags, mode);
183 	if (db->dbm_pagf < 0) {
184 		serrno = errno;
185 		goto bad;
186 	}
187 	/*
188 	 * We know this won't overflow so it is safe to ignore the
189 	 * return value; we use strl* to prevent false hits in
190 	 * code sweeps.
191 	 */
192 	(void) strlcpy(db->dbm_pagbuf, file, sizeof (db->dbm_pagbuf));
193 	(void) strlcat(db->dbm_pagbuf, ".dir", sizeof (db->dbm_pagbuf));
194 	db->dbm_dirf = open64(db->dbm_pagbuf, flags, mode);
195 	if (db->dbm_dirf < 0) {
196 		serrno = errno;
197 		goto bad1;
198 	}
199 	(void) fstat64(db->dbm_dirf, &statb);
200 	db->dbm_maxbno = statb.st_size * BYTESIZ-1;
201 	db->dbm_pagbno = db->dbm_dirbno = -1;
202 	return (db);
203 bad1:
204 	(void) close(db->dbm_pagf);
205 bad:
206 	free((char *)db);
207 	errno = serrno;
208 	return ((DBM *)0);
209 }
210 
211 void
212 dbm_close(DBM *db)
213 {
214 (void) dbm_close_status(db);
215 }
216 
217 /* close with return code */
218 int
219 dbm_close_status(DBM *db)
220 {
221 	int ok;
222 	ok = 0;
223 
224 	if (dbm_flush(db) < 0)
225 		ok = -1;
226 	if (close(db->dbm_dirf) < 0)
227 		ok = -1;
228 	if (close(db->dbm_pagf) < 0)
229 		ok = -1;
230 	free((char *)db);
231 	return (ok);
232 }
233 
234 long
235 dbm_forder(DBM *db, datum key)
236 {
237 	unsigned long hash;
238 
239 	hash = dcalchash(key);
240 	for (db->dbm_hmask = 0; ; db->dbm_hmask = (db->dbm_hmask<<1)+1) {
241 		db->dbm_blkno = hash & db->dbm_hmask;
242 		db->dbm_bitno = db->dbm_blkno + db->dbm_hmask;
243 		if (getbit(db) == 0)
244 			break;
245 	}
246 	return (db->dbm_blkno);
247 }
248 
249 datum
250 dbm_fetch(DBM *db, datum key)
251 {
252 	int i;
253 	datum item;
254 
255 	if (dbm_error(db))
256 		goto err;
257 	dbm_access(db, dcalchash(key));
258 	if ((i = finddatum(db->dbm_pagbuf, key)) >= 0) {
259 		item = makdatum(db->dbm_pagbuf, i+1);
260 		if (item.dptr != NULL)
261 			return (item);
262 	}
263 err:
264 	item.dptr = NULL;
265 	item.dsize = 0;
266 	return (item);
267 }
268 
269 int
270 dbm_delete(DBM *db, datum key)
271 {
272 	int i;
273 	off64_t where;
274 
275 	if (dbm_error(db))
276 		return (-1);
277 	if (dbm_rdonly(db)) {
278 		errno = EPERM;
279 		return (-1);
280 	}
281 	dbm_access(db, dcalchash(key));
282 	if ((i = finddatum(db->dbm_pagbuf, key)) < 0)
283 		return (-1);
284 	if (!delitem(db->dbm_pagbuf, i))
285 		goto err;
286 	db->dbm_pagbno = db->dbm_blkno;
287 	if (dbm_defwrite(db)) {
288 		dbm_setdirty(db);
289 	} else {
290 		where = (((off64_t)db->dbm_blkno) * PBLKSIZ);
291 		if ((lseek64(db->dbm_pagf, where, L_SET) != where) ||
292 		    (write(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) != PBLKSIZ)) {
293 			err:
294 				db->dbm_flags |= _DBM_IOERR;
295 				return (-1);
296 		}
297 	}
298 	return (0);
299 }
300 
301 int
302 dbm_store(DBM *db, datum key, datum dat, int replace)
303 {
304 	int i;
305 	datum item, item1;
306 	char ovfbuf[PBLKSIZ];
307 	unsigned long hashdiff, key_hash, item_hash;
308 	off64_t where;
309 
310 	if (dbm_error(db))
311 		return (-1);
312 	if (dbm_rdonly(db)) {
313 		errno = EPERM;
314 		return (-1);
315 	}
316 loop:
317 	key_hash = dcalchash(key);
318 	dbm_access(db, key_hash);
319 	if ((i = finddatum(db->dbm_pagbuf, key)) >= 0) {
320 		if (!replace)
321 			return (1);
322 		if (!delitem(db->dbm_pagbuf, i)) {
323 			db->dbm_flags |= _DBM_IOERR;
324 			return (-1);
325 		}
326 	}
327 	if (!additem(db->dbm_pagbuf, key, dat))
328 		goto split;
329 	db->dbm_pagbno = db->dbm_blkno;
330 	if (dbm_defwrite(db)) {
331 		dbm_setdirty(db);
332 	} else {
333 		where = (((off64_t)db->dbm_blkno) * PBLKSIZ);
334 		if ((lseek64(db->dbm_pagf, where, L_SET) != where) ||
335 		    (write(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) != PBLKSIZ)) {
336 			db->dbm_flags |= _DBM_IOERR;
337 			return (-1);
338 		}
339 	}
340 	return (0);
341 split:
342 	hashdiff = 0;
343 	if (key.dsize + dat.dsize + 3 * (int)sizeof (short) >= PBLKSIZ) {
344 		db->dbm_flags |= _DBM_IOERR;
345 		errno = ENOSPC;
346 		return (-1);
347 	}
348 	(void) memset(ovfbuf, 0, PBLKSIZ);
349 	for (i = 0; ; ) {
350 		item = makdatum(db->dbm_pagbuf, i);
351 		if (item.dptr == NULL)
352 			break;
353 		item_hash = dcalchash(item);
354 		if (item_hash != key_hash) {
355 			hashdiff++;
356 		}
357 
358 		if (item_hash & (db->dbm_hmask+1)) {
359 			item1 = makdatum(db->dbm_pagbuf, i+1);
360 			if (item1.dptr == NULL) {
361 				/* (void) fprintf(stderr, "ndbm: */
362 				/* split not paired\n"); */
363 				db->dbm_flags |= _DBM_IOERR;
364 				break;
365 			}
366 			if (!additem(ovfbuf, item, item1) ||
367 			    !delitem(db->dbm_pagbuf, i)) {
368 				db->dbm_flags |= _DBM_IOERR;
369 				return (-1);
370 			}
371 			continue;
372 		}
373 		i += 2;
374 	}
375 	db->dbm_pagbno = db->dbm_blkno;
376 	where = (((off64_t)db->dbm_blkno) * PBLKSIZ);
377 	if ((lseek64(db->dbm_pagf, where, L_SET) != where) ||
378 	    (write(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) != PBLKSIZ)) {
379 		db->dbm_flags |= _DBM_IOERR;
380 		return (-1);
381 	}
382 	dbm_clrdirty(db); /* clear dirty */
383 	where = (((off64_t)db->dbm_blkno + db->dbm_hmask + 1) * PBLKSIZ);
384 	if ((lseek64(db->dbm_pagf, where, L_SET) != where) ||
385 	    (write(db->dbm_pagf, ovfbuf, PBLKSIZ) != PBLKSIZ)) {
386 		db->dbm_flags |= _DBM_IOERR;
387 		return (-1);
388 	}
389 	if (setbit(db) < 0) {
390 		db->dbm_flags |= _DBM_IOERR;
391 		return (-1);
392 	}
393 	if (hashdiff == 0) {
394 		db->dbm_flags |= _DBM_IOERR;
395 		return (-1);
396 	}
397 	goto loop;
398 }
399 
400 static unsigned long
401 dbm_hashinc(DBM *db, unsigned long hash)
402 {
403 	long bit;
404 
405 	hash &= db->dbm_hmask;
406 	bit = db->dbm_hmask+1;
407 	for (;;) {
408 		bit >>= 1;
409 		if (bit == 0)
410 			return (0L);
411 		if ((hash&bit) == 0)
412 			return (hash|bit);
413 		hash &= ~bit;
414 	}
415 }
416 
417 
418 
419 static datum nullkey = {NULL, 0};
420 
421 datum
422 dbm_firsthash(DBM *db, unsigned long hash)
423 {
424 	int i, j;
425 	datum item, bitem;
426 
427 loop:
428 	dbm_access(db, hash);
429 	j = 0;
430 	bitem = makdatum(db->dbm_pagbuf, 0);
431 	for (i = 2; ; i += 2) {
432 		item = makdatum(db->dbm_pagbuf, i);
433 		if (item.dptr == NULL)
434 			break;
435 		if (cmpdatum(bitem, item) < 0) {
436 			j = i;
437 			bitem = item;
438 		}
439 	}
440 	if (bitem.dptr != NULL) {
441 		db->dbm_keyptr = j + 2;
442 		db->dbm_blkptr = db->dbm_blkno;
443 		return (bitem);
444 	}
445 	hash = dbm_hashinc(db, hash);
446 	if (hash == 0)
447 		return (item); /* null item */
448 	goto loop;
449 
450 }
451 
452 datum
453 dbm_firstkey(DBM *db)
454 {
455 	/*
456 	 * For some reason, the Posix specification (SUSv3)
457 	 * says that dbm_firstkey() is not a cancellation point.
458 	 * It really should be, but in order to pass the SUSv3
459 	 * test suite we make it not a cancellation point.
460 	 * XXX: fix me when the SUSv3 specification gets fixed.
461 	 */
462 	int cancel_state;
463 	datum rval;
464 
465 	db->dbm_blkptr = 0L;
466 	db->dbm_keyptr = 0;
467 	(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
468 	rval = dbm_firsthash(db, 0L);
469 	(void) pthread_setcancelstate(cancel_state, NULL);
470 	return (rval);
471 }
472 
473 datum
474 dbm_nextkey(DBM *db)
475 {
476 
477 	return (dbm_do_nextkey(db, nullkey));
478 }
479 
480 /*
481  * this is used if keyptr-2, blocknum doesn't point to the previous
482  * specific key allowing the fast hash order search --
483  * its use indicates user tampering with our state variables,
484  * which some evil users might do to search from some specific place.
485  * It finds the first key at or after blkptr, keyptr in block seq order
486  * this requires looking at all sorts of emtpy blocks in many cases
487  */
488 
489 static
490 datum
491 dbm_slow_nextkey(DBM *db)
492 {
493 
494 	struct stat64 statb;
495 	datum item;
496 	off64_t where;
497 
498 	if (dbm_error(db) || fstat64(db->dbm_pagf, &statb) < 0)
499 		goto err;
500 	statb.st_size /= PBLKSIZ;
501 
502 	for (;;) {
503 		if (db->dbm_blkptr != db->dbm_pagbno) {
504 
505 			if (dbm_dirty(db)) (void) dbm_flushpag(db);
506 
507 			db->dbm_pagbno = db->dbm_blkptr;
508 			where = (((off64_t)db->dbm_blkptr) * PBLKSIZ);
509 			if ((lseek64(db->dbm_pagf, where, L_SET) != where) ||
510 			    (read(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) !=
511 			    PBLKSIZ))
512 				(void) memset(db->dbm_pagbuf, 0, PBLKSIZ);
513 #ifdef DEBUG
514 			else if (chkblk(db->dbm_pagbuf) < 0)
515 				db->dbm_flags |= _DBM_IOERR;
516 #endif
517 		}
518 		/* Am I an empty block? */
519 		if (((short *)(uintptr_t)db->dbm_pagbuf)[0] != 0) {
520 			item = makdatum(db->dbm_pagbuf, db->dbm_keyptr);
521 			if (item.dptr != NULL) {
522 				db->dbm_keyptr += 2;
523 				return (item);
524 			}
525 			db->dbm_keyptr = 0;
526 		}
527 		/* go to next sequential block */
528 		if (++db->dbm_blkptr >= statb.st_size)
529 			break;
530 	}
531 err:
532 	item.dptr = NULL;
533 	item.dsize = 0;
534 	return (item);
535 }
536 
537 
538 
539 datum
540 dbm_do_nextkey(DBM *db, datum inkey)
541 {
542 	datum item, bitem;
543 	unsigned long hash;
544 	datum key;
545 	int f;
546 	int i;
547 	int j;
548 	short *sp;
549 	long n;
550 	char *p1, *p2;
551 	off64_t where;
552 
553 	if (dbm_error(db)) {
554 		item.dptr = NULL;
555 		item.dsize = 0;
556 		return (item);
557 	}
558 
559 	/* user has supplied lastkey */
560 
561 	if (inkey.dptr != NULL) {
562 		dbm_access(db, (hash = dcalchash(inkey)));
563 		if ((i = finddatum(db->dbm_pagbuf, inkey)) >= 0) {
564 			db->dbm_keyptr = i + 2;
565 			db->dbm_blkptr = db->dbm_blkno;
566 		}
567 		key = inkey;
568 	} else  {
569 		/* is this a manual firstkey request? */
570 
571 		if (db->dbm_blkptr == 0L &&
572 		    db->dbm_keyptr == 0)
573 			return (dbm_firsthash(db, 0L));
574 
575 		/* no -- get lastkey this is like dbm_access by blkptr */
576 
577 		if (db->dbm_blkptr != db->dbm_pagbno) {
578 
579 			if (dbm_dirty(db)) (void) dbm_flushpag(db);
580 			db->dbm_pagbno = db->dbm_blkptr;
581 			where = (((off64_t)db->dbm_blkptr) * PBLKSIZ);
582 			if ((lseek64(db->dbm_pagf, where, L_SET) != where) ||
583 			    (read(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) !=
584 			    PBLKSIZ))
585 				(void) memset(db->dbm_pagbuf, 0, PBLKSIZ);
586 #ifdef DEBUG
587 			else if (chkblk(db->dbm_pagbuf) < 0)
588 			db->dbm_flags |= _DBM_IOERR;
589 #endif
590 		}
591 		/* now just make up last key datum */
592 		if (db->dbm_keyptr >= 2)
593 			key = makdatum(db->dbm_pagbuf, (db->dbm_keyptr-2));
594 		else key = nullkey;
595 
596 		/*
597 		 * the keyptr pagbuf have failed us, the user must
598 		 * be an extra clever moron who depends on
599 		 * these variables and their former meaning.
600 		 * If we set the variables this would have got
601 		 * us the key for sure! So give it the old algorithm.
602 		 */
603 		if (key.dptr == NULL)
604 			return (dbm_slow_nextkey(db));
605 	}
606 
607 	/*
608 	 * at this point the last key is paged in and
609 	 * we can proceed as in old dbm -- like Ken did his.
610 	 */
611 
612 	f = 1;
613 	j = 0;
614 	sp = (short *)(uintptr_t)db->dbm_pagbuf;
615 
616 	for (i = 0; ; i += 2) {
617 
618 		/* begin put makdatum inline */
619 
620 		if ((unsigned)i >= sp[0]) {
621 			item.dptr = NULL;
622 			item.dsize = 0;
623 			break; /* from below */
624 		} else {
625 			if (i > 0) item.dsize = sp[i] - sp[i + 1];
626 			else item.dsize = PBLKSIZ - sp[i + 1];
627 			item.dptr = db->dbm_pagbuf+sp[i + 1];
628 		}
629 
630 		/* item = makdatum(db->dbm_pagbuf, i); */
631 		/* end put makdatum inline */
632 
633 		if (item.dptr == NULL)
634 			break;
635 /* inline cmpdatum */
636 
637 
638 		n = key.dsize;
639 		if (n != item.dsize) {
640 			if ((n - item.dsize) <= 0)
641 				continue;
642 		} else {
643 			if (n == 0) continue;
644 			p1 = key.dptr;
645 			p2 = item.dptr;
646 			do {
647 				if (*p1++ != *p2++)
648 					if ((*--p1 - *--p2) > 0)
649 						goto keep_going;
650 					else continue;
651 			} while (--n);
652 			continue;
653 		}
654 
655 keep_going:
656 
657 /* end inline cmpdatum */
658 		/*
659 		 * if (cmpdatum(key, item) <= 0)
660 		 *	continue;
661 		 */
662 
663 		if (f) {
664 			bitem = item;
665 			j = i;
666 			f = 0;
667 		} else {
668 
669 			/* if (cmpdatum(bitem, item) < 0) */
670 
671 			n = bitem.dsize;
672 			if (n != item.dsize) {
673 				if ((n - item.dsize) < 0) {
674 					bitem = item;
675 					j = i;
676 				}
677 			} else
678 				if (n != 0) {
679 					p1 = bitem.dptr;
680 					p2 = item.dptr;
681 					do {
682 					if (*p1++ != *p2++) {
683 						if ((*--p1 - *--p2) < 0) {
684 							bitem = item;
685 							j = i;
686 						}
687 						break;
688 					}
689 					} while (--n);
690 				}
691 			}
692 	}
693 
694 	if (f == 0) {
695 		db->dbm_keyptr = j + 2;
696 		db->dbm_blkptr = db->dbm_blkno;
697 		return (bitem);
698 	}
699 
700 	/* really need hash at this point */
701 	/* if it gave us a key we have already calculated the hash */
702 	/* if not get the hash */
703 	if (inkey.dptr == NULL)
704 		hash = dcalchash(key);
705 	hash = dbm_hashinc(db, hash);
706 
707 	if (hash == 0)
708 		return (item); /* null */
709 	/* get first item on next page in hash table order */
710 	return (dbm_firsthash(db, hash));
711 
712 
713 }
714 
715 static void
716 dbm_access(DBM *db, unsigned long hash)
717 {
718 	long b, i, n;
719 	long bn;
720 	long my_bitno;
721 	long my_hmask;
722 	long my_blkno;
723 	int j = (sizeof (my_hmask)) * 8;
724 	off64_t where;
725 
726 	for (my_hmask = 0; j-- > 0; my_hmask = (my_hmask<<1) + 1) {
727 		my_blkno = hash & my_hmask;
728 		my_bitno = my_blkno + my_hmask;
729 		/* getbit inline */
730 		if (my_bitno > db->dbm_maxbno) break;
731 		n = my_bitno % BYTESIZ;
732 		bn = my_bitno / BYTESIZ;
733 		i = bn % DBLKSIZ;
734 		b = bn / DBLKSIZ;
735 		if (b != db->dbm_dirbno) {
736 			if (dbm_dirdirty(db))
737 				(void) dbm_flushdir(db); /* must flush */
738 			db->dbm_dirbno = b;
739 			where = (((off64_t)b) * DBLKSIZ);
740 			if ((lseek64(db->dbm_dirf, where, L_SET) != where) ||
741 			    (read(db->dbm_dirf, db->dbm_dirbuf, DBLKSIZ) !=
742 			    DBLKSIZ))
743 				(void) memset(db->dbm_dirbuf, 0, DBLKSIZ);
744 		}
745 		if ((db->dbm_dirbuf[i] & (1<<n)) == 0) break;
746 
747 		/*
748 		 * if (getbit(db) == 0)
749 		 *	break;
750 		 */
751 	}
752 	/* copy */
753 	db->dbm_blkno = my_blkno;
754 	db->dbm_bitno = my_bitno;
755 	db->dbm_hmask = my_hmask;
756 
757 	if (my_blkno != db->dbm_pagbno) {
758 		if (dbm_dirty(db)) { /* must page out the page */
759 			where = (((off64_t)db->dbm_pagbno) * PBLKSIZ);
760 			if ((lseek64(db->dbm_pagf, where, L_SET) != where) ||
761 			    (write(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) !=
762 			    PBLKSIZ)) {
763 				db->dbm_flags |= _DBM_IOERR;
764 			}
765 		dbm_clrdirty(db);
766 		}
767 
768 		db->dbm_pagbno = my_blkno;
769 		where = (((off64_t)my_blkno) * PBLKSIZ);
770 		if ((lseek64(db->dbm_pagf, where, L_SET) != where) ||
771 		    (read(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) != PBLKSIZ))
772 			(void) memset(db->dbm_pagbuf, 0, PBLKSIZ);
773 #ifdef DEBUG
774 		else if (chkblk(db->dbm_pagbuf) < 0)
775 			db->dbm_flags |= _DBM_IOERR;
776 #endif
777 	}
778 }
779 
780 static int
781 getbit(DBM *db)
782 {
783 	long bn;
784 	long b, i, n;
785 	off64_t where;
786 
787 	if (db->dbm_bitno > db->dbm_maxbno)
788 		return (0);
789 	n = db->dbm_bitno % BYTESIZ;
790 	bn = db->dbm_bitno / BYTESIZ;
791 	i = bn % DBLKSIZ;
792 	b = bn / DBLKSIZ;
793 	if (b != db->dbm_dirbno) {
794 		if (dbm_dirdirty(db))
795 			(void) dbm_flushdir(db); /* must flush */
796 		db->dbm_dirbno = b;
797 		where = (((off64_t)b) * DBLKSIZ);
798 		if ((lseek64(db->dbm_dirf, where, L_SET) != where) ||
799 		    (read(db->dbm_dirf, db->dbm_dirbuf, DBLKSIZ) != DBLKSIZ))
800 			(void) memset(db->dbm_dirbuf, 0, DBLKSIZ);
801 	}
802 	return (db->dbm_dirbuf[i] & (1<<n));
803 }
804 
805 static int
806 setbit(DBM *db)
807 {
808 	long bn;
809 	long i, n, b;
810 	off64_t where;
811 
812 	if (db->dbm_bitno > db->dbm_maxbno)
813 		db->dbm_maxbno = db->dbm_bitno;
814 	n = db->dbm_bitno % BYTESIZ;
815 	bn = db->dbm_bitno / BYTESIZ;
816 	i = bn % DBLKSIZ;
817 	b = bn / DBLKSIZ;
818 	if (b != db->dbm_dirbno) {
819 		if (dbm_dirdirty(db))
820 			(void) dbm_flushdir(db);
821 		db->dbm_dirbno = b;
822 		where = (((off64_t)b) * DBLKSIZ);
823 		if ((lseek64(db->dbm_dirf, where, L_SET) != where) ||
824 		    (read(db->dbm_dirf, db->dbm_dirbuf, DBLKSIZ) != DBLKSIZ))
825 			(void) memset(db->dbm_dirbuf, 0, DBLKSIZ);
826 	}
827 	db->dbm_dirbuf[i] |= 1<<n;
828 	db->dbm_dirbno = b;
829 	if (dbm_defwrite(db)) {
830 		dbm_setdirdirty(db);
831 	} else {
832 		where = (((off64_t)b) * DBLKSIZ);
833 		if ((lseek64(db->dbm_dirf, where, L_SET) != where) ||
834 		    (write(db->dbm_dirf, db->dbm_dirbuf, DBLKSIZ) != DBLKSIZ)) {
835 			return (-1);
836 		}
837 	}
838 	return (0);
839 }
840 
841 static datum
842 makdatum(char *buf, int n)
843 {
844 	short *sp;
845 	int t;
846 	datum item;
847 
848 	sp = (short *)(uintptr_t)buf;
849 	if ((unsigned)n >= sp[0]) {
850 		item.dptr = NULL;
851 		item.dsize = 0;
852 		return (item);
853 	}
854 	t = PBLKSIZ;
855 	if (n > 0)
856 		t = sp[n];
857 	item.dptr = buf + sp[n + 1];
858 	item.dsize = t - sp[n + 1];
859 	return (item);
860 }
861 
862 static int
863 cmpdatum(datum d1, datum d2)
864 {
865 	long n;
866 	char *p1, *p2;
867 
868 	n = d1.dsize;
869 	if (n != d2.dsize)
870 		return ((int)(n - d2.dsize));
871 	if (n == 0)
872 		return (0);
873 	p1 = d1.dptr;
874 	p2 = d2.dptr;
875 	do {
876 		if (*p1 != *p2)
877 			return (*p1 - *p2);
878 		else {
879 			p1++;
880 			p2++;
881 		}
882 	} while (--n);
883 	return (0);
884 }
885 
886 static int
887 finddatum(char *buf, datum item)
888 {
889 	short *sp;
890 	int i, n, j;
891 
892 	sp = (short *)(uintptr_t)buf;
893 	n = PBLKSIZ;
894 	for (i = 0, j = sp[0]; i < j; i += 2, n = sp[i]) {
895 		n -= sp[i + 1];
896 		if (n != item.dsize)
897 			continue;
898 		if (n == 0 || memcmp(&buf[sp[i+1]], item.dptr, n) == 0)
899 			return (i);
900 	}
901 	return (-1);
902 }
903 
904 static const int hitab[16]
905 /*
906  * ken's
907  * {
908  *	055, 043, 036, 054, 063, 014, 004, 005,
909  *	010, 064, 077, 000, 035, 027, 025, 071,
910  * };
911  */
912 	= {    61, 57, 53, 49, 45, 41, 37, 33,
913 		29, 25, 21, 17, 13,  9,  5,  1,
914 };
915 
916 /* could consider to make it 32-bit int table to minimize memory usage */
917 static const long hltab[64]
918 	= {
919 	06100151277L, 06106161736L, 06452611562L, 05001724107L,
920 	02614772546L, 04120731531L, 04665262210L, 07347467531L,
921 	06735253126L, 06042345173L, 03072226605L, 01464164730L,
922 	03247435524L, 07652510057L, 01546775256L, 05714532133L,
923 	06173260402L, 07517101630L, 02431460343L, 01743245566L,
924 	00261675137L, 02433103631L, 03421772437L, 04447707466L,
925 	04435620103L, 03757017115L, 03641531772L, 06767633246L,
926 	02673230344L, 00260612216L, 04133454451L, 00615531516L,
927 	06137717526L, 02574116560L, 02304023373L, 07061702261L,
928 	05153031405L, 05322056705L, 07401116734L, 06552375715L,
929 	06165233473L, 05311063631L, 01212221723L, 01052267235L,
930 	06000615237L, 01075222665L, 06330216006L, 04402355630L,
931 	01451177262L, 02000133436L, 06025467062L, 07121076461L,
932 	03123433522L, 01010635225L, 01716177066L, 05161746527L,
933 	01736635071L, 06243505026L, 03637211610L, 01756474365L,
934 	04723077174L, 03642763134L, 05750130273L, 03655541561L,
935 };
936 
937 static unsigned long
938 dcalchash(datum item)
939 {
940 	long s;
941 	int c, j;
942 	char *cp;
943 	unsigned long hashl;
944 	long hashi;
945 
946 	hashl = 0;
947 	hashi = 0;
948 	for (cp = item.dptr, s = item.dsize; --s >= 0; ) {
949 		c = *cp++;
950 		for (j = 0; j < BYTESIZ; j += 4) {
951 			hashi += hitab[c&017];
952 			hashl += hltab[hashi&63];
953 			c >>= 4;
954 		}
955 	}
956 	return (hashl);
957 }
958 
959 /*
960  * Delete pairs of items (n & n+1).
961  */
962 static int
963 delitem(char *buf, int n)
964 {
965 	short *sp, *sp1;
966 	int i1, i2;
967 
968 	sp = (short *)(uintptr_t)buf;
969 	i2 = sp[0];
970 	if ((unsigned)n >= i2 || (n & 1))
971 		return (0);
972 	if (n == i2-2) {
973 		sp[0] -= 2;
974 		return (1);
975 	}
976 	i1 = PBLKSIZ;
977 	if (n > 0)
978 		i1 = sp[n];
979 	i1 -= sp[n+2];
980 	if (i1 > 0) {
981 		i2 = sp[i2];
982 		(void) memmove(&buf[i2 + i1], &buf[i2], sp[n+2] - i2);
983 	}
984 	sp[0] -= 2;
985 	for (sp1 = sp + sp[0], sp += n+1; sp <= sp1; sp++)
986 		sp[0] = sp[2] + i1;
987 	return (1);
988 }
989 
990 /*
991  * Add pairs of items (item & item1).
992  */
993 static int
994 additem(char *buf, datum item, datum item1)
995 {
996 	short *sp;
997 	int i1, i2;
998 
999 	sp = (short *)(uintptr_t)buf;
1000 	i1 = PBLKSIZ;
1001 	i2 = sp[0];
1002 	if (i2 > 0)
1003 		i1 = sp[i2];
1004 	i1 -= item.dsize + item1.dsize;
1005 	if (i1 <= (i2+3) * (int)sizeof (short))
1006 		return (0);
1007 	sp[0] += 2;
1008 	sp[++i2] = (short)(i1 + item1.dsize);
1009 	(void) memmove(&buf[i1 + item1.dsize], item.dptr, item.dsize);
1010 	sp[++i2] = i1;
1011 	(void) memmove(&buf[i1], item1.dptr, item1.dsize);
1012 	return (1);
1013 }
1014 
1015 #ifdef DEBUG
1016 static int
1017 chkblk(char buf[PBLKSIZ])
1018 {
1019 	short *sp;
1020 	int t, i;
1021 
1022 	sp = (short *)buf;
1023 	t = PBLKSIZ;
1024 	for (i = 0; i < sp[0]; i++) {
1025 		if (sp[i + 1] > t)
1026 			return (-1);
1027 		t = sp[i + 1];
1028 	}
1029 	if (t < (sp[0] + 1) * sizeof (short))
1030 		return (-1);
1031 	return (0);
1032 }
1033 #endif
1034