symtab.c revision 92837
1233294Sstas/* 2178825Sdfr * Copyright (c) 1983, 1993 3178825Sdfr * The Regents of the University of California. All rights reserved. 4120945Snectar * 5178825Sdfr * Redistribution and use in source and binary forms, with or without 6178825Sdfr * modification, are permitted provided that the following conditions 7178825Sdfr * are met: 8120945Snectar * 1. Redistributions of source code must retain the above copyright 9178825Sdfr * notice, this list of conditions and the following disclaimer. 10178825Sdfr * 2. Redistributions in binary form must reproduce the above copyright 11120945Snectar * notice, this list of conditions and the following disclaimer in the 12178825Sdfr * documentation and/or other materials provided with the distribution. 13178825Sdfr * 3. All advertising materials mentioning features or use of this software 14178825Sdfr * must display the following acknowledgement: 15120945Snectar * This product includes software developed by the University of 16178825Sdfr * California, Berkeley and its contributors. 17178825Sdfr * 4. Neither the name of the University nor the names of its contributors 18178825Sdfr * may be used to endorse or promote products derived from this software 19120945Snectar * without specific prior written permission. 20178825Sdfr * 21178825Sdfr * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22178825Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23178825Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24178825Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25178825Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26178825Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27178825Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28178825Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29178825Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30178825Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31178825Sdfr * SUCH DAMAGE. 32233294Sstas */ 33178825Sdfr 34178825Sdfr#ifndef lint 3572445Sassar#if 0 3672445Sassarstatic char sccsid[] = "@(#)symtab.c 8.3 (Berkeley) 4/28/95"; 3772445Sassar#endif 38178825Sdfrstatic const char rcsid[] = 39178825Sdfr "$FreeBSD: head/sbin/restore/symtab.c 92837 2002-03-20 22:49:40Z imp $"; 40178825Sdfr#endif /* not lint */ 41178825Sdfr 42178825Sdfr/* 43178825Sdfr * These routines maintain the symbol table which tracks the state 44178825Sdfr * of the file system being restored. They provide lookup by either 45178825Sdfr * name or inode number. They also provide for creation, deletion, 46178825Sdfr * and renaming of entries. Because of the dynamic nature of pathnames, 47178825Sdfr * names should not be saved, but always constructed just before they 48178825Sdfr * are needed, by calling "myname". 49178825Sdfr */ 50178825Sdfr 51178825Sdfr#include <sys/param.h> 5272445Sassar#include <sys/stat.h> 53178825Sdfr 54178825Sdfr#include <ufs/ufs/dinode.h> 55178825Sdfr 56178825Sdfr#include <errno.h> 57178825Sdfr#include <fcntl.h> 58178825Sdfr#include <stdio.h> 59178825Sdfr#include <stdlib.h> 60178825Sdfr#include <string.h> 61178825Sdfr#include <unistd.h> 62178825Sdfr 63178825Sdfr#include "restore.h" 64102644Snectar#include "extern.h" 65102644Snectar 6672445Sassar/* 67120945Snectar * The following variables define the inode symbol table. 68178825Sdfr * The primary hash table is dynamically allocated based on 69178825Sdfr * the number of inodes in the file system (maxino), scaled by 70178825Sdfr * HASHFACTOR. The variable "entry" points to the hash table; 7172445Sassar * the variable "entrytblsize" indicates its size (in entries). 72178825Sdfr */ 73178825Sdfr#define HASHFACTOR 5 74178825Sdfrstatic struct entry **entry; 7572445Sassarstatic long entrytblsize; 76178825Sdfr 77178825Sdfrstatic void addino(ino_t, struct entry *); 78178825Sdfrstatic struct entry *lookupparent(char *); 79178825Sdfrstatic void removeentry(struct entry *); 80178825Sdfr 81178825Sdfr/* 82178825Sdfr * Look up an entry by inode number 83178825Sdfr */ 84178825Sdfrstruct entry * 85178825Sdfrlookupino(ino_t inum) 86178825Sdfr{ 87178825Sdfr struct entry *ep; 88178825Sdfr 89178825Sdfr if (inum < WINO || inum >= maxino) 90178825Sdfr return (NULL); 91178825Sdfr for (ep = entry[inum % entrytblsize]; ep != NULL; ep = ep->e_next) 92178825Sdfr if (ep->e_ino == inum) 93178825Sdfr return (ep); 94178825Sdfr return (NULL); 95178825Sdfr} 96178825Sdfr 97178825Sdfr/* 98178825Sdfr * Add an entry into the entry table 99178825Sdfr */ 100178825Sdfrstatic void 101178825Sdfraddino(ino_t inum, struct entry *np) 102178825Sdfr{ 103178825Sdfr struct entry **epp; 104178825Sdfr 105178825Sdfr if (inum < WINO || inum >= maxino) 106178825Sdfr panic("addino: out of range %d\n", inum); 107178825Sdfr epp = &entry[inum % entrytblsize]; 108178825Sdfr np->e_ino = inum; 109178825Sdfr np->e_next = *epp; 110178825Sdfr *epp = np; 111178825Sdfr if (dflag) 112178825Sdfr for (np = np->e_next; np != NULL; np = np->e_next) 113178825Sdfr if (np->e_ino == inum) 114178825Sdfr badentry(np, "duplicate inum"); 115178825Sdfr} 116178825Sdfr 117178825Sdfr/* 118178825Sdfr * Delete an entry from the entry table 119178825Sdfr */ 120178825Sdfrvoid 121178825Sdfrdeleteino(ino_t inum) 122178825Sdfr{ 123178825Sdfr struct entry *next; 124178825Sdfr struct entry **prev; 125178825Sdfr 126178825Sdfr if (inum < WINO || inum >= maxino) 127178825Sdfr panic("deleteino: out of range %d\n", inum); 128178825Sdfr prev = &entry[inum % entrytblsize]; 129178825Sdfr for (next = *prev; next != NULL; next = next->e_next) { 130178825Sdfr if (next->e_ino == inum) { 131178825Sdfr next->e_ino = 0; 132178825Sdfr *prev = next->e_next; 133178825Sdfr return; 134178825Sdfr } 135178825Sdfr prev = &next->e_next; 136178825Sdfr } 137178825Sdfr panic("deleteino: %d not found\n", inum); 138178825Sdfr} 139178825Sdfr 140178825Sdfr/* 141178825Sdfr * Look up an entry by name 142178825Sdfr */ 143178825Sdfrstruct entry * 144178825Sdfrlookupname(char *name) 145178825Sdfr{ 146178825Sdfr struct entry *ep; 147178825Sdfr char *np, *cp; 148178825Sdfr char buf[MAXPATHLEN]; 149178825Sdfr 150178825Sdfr cp = name; 151178825Sdfr for (ep = lookupino(ROOTINO); ep != NULL; ep = ep->e_entries) { 152178825Sdfr for (np = buf; *cp != '/' && *cp != '\0' && 153178825Sdfr np < &buf[sizeof(buf)]; ) 154178825Sdfr *np++ = *cp++; 155178825Sdfr if (np == &buf[sizeof(buf)]) 156178825Sdfr break; 157178825Sdfr *np = '\0'; 158178825Sdfr for ( ; ep != NULL; ep = ep->e_sibling) 159233294Sstas if (strcmp(ep->e_name, buf) == 0) 160178825Sdfr break; 161178825Sdfr if (ep == NULL) 162178825Sdfr break; 163178825Sdfr if (*cp++ == '\0') 164178825Sdfr return (ep); 165178825Sdfr } 166178825Sdfr return (NULL); 167178825Sdfr} 168178825Sdfr 169178825Sdfr/* 170178825Sdfr * Look up the parent of a pathname 171178825Sdfr */ 172178825Sdfrstatic struct entry * 173178825Sdfrlookupparent(char *name) 174178825Sdfr{ 175178825Sdfr struct entry *ep; 176178825Sdfr char *tailindex; 177178825Sdfr 178178825Sdfr tailindex = strrchr(name, '/'); 179178825Sdfr if (tailindex == NULL) 180178825Sdfr return (NULL); 181178825Sdfr *tailindex = '\0'; 182178825Sdfr ep = lookupname(name); 183178825Sdfr *tailindex = '/'; 184178825Sdfr if (ep == NULL) 18572445Sassar return (NULL); 18672445Sassar if (ep->e_type != NODE) 18772445Sassar panic("%s is not a directory\n", name); 18872445Sassar return (ep); 18972445Sassar} 190103423Snectar 19172445Sassar/* 19272445Sassar * Determine the current pathname of a node or leaf 19372445Sassar */ 19472445Sassarchar * 19572445Sassarmyname(struct entry *ep) 196178825Sdfr{ 19772445Sassar char *cp; 19872445Sassar static char namebuf[MAXPATHLEN]; 19972445Sassar 20072445Sassar for (cp = &namebuf[MAXPATHLEN - 2]; cp > &namebuf[ep->e_namlen]; ) { 20172445Sassar cp -= ep->e_namlen; 20272445Sassar memmove(cp, ep->e_name, (long)ep->e_namlen); 20372445Sassar if (ep == lookupino(ROOTINO)) 20472445Sassar return (cp); 205178825Sdfr *(--cp) = '/'; 206178825Sdfr ep = ep->e_parent; 207178825Sdfr } 208178825Sdfr panic("%s: pathname too long\n", cp); 209178825Sdfr return(cp); 210178825Sdfr} 211178825Sdfr 212178825Sdfr/* 213178825Sdfr * Unused symbol table entries are linked together on a free list 214233294Sstas * headed by the following pointer. 215178825Sdfr */ 216178825Sdfrstatic struct entry *freelist = NULL; 217178825Sdfr 218178825Sdfr/* 219178825Sdfr * add an entry to the symbol table 220178825Sdfr */ 221178825Sdfrstruct entry * 222233294Sstasaddentry(char *name, ino_t inum, int type) 223178825Sdfr{ 224178825Sdfr struct entry *np, *ep; 225178825Sdfr 226178825Sdfr if (freelist != NULL) { 227178825Sdfr np = freelist; 228178825Sdfr freelist = np->e_next; 229178825Sdfr memset(np, 0, (long)sizeof(struct entry)); 230178825Sdfr } else { 231178825Sdfr np = (struct entry *)calloc(1, sizeof(struct entry)); 232178825Sdfr if (np == NULL) 233178825Sdfr panic("no memory to extend symbol table\n"); 234178825Sdfr } 235178825Sdfr np->e_type = type & ~LINK; 236178825Sdfr ep = lookupparent(name); 237178825Sdfr if (ep == NULL) { 238178825Sdfr if (inum != ROOTINO || lookupino(ROOTINO) != NULL) 239178825Sdfr panic("bad name to addentry %s\n", name); 240178825Sdfr np->e_name = savename(name); 241178825Sdfr np->e_namlen = strlen(name); 242178825Sdfr np->e_parent = np; 243178825Sdfr addino(ROOTINO, np); 244178825Sdfr return (np); 245178825Sdfr } 246178825Sdfr np->e_name = savename(strrchr(name, '/') + 1); 247178825Sdfr np->e_namlen = strlen(np->e_name); 248178825Sdfr np->e_parent = ep; 249178825Sdfr np->e_sibling = ep->e_entries; 250178825Sdfr ep->e_entries = np; 251178825Sdfr if (type & LINK) { 252178825Sdfr ep = lookupino(inum); 253178825Sdfr if (ep == NULL) 254178825Sdfr panic("link to non-existent name\n"); 255178825Sdfr np->e_ino = inum; 256178825Sdfr np->e_links = ep->e_links; 257178825Sdfr ep->e_links = np; 258178825Sdfr } else if (inum != 0) { 259178825Sdfr if (lookupino(inum) != NULL) 260178825Sdfr panic("duplicate entry\n"); 261178825Sdfr addino(inum, np); 262178825Sdfr } 263178825Sdfr return (np); 264178825Sdfr} 265178825Sdfr 266178825Sdfr/* 267178825Sdfr * delete an entry from the symbol table 268233294Sstas */ 269178825Sdfrvoid 270178825Sdfrfreeentry(struct entry *ep) 271178825Sdfr{ 272178825Sdfr struct entry *np; 273178825Sdfr ino_t inum; 274178825Sdfr 275178825Sdfr if (ep->e_flags != REMOVED) 276178825Sdfr badentry(ep, "not marked REMOVED"); 277178825Sdfr if (ep->e_type == NODE) { 278233294Sstas if (ep->e_links != NULL) 279178825Sdfr badentry(ep, "freeing referenced directory"); 280178825Sdfr if (ep->e_entries != NULL) 281178825Sdfr badentry(ep, "freeing non-empty directory"); 282178825Sdfr } 283178825Sdfr if (ep->e_ino != 0) { 284178825Sdfr np = lookupino(ep->e_ino); 285178825Sdfr if (np == NULL) 286178825Sdfr badentry(ep, "lookupino failed"); 287178825Sdfr if (np == ep) { 288178825Sdfr inum = ep->e_ino; 289178825Sdfr deleteino(inum); 290178825Sdfr if (ep->e_links != NULL) 291178825Sdfr addino(inum, ep->e_links); 292178825Sdfr } else { 293178825Sdfr for (; np != NULL; np = np->e_links) { 294178825Sdfr if (np->e_links == ep) { 295178825Sdfr np->e_links = ep->e_links; 296178825Sdfr break; 297178825Sdfr } 298178825Sdfr } 299178825Sdfr if (np == NULL) 300178825Sdfr badentry(ep, "link not found"); 301178825Sdfr } 302178825Sdfr } 30372445Sassar removeentry(ep); 30490926Snectar freename(ep->e_name); 305178825Sdfr ep->e_next = freelist; 306178825Sdfr freelist = ep; 30772445Sassar} 30872445Sassar 309/* 310 * Relocate an entry in the tree structure 311 */ 312void 313moveentry(struct entry *ep, char *newname) 314{ 315 struct entry *np; 316 char *cp; 317 318 np = lookupparent(newname); 319 if (np == NULL) 320 badentry(ep, "cannot move ROOT"); 321 if (np != ep->e_parent) { 322 removeentry(ep); 323 ep->e_parent = np; 324 ep->e_sibling = np->e_entries; 325 np->e_entries = ep; 326 } 327 cp = strrchr(newname, '/') + 1; 328 freename(ep->e_name); 329 ep->e_name = savename(cp); 330 ep->e_namlen = strlen(cp); 331 if (strcmp(gentempname(ep), ep->e_name) == 0) 332 ep->e_flags |= TMPNAME; 333 else 334 ep->e_flags &= ~TMPNAME; 335} 336 337/* 338 * Remove an entry in the tree structure 339 */ 340static void 341removeentry(struct entry *ep) 342{ 343 struct entry *np; 344 345 np = ep->e_parent; 346 if (np->e_entries == ep) { 347 np->e_entries = ep->e_sibling; 348 } else { 349 for (np = np->e_entries; np != NULL; np = np->e_sibling) { 350 if (np->e_sibling == ep) { 351 np->e_sibling = ep->e_sibling; 352 break; 353 } 354 } 355 if (np == NULL) 356 badentry(ep, "cannot find entry in parent list"); 357 } 358} 359 360/* 361 * Table of unused string entries, sorted by length. 362 * 363 * Entries are allocated in STRTBLINCR sized pieces so that names 364 * of similar lengths can use the same entry. The value of STRTBLINCR 365 * is chosen so that every entry has at least enough space to hold 366 * a "struct strtbl" header. Thus every entry can be linked onto an 367 * appropriate free list. 368 * 369 * NB. The macro "allocsize" below assumes that "struct strhdr" 370 * has a size that is a power of two. 371 */ 372struct strhdr { 373 struct strhdr *next; 374}; 375 376#define STRTBLINCR (sizeof(struct strhdr)) 377#define allocsize(size) (((size) + 1 + STRTBLINCR - 1) & ~(STRTBLINCR - 1)) 378 379static struct strhdr strtblhdr[allocsize(NAME_MAX) / STRTBLINCR]; 380 381/* 382 * Allocate space for a name. It first looks to see if it already 383 * has an appropriate sized entry, and if not allocates a new one. 384 */ 385char * 386savename(char *name) 387{ 388 struct strhdr *np; 389 long len; 390 char *cp; 391 392 if (name == NULL) 393 panic("bad name\n"); 394 len = strlen(name); 395 np = strtblhdr[len / STRTBLINCR].next; 396 if (np != NULL) { 397 strtblhdr[len / STRTBLINCR].next = np->next; 398 cp = (char *)np; 399 } else { 400 cp = malloc((unsigned)allocsize(len)); 401 if (cp == NULL) 402 panic("no space for string table\n"); 403 } 404 (void) strcpy(cp, name); 405 return (cp); 406} 407 408/* 409 * Free space for a name. The resulting entry is linked onto the 410 * appropriate free list. 411 */ 412void 413freename(char *name) 414{ 415 struct strhdr *tp, *np; 416 417 tp = &strtblhdr[strlen(name) / STRTBLINCR]; 418 np = (struct strhdr *)name; 419 np->next = tp->next; 420 tp->next = np; 421} 422 423/* 424 * Useful quantities placed at the end of a dumped symbol table. 425 */ 426struct symtableheader { 427 int32_t volno; 428 int32_t stringsize; 429 int32_t entrytblsize; 430 time_t dumptime; 431 time_t dumpdate; 432 ino_t maxino; 433 int32_t ntrec; 434}; 435 436/* 437 * dump a snapshot of the symbol table 438 */ 439void 440dumpsymtable(char *filename, long checkpt) 441{ 442 struct entry *ep, *tep; 443 ino_t i; 444 struct entry temp, *tentry; 445 long mynum = 1, stroff = 0; 446 FILE *fd; 447 struct symtableheader hdr; 448 449 vprintf(stdout, "Check pointing the restore\n"); 450 if (Nflag) 451 return; 452 if ((fd = fopen(filename, "w")) == NULL) { 453 fprintf(stderr, "fopen: %s\n", strerror(errno)); 454 panic("cannot create save file %s for symbol table\n", 455 filename); 456 done(1); 457 } 458 clearerr(fd); 459 /* 460 * Assign indices to each entry 461 * Write out the string entries 462 */ 463 for (i = WINO; i <= maxino; i++) { 464 for (ep = lookupino(i); ep != NULL; ep = ep->e_links) { 465 ep->e_index = mynum++; 466 (void) fwrite(ep->e_name, sizeof(char), 467 (int)allocsize(ep->e_namlen), fd); 468 } 469 } 470 /* 471 * Convert pointers to indexes, and output 472 */ 473 tep = &temp; 474 stroff = 0; 475 for (i = WINO; i <= maxino; i++) { 476 for (ep = lookupino(i); ep != NULL; ep = ep->e_links) { 477 memmove(tep, ep, (long)sizeof(struct entry)); 478 tep->e_name = (char *)stroff; 479 stroff += allocsize(ep->e_namlen); 480 tep->e_parent = (struct entry *)ep->e_parent->e_index; 481 if (ep->e_links != NULL) 482 tep->e_links = 483 (struct entry *)ep->e_links->e_index; 484 if (ep->e_sibling != NULL) 485 tep->e_sibling = 486 (struct entry *)ep->e_sibling->e_index; 487 if (ep->e_entries != NULL) 488 tep->e_entries = 489 (struct entry *)ep->e_entries->e_index; 490 if (ep->e_next != NULL) 491 tep->e_next = 492 (struct entry *)ep->e_next->e_index; 493 (void) fwrite((char *)tep, sizeof(struct entry), 1, fd); 494 } 495 } 496 /* 497 * Convert entry pointers to indexes, and output 498 */ 499 for (i = 0; i < entrytblsize; i++) { 500 if (entry[i] == NULL) 501 tentry = NULL; 502 else 503 tentry = (struct entry *)entry[i]->e_index; 504 (void) fwrite((char *)&tentry, sizeof(struct entry *), 1, fd); 505 } 506 hdr.volno = checkpt; 507 hdr.maxino = maxino; 508 hdr.entrytblsize = entrytblsize; 509 hdr.stringsize = stroff; 510 hdr.dumptime = dumptime; 511 hdr.dumpdate = dumpdate; 512 hdr.ntrec = ntrec; 513 (void) fwrite((char *)&hdr, sizeof(struct symtableheader), 1, fd); 514 if (ferror(fd)) { 515 fprintf(stderr, "fwrite: %s\n", strerror(errno)); 516 panic("output error to file %s writing symbol table\n", 517 filename); 518 } 519 (void) fclose(fd); 520} 521 522/* 523 * Initialize a symbol table from a file 524 */ 525void 526initsymtable(char *filename) 527{ 528 char *base; 529 long tblsize; 530 struct entry *ep; 531 struct entry *baseep, *lep; 532 struct symtableheader hdr; 533 struct stat stbuf; 534 long i; 535 int fd; 536 537 vprintf(stdout, "Initialize symbol table.\n"); 538 if (filename == NULL) { 539 entrytblsize = maxino / HASHFACTOR; 540 entry = (struct entry **) 541 calloc((unsigned)entrytblsize, sizeof(struct entry *)); 542 if (entry == (struct entry **)NULL) 543 panic("no memory for entry table\n"); 544 ep = addentry(".", ROOTINO, NODE); 545 ep->e_flags |= NEW; 546 return; 547 } 548 if ((fd = open(filename, O_RDONLY, 0)) < 0) { 549 fprintf(stderr, "open: %s\n", strerror(errno)); 550 panic("cannot open symbol table file %s\n", filename); 551 } 552 if (fstat(fd, &stbuf) < 0) { 553 fprintf(stderr, "stat: %s\n", strerror(errno)); 554 panic("cannot stat symbol table file %s\n", filename); 555 } 556 tblsize = stbuf.st_size - sizeof(struct symtableheader); 557 base = calloc(sizeof(char), (unsigned)tblsize); 558 if (base == NULL) 559 panic("cannot allocate space for symbol table\n"); 560 if (read(fd, base, (int)tblsize) < 0 || 561 read(fd, (char *)&hdr, sizeof(struct symtableheader)) < 0) { 562 fprintf(stderr, "read: %s\n", strerror(errno)); 563 panic("cannot read symbol table file %s\n", filename); 564 } 565 switch (command) { 566 case 'r': 567 /* 568 * For normal continuation, insure that we are using 569 * the next incremental tape 570 */ 571 if (hdr.dumpdate != dumptime) { 572 if (hdr.dumpdate < dumptime) 573 fprintf(stderr, "Incremental tape too low\n"); 574 else 575 fprintf(stderr, "Incremental tape too high\n"); 576 done(1); 577 } 578 break; 579 case 'R': 580 /* 581 * For restart, insure that we are using the same tape 582 */ 583 curfile.action = SKIP; 584 dumptime = hdr.dumptime; 585 dumpdate = hdr.dumpdate; 586 if (!bflag) 587 newtapebuf(hdr.ntrec); 588 getvol(hdr.volno); 589 break; 590 default: 591 panic("initsymtable called from command %c\n", command); 592 break; 593 } 594 maxino = hdr.maxino; 595 entrytblsize = hdr.entrytblsize; 596 entry = (struct entry **) 597 (base + tblsize - (entrytblsize * sizeof(struct entry *))); 598 baseep = (struct entry *)(base + hdr.stringsize - sizeof(struct entry)); 599 lep = (struct entry *)entry; 600 for (i = 0; i < entrytblsize; i++) { 601 if (entry[i] == NULL) 602 continue; 603 entry[i] = &baseep[(long)entry[i]]; 604 } 605 for (ep = &baseep[1]; ep < lep; ep++) { 606 ep->e_name = base + (long)ep->e_name; 607 ep->e_parent = &baseep[(long)ep->e_parent]; 608 if (ep->e_sibling != NULL) 609 ep->e_sibling = &baseep[(long)ep->e_sibling]; 610 if (ep->e_links != NULL) 611 ep->e_links = &baseep[(long)ep->e_links]; 612 if (ep->e_entries != NULL) 613 ep->e_entries = &baseep[(long)ep->e_entries]; 614 if (ep->e_next != NULL) 615 ep->e_next = &baseep[(long)ep->e_next]; 616 } 617} 618