1 /* 2 * Copyright (c) 1983 Regents of the University of California. 3 * All rights reserved. The Berkeley software License Agreement 4 * specifies the terms and conditions for redistribution. 5 */ 6 7 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 8 /* All Rights Reserved */ 9 10 /* 11 * Copyright (c) 1996,1998,2001 by Sun Microsystems, Inc. 12 * All rights reserved. 13 */ 14 15 /* 16 * These routines maintain the symbol table which tracks the state 17 * of the file system being restored. They provide lookup by either 18 * name or inode number. They also provide for creation, deletion, 19 * and renaming of entries. Because of the dynamic nature of pathnames, 20 * names should not be saved, but always constructed just before they 21 * are needed, by calling "myname". 22 */ 23 24 #include "restore.h" 25 #include <limits.h> 26 27 /* 28 * The following variables define the inode symbol table. 29 * The primary hash table is dynamically allocated based on 30 * the number of inodes in the file system (maxino), scaled by 31 * HASHFACTOR. The variable "entry" points to the hash table; 32 * the variable "entrytblsize" indicates its size (in entries). 33 */ 34 #define HASHFACTOR 5 35 static struct entry **entry; 36 static uint_t entrytblsize; 37 38 static void addino(ino_t, struct entry *); 39 static struct entry *lookupparent(char *); 40 static void removeentry(struct entry *); 41 42 /* 43 * Look up an entry by inode number 44 */ 45 struct entry * 46 lookupino(ino_t inum) 47 { 48 struct entry *ep; 49 50 if (inum < ROOTINO || inum >= maxino) 51 return (NIL); 52 for (ep = entry[inum % entrytblsize]; ep != NIL; ep = ep->e_next) 53 if (ep->e_ino == inum) 54 return (ep); 55 return (NIL); 56 } 57 58 /* 59 * We now ignore inodes that are out of range. This 60 * allows us to attempt to proceed in the face of 61 * a corrupted archive, albeit with future complaints 62 * about failed inode lookups. We only complain once 63 * about range problems, to avoid irritating the user 64 * without providing any useful information. Failed 65 * lookups have the bogus name, which is useful, so 66 * they always happen. 67 */ 68 static int complained_about_range = 0; 69 70 /* 71 * Add an entry into the entry table 72 */ 73 static void 74 addino(ino_t inum, struct entry *np) 75 { 76 struct entry **epp; 77 78 if (inum < ROOTINO || inum >= maxino) { 79 if (!complained_about_range) { 80 panic(gettext("%s: out of range %d\n"), 81 "addino", inum); 82 complained_about_range = 1; 83 } 84 return; 85 } 86 epp = &entry[inum % entrytblsize]; 87 np->e_ino = inum; 88 np->e_next = *epp; 89 *epp = np; 90 if (dflag) 91 for (np = np->e_next; np != NIL; np = np->e_next) 92 if (np->e_ino == inum) 93 badentry(np, gettext("duplicate inum")); 94 } 95 96 /* 97 * Delete an entry from the entry table. We assume our caller 98 * arranges for the necessary memory reclamation, if needed. 99 */ 100 void 101 deleteino(ino_t inum) 102 { 103 struct entry *next; 104 struct entry **prev; 105 106 if (inum < ROOTINO || inum >= maxino) { 107 if (!complained_about_range) { 108 panic(gettext("%s: out of range %d\n"), 109 "deleteino", inum); 110 complained_about_range = 1; 111 } 112 return; 113 } 114 115 prev = &entry[inum % entrytblsize]; 116 for (next = *prev; next != NIL; next = next->e_next) { 117 if (next->e_ino == inum) { 118 next->e_ino = 0; 119 *prev = next->e_next; 120 return; 121 } 122 prev = &next->e_next; 123 } 124 } 125 126 /* 127 * Look up an entry by name. 128 * NOTE: this function handles "complex" pathnames (as returned 129 * by myname()) for extended file attributes. The name string 130 * provided to this function should be terminated with *two* 131 * NULL characters. 132 */ 133 struct entry * 134 lookupname(char *name) 135 { 136 struct entry *ep; 137 char *np, *cp; 138 char buf[MAXPATHLEN]; 139 140 if (strlen(name) > (sizeof (buf) - 1)) { 141 (void) fprintf(stderr, gettext("%s: ignoring too-long name\n"), 142 "lookupname"); 143 return (NIL); 144 } 145 146 cp = name; 147 for (ep = lookupino(ROOTINO); ep != NIL; ep = ep->e_entries) { 148 np = buf; 149 while (*cp != '/' && *cp != '\0') 150 *np++ = *cp++; 151 *np = '\0'; 152 for (; ep != NIL; ep = ep->e_sibling) 153 if (strcmp(ep->e_name, buf) == 0) 154 break; 155 if (*cp++ == '\0') { 156 if (*cp != '\0') { 157 ep = ep->e_xattrs; 158 /* 159 * skip over the "./" prefix on all 160 * extended attribute paths 161 */ 162 cp += 2; 163 } 164 if (*cp == '\0') 165 return (ep); 166 } 167 if (ep == NIL) 168 break; 169 } 170 return (NIL); 171 } 172 173 /* 174 * Look up the parent of a pathname. This routine accepts complex 175 * names so the provided name argument must terminate with two NULLs. 176 */ 177 static struct entry * 178 lookupparent(char *name) 179 { 180 struct entry *ep; 181 char *tailindex, savechar, *lastpart; 182 int xattrparent = 0; 183 184 /* find the last component of the complex name */ 185 lastpart = name; 186 LASTPART(lastpart); 187 tailindex = strrchr(lastpart, '/'); 188 if (tailindex == 0) { 189 if (lastpart == name) 190 return (NIL); 191 /* 192 * tailindex normaly points to the '/' character 193 * dividing the path, but in the case of an extended 194 * attribute transition it will point to the NULL 195 * separator in front of the attribute path. 196 */ 197 tailindex = lastpart - 1; 198 xattrparent = 1; 199 } else { 200 *tailindex = '\0'; 201 } 202 savechar = *(tailindex+1); 203 *(tailindex+1) = '\0'; 204 ep = lookupname(name); 205 if (ep != NIL && !xattrparent && ep->e_type != NODE) 206 panic(gettext("%s is not a directory\n"), name); 207 if (!xattrparent) *tailindex = '/'; 208 *(tailindex+1) = savechar; 209 return (ep); 210 } 211 212 /* 213 * Determine the current pathname of a node or leaf. 214 * The returned pathname will be multiple strings with NULL separators: 215 * 216 * ./<path>/entry\0<path>/attrentry\0<path>/...\0\0 217 * ^ ^ ^ ^ 218 * return pntr entry attr recursive attr terminator 219 * 220 * Guaranteed to return a name that fits within MAXCOMPLEXLEN and is 221 * terminated with two NULLs. 222 */ 223 char * 224 myname(struct entry *ep) 225 { 226 char *cp; 227 struct entry *root = lookupino(ROOTINO); 228 static char namebuf[MAXCOMPLEXLEN]; 229 230 cp = &namebuf[MAXCOMPLEXLEN - 3]; 231 *(cp + 1) = '\0'; 232 *(cp + 2) = '\0'; 233 while (cp > &namebuf[ep->e_namlen]) { 234 cp -= ep->e_namlen; 235 bcopy(ep->e_name, cp, (size_t)ep->e_namlen); 236 if (ep == root) 237 return (cp); 238 if (ep->e_flags & XATTRROOT) 239 *(--cp) = '\0'; 240 else 241 *(--cp) = '/'; 242 ep = ep->e_parent; 243 } 244 panic(gettext("%s%s: pathname too long\n"), "...", cp); 245 return (cp); 246 } 247 248 /* 249 * Unused symbol table entries are linked together on a freelist 250 * headed by the following pointer. 251 */ 252 static struct entry *freelist = NIL; 253 254 /* 255 * add an entry to the symbol table 256 */ 257 struct entry * 258 addentry(char *name, ino_t inum, int type) 259 { 260 struct entry *np, *ep; 261 char *cp; 262 263 if (freelist != NIL) { 264 np = freelist; 265 freelist = np->e_next; 266 (void) bzero((char *)np, (size_t)sizeof (*np)); 267 } else { 268 np = (struct entry *)calloc(1, sizeof (*np)); 269 if (np == NIL) { 270 (void) fprintf(stderr, 271 gettext("no memory to extend symbol table\n")); 272 done(1); 273 } 274 } 275 np->e_type = type & ~(LINK|ROOT); 276 if (inattrspace) 277 np->e_flags |= XATTR; 278 ep = lookupparent(name); 279 if (ep == NIL) { 280 if (inum != ROOTINO || lookupino(ROOTINO) != NIL) { 281 (void) fprintf(stderr, gettext( 282 "%s: bad name %s\n"), "addentry", name); 283 assert(0); 284 done(1); 285 } 286 np->e_name = savename(name); 287 /* LINTED: savename guarantees that strlen fits in e_namlen */ 288 np->e_namlen = strlen(name); 289 np->e_parent = np; 290 addino(ROOTINO, np); 291 return (np); 292 } 293 294 if (np->e_flags & XATTR) { 295 /* 296 * skip to the last part of the complex string: it 297 * containes the extended attribute file name. 298 */ 299 LASTPART(name); 300 } 301 cp = strrchr(name, '/'); 302 if (cp == NULL) 303 cp = name; 304 else 305 cp++; 306 307 np->e_name = savename(cp); 308 /* LINTED: savename guarantees that strlen will fit */ 309 np->e_namlen = strlen(np->e_name); 310 np->e_parent = ep; 311 /* 312 * Extended attribute root directories must be linked to their 313 * "parents" via the e_xattrs field. Other entries are simply 314 * added to their parent directories e_entries list. 315 */ 316 if ((type & ROOT) && (np->e_flags & XATTR)) { 317 /* link this extended attribute root dir to its "parent" */ 318 ep->e_xattrs = np; 319 } else { 320 /* add this entry to the entry list of the parent dir */ 321 np->e_sibling = ep->e_entries; 322 ep->e_entries = np; 323 } 324 if (type & LINK) { 325 ep = lookupino(inum); 326 if (ep == NIL) { 327 /* XXX just bail on this one and continue? */ 328 (void) fprintf(stderr, 329 gettext("link to non-existent name\n")); 330 done(1); 331 } 332 np->e_ino = inum; 333 np->e_links = ep->e_links; 334 ep->e_links = np; 335 } else if (inum != 0) { 336 ep = lookupino(inum); 337 if (ep != NIL) 338 panic(gettext("duplicate entry\n")); 339 else 340 addino(inum, np); 341 } 342 return (np); 343 } 344 345 /* 346 * delete an entry from the symbol table 347 */ 348 void 349 freeentry(struct entry *ep) 350 { 351 struct entry *np; 352 ino_t inum; 353 354 if ((ep->e_flags & REMOVED) == 0) 355 badentry(ep, gettext("not marked REMOVED")); 356 if (ep->e_type == NODE) { 357 if (ep->e_links != NIL) 358 badentry(ep, gettext("freeing referenced directory")); 359 if (ep->e_entries != NIL) 360 badentry(ep, gettext("freeing non-empty directory")); 361 } 362 if (ep->e_ino != 0) { 363 np = lookupino(ep->e_ino); 364 if (np == NIL) 365 badentry(ep, gettext("lookupino failed")); 366 if (np == ep) { 367 inum = ep->e_ino; 368 deleteino(inum); 369 if (ep->e_links != NIL) 370 addino(inum, ep->e_links); 371 } else { 372 for (; np != NIL; np = np->e_links) { 373 if (np->e_links == ep) { 374 np->e_links = ep->e_links; 375 break; 376 } 377 } 378 if (np == NIL) 379 badentry(ep, gettext("link not found")); 380 } 381 } 382 removeentry(ep); 383 freename(ep->e_name); 384 ep->e_next = freelist; 385 freelist = ep; 386 } 387 388 /* 389 * Relocate an entry in the tree structure 390 */ 391 void 392 moveentry(struct entry *ep, char *newname) 393 { 394 struct entry *np; 395 char *cp; 396 397 np = lookupparent(newname); 398 if (np == NIL) 399 badentry(ep, gettext("cannot move ROOT")); 400 if (np != ep->e_parent) { 401 removeentry(ep); 402 ep->e_parent = np; 403 ep->e_sibling = np->e_entries; 404 np->e_entries = ep; 405 } 406 /* find the last component of the complex name */ 407 LASTPART(newname); 408 cp = strrchr(newname, '/') + 1; 409 if (cp == (char *)1) 410 cp = newname; 411 freename(ep->e_name); 412 ep->e_name = savename(cp); 413 /* LINTED: savename guarantees that strlen will fit */ 414 ep->e_namlen = strlen(cp); 415 if (strcmp(gentempname(ep), ep->e_name) == 0) { 416 /* LINTED: result fits in a short */ 417 ep->e_flags |= TMPNAME; 418 } else { 419 /* LINTED: result fits in a short */ 420 ep->e_flags &= ~TMPNAME; 421 } 422 } 423 424 /* 425 * Remove an entry in the tree structure 426 */ 427 static void 428 removeentry(struct entry *ep) 429 { 430 struct entry *np; 431 432 np = ep->e_parent; 433 if (ep->e_flags & XATTRROOT) { 434 if (np->e_xattrs == ep) 435 np->e_xattrs = NIL; 436 else 437 badentry(ep, gettext( 438 "parent does not reference this xattr tree")); 439 } else if (np->e_entries == ep) { 440 np->e_entries = ep->e_sibling; 441 } else { 442 for (np = np->e_entries; np != NIL; np = np->e_sibling) { 443 if (np->e_sibling == ep) { 444 np->e_sibling = ep->e_sibling; 445 break; 446 } 447 } 448 if (np == NIL) 449 badentry(ep, gettext( 450 "cannot find entry in parent list")); 451 } 452 } 453 454 /* 455 * Table of unused string entries, sorted by length. 456 * 457 * Entries are allocated in STRTBLINCR sized pieces so that names 458 * of similar lengths can use the same entry. The value of STRTBLINCR 459 * is chosen so that every entry has at least enough space to hold 460 * a "struct strtbl" header. Thus every entry can be linked onto an 461 * apprpriate free list. 462 * 463 * NB. The macro "allocsize" below assumes that "struct strhdr" 464 * has a size that is a power of two. Also, an extra byte is 465 * allocated for the string to provide space for the two NULL 466 * string terminator required for extended attribute paths. 467 */ 468 struct strhdr { 469 struct strhdr *next; 470 }; 471 472 #define STRTBLINCR ((size_t)sizeof (struct strhdr)) 473 #define allocsize(size) (((size) + 2 + STRTBLINCR - 1) & ~(STRTBLINCR - 1)) 474 475 static struct strhdr strtblhdr[allocsize(MAXCOMPLEXLEN) / STRTBLINCR]; 476 477 /* 478 * Allocate space for a name. It first looks to see if it already 479 * has an appropriate sized entry, and if not allocates a new one. 480 */ 481 char * 482 savename(char *name) 483 { 484 struct strhdr *np; 485 size_t len, as; 486 char *cp; 487 488 if (name == NULL) { 489 (void) fprintf(stderr, gettext("bad name\n")); 490 done(1); 491 } 492 len = strlen(name); 493 if (len > MAXPATHLEN) { 494 (void) fprintf(stderr, gettext("name too long\n")); 495 done(1); 496 } 497 as = allocsize(len); 498 np = strtblhdr[as / STRTBLINCR].next; 499 if (np != NULL) { 500 strtblhdr[as / STRTBLINCR].next = np->next; 501 cp = (char *)np; 502 } else { 503 /* Note that allocsize() adds 2 for the trailing \0s */ 504 cp = malloc(as); 505 if (cp == NULL) { 506 (void) fprintf(stderr, 507 gettext("no space for string table\n")); 508 done(1); 509 } 510 } 511 (void) strcpy(cp, name); 512 /* add an extra null for complex (attribute) name support */ 513 cp[len+1] = '\0'; 514 return (cp); 515 } 516 517 /* 518 * Free space for a name. The resulting entry is linked onto the 519 * appropriate free list. 520 */ 521 void 522 freename(char *name) 523 { 524 struct strhdr *tp, *np; 525 526 /* NULL case should never happen, but might as well be careful */ 527 if (name != NULL) { 528 tp = &strtblhdr[allocsize(strlen(name)) / STRTBLINCR]; 529 /*LINTED [name points to at least sizeof (struct strhdr)]*/ 530 np = (struct strhdr *)name; 531 np->next = tp->next; 532 tp->next = np; 533 } 534 } 535 536 /* 537 * Useful quantities placed at the end of a dumped symbol table. 538 */ 539 struct symtableheader { 540 int volno; 541 uint_t stringsize; 542 uint_t entrytblsize; 543 time_t dumptime; 544 time_t dumpdate; 545 ino_t maxino; 546 uint_t ntrec; 547 }; 548 549 /* 550 * dump a snapshot of the symbol table 551 */ 552 void 553 dumpsymtable(char *filename, int checkpt) 554 { 555 struct entry *ep, *tep; 556 ino_t i; 557 struct entry temp, *tentry; 558 int mynum = 1; 559 uint_t stroff; 560 FILE *fp; 561 struct symtableheader hdr; 562 563 vprintf(stdout, gettext("Check pointing the restore\n")); 564 if ((fp = safe_fopen(filename, "w", 0600)) == (FILE *)NULL) { 565 perror("fopen"); 566 (void) fprintf(stderr, 567 gettext("cannot create save file %s for symbol table\n"), 568 filename); 569 done(1); 570 } 571 clearerr(fp); 572 /* 573 * Assign an index to each entry 574 * Write out the string entries 575 */ 576 for (i = ROOTINO; i < maxino; i++) { 577 for (ep = lookupino(i); ep != NIL; ep = ep->e_links) { 578 ep->e_index = mynum++; 579 (void) fwrite(ep->e_name, sizeof (ep->e_name[0]), 580 (size_t)allocsize(ep->e_namlen), fp); 581 } 582 } 583 /* 584 * Convert e_name pointers to offsets, other pointers 585 * to indices, and output 586 */ 587 tep = &temp; 588 stroff = 0; 589 for (i = ROOTINO; !ferror(fp) && i < maxino; i++) { 590 for (ep = lookupino(i); 591 !ferror(fp) && ep != NIL; 592 ep = ep->e_links) { 593 bcopy((char *)ep, (char *)tep, sizeof (*tep)); 594 /* LINTED: type pun ok */ 595 tep->e_name = (char *)stroff; 596 stroff += allocsize(ep->e_namlen); 597 tep->e_parent = (struct entry *)ep->e_parent->e_index; 598 if (ep->e_links != NIL) 599 tep->e_links = 600 (struct entry *)ep->e_links->e_index; 601 if (ep->e_sibling != NIL) 602 tep->e_sibling = 603 (struct entry *)ep->e_sibling->e_index; 604 if (ep->e_entries != NIL) 605 tep->e_entries = 606 (struct entry *)ep->e_entries->e_index; 607 if (ep->e_xattrs != NIL) 608 tep->e_xattrs = 609 (struct entry *)ep->e_xattrs->e_index; 610 if (ep->e_next != NIL) 611 tep->e_next = 612 (struct entry *)ep->e_next->e_index; 613 (void) fwrite((char *)tep, sizeof (*tep), 1, fp); 614 } 615 } 616 /* 617 * Convert entry pointers to indices, and output 618 */ 619 for (i = 0; !ferror(fp) && i < (ino_t)entrytblsize; i++) { 620 if (entry[i] == NIL) 621 tentry = NIL; 622 else 623 tentry = (struct entry *)entry[i]->e_index; 624 (void) fwrite((char *)&tentry, sizeof (tentry), 1, fp); 625 } 626 627 if (!ferror(fp)) { 628 /* Ought to have a checksum or magic number */ 629 hdr.volno = checkpt; 630 hdr.maxino = maxino; 631 hdr.entrytblsize = entrytblsize; 632 hdr.stringsize = stroff; 633 hdr.dumptime = dumptime; 634 hdr.dumpdate = dumpdate; 635 hdr.ntrec = ntrec; 636 (void) fwrite((char *)&hdr, sizeof (hdr), 1, fp); 637 } 638 639 if (ferror(fp)) { 640 perror("fwrite"); 641 panic(gettext("output error to file %s writing symbol table\n"), 642 filename); 643 } 644 (void) fclose(fp); 645 } 646 647 /* 648 * Initialize a symbol table from a file 649 */ 650 void 651 initsymtable(char *filename) 652 { 653 char *base; 654 off64_t tblsize; 655 struct entry *ep; 656 struct entry *baseep, *lep; 657 struct symtableheader hdr; 658 struct stat64 stbuf; 659 uint_t i; 660 int fd; 661 662 vprintf(stdout, gettext("Initialize symbol table.\n")); 663 if (filename == NULL) { 664 if ((maxino / HASHFACTOR) > UINT_MAX) { 665 (void) fprintf(stderr, 666 gettext("file system too large\n")); 667 done(1); 668 } 669 /* LINTED: result fits in entrytblsize */ 670 entrytblsize = maxino / HASHFACTOR; 671 entry = calloc((size_t)entrytblsize, sizeof (*entry)); 672 if (entry == NULL) { 673 (void) fprintf(stderr, 674 gettext("no memory for entry table\n")); 675 done(1); 676 } 677 ep = calloc(1, sizeof (*ep)); 678 if (ep == NULL) { 679 (void) fprintf(stderr, 680 gettext("no memory for entry\n")); 681 done(1); 682 } 683 ep->e_type = NODE; 684 ep->e_name = savename("."); 685 ep->e_namlen = 1; 686 ep->e_parent = ep; 687 addino(ROOTINO, ep); 688 /* LINTED: result fits in a short */ 689 ep->e_flags |= NEW; 690 return; 691 } 692 if ((fd = open(filename, O_RDONLY|O_LARGEFILE)) < 0) { 693 perror("open"); 694 (void) fprintf(stderr, 695 gettext("cannot open symbol table file %s\n"), filename); 696 done(1); 697 } 698 if (fstat64(fd, &stbuf) < 0) { 699 perror("stat"); 700 (void) fprintf(stderr, 701 gettext("cannot stat symbol table file %s\n"), filename); 702 (void) close(fd); 703 done(1); 704 } 705 /* 706 * The symbol table file is too small so say we can't read it. 707 */ 708 if (stbuf.st_size < sizeof (hdr)) { 709 (void) fprintf(stderr, 710 gettext("cannot read symbol table file %s\n"), filename); 711 (void) close(fd); 712 done(1); 713 } 714 tblsize = stbuf.st_size - sizeof (hdr); 715 if (tblsize > ULONG_MAX) { 716 (void) fprintf(stderr, 717 gettext("symbol table file too large\n")); 718 (void) close(fd); 719 done(1); 720 } 721 /* LINTED tblsize fits in a size_t */ 722 base = calloc((size_t)sizeof (char), (size_t)tblsize); 723 if (base == NULL) { 724 (void) fprintf(stderr, 725 gettext("cannot allocate space for symbol table\n")); 726 (void) close(fd); 727 done(1); 728 } 729 /* LINTED tblsize fits in a size_t */ 730 if (read(fd, base, (size_t)tblsize) < 0 || 731 read(fd, (char *)&hdr, sizeof (hdr)) < 0) { 732 perror("read"); 733 (void) fprintf(stderr, 734 gettext("cannot read symbol table file %s\n"), filename); 735 (void) close(fd); 736 done(1); 737 } 738 (void) close(fd); 739 switch (command) { 740 case 'r': 741 case 'M': 742 /* 743 * For normal continuation, insure that we are using 744 * the next incremental tape 745 */ 746 if (hdr.dumpdate != dumptime) { 747 if (hdr.dumpdate < dumptime) 748 (void) fprintf(stderr, gettext( 749 "Incremental volume too low\n")); 750 else 751 (void) fprintf(stderr, gettext( 752 "Incremental volume too high\n")); 753 done(1); 754 } 755 break; 756 case 'R': 757 /* 758 * For restart, insure that we are using the same tape 759 */ 760 curfile.action = SKIP; 761 dumptime = hdr.dumptime; 762 dumpdate = hdr.dumpdate; 763 if (!bflag) 764 newtapebuf(hdr.ntrec); 765 getvol(hdr.volno); 766 break; 767 default: 768 (void) fprintf(stderr, 769 gettext("initsymtable called from command %c\n"), 770 (uchar_t)command); 771 done(1); 772 /*NOTREACHED*/ 773 } 774 maxino = hdr.maxino; 775 entrytblsize = hdr.entrytblsize; 776 /*LINTED [pointer cast alignment]*/ 777 entry = (struct entry **) 778 (base + tblsize - (entrytblsize * sizeof (*entry))); 779 if (((ulong_t)entry % 4) != 0) { 780 (void) fprintf(stderr, 781 gettext("Symbol table file corrupted\n")); 782 done(1); 783 } 784 /*LINTED [rvalue % 4 == 0] */ 785 baseep = (struct entry *) 786 (base + hdr.stringsize - sizeof (*baseep)); 787 if (((ulong_t)baseep % 4) != 0) { 788 (void) fprintf(stderr, 789 gettext("Symbol table file corrupted\n")); 790 done(1); 791 } 792 lep = (struct entry *)entry; 793 for (i = 0; i < entrytblsize; i++) { 794 if (entry[i] == NIL) 795 continue; 796 entry[i] = &baseep[(long)entry[i]]; 797 } 798 for (ep = &baseep[1]; ep < lep; ep++) { 799 ep->e_name = base + (long)ep->e_name; 800 ep->e_parent = &baseep[(long)ep->e_parent]; 801 if (ep->e_sibling != NIL) 802 ep->e_sibling = &baseep[(long)ep->e_sibling]; 803 if (ep->e_links != NIL) 804 ep->e_links = &baseep[(long)ep->e_links]; 805 if (ep->e_entries != NIL) 806 ep->e_entries = &baseep[(long)ep->e_entries]; 807 if (ep->e_xattrs != NIL) 808 ep->e_xattrs = &baseep[(long)ep->e_xattrs]; 809 if (ep->e_next != NIL) 810 ep->e_next = &baseep[(long)ep->e_next]; 811 } 812 } 813