xref: /csrg-svn/sbin/restore/restore.c (revision 67777)
121168Sdist /*
261536Sbostic  * Copyright (c) 1983, 1993
361536Sbostic  *	The Regents of the University of California.  All rights reserved.
436105Sbostic  *
542709Sbostic  * %sccs.include.redist.c%
621168Sdist  */
721168Sdist 
810313Smckusick #ifndef lint
9*67777Smckusick static char sccsid[] = "@(#)restore.c	8.3 (Berkeley) 09/13/94";
1036105Sbostic #endif /* not lint */
1110313Smckusick 
1256567Sbostic #include <sys/types.h>
1356567Sbostic #include <sys/stat.h>
1456567Sbostic 
1556567Sbostic #include <ufs/ufs/dinode.h>
1656567Sbostic 
1756567Sbostic #include <stdio.h>
1856567Sbostic #include <string.h>
1956567Sbostic 
2010313Smckusick #include "restore.h"
2156567Sbostic #include "extern.h"
2210313Smckusick 
2356567Sbostic static char *keyval __P((int));
2456567Sbostic 
2510313Smckusick /*
2611742Smckusick  * This implements the 't' option.
2711742Smckusick  * List entries on the tape.
2810313Smckusick  */
2911742Smckusick long
listfile(name,ino,type)3010313Smckusick listfile(name, ino, type)
3110313Smckusick 	char *name;
3210313Smckusick 	ino_t ino;
3310313Smckusick 	int type;
3410313Smckusick {
3511742Smckusick 	long descend = hflag ? GOOD : FAIL;
3610313Smckusick 
3756427Smckusick 	if (TSTINO(ino, dumpmap) == 0)
3811742Smckusick 		return (descend);
3911439Smckusick 	vprintf(stdout, "%s", type == LEAF ? "leaf" : "dir ");
4010313Smckusick 	fprintf(stdout, "%10d\t%s\n", ino, name);
4111742Smckusick 	return (descend);
4210313Smckusick }
4310313Smckusick 
4410313Smckusick /*
4511742Smckusick  * This implements the 'x' option.
4611742Smckusick  * Request that new entries be extracted.
4710313Smckusick  */
4811742Smckusick long
addfile(name,ino,type)4910313Smckusick addfile(name, ino, type)
5010313Smckusick 	char *name;
5110313Smckusick 	ino_t ino;
5210313Smckusick 	int type;
5310313Smckusick {
5410313Smckusick 	register struct entry *ep;
5511742Smckusick 	long descend = hflag ? GOOD : FAIL;
5610313Smckusick 	char buf[100];
5710313Smckusick 
5856427Smckusick 	if (TSTINO(ino, dumpmap) == 0) {
5934426Smckusick 		dprintf(stdout, "%s: not on the tape\n", name);
6011742Smckusick 		return (descend);
6110313Smckusick 	}
62*67777Smckusick 	if (ino == WINO && command == 'i' && !vflag)
63*67777Smckusick 		return (descend);
6411323Smckusick 	if (!mflag) {
6511742Smckusick 		(void) sprintf(buf, "./%u", ino);
6611323Smckusick 		name = buf;
6711323Smckusick 		if (type == NODE) {
6811439Smckusick 			(void) genliteraldir(name, ino);
6911742Smckusick 			return (descend);
7011323Smckusick 		}
7110313Smckusick 	}
7211323Smckusick 	ep = lookupino(ino);
7356567Sbostic 	if (ep != NULL) {
7411994Smckusick 		if (strcmp(name, myname(ep)) == 0) {
7511994Smckusick 			ep->e_flags |= NEW;
7611742Smckusick 			return (descend);
7711994Smckusick 		}
7811323Smckusick 		type |= LINK;
7911323Smckusick 	}
8011323Smckusick 	ep = addentry(name, ino, type);
8111994Smckusick 	if (type == NODE)
8211323Smckusick 		newnode(ep);
8311994Smckusick 	ep->e_flags |= NEW;
8411994Smckusick 	return (descend);
8511994Smckusick }
8611994Smckusick 
8711994Smckusick /*
8811994Smckusick  * This is used by the 'i' option to undo previous requests made by addfile.
8911994Smckusick  * Delete entries from the request queue.
9011994Smckusick  */
9111994Smckusick /* ARGSUSED */
9211994Smckusick long
deletefile(name,ino,type)9311994Smckusick deletefile(name, ino, type)
9411994Smckusick 	char *name;
9511994Smckusick 	ino_t ino;
9611994Smckusick 	int type;
9711994Smckusick {
9811994Smckusick 	long descend = hflag ? GOOD : FAIL;
9911994Smckusick 	struct entry *ep;
10011994Smckusick 
10156427Smckusick 	if (TSTINO(ino, dumpmap) == 0)
10211742Smckusick 		return (descend);
10367756Smckusick 	ep = lookupname(name);
10467756Smckusick 	if (ep != NULL) {
10511994Smckusick 		ep->e_flags &= ~NEW;
10667756Smckusick 		ep->e_flags |= REMOVED;
10767756Smckusick 		if (ep->e_type != NODE)
10867756Smckusick 			freeentry(ep);
10967756Smckusick 	}
11011742Smckusick 	return (descend);
11110313Smckusick }
11210313Smckusick 
11311742Smckusick /*
11411742Smckusick  * The following four routines implement the incremental
11511742Smckusick  * restore algorithm. The first removes old entries, the second
11611742Smckusick  * does renames and calculates the extraction list, the third
11711742Smckusick  * cleans up link names missed by the first two, and the final
11811742Smckusick  * one deletes old directories.
11911742Smckusick  *
12011742Smckusick  * Directories cannot be immediately deleted, as they may have
12111742Smckusick  * other files in them which need to be moved out first. As
12211742Smckusick  * directories to be deleted are found, they are put on the
12311742Smckusick  * following deletion list. After all deletions and renames
12411742Smckusick  * are done, this list is actually deleted.
12511742Smckusick  */
12611742Smckusick static struct entry *removelist;
12711742Smckusick 
12810313Smckusick /*
129*67777Smckusick  *	Remove invalid whiteouts from the old tree.
13011742Smckusick  *	Remove unneeded leaves from the old tree.
13111742Smckusick  *	Remove directories from the lookup chains.
13211742Smckusick  */
13356567Sbostic void
removeoldleaves()13411742Smckusick removeoldleaves()
13511742Smckusick {
136*67777Smckusick 	register struct entry *ep, *nextep;
137*67777Smckusick 	register ino_t i, mydirino;
13811742Smckusick 
13911742Smckusick 	vprintf(stdout, "Mark entries to be removed.\n");
140*67777Smckusick 	if (ep = lookupino(WINO)) {
141*67777Smckusick 		vprintf(stdout, "Delete whiteouts\n");
142*67777Smckusick 		for ( ; ep != NULL; ep = nextep) {
143*67777Smckusick 			nextep = ep->e_links;
144*67777Smckusick 			mydirino = ep->e_parent->e_ino;
145*67777Smckusick 			/*
146*67777Smckusick 			 * We remove all whiteouts that are in directories
147*67777Smckusick 			 * that have been removed or that have been dumped.
148*67777Smckusick 			 */
149*67777Smckusick 			if (TSTINO(mydirino, usedinomap) &&
150*67777Smckusick 			    !TSTINO(mydirino, dumpmap))
151*67777Smckusick 				continue;
152*67777Smckusick 			delwhiteout(ep);
153*67777Smckusick 			freeentry(ep);
154*67777Smckusick 		}
155*67777Smckusick 	}
15611742Smckusick 	for (i = ROOTINO + 1; i < maxino; i++) {
15711742Smckusick 		ep = lookupino(i);
15856567Sbostic 		if (ep == NULL)
15911742Smckusick 			continue;
160*67777Smckusick 		if (TSTINO(i, usedinomap))
16111742Smckusick 			continue;
16256567Sbostic 		for ( ; ep != NULL; ep = ep->e_links) {
16311742Smckusick 			dprintf(stdout, "%s: REMOVE\n", myname(ep));
16411742Smckusick 			if (ep->e_type == LEAF) {
16511742Smckusick 				removeleaf(ep);
16611742Smckusick 				freeentry(ep);
16711742Smckusick 			} else {
16811742Smckusick 				mktempname(ep);
16911742Smckusick 				deleteino(ep->e_ino);
17011742Smckusick 				ep->e_next = removelist;
17111742Smckusick 				removelist = ep;
17211742Smckusick 			}
17311742Smckusick 		}
17411742Smckusick 	}
17511742Smckusick }
17611742Smckusick 
17711742Smckusick /*
17811439Smckusick  *	For each directory entry on the incremental tape, determine which
17911439Smckusick  *	category it falls into as follows:
18010313Smckusick  *	KEEP - entries that are to be left alone.
18110313Smckusick  *	NEW - new entries to be added.
18210313Smckusick  *	EXTRACT - files that must be updated with new contents.
18311439Smckusick  *	LINK - new links to be added.
18411439Smckusick  *	Renames are done at the same time.
18510313Smckusick  */
18611742Smckusick long
nodeupdates(name,ino,type)18711439Smckusick nodeupdates(name, ino, type)
18810313Smckusick 	char *name;
18910313Smckusick 	ino_t ino;
19010313Smckusick 	int type;
19110313Smckusick {
19210313Smckusick 	register struct entry *ep, *np, *ip;
19311742Smckusick 	long descend = GOOD;
19412454Smckusick 	int lookuptype = 0;
19510313Smckusick 	int key = 0;
19610313Smckusick 		/* key values */
19711742Smckusick #		define ONTAPE	0x1	/* inode is on the tape */
19811742Smckusick #		define INOFND	0x2	/* inode already exists */
19911742Smckusick #		define NAMEFND	0x4	/* name already exists */
20011742Smckusick #		define MODECHG	0x8	/* mode of inode changed */
20110313Smckusick 
20210313Smckusick 	/*
20310313Smckusick 	 * This routine is called once for each element in the
20411402Smckusick 	 * directory hierarchy, with a full path name.
20510313Smckusick 	 * The "type" value is incorrectly specified as LEAF for
20610313Smckusick 	 * directories that are not on the dump tape.
20712454Smckusick 	 *
20812454Smckusick 	 * Check to see if the file is on the tape.
20910313Smckusick 	 */
21056427Smckusick 	if (TSTINO(ino, dumpmap))
21110313Smckusick 		key |= ONTAPE;
21212454Smckusick 	/*
21312454Smckusick 	 * Check to see if the name exists, and if the name is a link.
21412454Smckusick 	 */
21510313Smckusick 	np = lookupname(name);
21656567Sbostic 	if (np != NULL) {
21710313Smckusick 		key |= NAMEFND;
21812454Smckusick 		ip = lookupino(np->e_ino);
21912454Smckusick 		if (ip == NULL)
22012454Smckusick 			panic("corrupted symbol table\n");
22112454Smckusick 		if (ip != np)
22212454Smckusick 			lookuptype = LINK;
22312454Smckusick 	}
22412454Smckusick 	/*
22555368Smckusick 	 * Check to see if the inode exists, and if one of its links
22655368Smckusick 	 * corresponds to the name (if one was found).
22712454Smckusick 	 */
22810313Smckusick 	ip = lookupino(ino);
22956567Sbostic 	if (ip != NULL) {
23010313Smckusick 		key |= INOFND;
23156567Sbostic 		for (ep = ip->e_links; ep != NULL; ep = ep->e_links) {
23255368Smckusick 			if (ep == np) {
23355368Smckusick 				ip = ep;
23455368Smckusick 				break;
23555368Smckusick 			}
23655368Smckusick 		}
23755368Smckusick 	}
23811742Smckusick 	/*
23912454Smckusick 	 * If both a name and an inode are found, but they do not
24012454Smckusick 	 * correspond to the same file, then both the inode that has
24112454Smckusick 	 * been found and the inode corresponding to the name that
24212454Smckusick 	 * has been found need to be renamed. The current pathname
24312454Smckusick 	 * is the new name for the inode that has been found. Since
24412454Smckusick 	 * all files to be deleted have already been removed, the
24512454Smckusick 	 * named file is either a now unneeded link, or it must live
24612454Smckusick 	 * under a new name in this dump level. If it is a link, it
24712454Smckusick 	 * can be removed. If it is not a link, it is given a
24812454Smckusick 	 * temporary name in anticipation that it will be renamed
24912454Smckusick 	 * when it is later found by inode number.
25011742Smckusick 	 */
25111439Smckusick 	if (((key & (INOFND|NAMEFND)) == (INOFND|NAMEFND)) && ip != np) {
25212454Smckusick 		if (lookuptype == LINK) {
25312454Smckusick 			removeleaf(np);
25412454Smckusick 			freeentry(np);
25512454Smckusick 		} else {
25612454Smckusick 			dprintf(stdout, "name/inode conflict, mktempname %s\n",
25712454Smckusick 				myname(np));
25812454Smckusick 			mktempname(np);
25912454Smckusick 		}
26056567Sbostic 		np = NULL;
26110313Smckusick 		key &= ~NAMEFND;
26210313Smckusick 	}
26310313Smckusick 	if ((key & ONTAPE) &&
26410313Smckusick 	  (((key & INOFND) && ip->e_type != type) ||
26511898Smckusick 	   ((key & NAMEFND) && np->e_type != type)))
26610313Smckusick 		key |= MODECHG;
26711742Smckusick 
26811742Smckusick 	/*
26911742Smckusick 	 * Decide on the disposition of the file based on its flags.
27011742Smckusick 	 * Note that we have already handled the case in which
27111742Smckusick 	 * a name and inode are found that correspond to different files.
27211742Smckusick 	 * Thus if both NAMEFND and INOFND are set then ip == np.
27311742Smckusick 	 */
27410313Smckusick 	switch (key) {
27510313Smckusick 
27611742Smckusick 	/*
27711742Smckusick 	 * A previously existing file has been found.
27811742Smckusick 	 * Mark it as KEEP so that other links to the inode can be
27911742Smckusick 	 * detected, and so that it will not be reclaimed by the search
28011742Smckusick 	 * for unreferenced names.
28111742Smckusick 	 */
28210313Smckusick 	case INOFND|NAMEFND:
28310313Smckusick 		ip->e_flags |= KEEP;
28411898Smckusick 		dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
28511898Smckusick 			flagvalues(ip));
28610313Smckusick 		break;
28710313Smckusick 
28811742Smckusick 	/*
28912445Smckusick 	 * A file on the tape has a name which is the same as a name
29012445Smckusick 	 * corresponding to a different file in the previous dump.
29112445Smckusick 	 * Since all files to be deleted have already been removed,
29212454Smckusick 	 * this file is either a now unneeded link, or it must live
29312454Smckusick 	 * under a new name in this dump level. If it is a link, it
29412454Smckusick 	 * can simply be removed. If it is not a link, it is given a
29512454Smckusick 	 * temporary name in anticipation that it will be renamed
29612454Smckusick 	 * when it is later found by inode number (see INOFND case
29712454Smckusick 	 * below). The entry is then treated as a new file.
29812445Smckusick 	 */
29912445Smckusick 	case ONTAPE|NAMEFND:
30012445Smckusick 	case ONTAPE|NAMEFND|MODECHG:
30112454Smckusick 		if (lookuptype == LINK) {
30212454Smckusick 			removeleaf(np);
30312454Smckusick 			freeentry(np);
30412454Smckusick 		} else {
30512454Smckusick 			mktempname(np);
30612454Smckusick 		}
30712445Smckusick 		/* fall through */
30812445Smckusick 
30912445Smckusick 	/*
31011742Smckusick 	 * A previously non-existent file.
31111742Smckusick 	 * Add it to the file system, and request its extraction.
31211742Smckusick 	 * If it is a directory, create it immediately.
31311742Smckusick 	 * (Since the name is unused there can be no conflict)
31411742Smckusick 	 */
31510313Smckusick 	case ONTAPE:
31610313Smckusick 		ep = addentry(name, ino, type);
31711439Smckusick 		if (type == NODE)
31811439Smckusick 			newnode(ep);
31911994Smckusick 		ep->e_flags |= NEW|KEEP;
32011898Smckusick 		dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
32111898Smckusick 			flagvalues(ep));
32210313Smckusick 		break;
32310313Smckusick 
32411742Smckusick 	/*
32511742Smckusick 	 * A file with the same inode number, but a different
32611742Smckusick 	 * name has been found. If the other name has not already
32711742Smckusick 	 * been found (indicated by the KEEP flag, see above) then
32811742Smckusick 	 * this must be a new name for the file, and it is renamed.
32911742Smckusick 	 * If the other name has been found then this must be a
33011742Smckusick 	 * link to the file. Hard links to directories are not
33111742Smckusick 	 * permitted, and are either deleted or converted to
33211742Smckusick 	 * symbolic links. Finally, if the file is on the tape,
33311742Smckusick 	 * a request is made to extract it.
33411742Smckusick 	 */
33510313Smckusick 	case ONTAPE|INOFND:
33611898Smckusick 		if (type == LEAF && (ip->e_flags & KEEP) == 0)
33711439Smckusick 			ip->e_flags |= EXTRACT;
33810313Smckusick 		/* fall through */
33910313Smckusick 	case INOFND:
34011742Smckusick 		if ((ip->e_flags & KEEP) == 0) {
34111742Smckusick 			renameit(myname(ip), name);
34211742Smckusick 			moveentry(ip, name);
34311742Smckusick 			ip->e_flags |= KEEP;
34412220Smckusick 			dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
34511898Smckusick 				flagvalues(ip));
34610313Smckusick 			break;
34710313Smckusick 		}
34811742Smckusick 		if (ip->e_type == NODE) {
34911742Smckusick 			descend = FAIL;
35011742Smckusick 			fprintf(stderr,
35111742Smckusick 				"deleted hard link %s to directory %s\n",
35211742Smckusick 				name, myname(ip));
35311742Smckusick 			break;
35411742Smckusick 		}
35511742Smckusick 		ep = addentry(name, ino, type|LINK);
35611742Smckusick 		ep->e_flags |= NEW;
35711898Smckusick 		dprintf(stdout, "[%s] %s: %s|LINK\n", keyval(key), name,
35811898Smckusick 			flagvalues(ep));
35910313Smckusick 		break;
36010313Smckusick 
36111742Smckusick 	/*
36255368Smckusick 	 * A previously known file which is to be updated. If it is a link,
36355368Smckusick 	 * then all names referring to the previous file must be removed
36455368Smckusick 	 * so that the subset of them that remain can be recreated.
36511742Smckusick 	 */
36610313Smckusick 	case ONTAPE|INOFND|NAMEFND:
36755368Smckusick 		if (lookuptype == LINK) {
36855368Smckusick 			removeleaf(np);
36955368Smckusick 			freeentry(np);
37055368Smckusick 			ep = addentry(name, ino, type|LINK);
37155368Smckusick 			if (type == NODE)
37255368Smckusick 			        newnode(ep);
37355368Smckusick 			ep->e_flags |= NEW|KEEP;
37455368Smckusick 			dprintf(stdout, "[%s] %s: %s|LINK\n", keyval(key), name,
37555368Smckusick 				flagvalues(ep));
37655368Smckusick 			break;
37755368Smckusick 		}
37812454Smckusick 		if (type == LEAF && lookuptype != LINK)
37911439Smckusick 			np->e_flags |= EXTRACT;
38011439Smckusick 		np->e_flags |= KEEP;
38111898Smckusick 		dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
38211898Smckusick 			flagvalues(np));
38310313Smckusick 		break;
38410313Smckusick 
38511742Smckusick 	/*
38611742Smckusick 	 * An inode is being reused in a completely different way.
38711742Smckusick 	 * Normally an extract can simply do an "unlink" followed
38811742Smckusick 	 * by a "creat". Here we must do effectively the same
38911742Smckusick 	 * thing. The complications arise because we cannot really
39011742Smckusick 	 * delete a directory since it may still contain files
39111742Smckusick 	 * that we need to rename, so we delete it from the symbol
39211742Smckusick 	 * table, and put it on the list to be deleted eventually.
39311742Smckusick 	 * Conversely if a directory is to be created, it must be
39411742Smckusick 	 * done immediately, rather than waiting until the
39511742Smckusick 	 * extraction phase.
39611742Smckusick 	 */
39710313Smckusick 	case ONTAPE|INOFND|MODECHG:
39810313Smckusick 	case ONTAPE|INOFND|NAMEFND|MODECHG:
39911439Smckusick 		if (ip->e_flags & KEEP) {
40011439Smckusick 			badentry(ip, "cannot KEEP and change modes");
40111439Smckusick 			break;
40211439Smckusick 		}
40311439Smckusick 		if (ip->e_type == LEAF) {
40411439Smckusick 			/* changing from leaf to node */
40511439Smckusick 			removeleaf(ip);
40611439Smckusick 			freeentry(ip);
40711439Smckusick 			ip = addentry(name, ino, type);
40811439Smckusick 			newnode(ip);
40911439Smckusick 		} else {
41011439Smckusick 			/* changing from node to leaf */
41112457Smckusick 			if ((ip->e_flags & TMPNAME) == 0)
41212457Smckusick 				mktempname(ip);
41311439Smckusick 			deleteino(ip->e_ino);
41411439Smckusick 			ip->e_next = removelist;
41511439Smckusick 			removelist = ip;
41611439Smckusick 			ip = addentry(name, ino, type);
41711439Smckusick 		}
41811994Smckusick 		ip->e_flags |= NEW|KEEP;
41911898Smckusick 		dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
42011898Smckusick 			flagvalues(ip));
42110313Smckusick 		break;
42210313Smckusick 
42311742Smckusick 	/*
42411742Smckusick 	 * A hard link to a diirectory that has been removed.
42511742Smckusick 	 * Ignore it.
42611742Smckusick 	 */
42711742Smckusick 	case NAMEFND:
42811898Smckusick 		dprintf(stdout, "[%s] %s: Extraneous name\n", keyval(key),
42911898Smckusick 			name);
43011742Smckusick 		descend = FAIL;
43111742Smckusick 		break;
43211742Smckusick 
43311742Smckusick 	/*
43426487Smckusick 	 * If we find a directory entry for a file that is not on
43526487Smckusick 	 * the tape, then we must have found a file that was created
43626487Smckusick 	 * while the dump was in progress. Since we have no contents
43726487Smckusick 	 * for it, we discard the name knowing that it will be on the
43826487Smckusick 	 * next incremental tape.
43926487Smckusick 	 */
44056567Sbostic 	case NULL:
44131541Smckusick 		fprintf(stderr, "%s: (inode %d) not found on tape\n",
44231541Smckusick 			name, ino);
44326487Smckusick 		break;
44426487Smckusick 
44526487Smckusick 	/*
44611742Smckusick 	 * If any of these arise, something is grievously wrong with
44711742Smckusick 	 * the current state of the symbol table.
44811742Smckusick 	 */
44910313Smckusick 	case INOFND|NAMEFND|MODECHG:
45010313Smckusick 	case NAMEFND|MODECHG:
45110313Smckusick 	case INOFND|MODECHG:
45239941Sbostic 		fprintf(stderr, "[%s] %s: inconsistent state\n", keyval(key),
45339941Sbostic 			name);
45410313Smckusick 		break;
45510313Smckusick 
45611742Smckusick 	/*
45711742Smckusick 	 * These states "cannot" arise for any state of the symbol table.
45811742Smckusick 	 */
45910313Smckusick 	case ONTAPE|MODECHG:
46010313Smckusick 	case MODECHG:
46110313Smckusick 	default:
46211898Smckusick 		panic("[%s] %s: impossible state\n", keyval(key), name);
46310313Smckusick 		break;
46410313Smckusick 	}
46511742Smckusick 	return (descend);
46610313Smckusick }
46710313Smckusick 
46810313Smckusick /*
46911898Smckusick  * Calculate the active flags in a key.
47011898Smckusick  */
47156567Sbostic static char *
keyval(key)47211898Smckusick keyval(key)
47311898Smckusick 	int key;
47411898Smckusick {
47511898Smckusick 	static char keybuf[32];
47611898Smckusick 
47711994Smckusick 	(void) strcpy(keybuf, "|NIL");
47811898Smckusick 	keybuf[0] = '\0';
47911898Smckusick 	if (key & ONTAPE)
48011898Smckusick 		(void) strcat(keybuf, "|ONTAPE");
48111898Smckusick 	if (key & INOFND)
48211898Smckusick 		(void) strcat(keybuf, "|INOFND");
48311898Smckusick 	if (key & NAMEFND)
48411898Smckusick 		(void) strcat(keybuf, "|NAMEFND");
48511898Smckusick 	if (key & MODECHG)
48611898Smckusick 		(void) strcat(keybuf, "|MODECHG");
48711898Smckusick 	return (&keybuf[1]);
48811898Smckusick }
48911898Smckusick 
49011898Smckusick /*
49111742Smckusick  * Find unreferenced link names.
49210313Smckusick  */
49356567Sbostic void
findunreflinks()49411439Smckusick findunreflinks()
49510313Smckusick {
49610313Smckusick 	register struct entry *ep, *np;
49711439Smckusick 	register ino_t i;
49810313Smckusick 
49910313Smckusick 	vprintf(stdout, "Find unreferenced names.\n");
50010313Smckusick 	for (i = ROOTINO; i < maxino; i++) {
50110313Smckusick 		ep = lookupino(i);
50256567Sbostic 		if (ep == NULL || ep->e_type == LEAF || TSTINO(i, dumpmap) == 0)
50310313Smckusick 			continue;
50456567Sbostic 		for (np = ep->e_entries; np != NULL; np = np->e_sibling) {
50510313Smckusick 			if (np->e_flags == 0) {
50611439Smckusick 				dprintf(stdout,
50711439Smckusick 				    "%s: remove unreferenced name\n",
50811439Smckusick 				    myname(np));
50911439Smckusick 				removeleaf(np);
51011439Smckusick 				freeentry(np);
51110313Smckusick 			}
51210313Smckusick 		}
51310313Smckusick 	}
51412457Smckusick 	/*
51512457Smckusick 	 * Any leaves remaining in removed directories is unreferenced.
51612457Smckusick 	 */
51756567Sbostic 	for (ep = removelist; ep != NULL; ep = ep->e_next) {
51856567Sbostic 		for (np = ep->e_entries; np != NULL; np = np->e_sibling) {
51912457Smckusick 			if (np->e_type == LEAF) {
52012457Smckusick 				if (np->e_flags != 0)
52112457Smckusick 					badentry(np, "unreferenced with flags");
52212457Smckusick 				dprintf(stdout,
52312457Smckusick 				    "%s: remove unreferenced name\n",
52412457Smckusick 				    myname(np));
52512457Smckusick 				removeleaf(np);
52612457Smckusick 				freeentry(np);
52712457Smckusick 			}
52812457Smckusick 		}
52912457Smckusick 	}
53010313Smckusick }
53110313Smckusick 
53210313Smckusick /*
53311742Smckusick  * Remove old nodes (directories).
53411742Smckusick  * Note that this routine runs in O(N*D) where:
53511742Smckusick  *	N is the number of directory entries to be removed.
53611742Smckusick  *	D is the maximum depth of the tree.
53711742Smckusick  * If N == D this can be quite slow. If the list were
53811742Smckusick  * topologically sorted, the deletion could be done in
53911742Smckusick  * time O(N).
54010313Smckusick  */
54156567Sbostic void
removeoldnodes()54211439Smckusick removeoldnodes()
54310313Smckusick {
54411439Smckusick 	register struct entry *ep, **prev;
54511439Smckusick 	long change;
54610313Smckusick 
54711439Smckusick 	vprintf(stdout, "Remove old nodes (directories).\n");
54811439Smckusick 	do	{
54911439Smckusick 		change = 0;
55011439Smckusick 		prev = &removelist;
55156567Sbostic 		for (ep = removelist; ep != NULL; ep = *prev) {
55256567Sbostic 			if (ep->e_entries != NULL) {
55311439Smckusick 				prev = &ep->e_next;
55411128Smckusick 				continue;
55510313Smckusick 			}
55611439Smckusick 			*prev = ep->e_next;
55711439Smckusick 			removenode(ep);
55811439Smckusick 			freeentry(ep);
55911439Smckusick 			change++;
56010313Smckusick 		}
56111439Smckusick 	} while (change);
56256567Sbostic 	for (ep = removelist; ep != NULL; ep = ep->e_next)
56311439Smckusick 		badentry(ep, "cannot remove, non-empty");
56410313Smckusick }
56510313Smckusick 
56610313Smckusick /*
56711742Smckusick  * This is the routine used to extract files for the 'r' command.
56811742Smckusick  * Extract new leaves.
56910313Smckusick  */
57056567Sbostic void
createleaves(symtabfile)57110313Smckusick createleaves(symtabfile)
57210313Smckusick 	char *symtabfile;
57310313Smckusick {
57410313Smckusick 	register struct entry *ep;
57510313Smckusick 	ino_t first;
57610313Smckusick 	long curvol;
57710313Smckusick 
57811439Smckusick 	if (command == 'R') {
57910313Smckusick 		vprintf(stdout, "Continue extraction of new leaves\n");
58011439Smckusick 	} else {
58110313Smckusick 		vprintf(stdout, "Extract new leaves.\n");
58211439Smckusick 		dumpsymtable(symtabfile, volno);
58311439Smckusick 	}
58410313Smckusick 	first = lowerbnd(ROOTINO);
58510313Smckusick 	curvol = volno;
58610313Smckusick 	while (curfile.ino < maxino) {
58711439Smckusick 		first = lowerbnd(first);
58811742Smckusick 		/*
58911742Smckusick 		 * If the next available file is not the one which we
59011742Smckusick 		 * expect then we have missed one or more files. Since
59111742Smckusick 		 * we do not request files that were not on the tape,
59211924Smckusick 		 * the lost files must have been due to a tape read error,
59311924Smckusick 		 * or a file that was removed while the dump was in progress.
59411742Smckusick 		 */
59510313Smckusick 		while (first < curfile.ino) {
59610313Smckusick 			ep = lookupino(first);
59756567Sbostic 			if (ep == NULL)
59810313Smckusick 				panic("%d: bad first\n", first);
59910313Smckusick 			fprintf(stderr, "%s: not found on tape\n", myname(ep));
60011439Smckusick 			ep->e_flags &= ~(NEW|EXTRACT);
60111439Smckusick 			first = lowerbnd(first);
60210313Smckusick 		}
60311924Smckusick 		/*
60411924Smckusick 		 * If we find files on the tape that have no corresponding
60511924Smckusick 		 * directory entries, then we must have found a file that
60611924Smckusick 		 * was created while the dump was in progress. Since we have
60711924Smckusick 		 * no name for it, we discard it knowing that it will be
60811924Smckusick 		 * on the next incremental tape.
60911924Smckusick 		 */
61011922Smckusick 		if (first != curfile.ino) {
61111922Smckusick 			fprintf(stderr, "expected next file %d, got %d\n",
61210313Smckusick 				first, curfile.ino);
61311922Smckusick 			skipfile();
61411922Smckusick 			goto next;
61511922Smckusick 		}
61610313Smckusick 		ep = lookupino(curfile.ino);
61756567Sbostic 		if (ep == NULL)
61810313Smckusick 			panic("unknown file on tape\n");
61911439Smckusick 		if ((ep->e_flags & (NEW|EXTRACT)) == 0)
62010313Smckusick 			badentry(ep, "unexpected file on tape");
62111742Smckusick 		/*
62211742Smckusick 		 * If the file is to be extracted, then the old file must
62311742Smckusick 		 * be removed since its type may change from one leaf type
62411742Smckusick 		 * to another (eg "file" to "character special").
62511742Smckusick 		 */
62611439Smckusick 		if ((ep->e_flags & EXTRACT) != 0) {
62711439Smckusick 			removeleaf(ep);
62811439Smckusick 			ep->e_flags &= ~REMOVED;
62911439Smckusick 		}
63011742Smckusick 		(void) extractfile(myname(ep));
63111439Smckusick 		ep->e_flags &= ~(NEW|EXTRACT);
63211742Smckusick 		/*
63311742Smckusick 		 * We checkpoint the restore after every tape reel, so
63411742Smckusick 		 * as to simplify the amount of work re quired by the
63511742Smckusick 		 * 'R' command.
63611742Smckusick 		 */
63711922Smckusick 	next:
63810313Smckusick 		if (curvol != volno) {
63910313Smckusick 			dumpsymtable(symtabfile, volno);
64011439Smckusick 			skipmaps();
64110313Smckusick 			curvol = volno;
64210313Smckusick 		}
64310313Smckusick 	}
64410313Smckusick }
64510313Smckusick 
64610313Smckusick /*
64712454Smckusick  * This is the routine used to extract files for the 'x' and 'i' commands.
64812454Smckusick  * Efficiently extract a subset of the files on a tape.
64910313Smckusick  */
65056567Sbostic void
createfiles()65110313Smckusick createfiles()
65210313Smckusick {
65310313Smckusick 	register ino_t first, next, last;
65410313Smckusick 	register struct entry *ep;
65510313Smckusick 	long curvol;
65610313Smckusick 
65710313Smckusick 	vprintf(stdout, "Extract requested files\n");
65811310Smckusick 	curfile.action = SKIP;
65911313Smckusick 	getvol((long)1);
66011402Smckusick 	skipmaps();
66111402Smckusick 	skipdirs();
66210313Smckusick 	first = lowerbnd(ROOTINO);
66311310Smckusick 	last = upperbnd(maxino - 1);
66410313Smckusick 	for (;;) {
66510313Smckusick 		first = lowerbnd(first);
66610313Smckusick 		last = upperbnd(last);
66711402Smckusick 		/*
66811402Smckusick 		 * Check to see if any files remain to be extracted
66911402Smckusick 		 */
67010313Smckusick 		if (first > last)
67110313Smckusick 			return;
67211402Smckusick 		/*
67311402Smckusick 		 * Reject any volumes with inodes greater
67411402Smckusick 		 * than the last one needed
67511402Smckusick 		 */
67610313Smckusick 		while (curfile.ino > last) {
67710313Smckusick 			curfile.action = SKIP;
67810313Smckusick 			getvol((long)0);
67911402Smckusick 			skipmaps();
68011402Smckusick 			skipdirs();
68110313Smckusick 		}
68211402Smckusick 		/*
68311402Smckusick 		 * Decide on the next inode needed.
68411402Smckusick 		 * Skip across the inodes until it is found
68511402Smckusick 		 * or an out of order volume change is encountered
68611402Smckusick 		 */
68710313Smckusick 		next = lowerbnd(curfile.ino);
68810313Smckusick 		do	{
68910313Smckusick 			curvol = volno;
69010313Smckusick 			while (next > curfile.ino && volno == curvol)
69110313Smckusick 				skipfile();
69211402Smckusick 			skipmaps();
69311402Smckusick 			skipdirs();
69410313Smckusick 		} while (volno == curvol + 1);
69511402Smckusick 		/*
69611402Smckusick 		 * If volume change out of order occurred the
69711994Smckusick 		 * current state must be recalculated
69811402Smckusick 		 */
69910313Smckusick 		if (volno != curvol)
70010313Smckusick 			continue;
70111402Smckusick 		/*
70211402Smckusick 		 * If the current inode is greater than the one we were
70311402Smckusick 		 * looking for then we missed the one we were looking for.
70411402Smckusick 		 * Since we only attempt to extract files listed in the
70511994Smckusick 		 * dump map, the lost files must have been due to a tape
70611994Smckusick 		 * read error, or a file that was removed while the dump
70711994Smckusick 		 * was in progress. Thus we report all requested files
70811994Smckusick 		 * between the one we were looking for, and the one we
70911994Smckusick 		 * found as missing, and delete their request flags.
71011402Smckusick 		 */
71110313Smckusick 		while (next < curfile.ino) {
71210313Smckusick 			ep = lookupino(next);
71356567Sbostic 			if (ep == NULL)
71410313Smckusick 				panic("corrupted symbol table\n");
71510313Smckusick 			fprintf(stderr, "%s: not found on tape\n", myname(ep));
71610313Smckusick 			ep->e_flags &= ~NEW;
71711402Smckusick 			next = lowerbnd(next);
71810313Smckusick 		}
71911402Smckusick 		/*
72011402Smckusick 		 * The current inode is the one that we are looking for,
72111402Smckusick 		 * so extract it per its requested name.
72211402Smckusick 		 */
72311313Smckusick 		if (next == curfile.ino && next <= last) {
72410313Smckusick 			ep = lookupino(next);
72556567Sbostic 			if (ep == NULL)
72610313Smckusick 				panic("corrupted symbol table\n");
72711742Smckusick 			(void) extractfile(myname(ep));
72810313Smckusick 			ep->e_flags &= ~NEW;
72916228Smckusick 			if (volno != curvol)
73016228Smckusick 				skipmaps();
73110313Smckusick 		}
73210313Smckusick 	}
73310313Smckusick }
73410313Smckusick 
73510313Smckusick /*
73611742Smckusick  * Add links.
73710313Smckusick  */
73856567Sbostic void
createlinks()73910313Smckusick createlinks()
74010313Smckusick {
74110313Smckusick 	register struct entry *np, *ep;
74211439Smckusick 	register ino_t i;
74310313Smckusick 	char name[BUFSIZ];
74410313Smckusick 
745*67777Smckusick 	if (ep = lookupino(WINO)) {
746*67777Smckusick 		vprintf(stdout, "Add whiteouts\n");
747*67777Smckusick 		for ( ; ep != NULL; ep = ep->e_links) {
748*67777Smckusick 			if ((ep->e_flags & NEW) == 0)
749*67777Smckusick 				continue;
750*67777Smckusick 			(void) addwhiteout(myname(ep));
751*67777Smckusick 			ep->e_flags &= ~NEW;
752*67777Smckusick 		}
753*67777Smckusick 	}
75410313Smckusick 	vprintf(stdout, "Add links\n");
75510313Smckusick 	for (i = ROOTINO; i < maxino; i++) {
75610313Smckusick 		ep = lookupino(i);
75756567Sbostic 		if (ep == NULL)
75810313Smckusick 			continue;
75956567Sbostic 		for (np = ep->e_links; np != NULL; np = np->e_links) {
76011924Smckusick 			if ((np->e_flags & NEW) == 0)
76111924Smckusick 				continue;
76211439Smckusick 			(void) strcpy(name, myname(ep));
76310313Smckusick 			if (ep->e_type == NODE) {
76415780Smckusick 				(void) linkit(name, myname(np), SYMLINK);
76510313Smckusick 			} else {
76615780Smckusick 				(void) linkit(name, myname(np), HARDLINK);
76710313Smckusick 			}
76811310Smckusick 			np->e_flags &= ~NEW;
76910313Smckusick 		}
77010313Smckusick 	}
77110313Smckusick }
77210313Smckusick 
77310313Smckusick /*
77411742Smckusick  * Check the symbol table.
77511742Smckusick  * We do this to insure that all the requested work was done, and
77611742Smckusick  * that no temporary names remain.
77710313Smckusick  */
77856567Sbostic void
checkrestore()77910313Smckusick checkrestore()
78010313Smckusick {
78110313Smckusick 	register struct entry *ep;
78211439Smckusick 	register ino_t i;
78310313Smckusick 
78410313Smckusick 	vprintf(stdout, "Check the symbol table.\n");
785*67777Smckusick 	for (i = WINO; i < maxino; i++) {
78656567Sbostic 		for (ep = lookupino(i); ep != NULL; ep = ep->e_links) {
78710313Smckusick 			ep->e_flags &= ~KEEP;
78812454Smckusick 			if (ep->e_type == NODE)
78918007Smckusick 				ep->e_flags &= ~(NEW|EXISTED);
79010313Smckusick 			if (ep->e_flags != NULL)
79110313Smckusick 				badentry(ep, "incomplete operations");
79210313Smckusick 		}
79310313Smckusick 	}
79410313Smckusick }
79510313Smckusick 
79610313Smckusick /*
79711742Smckusick  * Compare with the directory structure on the tape
79811742Smckusick  * A paranoid check that things are as they should be.
79910313Smckusick  */
80011742Smckusick long
verifyfile(name,ino,type)80110313Smckusick verifyfile(name, ino, type)
80210313Smckusick 	char *name;
80310313Smckusick 	ino_t ino;
80410313Smckusick 	int type;
80510313Smckusick {
80610313Smckusick 	struct entry *np, *ep;
80711742Smckusick 	long descend = GOOD;
80810313Smckusick 
80910313Smckusick 	ep = lookupname(name);
81056567Sbostic 	if (ep == NULL) {
81111742Smckusick 		fprintf(stderr, "Warning: missing name %s\n", name);
81211742Smckusick 		return (FAIL);
81311742Smckusick 	}
81411742Smckusick 	np = lookupino(ino);
81511742Smckusick 	if (np != ep)
81611742Smckusick 		descend = FAIL;
81756567Sbostic 	for ( ; np != NULL; np = np->e_links)
81810313Smckusick 		if (np == ep)
81910313Smckusick 			break;
82056567Sbostic 	if (np == NULL)
82110313Smckusick 		panic("missing inumber %d\n", ino);
82210313Smckusick 	if (ep->e_type == LEAF && type != LEAF)
82310313Smckusick 		badentry(ep, "type should be LEAF");
82411742Smckusick 	return (descend);
82510313Smckusick }
826