xref: /csrg-svn/sbin/restore/utilities.c (revision 14452)
110315Smckusick /* Copyright (c) 1983 Regents of the University of California */
210315Smckusick 
310315Smckusick #ifndef lint
4*14452Smckusick static char sccsid[] = "@(#)utilities.c	3.14	(Berkeley)	83/08/11";
510315Smckusick #endif
610315Smckusick 
710315Smckusick #include "restore.h"
810315Smckusick 
9*14452Smckusick char *copynext();
10*14452Smckusick 
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,
16712557Smckusick 				"Warning: cannot create symbolic link %s->%s",
16810315Smckusick 				new, existing);
16912557Smckusick 			(void) fflush(stderr);
17012557Smckusick 			perror("");
17111923Smckusick 			return;
17210315Smckusick 		}
17310315Smckusick 	} else if (type == HARDLINK) {
17410315Smckusick 		if (link(existing, new) < 0) {
17511923Smckusick 			fprintf(stderr,
17612557Smckusick 				"Warning: cannot create hard link %s->%s",
17710315Smckusick 				new, existing);
17812557Smckusick 			(void) fflush(stderr);
17912557Smckusick 			perror("");
18011923Smckusick 			return;
18110315Smckusick 		}
18210315Smckusick 	} else {
18310315Smckusick 		panic("linkit: unknown type %d\n", type);
18410315Smckusick 	}
18510315Smckusick 	vprintf(stdout, "Create %s link %s->%s\n",
18610315Smckusick 		type == SYMLINK ? "symbolic" : "hard", new, existing);
18710315Smckusick }
18810315Smckusick 
18910315Smckusick /*
19010315Smckusick  * find lowest number file (above "start") that needs to be extracted
19110315Smckusick  */
19210315Smckusick ino_t
19310315Smckusick lowerbnd(start)
19410315Smckusick 	ino_t start;
19510315Smckusick {
19610315Smckusick 	register struct entry *ep;
19710315Smckusick 
19810315Smckusick 	for ( ; start < maxino; start++) {
19910315Smckusick 		ep = lookupino(start);
20011995Smckusick 		if (ep == NIL || ep->e_type == NODE)
20110315Smckusick 			continue;
20211645Smckusick 		if (ep->e_flags & (NEW|EXTRACT))
20310315Smckusick 			return (start);
20410315Smckusick 	}
20510315Smckusick 	return (start);
20610315Smckusick }
20710315Smckusick 
20810315Smckusick /*
20910315Smckusick  * find highest number file (below "start") that needs to be extracted
21010315Smckusick  */
21110315Smckusick ino_t
21210315Smckusick upperbnd(start)
21310315Smckusick 	ino_t start;
21410315Smckusick {
21510315Smckusick 	register struct entry *ep;
21610315Smckusick 
21710315Smckusick 	for ( ; start > ROOTINO; start--) {
21810315Smckusick 		ep = lookupino(start);
21911995Smckusick 		if (ep == NIL || ep->e_type == NODE)
22010315Smckusick 			continue;
22111645Smckusick 		if (ep->e_flags & (NEW|EXTRACT))
22210315Smckusick 			return (start);
22310315Smckusick 	}
22410315Smckusick 	return (start);
22510315Smckusick }
22610315Smckusick 
22710315Smckusick /*
22810315Smckusick  * report on a badly formed entry
22910315Smckusick  */
23010315Smckusick badentry(ep, msg)
23110315Smckusick 	register struct entry *ep;
23210315Smckusick 	char *msg;
23310315Smckusick {
23410315Smckusick 
23510315Smckusick 	fprintf(stderr, "bad entry: %s\n", msg);
23610315Smckusick 	fprintf(stderr, "name: %s\n", myname(ep));
23710315Smckusick 	fprintf(stderr, "parent name %s\n", myname(ep->e_parent));
23810315Smckusick 	if (ep->e_sibling != NIL)
23910315Smckusick 		fprintf(stderr, "sibling name: %s\n", myname(ep->e_sibling));
24010315Smckusick 	if (ep->e_entries != NIL)
24110315Smckusick 		fprintf(stderr, "next entry name: %s\n", myname(ep->e_entries));
24210315Smckusick 	if (ep->e_links != NIL)
24310315Smckusick 		fprintf(stderr, "next link name: %s\n", myname(ep->e_links));
24411645Smckusick 	if (ep->e_next != NIL)
24511645Smckusick 		fprintf(stderr, "next hashchain name: %s\n", myname(ep->e_next));
24610315Smckusick 	fprintf(stderr, "entry type: %s\n",
24710315Smckusick 		ep->e_type == NODE ? "NODE" : "LEAF");
24810315Smckusick 	fprintf(stderr, "inode number: %ld\n", ep->e_ino);
24911898Smckusick 	panic("flags: %s\n", flagvalues(ep));
25011898Smckusick }
25111898Smckusick 
25211898Smckusick /*
25311898Smckusick  * Construct a string indicating the active flag bits of an entry.
25411898Smckusick  */
25511898Smckusick char *
25611898Smckusick flagvalues(ep)
25711898Smckusick 	register struct entry *ep;
25811898Smckusick {
25911898Smckusick 	static char flagbuf[BUFSIZ];
26011898Smckusick 
26111898Smckusick 	(void) strcpy(flagbuf, "|NIL");
26210315Smckusick 	flagbuf[0] = '\0';
26310315Smckusick 	if (ep->e_flags & REMOVED)
26411898Smckusick 		(void) strcat(flagbuf, "|REMOVED");
26510315Smckusick 	if (ep->e_flags & TMPNAME)
26611898Smckusick 		(void) strcat(flagbuf, "|TMPNAME");
26710315Smckusick 	if (ep->e_flags & EXTRACT)
26811898Smckusick 		(void) strcat(flagbuf, "|EXTRACT");
26910315Smckusick 	if (ep->e_flags & NEW)
27011898Smckusick 		(void) strcat(flagbuf, "|NEW");
27110315Smckusick 	if (ep->e_flags & KEEP)
27211898Smckusick 		(void) strcat(flagbuf, "|KEEP");
27311898Smckusick 	return (&flagbuf[1]);
27410315Smckusick }
27510315Smckusick 
27610315Smckusick /*
27711995Smckusick  * Check to see if a name is on a dump tape.
27811321Smckusick  */
27911995Smckusick ino_t
28011995Smckusick dirlookup(name)
28111995Smckusick 	char *name;
28211995Smckusick {
28311995Smckusick 	ino_t ino;
28411995Smckusick 
28511995Smckusick 	ino = psearch(name);
28611995Smckusick 	if (ino == 0 || BIT(ino, dumpmap) == 0)
28711995Smckusick 		fprintf(stderr, "%s is not on tape\n", name);
28811995Smckusick 	return (ino);
28911995Smckusick }
29011995Smckusick 
29111995Smckusick /*
29211995Smckusick  * Canonicalize file names to always start with ``./'' and
29311995Smckusick  * remove any imbedded ".." components.
29411995Smckusick  */
29511321Smckusick canon(rawname, canonname)
29611321Smckusick 	char *rawname, *canonname;
29711321Smckusick {
29811995Smckusick 	register char *cp, *np;
29911321Smckusick 	int len;
30011321Smckusick 
30111321Smckusick 	if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
30211645Smckusick 		(void) strcpy(canonname, "");
30311321Smckusick 	else if (rawname[0] == '/')
30411645Smckusick 		(void) strcpy(canonname, ".");
30511321Smckusick 	else
30611645Smckusick 		(void) strcpy(canonname, "./");
30711645Smckusick 	(void) strcat(canonname, rawname);
30811321Smckusick 	len = strlen(canonname) - 1;
30911321Smckusick 	if (canonname[len] == '/')
31011321Smckusick 		canonname[len] = '\0';
31111995Smckusick 	/*
31211995Smckusick 	 * Eliminate extraneous ".." from pathnames.
31311995Smckusick 	 */
31411995Smckusick 	for (np = canonname; *np != '\0'; ) {
31511995Smckusick 		np++;
31611995Smckusick 		cp = np;
31711995Smckusick 		while (*np != '/' && *np != '\0')
31811995Smckusick 			np++;
31911995Smckusick 		if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
32011995Smckusick 			cp--;
32111995Smckusick 			while (cp > &canonname[1] && *--cp != '/')
32211995Smckusick 				/* find beginning of name */;
32311995Smckusick 			(void) strcpy(cp, np);
32411995Smckusick 			np = cp;
32511995Smckusick 		}
32611995Smckusick 	}
32711321Smckusick }
32811321Smckusick 
32911321Smckusick /*
33011995Smckusick  * Elicit a reply.
33110315Smckusick  */
33210315Smckusick reply(question)
33310315Smckusick 	char *question;
33410315Smckusick {
33510315Smckusick 	char c;
33610315Smckusick 
33710315Smckusick 	fprintf(stderr, "%s? ", question);
33810315Smckusick 	do	{
33910315Smckusick 		fprintf(stderr, "[yn] ");
34012557Smckusick 		(void) fflush(stderr);
34111995Smckusick 		c = getc(terminal);
34211995Smckusick 		while (c != '\n' && getc(terminal) != '\n')
34310315Smckusick 			/* void */;
34410315Smckusick 	} while (c != 'y' && c != 'n');
34510315Smckusick 	if (c == 'y')
34610315Smckusick 		return (GOOD);
34710315Smckusick 	return (FAIL);
34810315Smckusick }
34911995Smckusick 
35011995Smckusick /*
35111995Smckusick  * Read and parse an interactive command.
35211995Smckusick  * The first word on the line is assigned to "cmd". If
35311995Smckusick  * there are no arguments on the command line, then "curdir"
35411995Smckusick  * is returned as the argument. If there are arguments
35511995Smckusick  * on the line they are returned one at a time on each
35611995Smckusick  * successive call to getcmd. Each argument is first assigned
35711995Smckusick  * to "name". If it does not start with "/" the pathname in
35811995Smckusick  * "curdir" is prepended to it. Finally "canon" is called to
35911995Smckusick  * eliminate any embedded ".." components.
36011995Smckusick  */
36111995Smckusick getcmd(curdir, cmd, name)
36211995Smckusick 	char *curdir, *cmd, *name;
36311995Smckusick {
364*14452Smckusick 	register char *cp;
365*14452Smckusick 	static char *nextarg = NULL;
366*14452Smckusick 	static char input[BUFSIZ];
36711995Smckusick 	char output[BUFSIZ];
368*14452Smckusick #	define rawname input	/* save space by reusing input buffer */
36911995Smckusick 
37011995Smckusick 	/*
37111995Smckusick 	 * Check to see if still processing arguments.
37211995Smckusick 	 */
37311995Smckusick 	if (nextarg != NULL)
37411995Smckusick 		goto getnext;
37511995Smckusick 	/*
37611995Smckusick 	 * Read a command line and trim off trailing white space.
37711995Smckusick 	 */
37811995Smckusick 	do	{
37911995Smckusick 		fprintf(stderr, "restore > ");
38011995Smckusick 		(void) fflush(stderr);
38111995Smckusick 		(void) fgets(input, BUFSIZ, terminal);
38211995Smckusick 	} while (!feof(terminal) && input[0] == '\n');
38311995Smckusick 	if (feof(terminal)) {
38411995Smckusick 		(void) strcpy(cmd, "quit");
38511995Smckusick 		return;
38611995Smckusick 	}
38711995Smckusick 	for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
38811995Smckusick 		/* trim off trailing white space and newline */;
38911995Smckusick 	*++cp = '\0';
39011995Smckusick 	/*
39111995Smckusick 	 * Copy the command into "cmd".
39211995Smckusick 	 */
393*14452Smckusick 	cp = copynext(input, cmd);
39411995Smckusick 	/*
39511995Smckusick 	 * If no argument, use curdir as the default.
39611995Smckusick 	 */
39711995Smckusick 	if (*cp == '\0') {
39811995Smckusick 		(void) strcpy(name, curdir);
39911995Smckusick 		return;
40011995Smckusick 	}
40111995Smckusick 	nextarg = cp;
40211995Smckusick 	/*
40311995Smckusick 	 * Find the next argument.
40411995Smckusick 	 */
40511995Smckusick getnext:
406*14452Smckusick 	cp = copynext(nextarg, rawname);
40711995Smckusick 	if (*cp == '\0')
40811995Smckusick 		nextarg = NULL;
40911995Smckusick 	else
41011995Smckusick 		nextarg = cp;
41111995Smckusick 	/*
41211995Smckusick 	 * If it an absolute pathname, canonicalize it and return it.
41311995Smckusick 	 */
414*14452Smckusick 	if (rawname[0] == '/') {
415*14452Smckusick 		canon(rawname, name);
41611995Smckusick 		return;
41711995Smckusick 	}
41811995Smckusick 	/*
41911995Smckusick 	 * For relative pathnames, prepend the current directory to
42011995Smckusick 	 * it then canonicalize and return it.
42111995Smckusick 	 */
42211995Smckusick 	(void) strcpy(output, curdir);
42311995Smckusick 	(void) strcat(output, "/");
424*14452Smckusick 	(void) strcat(output, rawname);
42511995Smckusick 	canon(output, name);
426*14452Smckusick #	undef rawname
42711995Smckusick }
42811995Smckusick 
42911995Smckusick /*
430*14452Smckusick  * Strip off the next token of the input.
431*14452Smckusick  */
432*14452Smckusick char *
433*14452Smckusick copynext(input, output)
434*14452Smckusick 	char *input, *output;
435*14452Smckusick {
436*14452Smckusick 	register char *cp, *bp;
437*14452Smckusick 	char quote;
438*14452Smckusick 
439*14452Smckusick 	for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
440*14452Smckusick 		/* skip to argument */;
441*14452Smckusick 	bp = output;
442*14452Smckusick 	while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
443*14452Smckusick 		/*
444*14452Smckusick 		 * Handle back slashes.
445*14452Smckusick 		 */
446*14452Smckusick 		if (*cp == '\\') {
447*14452Smckusick 			if (*++cp == '\0') {
448*14452Smckusick 				fprintf(stderr,
449*14452Smckusick 					"command lines cannot be continued\n");
450*14452Smckusick 				continue;
451*14452Smckusick 			}
452*14452Smckusick 			*bp++ = *cp++;
453*14452Smckusick 			continue;
454*14452Smckusick 		}
455*14452Smckusick 		/*
456*14452Smckusick 		 * The usual unquoted case.
457*14452Smckusick 		 */
458*14452Smckusick 		if (*cp != '\'' && *cp != '"') {
459*14452Smckusick 			*bp++ = *cp++;
460*14452Smckusick 			continue;
461*14452Smckusick 		}
462*14452Smckusick 		/*
463*14452Smckusick 		 * Handle single and double quotes.
464*14452Smckusick 		 */
465*14452Smckusick 		quote = *cp++;
466*14452Smckusick 		while (*cp != quote && *cp != '\0')
467*14452Smckusick 			*bp++ = *cp++;
468*14452Smckusick 		if (*cp++ == '\0') {
469*14452Smckusick 			fprintf(stderr, "missing %c\n", quote);
470*14452Smckusick 			cp--;
471*14452Smckusick 			continue;
472*14452Smckusick 		}
473*14452Smckusick 	}
474*14452Smckusick 	*bp = '\0';
475*14452Smckusick 	return (cp);
476*14452Smckusick }
477*14452Smckusick 
478*14452Smckusick /*
47911995Smckusick  * respond to interrupts
48011995Smckusick  */
48111995Smckusick onintr()
48211995Smckusick {
48311995Smckusick 	if (reply("restore interrupted, continue") == FAIL)
48411995Smckusick 		done(1);
48511995Smckusick 	if (signal(SIGINT, onintr) == SIG_IGN)
48611995Smckusick 		(void) signal(SIGINT, SIG_IGN);
48711995Smckusick 	if (signal(SIGTERM, onintr) == SIG_IGN)
48811995Smckusick 		(void) signal(SIGTERM, SIG_IGN);
48911995Smckusick }
49011995Smckusick 
49111995Smckusick /*
49211995Smckusick  * handle unexpected inconsistencies
49311995Smckusick  */
49411995Smckusick /* VARARGS1 */
49511995Smckusick panic(msg, d1, d2)
49611995Smckusick 	char *msg;
49711995Smckusick 	long d1, d2;
49811995Smckusick {
49911995Smckusick 
50011995Smckusick 	fprintf(stderr, msg, d1, d2);
50111995Smckusick 	if (reply("abort") == GOOD) {
50211995Smckusick 		if (reply("dump core") == GOOD)
50311995Smckusick 			abort();
50411995Smckusick 		done(1);
50511995Smckusick 	}
50611995Smckusick }
507