vac.c (12502B)
1 #include "stdinc.h" 2 3 typedef struct MetaChunk MetaChunk; 4 5 struct MetaChunk { 6 ushort offset; 7 ushort size; 8 ushort index; 9 }; 10 11 static int stringUnpack(char **s, uchar **p, int *n); 12 static int meCmp(MetaEntry*, char *s); 13 static int meCmpOld(MetaEntry*, char *s); 14 15 16 17 static char EBadMeta[] = "corrupted meta data"; 18 static char ENoFile[] = "file does not exist"; 19 20 /* 21 * integer conversion routines 22 */ 23 #define U8GET(p) ((p)[0]) 24 #define U16GET(p) (((p)[0]<<8)|(p)[1]) 25 #define U32GET(p) (((p)[0]<<24)|((p)[1]<<16)|((p)[2]<<8)|(p)[3]) 26 #define U48GET(p) (((uvlong)U16GET(p)<<32)|(uvlong)U32GET((p)+2)) 27 #define U64GET(p) (((uvlong)U32GET(p)<<32)|(uvlong)U32GET((p)+4)) 28 29 #define U8PUT(p,v) (p)[0]=(v) 30 #define U16PUT(p,v) (p)[0]=(v)>>8;(p)[1]=(v) 31 #define U32PUT(p,v) (p)[0]=((v)>>24)&0xFF;(p)[1]=((v)>>16)&0xFF;(p)[2]=((v)>>8)&0xFF;(p)[3]=(v)&0xFF 32 #define U48PUT(p,v,t32) t32=(v)>>32;U16PUT(p,t32);t32=(v);U32PUT((p)+2,t32) 33 #define U64PUT(p,v,t32) t32=(v)>>32;U32PUT(p,t32);t32=(v);U32PUT((p)+4,t32) 34 35 static int 36 stringUnpack(char **s, uchar **p, int *n) 37 { 38 int nn; 39 40 if(*n < 2) 41 return 0; 42 43 nn = U16GET(*p); 44 *p += 2; 45 *n -= 2; 46 if(nn > *n) 47 return 0; 48 *s = vtmalloc(nn+1); 49 memmove(*s, *p, nn); 50 (*s)[nn] = 0; 51 *p += nn; 52 *n -= nn; 53 return 1; 54 } 55 56 static int 57 stringPack(char *s, uchar *p) 58 { 59 int n; 60 61 n = strlen(s); 62 U16PUT(p, n); 63 memmove(p+2, s, n); 64 return n+2; 65 } 66 67 int 68 mbSearch(MetaBlock *mb, char *elem, int *ri, MetaEntry *me) 69 { 70 int i; 71 int b, t, x; 72 if(0)fprint(2, "mbSearch %s\n", elem); 73 74 /* binary search within block */ 75 b = 0; 76 t = mb->nindex; 77 while(b < t){ 78 i = (b+t)>>1; 79 meUnpack(me, mb, i); 80 81 if(mb->botch) 82 x = meCmpOld(me, elem); 83 else 84 x = meCmp(me, elem); 85 86 if(x == 0){ 87 *ri = i; 88 return 1; 89 } 90 91 if(x < 0) 92 b = i+1; 93 else /* x > 0 */ 94 t = i; 95 } 96 97 assert(b == t); 98 99 *ri = b; /* b is the index to insert this entry */ 100 memset(me, 0, sizeof(*me)); 101 102 werrstr(ENoFile); 103 return 0; 104 } 105 106 void 107 mbInit(MetaBlock *mb, uchar *p, int n, int ne) 108 { 109 memset(p, 0, n); 110 mb->maxsize = n; 111 mb->maxindex = ne; 112 mb->nindex = 0; 113 mb->free = 0; 114 mb->size = MetaHeaderSize + ne*MetaIndexSize; 115 mb->buf = p; 116 mb->botch = 0; 117 } 118 119 int 120 mbUnpack(MetaBlock *mb, uchar *p, int n) 121 { 122 u32int magic; 123 int i; 124 int eo, en, omin; 125 uchar *q; 126 127 mb->maxsize = n; 128 mb->buf = p; 129 130 if(n == 0){ 131 memset(mb, 0, sizeof(MetaBlock)); 132 return 1; 133 } 134 135 magic = U32GET(p); 136 if(magic != MetaMagic && magic != MetaMagic-1) 137 goto Err; 138 mb->size = U16GET(p+4); 139 mb->free = U16GET(p+6); 140 mb->maxindex = U16GET(p+8); 141 mb->nindex = U16GET(p+10); 142 mb->botch = magic != MetaMagic; 143 if(mb->size > n) 144 goto Err; 145 146 omin = MetaHeaderSize + mb->maxindex*MetaIndexSize; 147 if(n < omin) 148 goto Err; 149 150 151 p += MetaHeaderSize; 152 153 /* check the index table - ensures that meUnpack and meCmp never fail */ 154 for(i=0; i<mb->nindex; i++){ 155 eo = U16GET(p); 156 en = U16GET(p+2); 157 if(eo < omin || eo+en > mb->size || en < 8) 158 goto Err; 159 q = mb->buf + eo; 160 if(U32GET(q) != DirMagic) 161 goto Err; 162 p += 4; 163 } 164 165 return 1; 166 Err: 167 werrstr(EBadMeta); 168 return 0; 169 } 170 171 172 void 173 mbPack(MetaBlock *mb) 174 { 175 uchar *p; 176 177 p = mb->buf; 178 179 assert(!mb->botch); 180 181 U32PUT(p, MetaMagic); 182 U16PUT(p+4, mb->size); 183 U16PUT(p+6, mb->free); 184 U16PUT(p+8, mb->maxindex); 185 U16PUT(p+10, mb->nindex); 186 } 187 188 189 void 190 mbDelete(MetaBlock *mb, int i) 191 { 192 uchar *p; 193 int n; 194 MetaEntry me; 195 196 assert(i < mb->nindex); 197 meUnpack(&me, mb, i); 198 memset(me.p, 0, me.size); 199 200 if(me.p - mb->buf + me.size == mb->size) 201 mb->size -= me.size; 202 else 203 mb->free += me.size; 204 205 p = mb->buf + MetaHeaderSize + i*MetaIndexSize; 206 n = (mb->nindex-i-1)*MetaIndexSize; 207 memmove(p, p+MetaIndexSize, n); 208 memset(p+n, 0, MetaIndexSize); 209 mb->nindex--; 210 } 211 212 void 213 mbInsert(MetaBlock *mb, int i, MetaEntry *me) 214 { 215 uchar *p; 216 int o, n; 217 218 assert(mb->nindex < mb->maxindex); 219 220 o = me->p - mb->buf; 221 n = me->size; 222 if(o+n > mb->size){ 223 mb->free -= mb->size - o; 224 mb->size = o + n; 225 }else 226 mb->free -= n; 227 228 p = mb->buf + MetaHeaderSize + i*MetaIndexSize; 229 n = (mb->nindex-i)*MetaIndexSize; 230 memmove(p+MetaIndexSize, p, n); 231 U16PUT(p, me->p - mb->buf); 232 U16PUT(p+2, me->size); 233 mb->nindex++; 234 } 235 236 int 237 mbResize(MetaBlock *mb, MetaEntry *me, int n) 238 { 239 uchar *p, *ep; 240 241 /* easy case */ 242 if(n <= me->size){ 243 me->size = n; 244 return 1; 245 } 246 247 /* try and expand entry */ 248 249 p = me->p + me->size; 250 ep = mb->buf + mb->maxsize; 251 while(p < ep && *p == 0) 252 p++; 253 if(n <= p - me->p){ 254 me->size = n; 255 return 1; 256 } 257 258 p = mbAlloc(mb, n); 259 if(p != nil){ 260 me->p = p; 261 me->size = n; 262 return 1; 263 } 264 265 return 0; 266 } 267 268 void 269 meUnpack(MetaEntry *me, MetaBlock *mb, int i) 270 { 271 uchar *p; 272 int eo, en; 273 274 assert(i >= 0 && i < mb->nindex); 275 276 p = mb->buf + MetaHeaderSize + i*MetaIndexSize; 277 eo = U16GET(p); 278 en = U16GET(p+2); 279 280 me->p = mb->buf + eo; 281 me->size = en; 282 283 /* checked by mbUnpack */ 284 assert(me->size >= 8); 285 } 286 287 /* assumes a small amount of checking has been done in mbEntry */ 288 static int 289 meCmp(MetaEntry *me, char *s) 290 { 291 int n; 292 uchar *p; 293 294 p = me->p; 295 296 /* skip magic & version */ 297 p += 6; 298 n = U16GET(p); 299 p += 2; 300 301 if(n > me->size - 8) 302 n = me->size - 8; 303 304 while(n > 0){ 305 if(*s == 0) 306 return 1; 307 if(*p < (uchar)*s) 308 return -1; 309 if(*p > (uchar)*s) 310 return 1; 311 p++; 312 s++; 313 n--; 314 } 315 return -(*s != 0); 316 } 317 318 /* 319 * This is the old and broken meCmp. 320 * This cmp routine reverse the sense of the comparison 321 * when one string is a prefix of the other. 322 * In other words, it put "ab" after "abc" rather 323 * than before. This behaviour is ok; binary search 324 * and sort still work. However, it is goes against 325 * the usual convention. 326 */ 327 static int 328 meCmpOld(MetaEntry *me, char *s) 329 { 330 int n; 331 uchar *p; 332 333 p = me->p; 334 335 /* skip magic & version */ 336 p += 6; 337 n = U16GET(p); 338 p += 2; 339 340 if(n > me->size - 8) 341 n = me->size - 8; 342 343 while(n > 0){ 344 if(*s == 0) 345 return -1; 346 if(*p < (uchar)*s) 347 return -1; 348 if(*p > (uchar)*s) 349 return 1; 350 p++; 351 s++; 352 n--; 353 } 354 return *s != 0; 355 } 356 357 static int 358 offsetCmp(const void *s0, const void *s1) 359 { 360 MetaChunk *mc0, *mc1; 361 362 mc0 = (MetaChunk*)s0; 363 mc1 = (MetaChunk*)s1; 364 if(mc0->offset < mc1->offset) 365 return -1; 366 if(mc0->offset > mc1->offset) 367 return 1; 368 return 0; 369 } 370 371 static MetaChunk * 372 metaChunks(MetaBlock *mb) 373 { 374 MetaChunk *mc; 375 int oo, o, n, i; 376 uchar *p; 377 378 mc = vtmalloc(mb->nindex*sizeof(MetaChunk)); 379 p = mb->buf + MetaHeaderSize; 380 for(i = 0; i<mb->nindex; i++){ 381 mc[i].offset = U16GET(p); 382 mc[i].size = U16GET(p+2); 383 mc[i].index = i; 384 p += MetaIndexSize; 385 } 386 387 qsort(mc, mb->nindex, sizeof(MetaChunk), offsetCmp); 388 389 /* check block looks ok */ 390 oo = MetaHeaderSize + mb->maxindex*MetaIndexSize; 391 o = oo; 392 n = 0; 393 for(i=0; i<mb->nindex; i++){ 394 o = mc[i].offset; 395 n = mc[i].size; 396 if(o < oo) 397 goto Err; 398 oo += n; 399 } 400 if(o+n > mb->size) 401 goto Err; 402 if(mb->size - oo != mb->free) 403 goto Err; 404 405 return mc; 406 Err: 407 fprint(2, "metaChunks failed!\n"); 408 oo = MetaHeaderSize + mb->maxindex*MetaIndexSize; 409 for(i=0; i<mb->nindex; i++){ 410 fprint(2, "\t%d: %d %d\n", i, mc[i].offset, mc[i].offset + mc[i].size); 411 oo += mc[i].size; 412 } 413 fprint(2, "\tused=%d size=%d free=%d free2=%d\n", oo, mb->size, mb->free, mb->size - oo); 414 werrstr(EBadMeta); 415 vtfree(mc); 416 return nil; 417 } 418 419 static void 420 mbCompact(MetaBlock *mb, MetaChunk *mc) 421 { 422 int oo, o, n, i; 423 424 oo = MetaHeaderSize + mb->maxindex*MetaIndexSize; 425 426 for(i=0; i<mb->nindex; i++){ 427 o = mc[i].offset; 428 n = mc[i].size; 429 if(o != oo){ 430 memmove(mb->buf + oo, mb->buf + o, n); 431 U16PUT(mb->buf + MetaHeaderSize + mc[i].index*MetaIndexSize, oo); 432 } 433 oo += n; 434 } 435 436 mb->size = oo; 437 mb->free = 0; 438 } 439 440 uchar * 441 mbAlloc(MetaBlock *mb, int n) 442 { 443 int i, o; 444 MetaChunk *mc; 445 446 /* off the end */ 447 if(mb->maxsize - mb->size >= n) 448 return mb->buf + mb->size; 449 450 /* check if possible */ 451 if(mb->maxsize - mb->size + mb->free < n) 452 return nil; 453 454 mc = metaChunks(mb); 455 if(mc == nil){ 456 fprint(2, "mbAlloc: metaChunks failed: %r\n"); 457 return nil; 458 } 459 460 /* look for hole */ 461 o = MetaHeaderSize + mb->maxindex*MetaIndexSize; 462 for(i=0; i<mb->nindex; i++){ 463 if(mc[i].offset - o >= n){ 464 vtfree(mc); 465 return mb->buf + o; 466 } 467 o = mc[i].offset + mc[i].size; 468 } 469 470 if(mb->maxsize - o >= n){ 471 vtfree(mc); 472 return mb->buf + o; 473 } 474 475 /* compact and return off the end */ 476 mbCompact(mb, mc); 477 vtfree(mc); 478 479 if(mb->maxsize - mb->size < n){ 480 werrstr(EBadMeta); 481 return nil; 482 } 483 return mb->buf + mb->size; 484 } 485 486 int 487 deSize(DirEntry *dir) 488 { 489 int n; 490 491 /* constant part */ 492 493 n = 4 + /* magic */ 494 2 + /* version */ 495 4 + /* entry */ 496 4 + /* guid */ 497 4 + /* mentry */ 498 4 + /* mgen */ 499 8 + /* qid */ 500 4 + /* mtime */ 501 4 + /* mcount */ 502 4 + /* ctime */ 503 4 + /* atime */ 504 4 + /* mode */ 505 0; 506 507 /* strings */ 508 n += 2 + strlen(dir->elem); 509 n += 2 + strlen(dir->uid); 510 n += 2 + strlen(dir->gid); 511 n += 2 + strlen(dir->mid); 512 513 /* optional sections */ 514 if(dir->qidSpace){ 515 n += 3 + /* option header */ 516 8 + /* qidOffset */ 517 8; /* qid Max */ 518 } 519 520 return n; 521 } 522 523 void 524 dePack(DirEntry *dir, MetaEntry *me) 525 { 526 uchar *p; 527 ulong t32; 528 529 p = me->p; 530 531 U32PUT(p, DirMagic); 532 U16PUT(p+4, 9); /* version */ 533 p += 6; 534 535 p += stringPack(dir->elem, p); 536 537 U32PUT(p, dir->entry); 538 U32PUT(p+4, dir->gen); 539 U32PUT(p+8, dir->mentry); 540 U32PUT(p+12, dir->mgen); 541 U64PUT(p+16, dir->qid, t32); 542 p += 24; 543 544 p += stringPack(dir->uid, p); 545 p += stringPack(dir->gid, p); 546 p += stringPack(dir->mid, p); 547 548 U32PUT(p, dir->mtime); 549 U32PUT(p+4, dir->mcount); 550 U32PUT(p+8, dir->ctime); 551 U32PUT(p+12, dir->atime); 552 U32PUT(p+16, dir->mode); 553 p += 5*4; 554 555 if(dir->qidSpace){ 556 U8PUT(p, DeQidSpace); 557 U16PUT(p+1, 2*8); 558 p += 3; 559 U64PUT(p, dir->qidOffset, t32); 560 U64PUT(p+8, dir->qidMax, t32); 561 p += 16; 562 } 563 564 assert(p == me->p + me->size); 565 } 566 567 568 int 569 deUnpack(DirEntry *dir, MetaEntry *me) 570 { 571 int t, nn, n, version; 572 uchar *p; 573 574 p = me->p; 575 n = me->size; 576 577 memset(dir, 0, sizeof(DirEntry)); 578 579 if(0)print("deUnpack\n"); 580 /* magic */ 581 if(n < 4 || U32GET(p) != DirMagic) 582 goto Err; 583 p += 4; 584 n -= 4; 585 586 if(0)print("deUnpack: got magic\n"); 587 /* version */ 588 if(n < 2) 589 goto Err; 590 version = U16GET(p); 591 if(version < 7 || version > 9) 592 goto Err; 593 p += 2; 594 n -= 2; 595 596 if(0)print("deUnpack: got version\n"); 597 598 /* elem */ 599 if(!stringUnpack(&dir->elem, &p, &n)) 600 goto Err; 601 602 if(0)print("deUnpack: got elem\n"); 603 604 /* entry */ 605 if(n < 4) 606 goto Err; 607 dir->entry = U32GET(p); 608 p += 4; 609 n -= 4; 610 611 if(0)print("deUnpack: got entry\n"); 612 613 if(version < 9){ 614 dir->gen = 0; 615 dir->mentry = dir->entry+1; 616 dir->mgen = 0; 617 }else{ 618 if(n < 3*4) 619 goto Err; 620 dir->gen = U32GET(p); 621 dir->mentry = U32GET(p+4); 622 dir->mgen = U32GET(p+8); 623 p += 3*4; 624 n -= 3*4; 625 } 626 627 if(0)print("deUnpack: got gen etc\n"); 628 629 /* size is gotten from VtEntry */ 630 dir->size = 0; 631 632 /* qid */ 633 if(n < 8) 634 goto Err; 635 dir->qid = U64GET(p); 636 p += 8; 637 n -= 8; 638 639 if(0)print("deUnpack: got qid\n"); 640 /* skip replacement */ 641 if(version == 7){ 642 if(n < VtScoreSize) 643 goto Err; 644 p += VtScoreSize; 645 n -= VtScoreSize; 646 } 647 648 /* uid */ 649 if(!stringUnpack(&dir->uid, &p, &n)) 650 goto Err; 651 652 /* gid */ 653 if(!stringUnpack(&dir->gid, &p, &n)) 654 goto Err; 655 656 /* mid */ 657 if(!stringUnpack(&dir->mid, &p, &n)) 658 goto Err; 659 660 if(0)print("deUnpack: got ids\n"); 661 if(n < 5*4) 662 goto Err; 663 dir->mtime = U32GET(p); 664 dir->mcount = U32GET(p+4); 665 dir->ctime = U32GET(p+8); 666 dir->atime = U32GET(p+12); 667 dir->mode = U32GET(p+16); 668 p += 5*4; 669 n -= 5*4; 670 671 if(0)print("deUnpack: got times\n"); 672 /* optional meta data */ 673 while(n > 0){ 674 if(n < 3) 675 goto Err; 676 t = p[0]; 677 nn = U16GET(p+1); 678 p += 3; 679 n -= 3; 680 if(n < nn) 681 goto Err; 682 switch(t){ 683 case DePlan9: 684 /* not valid in version >= 9 */ 685 if(version >= 9) 686 break; 687 if(dir->plan9 || nn != 12) 688 goto Err; 689 dir->plan9 = 1; 690 dir->p9path = U64GET(p); 691 dir->p9version = U32GET(p+8); 692 if(dir->mcount == 0) 693 dir->mcount = dir->p9version; 694 break; 695 case DeGen: 696 /* not valid in version >= 9 */ 697 if(version >= 9) 698 break; 699 break; 700 case DeQidSpace: 701 if(dir->qidSpace || nn != 16) 702 goto Err; 703 dir->qidSpace = 1; 704 dir->qidOffset = U64GET(p); 705 dir->qidMax = U64GET(p+8); 706 break; 707 } 708 p += nn; 709 n -= nn; 710 } 711 if(0)print("deUnpack: got options\n"); 712 713 if(p != me->p + me->size) 714 goto Err; 715 716 if(0)print("deUnpack: correct size\n"); 717 return 1; 718 Err: 719 if(0)print("deUnpack: XXXXXXXXXXXX EBadMeta\n"); 720 werrstr(EBadMeta); 721 deCleanup(dir); 722 return 0; 723 } 724 725 void 726 deCleanup(DirEntry *dir) 727 { 728 vtfree(dir->elem); 729 dir->elem = nil; 730 vtfree(dir->uid); 731 dir->uid = nil; 732 vtfree(dir->gid); 733 dir->gid = nil; 734 vtfree(dir->mid); 735 dir->mid = nil; 736 } 737 738 void 739 deCopy(DirEntry *dst, DirEntry *src) 740 { 741 *dst = *src; 742 dst->elem = vtstrdup(src->elem); 743 dst->uid = vtstrdup(src->uid); 744 dst->gid = vtstrdup(src->gid); 745 dst->mid = vtstrdup(src->mid); 746 }