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