110315Smckusick #ifndef lint 2*17711Smckusick static char sccsid[] = "@(#)utilities.c 3.17 (Berkeley) 85/01/14"; 310315Smckusick #endif 410315Smckusick 514568Ssam /* Copyright (c) 1983 Regents of the University of California */ 614568Ssam 710315Smckusick #include "restore.h" 810315Smckusick 914452Smckusick char *copynext(); 1014452Smckusick 1110315Smckusick /* 1210315Smckusick * Insure that all the components of a pathname exist. 1310315Smckusick */ 1411645Smckusick pathcheck(name) 1510315Smckusick char *name; 1610315Smckusick { 1710315Smckusick register char *cp; 1810315Smckusick struct entry *ep; 1911312Smckusick char *start; 2010315Smckusick 2110315Smckusick start = index(name, '/'); 2211312Smckusick if (start == 0) 2311645Smckusick return; 2410315Smckusick for (cp = start; *cp != '\0'; cp++) { 2510315Smckusick if (*cp != '/') 2610315Smckusick continue; 2710315Smckusick *cp = '\0'; 2810315Smckusick ep = lookupname(name); 2910315Smckusick if (ep == NIL) { 3011995Smckusick ep = addentry(name, psearch(name), NODE); 3110315Smckusick newnode(ep); 3211995Smckusick ep->e_flags |= NEW|KEEP; 3310315Smckusick } 3410315Smckusick *cp = '/'; 3510315Smckusick } 3610315Smckusick } 3710315Smckusick 3810315Smckusick /* 3910315Smckusick * Change a name to a unique temporary name. 4010315Smckusick */ 4110315Smckusick mktempname(ep) 4210315Smckusick register struct entry *ep; 4310315Smckusick { 4411645Smckusick char oldname[MAXPATHLEN]; 4510315Smckusick 4610315Smckusick if (ep->e_flags & TMPNAME) 4710315Smckusick badentry(ep, "mktempname: called with TMPNAME"); 4810315Smckusick ep->e_flags |= TMPNAME; 4911995Smckusick (void) strcpy(oldname, myname(ep)); 5011645Smckusick freename(ep->e_name); 5111645Smckusick ep->e_name = savename(gentempname(ep)); 5211645Smckusick ep->e_namlen = strlen(ep->e_name); 5310315Smckusick renameit(oldname, myname(ep)); 5410315Smckusick } 5510315Smckusick 5610315Smckusick /* 5711645Smckusick * Generate a temporary name for an entry. 5811645Smckusick */ 5911645Smckusick char * 6011645Smckusick gentempname(ep) 6111645Smckusick struct entry *ep; 6211645Smckusick { 6311645Smckusick static char name[MAXPATHLEN]; 6411645Smckusick struct entry *np; 6511645Smckusick long i = 0; 6611645Smckusick 6711645Smckusick for (np = lookupino(ep->e_ino); np != NIL && np != ep; np = np->e_links) 6811645Smckusick i++; 6911645Smckusick if (np == NIL) 7011645Smckusick badentry(ep, "not on ino list"); 7111733Smckusick (void) sprintf(name, "%s%d%d", TMPHDR, i, ep->e_ino); 7211645Smckusick return (name); 7311645Smckusick } 7411645Smckusick 7511645Smckusick /* 7610315Smckusick * Rename a file or directory. 7710315Smckusick */ 7810315Smckusick renameit(from, to) 7910315Smckusick char *from, *to; 8010315Smckusick { 8110315Smckusick if (rename(from, to) < 0) { 8212557Smckusick fprintf(stderr, "Warning: cannot rename %s to %s", from, to); 8312557Smckusick (void) fflush(stderr); 8412557Smckusick perror(""); 8511923Smckusick return; 8610315Smckusick } 8710315Smckusick vprintf(stdout, "rename %s to %s\n", from, to); 8810315Smckusick } 8910315Smckusick 9010315Smckusick /* 9110315Smckusick * Create a new node (directory). 9210315Smckusick */ 9310315Smckusick newnode(np) 9410315Smckusick struct entry *np; 9510315Smckusick { 9610315Smckusick char *cp; 9710315Smckusick 9810315Smckusick if (np->e_type != NODE) 9910315Smckusick badentry(np, "newnode: not a node"); 10010315Smckusick cp = myname(np); 10111312Smckusick if (mkdir(cp, 0777) < 0) { 10211740Smckusick fprintf(stderr, "Warning: "); 10311995Smckusick (void) fflush(stderr); 10411740Smckusick perror(cp); 10511740Smckusick return; 10610315Smckusick } 10710315Smckusick vprintf(stdout, "Make node %s\n", cp); 10810315Smckusick } 10910315Smckusick 11010315Smckusick /* 11110315Smckusick * Remove an old node (directory). 11210315Smckusick */ 11310315Smckusick removenode(ep) 11410315Smckusick register struct entry *ep; 11510315Smckusick { 11610315Smckusick char *cp; 11710315Smckusick 11810315Smckusick if (ep->e_type != NODE) 11910315Smckusick badentry(ep, "removenode: not a node"); 12010315Smckusick if (ep->e_entries != NIL) 12110315Smckusick badentry(ep, "removenode: non-empty directory"); 12211923Smckusick ep->e_flags |= REMOVED; 12311923Smckusick ep->e_flags &= ~TMPNAME; 12410315Smckusick cp = myname(ep); 12510315Smckusick if (rmdir(cp) < 0) { 12611923Smckusick fprintf(stderr, "Warning: "); 12711995Smckusick (void) fflush(stderr); 12811923Smckusick perror(cp); 12911923Smckusick return; 13010315Smckusick } 13110315Smckusick vprintf(stdout, "Remove node %s\n", cp); 13210315Smckusick } 13310315Smckusick 13410315Smckusick /* 13510315Smckusick * Remove a leaf. 13610315Smckusick */ 13710315Smckusick removeleaf(ep) 13810315Smckusick register struct entry *ep; 13910315Smckusick { 14010315Smckusick char *cp; 14110315Smckusick 14210315Smckusick if (ep->e_type != LEAF) 14310315Smckusick badentry(ep, "removeleaf: not a leaf"); 14411923Smckusick ep->e_flags |= REMOVED; 14511923Smckusick ep->e_flags &= ~TMPNAME; 14610315Smckusick cp = myname(ep); 14710315Smckusick if (unlink(cp) < 0) { 14811923Smckusick fprintf(stderr, "Warning: "); 14911995Smckusick (void) fflush(stderr); 15011923Smckusick perror(cp); 15111923Smckusick return; 15210315Smckusick } 15310315Smckusick vprintf(stdout, "Remove leaf %s\n", cp); 15410315Smckusick } 15510315Smckusick 15610315Smckusick /* 15710315Smckusick * Create a link. 15810315Smckusick */ 15910315Smckusick linkit(existing, new, type) 16010315Smckusick char *existing, *new; 16110315Smckusick int type; 16210315Smckusick { 16310315Smckusick 16410315Smckusick if (type == SYMLINK) { 16510315Smckusick if (symlink(existing, new) < 0) { 16611923Smckusick fprintf(stderr, 16715782Smckusick "Warning: cannot create symbolic link %s->%s: ", 16810315Smckusick new, existing); 16912557Smckusick (void) fflush(stderr); 17012557Smckusick perror(""); 17115782Smckusick return (FAIL); 17210315Smckusick } 17310315Smckusick } else if (type == HARDLINK) { 17410315Smckusick if (link(existing, new) < 0) { 17511923Smckusick fprintf(stderr, 17615782Smckusick "Warning: cannot create hard link %s->%s: ", 17710315Smckusick new, existing); 17812557Smckusick (void) fflush(stderr); 17912557Smckusick perror(""); 18015782Smckusick return (FAIL); 18110315Smckusick } 18210315Smckusick } else { 18310315Smckusick panic("linkit: unknown type %d\n", type); 18415782Smckusick return (FAIL); 18510315Smckusick } 18610315Smckusick vprintf(stdout, "Create %s link %s->%s\n", 18710315Smckusick type == SYMLINK ? "symbolic" : "hard", new, existing); 18815782Smckusick return (GOOD); 18910315Smckusick } 19010315Smckusick 19110315Smckusick /* 19210315Smckusick * find lowest number file (above "start") that needs to be extracted 19310315Smckusick */ 19410315Smckusick ino_t 19510315Smckusick lowerbnd(start) 19610315Smckusick ino_t start; 19710315Smckusick { 19810315Smckusick register struct entry *ep; 19910315Smckusick 20010315Smckusick for ( ; start < maxino; start++) { 20110315Smckusick ep = lookupino(start); 20211995Smckusick if (ep == NIL || ep->e_type == NODE) 20310315Smckusick continue; 20411645Smckusick if (ep->e_flags & (NEW|EXTRACT)) 20510315Smckusick return (start); 20610315Smckusick } 20710315Smckusick return (start); 20810315Smckusick } 20910315Smckusick 21010315Smckusick /* 21110315Smckusick * find highest number file (below "start") that needs to be extracted 21210315Smckusick */ 21310315Smckusick ino_t 21410315Smckusick upperbnd(start) 21510315Smckusick ino_t start; 21610315Smckusick { 21710315Smckusick register struct entry *ep; 21810315Smckusick 21910315Smckusick for ( ; start > ROOTINO; start--) { 22010315Smckusick ep = lookupino(start); 22111995Smckusick if (ep == NIL || ep->e_type == NODE) 22210315Smckusick continue; 22311645Smckusick if (ep->e_flags & (NEW|EXTRACT)) 22410315Smckusick return (start); 22510315Smckusick } 22610315Smckusick return (start); 22710315Smckusick } 22810315Smckusick 22910315Smckusick /* 23010315Smckusick * report on a badly formed entry 23110315Smckusick */ 23210315Smckusick badentry(ep, msg) 23310315Smckusick register struct entry *ep; 23410315Smckusick char *msg; 23510315Smckusick { 23610315Smckusick 23710315Smckusick fprintf(stderr, "bad entry: %s\n", msg); 23810315Smckusick fprintf(stderr, "name: %s\n", myname(ep)); 23910315Smckusick fprintf(stderr, "parent name %s\n", myname(ep->e_parent)); 24010315Smckusick if (ep->e_sibling != NIL) 24110315Smckusick fprintf(stderr, "sibling name: %s\n", myname(ep->e_sibling)); 24210315Smckusick if (ep->e_entries != NIL) 24310315Smckusick fprintf(stderr, "next entry name: %s\n", myname(ep->e_entries)); 24410315Smckusick if (ep->e_links != NIL) 24510315Smckusick fprintf(stderr, "next link name: %s\n", myname(ep->e_links)); 24611645Smckusick if (ep->e_next != NIL) 24711645Smckusick fprintf(stderr, "next hashchain name: %s\n", myname(ep->e_next)); 24810315Smckusick fprintf(stderr, "entry type: %s\n", 24910315Smckusick ep->e_type == NODE ? "NODE" : "LEAF"); 25010315Smckusick fprintf(stderr, "inode number: %ld\n", ep->e_ino); 25111898Smckusick panic("flags: %s\n", flagvalues(ep)); 25211898Smckusick } 25311898Smckusick 25411898Smckusick /* 25511898Smckusick * Construct a string indicating the active flag bits of an entry. 25611898Smckusick */ 25711898Smckusick char * 25811898Smckusick flagvalues(ep) 25911898Smckusick register struct entry *ep; 26011898Smckusick { 26111898Smckusick static char flagbuf[BUFSIZ]; 26211898Smckusick 26311898Smckusick (void) strcpy(flagbuf, "|NIL"); 26410315Smckusick flagbuf[0] = '\0'; 26510315Smckusick if (ep->e_flags & REMOVED) 26611898Smckusick (void) strcat(flagbuf, "|REMOVED"); 26710315Smckusick if (ep->e_flags & TMPNAME) 26811898Smckusick (void) strcat(flagbuf, "|TMPNAME"); 26910315Smckusick if (ep->e_flags & EXTRACT) 27011898Smckusick (void) strcat(flagbuf, "|EXTRACT"); 27110315Smckusick if (ep->e_flags & NEW) 27211898Smckusick (void) strcat(flagbuf, "|NEW"); 27310315Smckusick if (ep->e_flags & KEEP) 27411898Smckusick (void) strcat(flagbuf, "|KEEP"); 27511898Smckusick return (&flagbuf[1]); 27610315Smckusick } 27710315Smckusick 27810315Smckusick /* 27911995Smckusick * Check to see if a name is on a dump tape. 28011321Smckusick */ 28111995Smckusick ino_t 28211995Smckusick dirlookup(name) 28311995Smckusick char *name; 28411995Smckusick { 28511995Smckusick ino_t ino; 28611995Smckusick 28711995Smckusick ino = psearch(name); 28811995Smckusick if (ino == 0 || BIT(ino, dumpmap) == 0) 28911995Smckusick fprintf(stderr, "%s is not on tape\n", name); 29011995Smckusick return (ino); 29111995Smckusick } 29211995Smckusick 29311995Smckusick /* 29411995Smckusick * Canonicalize file names to always start with ``./'' and 29511995Smckusick * remove any imbedded ".." components. 29611995Smckusick */ 29711321Smckusick canon(rawname, canonname) 29811321Smckusick char *rawname, *canonname; 29911321Smckusick { 30011995Smckusick register char *cp, *np; 30111321Smckusick int len; 30211321Smckusick 30311321Smckusick if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0) 30411645Smckusick (void) strcpy(canonname, ""); 30511321Smckusick else if (rawname[0] == '/') 30611645Smckusick (void) strcpy(canonname, "."); 30711321Smckusick else 30811645Smckusick (void) strcpy(canonname, "./"); 30911645Smckusick (void) strcat(canonname, rawname); 31011321Smckusick len = strlen(canonname) - 1; 31111321Smckusick if (canonname[len] == '/') 31211321Smckusick canonname[len] = '\0'; 31311995Smckusick /* 31411995Smckusick * Eliminate extraneous ".." from pathnames. 31511995Smckusick */ 31611995Smckusick for (np = canonname; *np != '\0'; ) { 31711995Smckusick np++; 31811995Smckusick cp = np; 31911995Smckusick while (*np != '/' && *np != '\0') 32011995Smckusick np++; 32111995Smckusick if (np - cp == 2 && strncmp(cp, "..", 2) == 0) { 32211995Smckusick cp--; 32311995Smckusick while (cp > &canonname[1] && *--cp != '/') 32411995Smckusick /* find beginning of name */; 32511995Smckusick (void) strcpy(cp, np); 32611995Smckusick np = cp; 32711995Smckusick } 32811995Smckusick } 32911321Smckusick } 33011321Smckusick 33111321Smckusick /* 33211995Smckusick * Elicit a reply. 33310315Smckusick */ 33410315Smckusick reply(question) 33510315Smckusick char *question; 33610315Smckusick { 33710315Smckusick char c; 33810315Smckusick 33910315Smckusick fprintf(stderr, "%s? ", question); 34010315Smckusick do { 34110315Smckusick fprintf(stderr, "[yn] "); 34212557Smckusick (void) fflush(stderr); 34311995Smckusick c = getc(terminal); 34411995Smckusick while (c != '\n' && getc(terminal) != '\n') 345*17711Smckusick if (feof(terminal)) 346*17711Smckusick done(1); 34710315Smckusick } while (c != 'y' && c != 'n'); 34810315Smckusick if (c == 'y') 34910315Smckusick return (GOOD); 35010315Smckusick return (FAIL); 35110315Smckusick } 35211995Smckusick 35311995Smckusick /* 35411995Smckusick * Read and parse an interactive command. 35511995Smckusick * The first word on the line is assigned to "cmd". If 35611995Smckusick * there are no arguments on the command line, then "curdir" 35711995Smckusick * is returned as the argument. If there are arguments 35811995Smckusick * on the line they are returned one at a time on each 35911995Smckusick * successive call to getcmd. Each argument is first assigned 36011995Smckusick * to "name". If it does not start with "/" the pathname in 36111995Smckusick * "curdir" is prepended to it. Finally "canon" is called to 36211995Smckusick * eliminate any embedded ".." components. 36311995Smckusick */ 36411995Smckusick getcmd(curdir, cmd, name) 36511995Smckusick char *curdir, *cmd, *name; 36611995Smckusick { 36714452Smckusick register char *cp; 36814452Smckusick static char *nextarg = NULL; 36914452Smckusick static char input[BUFSIZ]; 37011995Smckusick char output[BUFSIZ]; 37114452Smckusick # define rawname input /* save space by reusing input buffer */ 37211995Smckusick 37311995Smckusick /* 37411995Smckusick * Check to see if still processing arguments. 37511995Smckusick */ 37611995Smckusick if (nextarg != NULL) 37711995Smckusick goto getnext; 37811995Smckusick /* 37911995Smckusick * Read a command line and trim off trailing white space. 38011995Smckusick */ 38111995Smckusick do { 38211995Smckusick fprintf(stderr, "restore > "); 38311995Smckusick (void) fflush(stderr); 38411995Smckusick (void) fgets(input, BUFSIZ, terminal); 38511995Smckusick } while (!feof(terminal) && input[0] == '\n'); 38611995Smckusick if (feof(terminal)) { 38711995Smckusick (void) strcpy(cmd, "quit"); 38811995Smckusick return; 38911995Smckusick } 39011995Smckusick for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--) 39111995Smckusick /* trim off trailing white space and newline */; 39211995Smckusick *++cp = '\0'; 39311995Smckusick /* 39411995Smckusick * Copy the command into "cmd". 39511995Smckusick */ 39614452Smckusick cp = copynext(input, cmd); 39711995Smckusick /* 39811995Smckusick * If no argument, use curdir as the default. 39911995Smckusick */ 40011995Smckusick if (*cp == '\0') { 40111995Smckusick (void) strcpy(name, curdir); 40211995Smckusick return; 40311995Smckusick } 40411995Smckusick nextarg = cp; 40511995Smckusick /* 40611995Smckusick * Find the next argument. 40711995Smckusick */ 40811995Smckusick getnext: 40914452Smckusick cp = copynext(nextarg, rawname); 41011995Smckusick if (*cp == '\0') 41111995Smckusick nextarg = NULL; 41211995Smckusick else 41311995Smckusick nextarg = cp; 41411995Smckusick /* 41511995Smckusick * If it an absolute pathname, canonicalize it and return it. 41611995Smckusick */ 41714452Smckusick if (rawname[0] == '/') { 41814452Smckusick canon(rawname, name); 41911995Smckusick return; 42011995Smckusick } 42111995Smckusick /* 42211995Smckusick * For relative pathnames, prepend the current directory to 42311995Smckusick * it then canonicalize and return it. 42411995Smckusick */ 42511995Smckusick (void) strcpy(output, curdir); 42611995Smckusick (void) strcat(output, "/"); 42714452Smckusick (void) strcat(output, rawname); 42811995Smckusick canon(output, name); 42914452Smckusick # undef rawname 43011995Smckusick } 43111995Smckusick 43211995Smckusick /* 43314452Smckusick * Strip off the next token of the input. 43414452Smckusick */ 43514452Smckusick char * 43614452Smckusick copynext(input, output) 43714452Smckusick char *input, *output; 43814452Smckusick { 43914452Smckusick register char *cp, *bp; 44014452Smckusick char quote; 44114452Smckusick 44214452Smckusick for (cp = input; *cp == ' ' || *cp == '\t'; cp++) 44314452Smckusick /* skip to argument */; 44414452Smckusick bp = output; 44514452Smckusick while (*cp != ' ' && *cp != '\t' && *cp != '\0') { 44614452Smckusick /* 44714452Smckusick * Handle back slashes. 44814452Smckusick */ 44914452Smckusick if (*cp == '\\') { 45014452Smckusick if (*++cp == '\0') { 45114452Smckusick fprintf(stderr, 45214452Smckusick "command lines cannot be continued\n"); 45314452Smckusick continue; 45414452Smckusick } 45514452Smckusick *bp++ = *cp++; 45614452Smckusick continue; 45714452Smckusick } 45814452Smckusick /* 45914452Smckusick * The usual unquoted case. 46014452Smckusick */ 46114452Smckusick if (*cp != '\'' && *cp != '"') { 46214452Smckusick *bp++ = *cp++; 46314452Smckusick continue; 46414452Smckusick } 46514452Smckusick /* 46614452Smckusick * Handle single and double quotes. 46714452Smckusick */ 46814452Smckusick quote = *cp++; 46914452Smckusick while (*cp != quote && *cp != '\0') 47014452Smckusick *bp++ = *cp++; 47114452Smckusick if (*cp++ == '\0') { 47214452Smckusick fprintf(stderr, "missing %c\n", quote); 47314452Smckusick cp--; 47414452Smckusick continue; 47514452Smckusick } 47614452Smckusick } 47714452Smckusick *bp = '\0'; 47814452Smckusick return (cp); 47914452Smckusick } 48014452Smckusick 48114452Smckusick /* 48211995Smckusick * respond to interrupts 48311995Smckusick */ 48411995Smckusick onintr() 48511995Smckusick { 48611995Smckusick if (reply("restore interrupted, continue") == FAIL) 48711995Smckusick done(1); 48811995Smckusick if (signal(SIGINT, onintr) == SIG_IGN) 48911995Smckusick (void) signal(SIGINT, SIG_IGN); 49011995Smckusick if (signal(SIGTERM, onintr) == SIG_IGN) 49111995Smckusick (void) signal(SIGTERM, SIG_IGN); 49211995Smckusick } 49311995Smckusick 49411995Smckusick /* 49511995Smckusick * handle unexpected inconsistencies 49611995Smckusick */ 49711995Smckusick /* VARARGS1 */ 49811995Smckusick panic(msg, d1, d2) 49911995Smckusick char *msg; 50011995Smckusick long d1, d2; 50111995Smckusick { 50211995Smckusick 50311995Smckusick fprintf(stderr, msg, d1, d2); 50411995Smckusick if (reply("abort") == GOOD) { 50511995Smckusick if (reply("dump core") == GOOD) 50611995Smckusick abort(); 50711995Smckusick done(1); 50811995Smckusick } 50911995Smckusick } 510