110315Smckusick /* Copyright (c) 1983 Regents of the University of California */ 210315Smckusick 310315Smckusick #ifndef lint 4*12557Smckusick static char sccsid[] = "@(#)utilities.c 3.13 (Berkeley) 83/05/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) { 2811995Smckusick ep = addentry(name, psearch(name), NODE); 2910315Smckusick newnode(ep); 3011995Smckusick 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; 4711995Smckusick (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) { 80*12557Smckusick fprintf(stderr, "Warning: cannot rename %s to %s", from, to); 81*12557Smckusick (void) fflush(stderr); 82*12557Smckusick perror(""); 8311923Smckusick return; 8410315Smckusick } 8510315Smckusick vprintf(stdout, "rename %s to %s\n", from, to); 8610315Smckusick } 8710315Smckusick 8810315Smckusick /* 8910315Smckusick * Create a new node (directory). 9010315Smckusick */ 9110315Smckusick newnode(np) 9210315Smckusick struct entry *np; 9310315Smckusick { 9410315Smckusick char *cp; 9510315Smckusick 9610315Smckusick if (np->e_type != NODE) 9710315Smckusick badentry(np, "newnode: not a node"); 9810315Smckusick cp = myname(np); 9911312Smckusick if (mkdir(cp, 0777) < 0) { 10011740Smckusick fprintf(stderr, "Warning: "); 10111995Smckusick (void) fflush(stderr); 10211740Smckusick perror(cp); 10311740Smckusick return; 10410315Smckusick } 10510315Smckusick vprintf(stdout, "Make node %s\n", cp); 10610315Smckusick } 10710315Smckusick 10810315Smckusick /* 10910315Smckusick * Remove an old node (directory). 11010315Smckusick */ 11110315Smckusick removenode(ep) 11210315Smckusick register struct entry *ep; 11310315Smckusick { 11410315Smckusick char *cp; 11510315Smckusick 11610315Smckusick if (ep->e_type != NODE) 11710315Smckusick badentry(ep, "removenode: not a node"); 11810315Smckusick if (ep->e_entries != NIL) 11910315Smckusick badentry(ep, "removenode: non-empty directory"); 12011923Smckusick ep->e_flags |= REMOVED; 12111923Smckusick ep->e_flags &= ~TMPNAME; 12210315Smckusick cp = myname(ep); 12310315Smckusick if (rmdir(cp) < 0) { 12411923Smckusick fprintf(stderr, "Warning: "); 12511995Smckusick (void) fflush(stderr); 12611923Smckusick perror(cp); 12711923Smckusick return; 12810315Smckusick } 12910315Smckusick vprintf(stdout, "Remove node %s\n", cp); 13010315Smckusick } 13110315Smckusick 13210315Smckusick /* 13310315Smckusick * Remove a leaf. 13410315Smckusick */ 13510315Smckusick removeleaf(ep) 13610315Smckusick register struct entry *ep; 13710315Smckusick { 13810315Smckusick char *cp; 13910315Smckusick 14010315Smckusick if (ep->e_type != LEAF) 14110315Smckusick badentry(ep, "removeleaf: not a leaf"); 14211923Smckusick ep->e_flags |= REMOVED; 14311923Smckusick ep->e_flags &= ~TMPNAME; 14410315Smckusick cp = myname(ep); 14510315Smckusick if (unlink(cp) < 0) { 14611923Smckusick fprintf(stderr, "Warning: "); 14711995Smckusick (void) fflush(stderr); 14811923Smckusick perror(cp); 14911923Smckusick return; 15010315Smckusick } 15110315Smckusick vprintf(stdout, "Remove leaf %s\n", cp); 15210315Smckusick } 15310315Smckusick 15410315Smckusick /* 15510315Smckusick * Create a link. 15610315Smckusick */ 15710315Smckusick linkit(existing, new, type) 15810315Smckusick char *existing, *new; 15910315Smckusick int type; 16010315Smckusick { 16110315Smckusick 16210315Smckusick if (type == SYMLINK) { 16310315Smckusick if (symlink(existing, new) < 0) { 16411923Smckusick fprintf(stderr, 165*12557Smckusick "Warning: cannot create symbolic link %s->%s", 16610315Smckusick new, existing); 167*12557Smckusick (void) fflush(stderr); 168*12557Smckusick perror(""); 16911923Smckusick return; 17010315Smckusick } 17110315Smckusick } else if (type == HARDLINK) { 17210315Smckusick if (link(existing, new) < 0) { 17311923Smckusick fprintf(stderr, 174*12557Smckusick "Warning: cannot create hard link %s->%s", 17510315Smckusick new, existing); 176*12557Smckusick (void) fflush(stderr); 177*12557Smckusick perror(""); 17811923Smckusick return; 17910315Smckusick } 18010315Smckusick } else { 18110315Smckusick panic("linkit: unknown type %d\n", type); 18210315Smckusick } 18310315Smckusick vprintf(stdout, "Create %s link %s->%s\n", 18410315Smckusick type == SYMLINK ? "symbolic" : "hard", new, existing); 18510315Smckusick } 18610315Smckusick 18710315Smckusick /* 18810315Smckusick * find lowest number file (above "start") that needs to be extracted 18910315Smckusick */ 19010315Smckusick ino_t 19110315Smckusick lowerbnd(start) 19210315Smckusick ino_t start; 19310315Smckusick { 19410315Smckusick register struct entry *ep; 19510315Smckusick 19610315Smckusick for ( ; start < maxino; start++) { 19710315Smckusick ep = lookupino(start); 19811995Smckusick if (ep == NIL || ep->e_type == NODE) 19910315Smckusick continue; 20011645Smckusick if (ep->e_flags & (NEW|EXTRACT)) 20110315Smckusick return (start); 20210315Smckusick } 20310315Smckusick return (start); 20410315Smckusick } 20510315Smckusick 20610315Smckusick /* 20710315Smckusick * find highest number file (below "start") that needs to be extracted 20810315Smckusick */ 20910315Smckusick ino_t 21010315Smckusick upperbnd(start) 21110315Smckusick ino_t start; 21210315Smckusick { 21310315Smckusick register struct entry *ep; 21410315Smckusick 21510315Smckusick for ( ; start > ROOTINO; start--) { 21610315Smckusick ep = lookupino(start); 21711995Smckusick if (ep == NIL || ep->e_type == NODE) 21810315Smckusick continue; 21911645Smckusick if (ep->e_flags & (NEW|EXTRACT)) 22010315Smckusick return (start); 22110315Smckusick } 22210315Smckusick return (start); 22310315Smckusick } 22410315Smckusick 22510315Smckusick /* 22610315Smckusick * report on a badly formed entry 22710315Smckusick */ 22810315Smckusick badentry(ep, msg) 22910315Smckusick register struct entry *ep; 23010315Smckusick char *msg; 23110315Smckusick { 23210315Smckusick 23310315Smckusick fprintf(stderr, "bad entry: %s\n", msg); 23410315Smckusick fprintf(stderr, "name: %s\n", myname(ep)); 23510315Smckusick fprintf(stderr, "parent name %s\n", myname(ep->e_parent)); 23610315Smckusick if (ep->e_sibling != NIL) 23710315Smckusick fprintf(stderr, "sibling name: %s\n", myname(ep->e_sibling)); 23810315Smckusick if (ep->e_entries != NIL) 23910315Smckusick fprintf(stderr, "next entry name: %s\n", myname(ep->e_entries)); 24010315Smckusick if (ep->e_links != NIL) 24110315Smckusick fprintf(stderr, "next link name: %s\n", myname(ep->e_links)); 24211645Smckusick if (ep->e_next != NIL) 24311645Smckusick fprintf(stderr, "next hashchain name: %s\n", myname(ep->e_next)); 24410315Smckusick fprintf(stderr, "entry type: %s\n", 24510315Smckusick ep->e_type == NODE ? "NODE" : "LEAF"); 24610315Smckusick fprintf(stderr, "inode number: %ld\n", ep->e_ino); 24711898Smckusick panic("flags: %s\n", flagvalues(ep)); 24811898Smckusick } 24911898Smckusick 25011898Smckusick /* 25111898Smckusick * Construct a string indicating the active flag bits of an entry. 25211898Smckusick */ 25311898Smckusick char * 25411898Smckusick flagvalues(ep) 25511898Smckusick register struct entry *ep; 25611898Smckusick { 25711898Smckusick static char flagbuf[BUFSIZ]; 25811898Smckusick 25911898Smckusick (void) strcpy(flagbuf, "|NIL"); 26010315Smckusick flagbuf[0] = '\0'; 26110315Smckusick if (ep->e_flags & REMOVED) 26211898Smckusick (void) strcat(flagbuf, "|REMOVED"); 26310315Smckusick if (ep->e_flags & TMPNAME) 26411898Smckusick (void) strcat(flagbuf, "|TMPNAME"); 26510315Smckusick if (ep->e_flags & EXTRACT) 26611898Smckusick (void) strcat(flagbuf, "|EXTRACT"); 26710315Smckusick if (ep->e_flags & NEW) 26811898Smckusick (void) strcat(flagbuf, "|NEW"); 26910315Smckusick if (ep->e_flags & KEEP) 27011898Smckusick (void) strcat(flagbuf, "|KEEP"); 27111898Smckusick return (&flagbuf[1]); 27210315Smckusick } 27310315Smckusick 27410315Smckusick /* 27511995Smckusick * Check to see if a name is on a dump tape. 27611321Smckusick */ 27711995Smckusick ino_t 27811995Smckusick dirlookup(name) 27911995Smckusick char *name; 28011995Smckusick { 28111995Smckusick ino_t ino; 28211995Smckusick 28311995Smckusick ino = psearch(name); 28411995Smckusick if (ino == 0 || BIT(ino, dumpmap) == 0) 28511995Smckusick fprintf(stderr, "%s is not on tape\n", name); 28611995Smckusick return (ino); 28711995Smckusick } 28811995Smckusick 28911995Smckusick /* 29011995Smckusick * Canonicalize file names to always start with ``./'' and 29111995Smckusick * remove any imbedded ".." components. 29211995Smckusick */ 29311321Smckusick canon(rawname, canonname) 29411321Smckusick char *rawname, *canonname; 29511321Smckusick { 29611995Smckusick register char *cp, *np; 29711321Smckusick int len; 29811321Smckusick 29911321Smckusick if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0) 30011645Smckusick (void) strcpy(canonname, ""); 30111321Smckusick else if (rawname[0] == '/') 30211645Smckusick (void) strcpy(canonname, "."); 30311321Smckusick else 30411645Smckusick (void) strcpy(canonname, "./"); 30511645Smckusick (void) strcat(canonname, rawname); 30611321Smckusick len = strlen(canonname) - 1; 30711321Smckusick if (canonname[len] == '/') 30811321Smckusick canonname[len] = '\0'; 30911995Smckusick /* 31011995Smckusick * Eliminate extraneous ".." from pathnames. 31111995Smckusick */ 31211995Smckusick for (np = canonname; *np != '\0'; ) { 31311995Smckusick np++; 31411995Smckusick cp = np; 31511995Smckusick while (*np != '/' && *np != '\0') 31611995Smckusick np++; 31711995Smckusick if (np - cp == 2 && strncmp(cp, "..", 2) == 0) { 31811995Smckusick cp--; 31911995Smckusick while (cp > &canonname[1] && *--cp != '/') 32011995Smckusick /* find beginning of name */; 32111995Smckusick (void) strcpy(cp, np); 32211995Smckusick np = cp; 32311995Smckusick } 32411995Smckusick } 32511321Smckusick } 32611321Smckusick 32711321Smckusick /* 32811995Smckusick * Elicit a reply. 32910315Smckusick */ 33010315Smckusick reply(question) 33110315Smckusick char *question; 33210315Smckusick { 33310315Smckusick char c; 33410315Smckusick 33510315Smckusick fprintf(stderr, "%s? ", question); 33610315Smckusick do { 33710315Smckusick fprintf(stderr, "[yn] "); 338*12557Smckusick (void) fflush(stderr); 33911995Smckusick c = getc(terminal); 34011995Smckusick while (c != '\n' && getc(terminal) != '\n') 34110315Smckusick /* void */; 34210315Smckusick } while (c != 'y' && c != 'n'); 34310315Smckusick if (c == 'y') 34410315Smckusick return (GOOD); 34510315Smckusick return (FAIL); 34610315Smckusick } 34711995Smckusick 34811995Smckusick /* 34911995Smckusick * Read and parse an interactive command. 35011995Smckusick * The first word on the line is assigned to "cmd". If 35111995Smckusick * there are no arguments on the command line, then "curdir" 35211995Smckusick * is returned as the argument. If there are arguments 35311995Smckusick * on the line they are returned one at a time on each 35411995Smckusick * successive call to getcmd. Each argument is first assigned 35511995Smckusick * to "name". If it does not start with "/" the pathname in 35611995Smckusick * "curdir" is prepended to it. Finally "canon" is called to 35711995Smckusick * eliminate any embedded ".." components. 35811995Smckusick */ 35911995Smckusick getcmd(curdir, cmd, name) 36011995Smckusick char *curdir, *cmd, *name; 36111995Smckusick { 36211995Smckusick register char *cp, *bp; 36311995Smckusick char output[BUFSIZ]; 36411995Smckusick static char input[BUFSIZ]; 36511995Smckusick static char *nextarg = NULL; 36611995Smckusick 36711995Smckusick /* 36811995Smckusick * Check to see if still processing arguments. 36911995Smckusick */ 37011995Smckusick if (nextarg != NULL) 37111995Smckusick goto getnext; 37211995Smckusick nextarg = NULL; 37311995Smckusick /* 37411995Smckusick * Read a command line and trim off trailing white space. 37511995Smckusick */ 37611995Smckusick do { 37711995Smckusick fprintf(stderr, "restore > "); 37811995Smckusick (void) fflush(stderr); 37911995Smckusick (void) fgets(input, BUFSIZ, terminal); 38011995Smckusick } while (!feof(terminal) && input[0] == '\n'); 38111995Smckusick if (feof(terminal)) { 38211995Smckusick (void) strcpy(cmd, "quit"); 38311995Smckusick return; 38411995Smckusick } 38511995Smckusick for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--) 38611995Smckusick /* trim off trailing white space and newline */; 38711995Smckusick *++cp = '\0'; 38811995Smckusick /* 38911995Smckusick * Copy the command into "cmd". 39011995Smckusick */ 39111995Smckusick for (cp = input; *cp == ' ' || *cp == '\t'; cp++) 39211995Smckusick /* skip to command */; 39311995Smckusick for (bp = cmd; *cp != ' ' && *cp != '\t' && *cp != '\0'; ) 39411995Smckusick *bp++ = *cp++; 39511995Smckusick *bp = '\0'; 39611995Smckusick /* 39711995Smckusick * If no argument, use curdir as the default. 39811995Smckusick */ 39911995Smckusick if (*cp == '\0') { 40011995Smckusick (void) strcpy(name, curdir); 40111995Smckusick return; 40211995Smckusick } 40311995Smckusick nextarg = cp; 40411995Smckusick /* 40511995Smckusick * Find the next argument. 40611995Smckusick */ 40711995Smckusick getnext: 40811995Smckusick for (cp = nextarg + 1; *cp == ' ' || *cp == '\t'; cp++) 40911995Smckusick /* skip to argument */; 41011995Smckusick for (bp = cp; *cp != ' ' && *cp != '\t' && *cp != '\0'; cp++) 41111995Smckusick /* skip to end of argument */; 41211995Smckusick if (*cp == '\0') 41311995Smckusick nextarg = NULL; 41411995Smckusick else 41511995Smckusick nextarg = cp; 41611995Smckusick *cp = '\0'; 41711995Smckusick /* 41811995Smckusick * If it an absolute pathname, canonicalize it and return it. 41911995Smckusick */ 42011995Smckusick if (*bp == '/') { 42111995Smckusick canon(bp, name); 42211995Smckusick return; 42311995Smckusick } 42411995Smckusick /* 42511995Smckusick * For relative pathnames, prepend the current directory to 42611995Smckusick * it then canonicalize and return it. 42711995Smckusick */ 42811995Smckusick (void) strcpy(output, curdir); 42911995Smckusick (void) strcat(output, "/"); 43011995Smckusick (void) strcat(output, bp); 43111995Smckusick canon(output, name); 43211995Smckusick } 43311995Smckusick 43411995Smckusick /* 43511995Smckusick * respond to interrupts 43611995Smckusick */ 43711995Smckusick onintr() 43811995Smckusick { 43911995Smckusick if (reply("restore interrupted, continue") == FAIL) 44011995Smckusick done(1); 44111995Smckusick if (signal(SIGINT, onintr) == SIG_IGN) 44211995Smckusick (void) signal(SIGINT, SIG_IGN); 44311995Smckusick if (signal(SIGTERM, onintr) == SIG_IGN) 44411995Smckusick (void) signal(SIGTERM, SIG_IGN); 44511995Smckusick } 44611995Smckusick 44711995Smckusick /* 44811995Smckusick * handle unexpected inconsistencies 44911995Smckusick */ 45011995Smckusick /* VARARGS1 */ 45111995Smckusick panic(msg, d1, d2) 45211995Smckusick char *msg; 45311995Smckusick long d1, d2; 45411995Smckusick { 45511995Smckusick 45611995Smckusick fprintf(stderr, msg, d1, d2); 45711995Smckusick if (reply("abort") == GOOD) { 45811995Smckusick if (reply("dump core") == GOOD) 45911995Smckusick abort(); 46011995Smckusick done(1); 46111995Smckusick } 46211995Smckusick } 463