xref: /csrg-svn/sbin/restore/utilities.c (revision 11995)
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