2 * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 * Copyright (c) 2016 by Delphix. All rights reserved.
8 * Copyright (c) 1983 Regents of the University of California.
9 * All rights reserved. The Berkeley software License Agreement
10 * specifies the terms and conditions for redistribution.
13 #pragma ident "%Z%%M% %I% %E% SMI"
15 #include <sys/types.h>
24 datum
dbm_do_nextkey(DBM
*, datum
);
27 /*add support for batched writing for NIS*/
29 #define _DBM_DEFWRITE 0x4
30 #define _DBM_DIRTY 0x8
31 #define _DBM_DIRDIRTY 0x10
32 #define dbm_dirty(db) ((db)->dbm_flags & _DBM_DIRTY)
33 #define dbm_dirdirty(db) ((db)->dbm_flags & _DBM_DIRDIRTY)
34 #define dbm_defwrite(db) ((db)->dbm_flags & _DBM_DEFWRITE)
35 #define dbm_setdirty(db) (db)->dbm_flags |= _DBM_DIRTY
36 #define dbm_clrdirty(db) (db)->dbm_flags &= ~_DBM_DIRTY
37 #define dbm_setdirdirty(db) (db)->dbm_flags |= _DBM_DIRDIRTY
38 #define dbm_clrdirdirty(db) (db)->dbm_flags &= ~_DBM_DIRDIRTY
41 static void dbm_access(DBM
*, long);
42 static int getbit(DBM
*);
43 static int setbit(DBM
*);
44 static int cmpdatum(datum
, datum
);
45 static int finddatum(char [PBLKSIZ
], datum
);
46 static int delitem(char [PBLKSIZ
], int);
47 static int additem(char [PBLKSIZ
], datum
, datum
);
48 static datum
makdatum(char [PBLKSIZ
], int);
49 static long dcalchash(datum
);
51 /*used to make a dbm file all at once instead of incrementally*/
53 dbm_setdefwrite(DBM
*db
)
55 db
->dbm_flags
|= _DBM_DEFWRITE
;
62 if (dbm_flushpag(db
)<0) ok
= -1;
63 if (dbm_flushdir(db
)<0) ok
= -1;
71 if (dbm_dirty(db
)){ /*must page out the page*/
72 (void) lseek(db
->dbm_pagf
, (long)(db
->dbm_pagbno
*PBLKSIZ
), L_SET
);
73 if (write(db
->dbm_pagf
, db
->dbm_pagbuf
, PBLKSIZ
) != PBLKSIZ
) {
74 db
->dbm_flags
|= _DBM_IOERR
;
86 if (dbm_dirdirty(db
)){ /*must page out the dir*/
87 (void) lseek(db
->dbm_dirf
, (long)(db
->dbm_dirbno
*DBLKSIZ
), L_SET
);
88 if (write(db
->dbm_dirf
, db
->dbm_dirbuf
, DBLKSIZ
) != DBLKSIZ
) {
99 dbm_open(char *file
, int flags
, int mode
)
105 if ((db
= (DBM
*)malloc(sizeof *db
)) == 0) {
109 db
->dbm_flags
= (flags
& 03) == O_RDONLY
? _DBM_RDONLY
: 0;
110 if ((flags
& 03) == O_WRONLY
)
111 flags
= (flags
& ~03) | O_RDWR
;
112 if (strlcpy(db
->dbm_pagbuf
, file
, sizeof (db
->dbm_pagbuf
)) >=
113 sizeof (db
->dbm_pagbuf
) ||
114 strlcat(db
->dbm_pagbuf
, ".pag", sizeof (db
->dbm_pagbuf
)) >=
115 sizeof (db
->dbm_pagbuf
)) {
117 * file.pag does not fit into dbm_pagbuf.
118 * fails with ENAMETOOLONG.
120 serrno
= ENAMETOOLONG
;
123 db
->dbm_pagf
= open(db
->dbm_pagbuf
, flags
, mode
);
124 if (db
->dbm_pagf
< 0) {
129 * We know this won't overflow so it is safe to ignore the
130 * return value; we use strl* to prevent false hits in
133 (void) strlcpy(db
->dbm_pagbuf
, file
, sizeof (db
->dbm_pagbuf
));
134 (void) strlcat(db
->dbm_pagbuf
, ".dir", sizeof (db
->dbm_pagbuf
));
135 db
->dbm_dirf
= open(db
->dbm_pagbuf
, flags
, mode
);
136 if (db
->dbm_dirf
< 0) {
140 (void) fstat(db
->dbm_dirf
, &statb
);
141 db
->dbm_maxbno
= statb
.st_size
*BYTESIZ
-1;
142 db
->dbm_pagbno
= db
->dbm_dirbno
= -1;
145 (void) close(db
->dbm_pagf
);
155 (void) dbm_close_status(db
);
158 /*close with return code*/
160 dbm_close_status(DBM
*db
)
165 if (dbm_flush(db
) <0) ok
= -1;
166 if (close(db
->dbm_dirf
)<0) ok
= -1;
167 if ( close(db
->dbm_pagf
)<0) ok
= -1;
173 dbm_forder(DBM
*db
, datum key
)
177 hash
= dcalchash(key
);
178 for (db
->dbm_hmask
=0;; db
->dbm_hmask
=(db
->dbm_hmask
<<1)+1) {
179 db
->dbm_blkno
= hash
& db
->dbm_hmask
;
180 db
->dbm_bitno
= db
->dbm_blkno
+ db
->dbm_hmask
;
184 return (db
->dbm_blkno
);
188 dbm_fetch(DBM
*db
, datum key
)
195 dbm_access(db
, dcalchash(key
));
196 if ((i
= finddatum(db
->dbm_pagbuf
, key
)) >= 0) {
197 item
= makdatum(db
->dbm_pagbuf
, i
+1);
198 if (item
.dptr
!= NULL
)
208 dbm_delete(DBM
*db
, datum key
)
214 if (dbm_rdonly(db
)) {
218 dbm_access(db
, dcalchash(key
));
219 if ((i
= finddatum(db
->dbm_pagbuf
, key
)) < 0)
221 if (!delitem(db
->dbm_pagbuf
, i
))
223 db
->dbm_pagbno
= db
->dbm_blkno
;
224 if (dbm_defwrite(db
)) {
228 (void) lseek(db
->dbm_pagf
, (long)(db
->dbm_blkno
*PBLKSIZ
), L_SET
);
229 if (write(db
->dbm_pagf
, db
->dbm_pagbuf
, PBLKSIZ
) != PBLKSIZ
) {
231 db
->dbm_flags
|= _DBM_IOERR
;
239 dbm_store(DBM
*db
, datum key
, datum dat
, int replace
)
243 char ovfbuf
[PBLKSIZ
];
247 if (dbm_rdonly(db
)) {
252 dbm_access(db
, dcalchash(key
));
253 if ((i
= finddatum(db
->dbm_pagbuf
, key
)) >= 0) {
256 if (!delitem(db
->dbm_pagbuf
, i
)) {
257 db
->dbm_flags
|= _DBM_IOERR
;
261 if (!additem(db
->dbm_pagbuf
, key
, dat
))
263 db
->dbm_pagbno
= db
->dbm_blkno
;
264 if (dbm_defwrite(db
)) {
269 (void) lseek(db
->dbm_pagf
, (long)(db
->dbm_blkno
*PBLKSIZ
), L_SET
);
270 if (write(db
->dbm_pagf
, db
->dbm_pagbuf
, PBLKSIZ
) != PBLKSIZ
) {
271 db
->dbm_flags
|= _DBM_IOERR
;
278 if (key
.dsize
+dat
.dsize
+3*sizeof(short) >= PBLKSIZ
) {
279 db
->dbm_flags
|= _DBM_IOERR
;
283 bzero(ovfbuf
, PBLKSIZ
);
285 item
= makdatum(db
->dbm_pagbuf
, i
);
286 if (item
.dptr
== NULL
)
288 if (dcalchash(item
) & (db
->dbm_hmask
+1)) {
289 item1
= makdatum(db
->dbm_pagbuf
, i
+1);
290 if (item1
.dptr
== NULL
) {
291 /*(void) fprintf(stderr, "ndbm: split not paired\n");*/
292 db
->dbm_flags
|= _DBM_IOERR
;
295 if (!additem(ovfbuf
, item
, item1
) ||
296 !delitem(db
->dbm_pagbuf
, i
)) {
297 db
->dbm_flags
|= _DBM_IOERR
;
304 db
->dbm_pagbno
= db
->dbm_blkno
;
305 (void) lseek(db
->dbm_pagf
, (long)(db
->dbm_blkno
*PBLKSIZ
), L_SET
);
306 if (write(db
->dbm_pagf
, db
->dbm_pagbuf
, PBLKSIZ
) != PBLKSIZ
) {
307 db
->dbm_flags
|= _DBM_IOERR
;
310 dbm_clrdirty(db
); /*clear dirty*/
311 (void) lseek(db
->dbm_pagf
,
312 (long)((db
->dbm_blkno
+db
->dbm_hmask
+1)*PBLKSIZ
), L_SET
);
313 if (write(db
->dbm_pagf
, ovfbuf
, PBLKSIZ
) != PBLKSIZ
) {
314 db
->dbm_flags
|= _DBM_IOERR
;
317 if (setbit(db
) < 0) {
318 db
->dbm_flags
|= _DBM_IOERR
;
325 dbm_hashinc(DBM
*db
, long hash
)
330 hash
&= db
->dbm_hmask
;
331 bit
= db
->dbm_hmask
+1;
344 static datum nullkey
= {NULL
, 0};
347 dbm_firsthash(DBM
*db
, long hash
)
353 dbm_access(db
, hash
);
355 bitem
= makdatum(db
->dbm_pagbuf
, 0);
357 item
= makdatum(db
->dbm_pagbuf
, i
);
358 if(item
.dptr
== NULL
)
360 if(cmpdatum(bitem
, item
) < 0) {
365 if(bitem
.dptr
!= NULL
) {
366 db
->dbm_keyptr
= j
+ 2;
367 db
->dbm_blkptr
= db
->dbm_blkno
;
370 hash
= dbm_hashinc(db
,hash
);
372 return(item
); /*null item*/
378 dbm_firstkey(DBM
*db
)
383 return (dbm_firsthash(db
, 0L));
390 return (dbm_do_nextkey(db
, nullkey
));
393 /*this is used if keyptr-2,blocknum doesn't point to the previous
394 specific key allowing the fast hash order search --
395 its use indicates user tampering with our state variables,
396 which some evil users might do to search from some specific place.
397 It finds the first key at or after blkptr,keyptr in block seq order
398 this requires looking at all sorts of emtpy blocks in many cases*/
401 dbm_slow_nextkey(DBM
*db
)
406 if (dbm_error(db
) || fstat(db
->dbm_pagf
, &statb
) < 0)
408 statb
.st_size
/= PBLKSIZ
;
411 if (db
->dbm_blkptr
!= db
->dbm_pagbno
) {
413 if (dbm_dirty(db
)) dbm_flushpag(db
);
415 db
->dbm_pagbno
= db
->dbm_blkptr
;
416 (void) lseek(db
->dbm_pagf
, (long)(db
->dbm_blkptr
*PBLKSIZ
), L_SET
);
417 if (read(db
->dbm_pagf
, db
->dbm_pagbuf
, PBLKSIZ
) != PBLKSIZ
)
418 bzero(db
->dbm_pagbuf
, PBLKSIZ
);
420 else if (chkblk(db
->dbm_pagbuf
) < 0)
421 db
->dbm_flags
|= _DBM_IOERR
;
424 /*Am I an empty block?*/
425 if (((short *)db
->dbm_pagbuf
)[0] != 0) {
426 item
= makdatum(db
->dbm_pagbuf
, db
->dbm_keyptr
);
427 if (item
.dptr
!= NULL
) {
433 /*go to next sequential block*/
434 if (++db
->dbm_blkptr
>= statb
.st_size
)
444 dbm_do_nextkey(DBM
*db
, datum inkey
)
456 if ( dbm_error(db
) ) {
462 /*user has supplied lastkey*/
464 if(inkey
.dptr
!= NULL
) {
465 dbm_access(db
, (hash
=dcalchash(inkey
)));
466 if ((i
= finddatum(db
->dbm_pagbuf
, inkey
)) >= 0) {
467 db
->dbm_keyptr
= i
+ 2;
468 db
->dbm_blkptr
= db
->dbm_blkno
;
473 /*is this a manual firstkey request? */
475 if (db
->dbm_blkptr
== 0L &&
477 return (dbm_firsthash(db
, 0L));
479 /*no -- get lastkey this is like dbm_access by blkptr*/
481 if (db
->dbm_blkptr
!= db
->dbm_pagbno
) {
483 if (dbm_dirty(db
)) dbm_flushpag(db
);
484 db
->dbm_pagbno
= db
->dbm_blkptr
;
485 (void) lseek(db
->dbm_pagf
, (long)(db
->dbm_blkptr
*PBLKSIZ
), L_SET
);
486 if (read(db
->dbm_pagf
, db
->dbm_pagbuf
, PBLKSIZ
) != PBLKSIZ
)
487 bzero(db
->dbm_pagbuf
, PBLKSIZ
);
489 else if (chkblk(db
->dbm_pagbuf
) < 0)
490 db
->dbm_flags
|= _DBM_IOERR
;
493 /*now just make up last key datum*/
494 if (db
->dbm_keyptr
>=2) key
= makdatum(db
->dbm_pagbuf
,(db
->dbm_keyptr
-2));
497 /* the keyptr pagbuf have failed us, the user must
498 be an extra clever moron who depends on
499 these variables and their former meaning.
500 If we set the variables this would have got
501 us the key for sure! So give him the old algorithm.*/
502 if (key
.dptr
== NULL
) return (dbm_slow_nextkey(db
));
505 /*at this point the last key is paged in and
506 we can proceed as in old dbm -- like Ken did his. */
510 sp
= (short *)db
->dbm_pagbuf
;
514 /*begin put makdatum inline*/
516 if ((unsigned)i
>= sp
[0]) {
519 break; /*from below*/
522 if (i
> 0) item
.dsize
= sp
[i
] - sp
[i
+1];
523 else item
.dsize
= PBLKSIZ
- sp
[i
+1];
524 item
.dptr
= db
->dbm_pagbuf
+sp
[i
+1];
527 /* item = makdatum(db->dbm_pagbuf, i);*/
528 /*end put makdatum inline*/
530 if(item
.dptr
== NULL
)
537 if( (n
- item
.dsize
) <= 0 ) continue;
545 if((*--p1
- *--p2
) > 0) goto keep_going
;
553 /*end inline cmpdatum*/
554 /*if(cmpdatum(key, item) <= 0)
563 /* if(cmpdatum(bitem, item) < 0)*/
568 if((n
- item
.dsize
) <0) {
578 if((*--p1
- *--p2
) <0) {
590 db
->dbm_keyptr
= j
+ 2;
591 db
->dbm_blkptr
= db
->dbm_blkno
;
595 /* really need hash at this point */
596 /* if it gave us a key we have already calculated the hash */
597 /* if not get the hash */
598 if (inkey
.dptr
== NULL
) hash
=dcalchash(key
);
599 hash
= dbm_hashinc(db
,hash
);
602 return (item
); /*null*/
603 /*get first item on next page in hash table order*/
604 return (dbm_firsthash(db
, hash
));
610 dbm_access(DBM
*db
, long hash
)
618 for (my_hmask
=0;; my_hmask
=(my_hmask
<<1)+1) {
619 my_blkno
= hash
& my_hmask
;
620 my_bitno
= my_blkno
+ my_hmask
;
622 if (my_bitno
> db
->dbm_maxbno
) break;
623 n
= my_bitno
% BYTESIZ
;
624 bn
= my_bitno
/ BYTESIZ
;
627 if (b
!= db
->dbm_dirbno
) {
628 if (dbm_dirdirty(db
)) dbm_flushdir(db
); /*must flush*/
630 (void) lseek(db
->dbm_dirf
, (long)(b
*DBLKSIZ
), L_SET
);
631 if (read(db
->dbm_dirf
, db
->dbm_dirbuf
, DBLKSIZ
) != DBLKSIZ
)
632 bzero(db
->dbm_dirbuf
, DBLKSIZ
);
634 if ( (db
->dbm_dirbuf
[i
] & (1<<n
)) == 0 ) break;
642 db
->dbm_blkno
=my_blkno
;
643 db
->dbm_bitno
=my_bitno
;
644 db
->dbm_hmask
=my_hmask
;
646 if (my_blkno
!= db
->dbm_pagbno
) {
647 if (dbm_dirty(db
)){ /*must page out the page*/
648 (void) lseek(db
->dbm_pagf
, (long)(db
->dbm_pagbno
*PBLKSIZ
), L_SET
);
649 if (write(db
->dbm_pagf
, db
->dbm_pagbuf
, PBLKSIZ
) != PBLKSIZ
) {
650 db
->dbm_flags
|= _DBM_IOERR
;
655 db
->dbm_pagbno
= my_blkno
;
656 (void) lseek(db
->dbm_pagf
, (long)(my_blkno
*PBLKSIZ
), L_SET
);
657 if (read(db
->dbm_pagf
, db
->dbm_pagbuf
, PBLKSIZ
) != PBLKSIZ
)
658 bzero(db
->dbm_pagbuf
, PBLKSIZ
);
660 else if (chkblk(db
->dbm_pagbuf
) < 0)
661 db
->dbm_flags
|= _DBM_IOERR
;
673 if (db
->dbm_bitno
> db
->dbm_maxbno
)
675 n
= db
->dbm_bitno
% BYTESIZ
;
676 bn
= db
->dbm_bitno
/ BYTESIZ
;
679 if (b
!= db
->dbm_dirbno
) {
680 if (dbm_dirdirty(db
)) dbm_flushdir(db
); /*must flush*/
682 (void) lseek(db
->dbm_dirf
, (long)(b
*DBLKSIZ
), L_SET
);
683 if (read(db
->dbm_dirf
, db
->dbm_dirbuf
, DBLKSIZ
) != DBLKSIZ
)
684 bzero(db
->dbm_dirbuf
, DBLKSIZ
);
686 return (db
->dbm_dirbuf
[i
] & (1<<n
));
695 if (db
->dbm_bitno
> db
->dbm_maxbno
)
696 db
->dbm_maxbno
= db
->dbm_bitno
;
697 n
= db
->dbm_bitno
% BYTESIZ
;
698 bn
= db
->dbm_bitno
/ BYTESIZ
;
701 if (b
!= db
->dbm_dirbno
) {
702 if (dbm_dirdirty(db
)) dbm_flushdir(db
);
704 (void) lseek(db
->dbm_dirf
, (long)(b
*DBLKSIZ
), L_SET
);
705 if (read(db
->dbm_dirf
, db
->dbm_dirbuf
, DBLKSIZ
) != DBLKSIZ
)
706 bzero(db
->dbm_dirbuf
, DBLKSIZ
);
708 db
->dbm_dirbuf
[i
] |= 1<<n
;
710 if (dbm_defwrite(db
)) {
713 (void) lseek(db
->dbm_dirf
, (long)(b
*DBLKSIZ
), L_SET
);
714 if (write(db
->dbm_dirf
, db
->dbm_dirbuf
, DBLKSIZ
) != DBLKSIZ
) {
722 makdatum(char buf
[PBLKSIZ
], int n
)
729 if ((unsigned)n
>= sp
[0]) {
737 item
.dptr
= buf
+sp
[n
+1];
738 item
.dsize
= t
- sp
[n
+1];
743 cmpdatum(datum d1
, datum d2
)
750 return(n
- d2
.dsize
);
757 return(*--p1
- *--p2
);
763 finddatum(char buf
[PBLKSIZ
], datum item
)
770 for (i
=0, j
=sp
[0]; i
<j
; i
+=2, n
= sp
[i
]) {
774 if (n
== 0 || bcmp(&buf
[sp
[i
+1]], item
.dptr
, n
) == 0)
781 = { 61, 57, 53, 49, 45, 41, 37, 33,
782 29, 25, 21, 17, 13, 9, 5, 1,
785 static long hltab
[64]
787 06100151277L,06106161736L,06452611562L,05001724107L,
788 02614772546L,04120731531L,04665262210L,07347467531L,
789 06735253126L,06042345173L,03072226605L,01464164730L,
790 03247435524L,07652510057L,01546775256L,05714532133L,
791 06173260402L,07517101630L,02431460343L,01743245566L,
792 00261675137L,02433103631L,03421772437L,04447707466L,
793 04435620103L,03757017115L,03641531772L,06767633246L,
794 02673230344L,00260612216L,04133454451L,00615531516L,
795 06137717526L,02574116560L,02304023373L,07061702261L,
796 05153031405L,05322056705L,07401116734L,06552375715L,
797 06165233473L,05311063631L,01212221723L,01052267235L,
798 06000615237L,01075222665L,06330216006L,04402355630L,
799 01451177262L,02000133436L,06025467062L,07121076461L,
800 03123433522L,01010635225L,01716177066L,05161746527L,
801 01736635071L,06243505026L,03637211610L,01756474365L,
802 04723077174L,03642763134L,05750130273L,03655541561L,
806 dcalchash(datum item
)
815 for (cp
= item
.dptr
, s
=item
.dsize
; --s
>= 0; ) {
817 for (j
=0; j
<BYTESIZ
; j
+=4) {
818 hashi
+= hitab
[c
&017];
819 hashl
+= hltab
[hashi
&63];
827 * Delete pairs of items (n & n+1).
830 delitem(char buf
[PBLKSIZ
], int n
)
837 if ((unsigned)n
>= i2
|| (n
& 1))
849 bcopy(&buf
[i2
], &buf
[i2
+ i1
], sp
[n
+2] - i2
);
852 for (sp1
= sp
+ sp
[0], sp
+= n
+1; sp
<= sp1
; sp
++)
858 * Add pairs of items (item & item1).
861 additem(char buf
[PBLKSIZ
], datum item
, datum item1
)
871 i1
-= item
.dsize
+ item1
.dsize
;
872 if (i1
<= (i2
+3) * sizeof(short))
875 sp
[++i2
] = i1
+ item1
.dsize
;
876 bcopy(item
.dptr
, &buf
[i1
+ item1
.dsize
], item
.dsize
);
878 bcopy(item1
.dptr
, &buf
[i1
], item1
.dsize
);
884 chkblk(char buf
[PBLKSIZ
])
891 for (i
=0; i
<sp
[0]; i
++) {
896 if (t
< (sp
[0]+1)*sizeof(short))