xref: /illumos-gate/usr/src/ucblib/libdbm/dbm.c (revision e616c49c963c6536f26b6d8888bc4bbb54716901)
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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 /*
30  * Portions of this source code were derived from Berkeley 4.3 BSD
31  * under license from the Regents of the University of California.
32  */
33 
34 /*
35  * Copyright (c) 2018, Joyent, Inc.
36  */
37 
38 /*LINTLIBRARY*/
39 
40 #include	<sys/types.h>
41 #include	<stdio.h>
42 #include	<unistd.h>
43 #include	<stdlib.h>
44 #include	<strings.h>
45 #include	<malloc.h>
46 #include	<sys/stat.h>
47 #include	<sys/fcntl.h>
48 #include	<dbm.h>
49 #include	<errno.h>
50 
51 /* forward declarations */
52 /* could be all static if not were for older *.a releases */
53 void chkblk(char buf[PBLKSIZ]);
54 void delitem(char buf[PBLKSIZ], int n);
55 void dbm_access(long hash);
56 int getbit(void);
57 int setbit(void);
58 int additem(char buf[PBLKSIZ], datum item);
59 int cmpdatum(datum d1, datum d2);
60 
61 int
62 dbminit(char *file)
63 {
64 	struct stat64 statb;
65 
66 	dbrdonly = 0;
67 	if (strlcpy(pagbuf, file, sizeof (pagbuf)) >= sizeof (pagbuf) ||
68 	    strlcat(pagbuf, ".pag", sizeof (pagbuf)) >= sizeof (pagbuf)) {
69 		/*
70 		 * file.pag does not fit into pagbuf.
71 		 * fails with ENAMETOOLONG.
72 		 */
73 		errno = ENAMETOOLONG;
74 		return (-1);
75 	}
76 	pagf = open(pagbuf, 2);
77 	if (pagf < 0) {
78 		pagf = open(pagbuf, 0);
79 		dbrdonly = 1;
80 	}
81 
82 	/*
83 	 * We know this won't overflow so it is safe to ignore the
84 	 * return value; we use strl* to prevent false hits in
85 	 * code sweeps.
86 	 */
87 	(void) strlcpy(pagbuf, file, sizeof (pagbuf));
88 	(void) strlcat(pagbuf, ".dir", sizeof (pagbuf));
89 	dirf = open(pagbuf, 2);
90 	if (dirf < 0) {
91 		dirf = open(pagbuf, 0);
92 		dbrdonly = 1;
93 	}
94 	if (pagf < 0 || dirf < 0) {
95 		(void) printf("cannot open database %s\n", file);
96 		return (-1);
97 	}
98 	/* Guards against large inodes */
99 	(void) fstat64(dirf, &statb);
100 	maxbno = (off_t)statb.st_size * BYTESIZ - 1;
101 	return (0);
102 }
103 
104 static long oldb1 = -1L;
105 static long oldb2 = -1L;
106 
107 /* Avoid using cached data for subsequent accesses. */
108 int
109 dbmflush(void)
110 {
111 	oldb1 = -1L;
112 	oldb2 = -1L;
113 	return (0);
114 }
115 
116 /* Clean up after ourself. */
117 int
118 dbmclose(void)
119 {
120 	(void) close(pagf);
121 	(void) close(dirf);
122 	bitno = 0;
123 	maxbno = 0;
124 	blkno = 0;
125 	hmask = 0;
126 	oldb1 = -1L;
127 	oldb2 = -1L;
128 	return (0);
129 }
130 
131 long
132 forder(datum key)
133 {
134 	long hash;
135 
136 	hash = calchash(key);
137 	for (hmask = 0; ; hmask = (hmask<<1)+1) {
138 		blkno = hash & hmask;
139 		bitno = blkno + hmask;
140 		if (getbit() == 0)
141 			break;
142 	}
143 	return (blkno);
144 }
145 
146 datum
147 fetch(datum key)
148 {
149 	int i;
150 	datum item;
151 
152 	dbm_access(calchash(key));
153 	for (i = 0; ; i += 2) {
154 		item = makdatum(pagbuf, i);
155 		if (item.dptr == NULL)
156 			return (item);
157 		if (cmpdatum(key, item) == 0) {
158 			item = makdatum(pagbuf, i+1);
159 			if (item.dptr == NULL)
160 				(void) printf("items not in pairs\n");
161 			return (item);
162 		}
163 	}
164 }
165 
166 int
167 delete(datum key)
168 {
169 	int i;
170 	datum item;
171 
172 	if (dbrdonly)
173 		return (-1);
174 	dbm_access(calchash(key));
175 	for (i = 0; ; i += 2) {
176 		item = makdatum(pagbuf, i);
177 		if (item.dptr == NULL)
178 			return (-1);
179 		if (cmpdatum(key, item) == 0) {
180 			delitem(pagbuf, i);
181 			delitem(pagbuf, i);
182 			break;
183 		}
184 	}
185 	(void) lseek(pagf, blkno*PBLKSIZ, 0);
186 	(void) write(pagf, pagbuf, PBLKSIZ);
187 	return (0);
188 }
189 
190 int
191 store(datum key, datum dat)
192 {
193 	int i;
194 	datum item;
195 	char ovfbuf[PBLKSIZ];
196 	datum Sentry;
197 	int Oneflag;
198 
199 	Sentry.dsize = 0;
200 	Sentry.dptr = NULL;
201 
202 	if (dbrdonly)
203 		return (-1);
204 loop:
205 	dbm_access(calchash(key));
206 	for (i = 0; ; i += 2) {
207 		item = makdatum(pagbuf, i);
208 		if (item.dptr == NULL)
209 			break;
210 		if (cmpdatum(key, item) == 0) {
211 			delitem(pagbuf, i);
212 			delitem(pagbuf, i);
213 			break;
214 		}
215 	}
216 	i = additem(pagbuf, key);
217 	if (i < 0)
218 		goto split;
219 	if (additem(pagbuf, dat) < 0) {
220 		delitem(pagbuf, i);
221 		goto split;
222 	}
223 	(void) lseek(pagf, blkno*PBLKSIZ, 0);
224 	(void) write(pagf, pagbuf, PBLKSIZ);
225 	return (0);
226 split:
227 	if (key.dsize+dat.dsize+3*sizeof (short) >= PBLKSIZ) {
228 		(void) printf("entry too big\n");
229 		return (-1);
230 	}
231 	bzero(ovfbuf, PBLKSIZ);
232 	Oneflag = 0;
233 	for (i = 0; ; ) {
234 		item = makdatum(pagbuf, i);
235 		Oneflag++;
236 		if (item.dptr == NULL) {
237 			if ((Sentry.dsize == key.dsize) &&
238 			    (strncmp(Sentry.dptr, key.dptr, key.dsize) == 0))
239 				return (-1);
240 			if (Oneflag == 2) {
241 				Sentry.dsize = key.dsize;
242 				Sentry.dptr = malloc(strlen(key.dptr)+1);
243 				(void) strncpy(Sentry.dptr, key.dptr,
244 					key.dsize);
245 			}
246 			break;
247 		}
248 		if (calchash(item) & (hmask+1)) {
249 			(void) additem(ovfbuf, item);
250 			delitem(pagbuf, i);
251 			item = makdatum(pagbuf, i);
252 			if (item.dptr == NULL) {
253 				(void) printf("split not paired\n");
254 				break;
255 			}
256 			(void) additem(ovfbuf, item);
257 			delitem(pagbuf, i);
258 			continue;
259 		}
260 		i += 2;
261 	}
262 	(void) lseek(pagf, blkno*PBLKSIZ, 0);
263 	if (write(pagf, pagbuf, PBLKSIZ) < 0)
264 		return (-1);
265 	(void) lseek(pagf, (blkno+hmask+1)*PBLKSIZ, 0);
266 	if (write(pagf, ovfbuf, PBLKSIZ) < 0)
267 		return (-1);
268 	if (setbit() < 0)
269 		return (-1);
270 	goto loop;
271 }
272 
273 datum
274 firstkey(void)
275 {
276 	return (firsthash(0L));
277 }
278 
279 datum
280 nextkey(datum key)
281 {
282 	int i;
283 	datum item, bitem;
284 	long hash;
285 	int f;
286 
287 	hash = calchash(key);
288 	dbm_access(hash);
289 	f = 1;
290 	for (i = 0; ; i += 2) {
291 		item = makdatum(pagbuf, i);
292 		if (item.dptr == NULL)
293 			break;
294 		if (cmpdatum(key, item) <= 0)
295 			continue;
296 		if (f || cmpdatum(bitem, item) < 0) {
297 			bitem = item;
298 			f = 0;
299 		}
300 	}
301 	if (f == 0)
302 		return (bitem);
303 	hash = hashinc(hash);
304 	if (hash == 0)
305 		return (item);
306 	return (firsthash(hash));
307 }
308 
309 datum
310 firsthash(long hash)
311 {
312 	int i;
313 	datum item, bitem;
314 
315 loop:
316 	dbm_access(hash);
317 	bitem = makdatum(pagbuf, 0);
318 	for (i = 2; ; i += 2) {
319 		item = makdatum(pagbuf, i);
320 		if (item.dptr == NULL)
321 			break;
322 		if (cmpdatum(bitem, item) < 0)
323 			bitem = item;
324 	}
325 	if (bitem.dptr != NULL)
326 		return (bitem);
327 	hash = hashinc(hash);
328 	if (hash == 0)
329 		return (item);
330 	goto loop;
331 }
332 
333 void
334 dbm_access(long hash)
335 {
336 	ssize_t readsize;
337 
338 	for (hmask = 0; ; hmask = (hmask<<1)+1) {
339 		blkno = hash & hmask;
340 		bitno = blkno + hmask;
341 		if (getbit() == 0)
342 			break;
343 	}
344 	if (blkno != oldb1) {
345 		(void) lseek(pagf, blkno*PBLKSIZ, 0);
346 		readsize = read(pagf, pagbuf, PBLKSIZ);
347 		if (readsize != PBLKSIZ) {
348 			if (readsize < 0) readsize = 0;
349 			bzero(pagbuf+readsize, PBLKSIZ-readsize);
350 		}
351 		chkblk(pagbuf);
352 		oldb1 = blkno;
353 	}
354 }
355 
356 int
357 getbit(void)
358 {
359 	long bn;
360 	ssize_t readsize;
361 	long b, i, n;
362 
363 	if (bitno > maxbno)
364 		return (0);
365 	n = bitno % BYTESIZ;
366 	bn = bitno / BYTESIZ;
367 	i = bn % DBLKSIZ;
368 	b = bn / DBLKSIZ;
369 	if (b != oldb2) {
370 		(void) lseek(dirf, (long)b*DBLKSIZ, 0);
371 		readsize = read(dirf, dirbuf, DBLKSIZ);
372 		if (readsize != DBLKSIZ) {
373 			if (readsize < 0) readsize = 0;
374 			bzero(dirbuf+readsize, DBLKSIZ-readsize);
375 		}
376 		oldb2 = b;
377 	}
378 	if (dirbuf[i] & (1<<n))
379 		return (1);
380 	return (0);
381 }
382 
383 int
384 setbit(void)
385 {
386 	long bn;
387 	long i, n, b;
388 
389 	if (dbrdonly)
390 		return (-1);
391 	if (bitno > maxbno) {
392 		maxbno = bitno;
393 		(void) getbit();
394 	}
395 	n = bitno % BYTESIZ;
396 	bn = bitno / BYTESIZ;
397 	i = bn % DBLKSIZ;
398 	b = bn / DBLKSIZ;
399 	dirbuf[i] |= 1<<n;
400 	(void) lseek(dirf, (long)b*DBLKSIZ, 0);
401 	if (write(dirf, dirbuf, DBLKSIZ) < 0)
402 		return (-1);
403 	return (0);
404 }
405 
406 datum
407 makdatum(char buf[PBLKSIZ], int n)
408 {
409 	short *sp;
410 	int t;
411 	datum item;
412 
413 	sp = (short *)buf;
414 	if (n < 0 || n >= sp[0])
415 		goto null;
416 	t = PBLKSIZ;
417 	if (n > 0)
418 		t = sp[n+1-1];
419 	item.dptr = buf+sp[n+1];
420 	item.dsize = t - sp[n+1];
421 	return (item);
422 
423 null:
424 	item.dptr = NULL;
425 	item.dsize = 0;
426 	return (item);
427 }
428 
429 int
430 cmpdatum(datum d1, datum d2)
431 {
432 	int n;
433 	char *p1, *p2;
434 
435 	n = d1.dsize;
436 	if (n != d2.dsize)
437 		return (n - d2.dsize);
438 	if (n == 0)
439 		return (0);
440 	p1 = d1.dptr;
441 	p2 = d2.dptr;
442 	do
443 		if (*p1++ != *p2++)
444 			return (*--p1 - *--p2);
445 	while (--n);
446 	return (0);
447 }
448 
449 int	hitab[16]
450 /*
451  * ken's
452  * {
453  *	055,043,036,054,063,014,004,005,
454  *	010,064,077,000,035,027,025,071,
455  * };
456  */
457 	= {	61, 57, 53, 49, 45, 41, 37, 33,
458 		29, 25, 21, 17, 13,  9,  5,  1,
459 	};
460 long	hltab[64] = {
461 	06100151277L, 06106161736L, 06452611562L, 05001724107L,
462 	02614772546L, 04120731531L, 04665262210L, 07347467531L,
463 	06735253126L, 06042345173L, 03072226605L, 01464164730L,
464 	03247435524L, 07652510057L, 01546775256L, 05714532133L,
465 	06173260402L, 07517101630L, 02431460343L, 01743245566L,
466 	00261675137L, 02433103631L, 03421772437L, 04447707466L,
467 	04435620103L, 03757017115L, 03641531772L, 06767633246L,
468 	02673230344L, 00260612216L, 04133454451L, 00615531516L,
469 	06137717526L, 02574116560L, 02304023373L, 07061702261L,
470 	05153031405L, 05322056705L, 07401116734L, 06552375715L,
471 	06165233473L, 05311063631L, 01212221723L, 01052267235L,
472 	06000615237L, 01075222665L, 06330216006L, 04402355630L,
473 	01451177262L, 02000133436L, 06025467062L, 07121076461L,
474 	03123433522L, 01010635225L, 01716177066L, 05161746527L,
475 	01736635071L, 06243505026L, 03637211610L, 01756474365L,
476 	04723077174L, 03642763134L, 05750130273L, 03655541561L,
477 };
478 
479 long
480 hashinc(long hash)
481 {
482 	long bit;
483 
484 	hash &= hmask;
485 	bit = hmask+1;
486 	for (;;) {
487 		bit >>= 1;
488 		if (bit == 0)
489 			return (0L);
490 		if ((hash&bit) == 0)
491 			return (hash|bit);
492 		hash &= ~bit;
493 	}
494 }
495 
496 long
497 calchash(datum item)
498 {
499 	int i, j, f;
500 	long hashl;
501 	int hashi;
502 
503 	hashl = 0;
504 	hashi = 0;
505 	for (i = 0; i < item.dsize; i++) {
506 		f = item.dptr[i];
507 		for (j = 0; j < BYTESIZ; j += 4) {
508 			hashi += hitab[f&017];
509 			hashl += hltab[hashi&63];
510 			f >>= 4;
511 		}
512 	}
513 	return (hashl);
514 }
515 
516 void
517 delitem(char buf[PBLKSIZ], int n)
518 {
519 	short *sp;
520 	int i1, i2, i3;
521 
522 	sp = (short *)buf;
523 	if (n < 0 || n >= sp[0])
524 		goto bad;
525 	i1 = sp[n+1];
526 	i2 = PBLKSIZ;
527 	if (n > 0)
528 		i2 = sp[n+1-1];
529 	i3 = sp[sp[0]+1-1];
530 	if (i2 > i1)
531 		while (i1 > i3) {
532 			i1--;
533 			i2--;
534 			buf[i2] = buf[i1];
535 			buf[i1] = 0;
536 		}
537 	i2 -= i1;
538 	for (i1 = n+1; i1 < sp[0]; i1++)
539 		sp[i1+1-1] = sp[i1+1] + i2;
540 	sp[0]--;
541 	sp[sp[0]+1] = 0;
542 	return;
543 
544 bad:
545 	(void) printf("bad delitem\n");
546 	abort();
547 }
548 
549 int
550 additem(char buf[PBLKSIZ], datum item)
551 {
552 	short *sp;
553 	int i1, i2;
554 
555 	sp = (short *)buf;
556 	i1 = PBLKSIZ;
557 	if (sp[0] > 0)
558 		i1 = sp[sp[0]+1-1];
559 	i1 -= item.dsize;
560 	i2 = (int)((sp[0]+2) * sizeof (short));
561 	if (i1 <= i2)
562 		return (-1);
563 	sp[sp[0]+1] = (short)i1;
564 	for (i2 = 0; i2 < item.dsize; i2++) {
565 		buf[i1] = item.dptr[i2];
566 		i1++;
567 	}
568 	sp[0]++;
569 	return (sp[0]-1);
570 }
571 
572 void
573 chkblk(char buf[PBLKSIZ])
574 {
575 	short *sp;
576 	int t, i;
577 
578 	sp = (short *)buf;
579 	t = PBLKSIZ;
580 	for (i = 0; i < sp[0]; i++) {
581 		if (sp[i+1] > t)
582 			goto bad;
583 		t = sp[i+1];
584 	}
585 	if (t < (sp[0]+1)*sizeof (short))
586 		goto bad;
587 	return;
588 
589 bad:
590 	(void) printf("bad block\n");
591 	abort();
592 	bzero(buf, PBLKSIZ);
593 }
594