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 }