110315Smckusick /* Copyright (c) 1983 Regents of the University of California */ 210315Smckusick 310315Smckusick #ifndef lint 4*11995Smckusick static char sccsid[] = "@(#)utilities.c 3.11 (Berkeley) 83/04/19"; 510315Smckusick #endif 610315Smckusick 710315Smckusick #include "restore.h" 810315Smckusick 910315Smckusick /* 1010315Smckusick * Insure that all the components of a pathname exist. 1110315Smckusick */ 1211645Smckusick pathcheck(name) 1310315Smckusick char *name; 1410315Smckusick { 1510315Smckusick register char *cp; 1610315Smckusick struct entry *ep; 1711312Smckusick char *start; 1810315Smckusick 1910315Smckusick start = index(name, '/'); 2011312Smckusick if (start == 0) 2111645Smckusick return; 2210315Smckusick for (cp = start; *cp != '\0'; cp++) { 2310315Smckusick if (*cp != '/') 2410315Smckusick continue; 2510315Smckusick *cp = '\0'; 2610315Smckusick ep = lookupname(name); 2710315Smckusick if (ep == NIL) { 28*11995Smckusick ep = addentry(name, psearch(name), NODE); 2910315Smckusick newnode(ep); 30*11995Smckusick ep->e_flags |= NEW|KEEP; 3110315Smckusick } 3210315Smckusick *cp = '/'; 3310315Smckusick } 3410315Smckusick } 3510315Smckusick 3610315Smckusick /* 3710315Smckusick * Change a name to a unique temporary name. 3810315Smckusick */ 3910315Smckusick mktempname(ep) 4010315Smckusick register struct entry *ep; 4110315Smckusick { 4211645Smckusick char oldname[MAXPATHLEN]; 4310315Smckusick 4410315Smckusick if (ep->e_flags & TMPNAME) 4510315Smckusick badentry(ep, "mktempname: called with TMPNAME"); 4610315Smckusick ep->e_flags |= TMPNAME; 47*11995Smckusick (void) strcpy(oldname, myname(ep)); 4811645Smckusick freename(ep->e_name); 4911645Smckusick ep->e_name = savename(gentempname(ep)); 5011645Smckusick ep->e_namlen = strlen(ep->e_name); 5110315Smckusick renameit(oldname, myname(ep)); 5210315Smckusick } 5310315Smckusick 5410315Smckusick /* 5511645Smckusick * Generate a temporary name for an entry. 5611645Smckusick */ 5711645Smckusick char * 5811645Smckusick gentempname(ep) 5911645Smckusick struct entry *ep; 6011645Smckusick { 6111645Smckusick static char name[MAXPATHLEN]; 6211645Smckusick struct entry *np; 6311645Smckusick long i = 0; 6411645Smckusick 6511645Smckusick for (np = lookupino(ep->e_ino); np != NIL && np != ep; np = np->e_links) 6611645Smckusick i++; 6711645Smckusick if (np == NIL) 6811645Smckusick badentry(ep, "not on ino list"); 6911733Smckusick (void) sprintf(name, "%s%d%d", TMPHDR, i, ep->e_ino); 7011645Smckusick return (name); 7111645Smckusick } 7211645Smckusick 7311645Smckusick /* 7410315Smckusick * Rename a file or directory. 7510315Smckusick */ 7610315Smckusick renameit(from, to) 7710315Smckusick char *from, *to; 7810315Smckusick { 7910315Smckusick if (rename(from, to) < 0) { 8011923Smckusick fprintf(stderr, "Warning: cannot rename %s to %s\n", from, to); 8111923Smckusick perror("rename"); 8211923Smckusick return; 8310315Smckusick } 8410315Smckusick vprintf(stdout, "rename %s to %s\n", from, to); 8510315Smckusick } 8610315Smckusick 8710315Smckusick /* 8810315Smckusick * Create a new node (directory). 8910315Smckusick */ 9010315Smckusick newnode(np) 9110315Smckusick struct entry *np; 9210315Smckusick { 9310315Smckusick char *cp; 9410315Smckusick 9510315Smckusick if (np->e_type != NODE) 9610315Smckusick badentry(np, "newnode: not a node"); 9710315Smckusick cp = myname(np); 9811312Smckusick if (mkdir(cp, 0777) < 0) { 9911740Smckusick fprintf(stderr, "Warning: "); 100*11995Smckusick (void) fflush(stderr); 10111740Smckusick perror(cp); 10211740Smckusick return; 10310315Smckusick } 10410315Smckusick vprintf(stdout, "Make node %s\n", cp); 10510315Smckusick } 10610315Smckusick 10710315Smckusick /* 10810315Smckusick * Remove an old node (directory). 10910315Smckusick */ 11010315Smckusick removenode(ep) 11110315Smckusick register struct entry *ep; 11210315Smckusick { 11310315Smckusick char *cp; 11410315Smckusick 11510315Smckusick if (ep->e_type != NODE) 11610315Smckusick badentry(ep, "removenode: not a node"); 11710315Smckusick if (ep->e_entries != NIL) 11810315Smckusick badentry(ep, "removenode: non-empty directory"); 11911923Smckusick ep->e_flags |= REMOVED; 12011923Smckusick ep->e_flags &= ~TMPNAME; 12110315Smckusick cp = myname(ep); 12210315Smckusick if (rmdir(cp) < 0) { 12311923Smckusick fprintf(stderr, "Warning: "); 124*11995Smckusick (void) fflush(stderr); 12511923Smckusick perror(cp); 12611923Smckusick return; 12710315Smckusick } 12810315Smckusick vprintf(stdout, "Remove node %s\n", cp); 12910315Smckusick } 13010315Smckusick 13110315Smckusick /* 13210315Smckusick * Remove a leaf. 13310315Smckusick */ 13410315Smckusick removeleaf(ep) 13510315Smckusick register struct entry *ep; 13610315Smckusick { 13710315Smckusick char *cp; 13810315Smckusick 13910315Smckusick if (ep->e_type != LEAF) 14010315Smckusick badentry(ep, "removeleaf: not a leaf"); 14111923Smckusick ep->e_flags |= REMOVED; 14211923Smckusick ep->e_flags &= ~TMPNAME; 14310315Smckusick cp = myname(ep); 14410315Smckusick if (unlink(cp) < 0) { 14511923Smckusick fprintf(stderr, "Warning: "); 146*11995Smckusick (void) fflush(stderr); 14711923Smckusick perror(cp); 14811923Smckusick return; 14910315Smckusick } 15010315Smckusick vprintf(stdout, "Remove leaf %s\n", cp); 15110315Smckusick } 15210315Smckusick 15310315Smckusick /* 15410315Smckusick * Create a link. 15510315Smckusick */ 15610315Smckusick linkit(existing, new, type) 15710315Smckusick char *existing, *new; 15810315Smckusick int type; 15910315Smckusick { 16010315Smckusick 16110315Smckusick if (type == SYMLINK) { 16210315Smckusick if (symlink(existing, new) < 0) { 16311923Smckusick fprintf(stderr, 16411923Smckusick "Warning: cannot create symbolic link %s->%s\n", 16510315Smckusick new, existing); 16611923Smckusick perror("symlink"); 16711923Smckusick return; 16810315Smckusick } 16910315Smckusick } else if (type == HARDLINK) { 17010315Smckusick if (link(existing, new) < 0) { 17111923Smckusick fprintf(stderr, 17211923Smckusick "Warning: cannot create hard link %s->%s\n", 17310315Smckusick new, existing); 17411923Smckusick perror("link"); 17511923Smckusick return; 17610315Smckusick } 17710315Smckusick } else { 17810315Smckusick panic("linkit: unknown type %d\n", type); 17910315Smckusick } 18010315Smckusick vprintf(stdout, "Create %s link %s->%s\n", 18110315Smckusick type == SYMLINK ? "symbolic" : "hard", new, existing); 18210315Smckusick } 18310315Smckusick 18410315Smckusick /* 18510315Smckusick * find lowest number file (above "start") that needs to be extracted 18610315Smckusick */ 18710315Smckusick ino_t 18810315Smckusick lowerbnd(start) 18910315Smckusick ino_t start; 19010315Smckusick { 19110315Smckusick register struct entry *ep; 19210315Smckusick 19310315Smckusick for ( ; start < maxino; start++) { 19410315Smckusick ep = lookupino(start); 195*11995Smckusick if (ep == NIL || ep->e_type == NODE) 19610315Smckusick continue; 19711645Smckusick if (ep->e_flags & (NEW|EXTRACT)) 19810315Smckusick return (start); 19910315Smckusick } 20010315Smckusick return (start); 20110315Smckusick } 20210315Smckusick 20310315Smckusick /* 20410315Smckusick * find highest number file (below "start") that needs to be extracted 20510315Smckusick */ 20610315Smckusick ino_t 20710315Smckusick upperbnd(start) 20810315Smckusick ino_t start; 20910315Smckusick { 21010315Smckusick register struct entry *ep; 21110315Smckusick 21210315Smckusick for ( ; start > ROOTINO; start--) { 21310315Smckusick ep = lookupino(start); 214*11995Smckusick if (ep == NIL || ep->e_type == NODE) 21510315Smckusick continue; 21611645Smckusick if (ep->e_flags & (NEW|EXTRACT)) 21710315Smckusick return (start); 21810315Smckusick } 21910315Smckusick return (start); 22010315Smckusick } 22110315Smckusick 22210315Smckusick /* 22310315Smckusick * report on a badly formed entry 22410315Smckusick */ 22510315Smckusick badentry(ep, msg) 22610315Smckusick register struct entry *ep; 22710315Smckusick char *msg; 22810315Smckusick { 22910315Smckusick 23010315Smckusick fprintf(stderr, "bad entry: %s\n", msg); 23110315Smckusick fprintf(stderr, "name: %s\n", myname(ep)); 23210315Smckusick fprintf(stderr, "parent name %s\n", myname(ep->e_parent)); 23310315Smckusick if (ep->e_sibling != NIL) 23410315Smckusick fprintf(stderr, "sibling name: %s\n", myname(ep->e_sibling)); 23510315Smckusick if (ep->e_entries != NIL) 23610315Smckusick fprintf(stderr, "next entry name: %s\n", myname(ep->e_entries)); 23710315Smckusick if (ep->e_links != NIL) 23810315Smckusick fprintf(stderr, "next link name: %s\n", myname(ep->e_links)); 23911645Smckusick if (ep->e_next != NIL) 24011645Smckusick fprintf(stderr, "next hashchain name: %s\n", myname(ep->e_next)); 24110315Smckusick fprintf(stderr, "entry type: %s\n", 24210315Smckusick ep->e_type == NODE ? "NODE" : "LEAF"); 24310315Smckusick fprintf(stderr, "inode number: %ld\n", ep->e_ino); 24411898Smckusick panic("flags: %s\n", flagvalues(ep)); 24511898Smckusick } 24611898Smckusick 24711898Smckusick /* 24811898Smckusick * Construct a string indicating the active flag bits of an entry. 24911898Smckusick */ 25011898Smckusick char * 25111898Smckusick flagvalues(ep) 25211898Smckusick register struct entry *ep; 25311898Smckusick { 25411898Smckusick static char flagbuf[BUFSIZ]; 25511898Smckusick 25611898Smckusick (void) strcpy(flagbuf, "|NIL"); 25710315Smckusick flagbuf[0] = '\0'; 25810315Smckusick if (ep->e_flags & REMOVED) 25911898Smckusick (void) strcat(flagbuf, "|REMOVED"); 26010315Smckusick if (ep->e_flags & TMPNAME) 26111898Smckusick (void) strcat(flagbuf, "|TMPNAME"); 26210315Smckusick if (ep->e_flags & EXTRACT) 26311898Smckusick (void) strcat(flagbuf, "|EXTRACT"); 26410315Smckusick if (ep->e_flags & NEW) 26511898Smckusick (void) strcat(flagbuf, "|NEW"); 26610315Smckusick if (ep->e_flags & KEEP) 26711898Smckusick (void) strcat(flagbuf, "|KEEP"); 26811898Smckusick return (&flagbuf[1]); 26910315Smckusick } 27010315Smckusick 27110315Smckusick /* 272*11995Smckusick * Check to see if a name is on a dump tape. 27311321Smckusick */ 274*11995Smckusick ino_t 275*11995Smckusick dirlookup(name) 276*11995Smckusick char *name; 277*11995Smckusick { 278*11995Smckusick ino_t ino; 279*11995Smckusick 280*11995Smckusick ino = psearch(name); 281*11995Smckusick if (ino == 0 || BIT(ino, dumpmap) == 0) 282*11995Smckusick fprintf(stderr, "%s is not on tape\n", name); 283*11995Smckusick return (ino); 284*11995Smckusick } 285*11995Smckusick 286*11995Smckusick /* 287*11995Smckusick * Canonicalize file names to always start with ``./'' and 288*11995Smckusick * remove any imbedded ".." components. 289*11995Smckusick */ 29011321Smckusick canon(rawname, canonname) 29111321Smckusick char *rawname, *canonname; 29211321Smckusick { 293*11995Smckusick register char *cp, *np; 29411321Smckusick int len; 29511321Smckusick 29611321Smckusick if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0) 29711645Smckusick (void) strcpy(canonname, ""); 29811321Smckusick else if (rawname[0] == '/') 29911645Smckusick (void) strcpy(canonname, "."); 30011321Smckusick else 30111645Smckusick (void) strcpy(canonname, "./"); 30211645Smckusick (void) strcat(canonname, rawname); 30311321Smckusick len = strlen(canonname) - 1; 30411321Smckusick if (canonname[len] == '/') 30511321Smckusick canonname[len] = '\0'; 306*11995Smckusick /* 307*11995Smckusick * Eliminate extraneous ".." from pathnames. 308*11995Smckusick */ 309*11995Smckusick for (np = canonname; *np != '\0'; ) { 310*11995Smckusick np++; 311*11995Smckusick cp = np; 312*11995Smckusick while (*np != '/' && *np != '\0') 313*11995Smckusick np++; 314*11995Smckusick if (np - cp == 2 && strncmp(cp, "..", 2) == 0) { 315*11995Smckusick cp--; 316*11995Smckusick while (cp > &canonname[1] && *--cp != '/') 317*11995Smckusick /* find beginning of name */; 318*11995Smckusick (void) strcpy(cp, np); 319*11995Smckusick np = cp; 320*11995Smckusick } 321*11995Smckusick } 32211321Smckusick } 32311321Smckusick 32411321Smckusick /* 325*11995Smckusick * Elicit a reply. 32610315Smckusick */ 32710315Smckusick reply(question) 32810315Smckusick char *question; 32910315Smckusick { 33010315Smckusick char c; 33110315Smckusick 33210315Smckusick fprintf(stderr, "%s? ", question); 33310315Smckusick do { 33410315Smckusick fprintf(stderr, "[yn] "); 335*11995Smckusick c = getc(terminal); 336*11995Smckusick while (c != '\n' && getc(terminal) != '\n') 33710315Smckusick /* void */; 33810315Smckusick } while (c != 'y' && c != 'n'); 33910315Smckusick if (c == 'y') 34010315Smckusick return (GOOD); 34110315Smckusick return (FAIL); 34210315Smckusick } 343*11995Smckusick 344*11995Smckusick /* 345*11995Smckusick * Read and parse an interactive command. 346*11995Smckusick * The first word on the line is assigned to "cmd". If 347*11995Smckusick * there are no arguments on the command line, then "curdir" 348*11995Smckusick * is returned as the argument. If there are arguments 349*11995Smckusick * on the line they are returned one at a time on each 350*11995Smckusick * successive call to getcmd. Each argument is first assigned 351*11995Smckusick * to "name". If it does not start with "/" the pathname in 352*11995Smckusick * "curdir" is prepended to it. Finally "canon" is called to 353*11995Smckusick * eliminate any embedded ".." components. 354*11995Smckusick */ 355*11995Smckusick getcmd(curdir, cmd, name) 356*11995Smckusick char *curdir, *cmd, *name; 357*11995Smckusick { 358*11995Smckusick register char *cp, *bp; 359*11995Smckusick char output[BUFSIZ]; 360*11995Smckusick static char input[BUFSIZ]; 361*11995Smckusick static char *nextarg = NULL; 362*11995Smckusick 363*11995Smckusick /* 364*11995Smckusick * Check to see if still processing arguments. 365*11995Smckusick */ 366*11995Smckusick if (nextarg != NULL) 367*11995Smckusick goto getnext; 368*11995Smckusick nextarg = NULL; 369*11995Smckusick /* 370*11995Smckusick * Read a command line and trim off trailing white space. 371*11995Smckusick */ 372*11995Smckusick do { 373*11995Smckusick fprintf(stderr, "restore > "); 374*11995Smckusick (void) fflush(stderr); 375*11995Smckusick (void) fgets(input, BUFSIZ, terminal); 376*11995Smckusick } while (!feof(terminal) && input[0] == '\n'); 377*11995Smckusick if (feof(terminal)) { 378*11995Smckusick (void) strcpy(cmd, "quit"); 379*11995Smckusick return; 380*11995Smckusick } 381*11995Smckusick for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--) 382*11995Smckusick /* trim off trailing white space and newline */; 383*11995Smckusick *++cp = '\0'; 384*11995Smckusick /* 385*11995Smckusick * Copy the command into "cmd". 386*11995Smckusick */ 387*11995Smckusick for (cp = input; *cp == ' ' || *cp == '\t'; cp++) 388*11995Smckusick /* skip to command */; 389*11995Smckusick for (bp = cmd; *cp != ' ' && *cp != '\t' && *cp != '\0'; ) 390*11995Smckusick *bp++ = *cp++; 391*11995Smckusick *bp = '\0'; 392*11995Smckusick /* 393*11995Smckusick * If no argument, use curdir as the default. 394*11995Smckusick */ 395*11995Smckusick if (*cp == '\0') { 396*11995Smckusick (void) strcpy(name, curdir); 397*11995Smckusick return; 398*11995Smckusick } 399*11995Smckusick nextarg = cp; 400*11995Smckusick /* 401*11995Smckusick * Find the next argument. 402*11995Smckusick */ 403*11995Smckusick getnext: 404*11995Smckusick for (cp = nextarg + 1; *cp == ' ' || *cp == '\t'; cp++) 405*11995Smckusick /* skip to argument */; 406*11995Smckusick for (bp = cp; *cp != ' ' && *cp != '\t' && *cp != '\0'; cp++) 407*11995Smckusick /* skip to end of argument */; 408*11995Smckusick if (*cp == '\0') 409*11995Smckusick nextarg = NULL; 410*11995Smckusick else 411*11995Smckusick nextarg = cp; 412*11995Smckusick *cp = '\0'; 413*11995Smckusick /* 414*11995Smckusick * If it an absolute pathname, canonicalize it and return it. 415*11995Smckusick */ 416*11995Smckusick if (*bp == '/') { 417*11995Smckusick canon(bp, name); 418*11995Smckusick return; 419*11995Smckusick } 420*11995Smckusick /* 421*11995Smckusick * For relative pathnames, prepend the current directory to 422*11995Smckusick * it then canonicalize and return it. 423*11995Smckusick */ 424*11995Smckusick (void) strcpy(output, curdir); 425*11995Smckusick (void) strcat(output, "/"); 426*11995Smckusick (void) strcat(output, bp); 427*11995Smckusick canon(output, name); 428*11995Smckusick } 429*11995Smckusick 430*11995Smckusick /* 431*11995Smckusick * respond to interrupts 432*11995Smckusick */ 433*11995Smckusick onintr() 434*11995Smckusick { 435*11995Smckusick if (reply("restore interrupted, continue") == FAIL) 436*11995Smckusick done(1); 437*11995Smckusick if (signal(SIGINT, onintr) == SIG_IGN) 438*11995Smckusick (void) signal(SIGINT, SIG_IGN); 439*11995Smckusick if (signal(SIGTERM, onintr) == SIG_IGN) 440*11995Smckusick (void) signal(SIGTERM, SIG_IGN); 441*11995Smckusick } 442*11995Smckusick 443*11995Smckusick /* 444*11995Smckusick * handle unexpected inconsistencies 445*11995Smckusick */ 446*11995Smckusick /* VARARGS1 */ 447*11995Smckusick panic(msg, d1, d2) 448*11995Smckusick char *msg; 449*11995Smckusick long d1, d2; 450*11995Smckusick { 451*11995Smckusick 452*11995Smckusick fprintf(stderr, msg, d1, d2); 453*11995Smckusick if (reply("abort") == GOOD) { 454*11995Smckusick if (reply("dump core") == GOOD) 455*11995Smckusick abort(); 456*11995Smckusick done(1); 457*11995Smckusick } 458*11995Smckusick } 459