11558Srgrimes/*
21558Srgrimes * Copyright (c) 1983, 1993
31558Srgrimes *	The Regents of the University of California.  All rights reserved.
41558Srgrimes *
51558Srgrimes * Redistribution and use in source and binary forms, with or without
61558Srgrimes * modification, are permitted provided that the following conditions
71558Srgrimes * are met:
81558Srgrimes * 1. Redistributions of source code must retain the above copyright
91558Srgrimes *    notice, this list of conditions and the following disclaimer.
101558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111558Srgrimes *    notice, this list of conditions and the following disclaimer in the
121558Srgrimes *    documentation and/or other materials provided with the distribution.
131558Srgrimes * 4. Neither the name of the University nor the names of its contributors
141558Srgrimes *    may be used to endorse or promote products derived from this software
151558Srgrimes *    without specific prior written permission.
161558Srgrimes *
171558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271558Srgrimes * SUCH DAMAGE.
281558Srgrimes */
291558Srgrimes
301558Srgrimes#ifndef lint
3137906Scharnier#if 0
3223685Speterstatic char sccsid[] = "@(#)restore.c	8.3 (Berkeley) 9/13/94";
3337906Scharnier#endif
341558Srgrimes#endif /* not lint */
351558Srgrimes
36146754Scharnier#include <sys/cdefs.h>
37146754Scharnier__FBSDID("$FreeBSD$");
38146754Scharnier
391558Srgrimes#include <sys/types.h>
401558Srgrimes
41103949Smike#include <limits.h>
421558Srgrimes#include <stdio.h>
431558Srgrimes#include <string.h>
441558Srgrimes
4598542Smckusick#include <ufs/ufs/dinode.h>
4698542Smckusick
471558Srgrimes#include "restore.h"
481558Srgrimes#include "extern.h"
491558Srgrimes
5092837Simpstatic char *keyval(int);
511558Srgrimes
521558Srgrimes/*
531558Srgrimes * This implements the 't' option.
541558Srgrimes * List entries on the tape.
551558Srgrimes */
561558Srgrimeslong
5792837Simplistfile(char *name, ino_t ino, int type)
581558Srgrimes{
591558Srgrimes	long descend = hflag ? GOOD : FAIL;
601558Srgrimes
611558Srgrimes	if (TSTINO(ino, dumpmap) == 0)
621558Srgrimes		return (descend);
631558Srgrimes	vprintf(stdout, "%s", type == LEAF ? "leaf" : "dir ");
641558Srgrimes	fprintf(stdout, "%10d\t%s\n", ino, name);
651558Srgrimes	return (descend);
661558Srgrimes}
671558Srgrimes
681558Srgrimes/*
691558Srgrimes * This implements the 'x' option.
701558Srgrimes * Request that new entries be extracted.
711558Srgrimes */
721558Srgrimeslong
7392837Simpaddfile(char *name, ino_t ino, int type)
741558Srgrimes{
7592806Sobrien	struct entry *ep;
761558Srgrimes	long descend = hflag ? GOOD : FAIL;
771558Srgrimes	char buf[100];
781558Srgrimes
791558Srgrimes	if (TSTINO(ino, dumpmap) == 0) {
801558Srgrimes		dprintf(stdout, "%s: not on the tape\n", name);
811558Srgrimes		return (descend);
821558Srgrimes	}
8323685Speter	if (ino == WINO && command == 'i' && !vflag)
8423685Speter		return (descend);
851558Srgrimes	if (!mflag) {
861558Srgrimes		(void) sprintf(buf, "./%u", ino);
871558Srgrimes		name = buf;
881558Srgrimes		if (type == NODE) {
891558Srgrimes			(void) genliteraldir(name, ino);
901558Srgrimes			return (descend);
911558Srgrimes		}
921558Srgrimes	}
931558Srgrimes	ep = lookupino(ino);
941558Srgrimes	if (ep != NULL) {
951558Srgrimes		if (strcmp(name, myname(ep)) == 0) {
961558Srgrimes			ep->e_flags |= NEW;
971558Srgrimes			return (descend);
981558Srgrimes		}
991558Srgrimes		type |= LINK;
1001558Srgrimes	}
1011558Srgrimes	ep = addentry(name, ino, type);
1021558Srgrimes	if (type == NODE)
1031558Srgrimes		newnode(ep);
1041558Srgrimes	ep->e_flags |= NEW;
1051558Srgrimes	return (descend);
1061558Srgrimes}
1071558Srgrimes
1081558Srgrimes/*
1091558Srgrimes * This is used by the 'i' option to undo previous requests made by addfile.
1101558Srgrimes * Delete entries from the request queue.
1111558Srgrimes */
1121558Srgrimes/* ARGSUSED */
1131558Srgrimeslong
11492837Simpdeletefile(char *name, ino_t ino, int type)
1151558Srgrimes{
1161558Srgrimes	long descend = hflag ? GOOD : FAIL;
1171558Srgrimes	struct entry *ep;
1181558Srgrimes
1191558Srgrimes	if (TSTINO(ino, dumpmap) == 0)
1201558Srgrimes		return (descend);
12123685Speter	ep = lookupname(name);
12223685Speter	if (ep != NULL) {
1231558Srgrimes		ep->e_flags &= ~NEW;
12423685Speter		ep->e_flags |= REMOVED;
12523685Speter		if (ep->e_type != NODE)
12623685Speter			freeentry(ep);
12723685Speter	}
1281558Srgrimes	return (descend);
1291558Srgrimes}
1301558Srgrimes
1318871Srgrimes/*
1321558Srgrimes * The following four routines implement the incremental
1331558Srgrimes * restore algorithm. The first removes old entries, the second
1341558Srgrimes * does renames and calculates the extraction list, the third
1351558Srgrimes * cleans up link names missed by the first two, and the final
1361558Srgrimes * one deletes old directories.
1371558Srgrimes *
1381558Srgrimes * Directories cannot be immediately deleted, as they may have
1391558Srgrimes * other files in them which need to be moved out first. As
1408871Srgrimes * directories to be deleted are found, they are put on the
1411558Srgrimes * following deletion list. After all deletions and renames
1421558Srgrimes * are done, this list is actually deleted.
1431558Srgrimes */
1441558Srgrimesstatic struct entry *removelist;
1451558Srgrimes
1461558Srgrimes/*
14723685Speter *	Remove invalid whiteouts from the old tree.
1481558Srgrimes *	Remove unneeded leaves from the old tree.
1491558Srgrimes *	Remove directories from the lookup chains.
1501558Srgrimes */
1511558Srgrimesvoid
15292837Simpremoveoldleaves(void)
1531558Srgrimes{
15492806Sobrien	struct entry *ep, *nextep;
15592806Sobrien	ino_t i, mydirino;
1561558Srgrimes
1571558Srgrimes	vprintf(stdout, "Mark entries to be removed.\n");
15837906Scharnier	if ((ep = lookupino(WINO))) {
15923685Speter		vprintf(stdout, "Delete whiteouts\n");
16023685Speter		for ( ; ep != NULL; ep = nextep) {
16123685Speter			nextep = ep->e_links;
16223685Speter			mydirino = ep->e_parent->e_ino;
16323685Speter			/*
16423685Speter			 * We remove all whiteouts that are in directories
16523685Speter			 * that have been removed or that have been dumped.
16623685Speter			 */
16723685Speter			if (TSTINO(mydirino, usedinomap) &&
16823685Speter			    !TSTINO(mydirino, dumpmap))
16923685Speter				continue;
17023685Speter			delwhiteout(ep);
17123685Speter			freeentry(ep);
17223685Speter		}
17323685Speter	}
1741558Srgrimes	for (i = ROOTINO + 1; i < maxino; i++) {
1751558Srgrimes		ep = lookupino(i);
1761558Srgrimes		if (ep == NULL)
1771558Srgrimes			continue;
17823685Speter		if (TSTINO(i, usedinomap))
1791558Srgrimes			continue;
1801558Srgrimes		for ( ; ep != NULL; ep = ep->e_links) {
1811558Srgrimes			dprintf(stdout, "%s: REMOVE\n", myname(ep));
1821558Srgrimes			if (ep->e_type == LEAF) {
1831558Srgrimes				removeleaf(ep);
1841558Srgrimes				freeentry(ep);
1851558Srgrimes			} else {
1861558Srgrimes				mktempname(ep);
1871558Srgrimes				deleteino(ep->e_ino);
1881558Srgrimes				ep->e_next = removelist;
1891558Srgrimes				removelist = ep;
1901558Srgrimes			}
1911558Srgrimes		}
1921558Srgrimes	}
1931558Srgrimes}
1941558Srgrimes
1951558Srgrimes/*
1961558Srgrimes *	For each directory entry on the incremental tape, determine which
1971558Srgrimes *	category it falls into as follows:
1981558Srgrimes *	KEEP - entries that are to be left alone.
1991558Srgrimes *	NEW - new entries to be added.
2001558Srgrimes *	EXTRACT - files that must be updated with new contents.
2011558Srgrimes *	LINK - new links to be added.
2021558Srgrimes *	Renames are done at the same time.
2031558Srgrimes */
2041558Srgrimeslong
20592837Simpnodeupdates(char *name, ino_t ino, int type)
2061558Srgrimes{
20792806Sobrien	struct entry *ep, *np, *ip;
2081558Srgrimes	long descend = GOOD;
2091558Srgrimes	int lookuptype = 0;
2101558Srgrimes	int key = 0;
2111558Srgrimes		/* key values */
2121558Srgrimes#		define ONTAPE	0x1	/* inode is on the tape */
2131558Srgrimes#		define INOFND	0x2	/* inode already exists */
2141558Srgrimes#		define NAMEFND	0x4	/* name already exists */
2151558Srgrimes#		define MODECHG	0x8	/* mode of inode changed */
2161558Srgrimes
2171558Srgrimes	/*
2188871Srgrimes	 * This routine is called once for each element in the
2191558Srgrimes	 * directory hierarchy, with a full path name.
2201558Srgrimes	 * The "type" value is incorrectly specified as LEAF for
2211558Srgrimes	 * directories that are not on the dump tape.
2221558Srgrimes	 *
2231558Srgrimes	 * Check to see if the file is on the tape.
2241558Srgrimes	 */
2251558Srgrimes	if (TSTINO(ino, dumpmap))
2261558Srgrimes		key |= ONTAPE;
2271558Srgrimes	/*
2281558Srgrimes	 * Check to see if the name exists, and if the name is a link.
2291558Srgrimes	 */
2301558Srgrimes	np = lookupname(name);
2311558Srgrimes	if (np != NULL) {
2321558Srgrimes		key |= NAMEFND;
2331558Srgrimes		ip = lookupino(np->e_ino);
2341558Srgrimes		if (ip == NULL)
2351558Srgrimes			panic("corrupted symbol table\n");
2361558Srgrimes		if (ip != np)
2371558Srgrimes			lookuptype = LINK;
2381558Srgrimes	}
2391558Srgrimes	/*
2401558Srgrimes	 * Check to see if the inode exists, and if one of its links
2411558Srgrimes	 * corresponds to the name (if one was found).
2421558Srgrimes	 */
2431558Srgrimes	ip = lookupino(ino);
2441558Srgrimes	if (ip != NULL) {
2451558Srgrimes		key |= INOFND;
2461558Srgrimes		for (ep = ip->e_links; ep != NULL; ep = ep->e_links) {
2471558Srgrimes			if (ep == np) {
2481558Srgrimes				ip = ep;
2491558Srgrimes				break;
2501558Srgrimes			}
2511558Srgrimes		}
2521558Srgrimes	}
2531558Srgrimes	/*
2541558Srgrimes	 * If both a name and an inode are found, but they do not
2551558Srgrimes	 * correspond to the same file, then both the inode that has
2561558Srgrimes	 * been found and the inode corresponding to the name that
2571558Srgrimes	 * has been found need to be renamed. The current pathname
2581558Srgrimes	 * is the new name for the inode that has been found. Since
2591558Srgrimes	 * all files to be deleted have already been removed, the
2601558Srgrimes	 * named file is either a now unneeded link, or it must live
2611558Srgrimes	 * under a new name in this dump level. If it is a link, it
2621558Srgrimes	 * can be removed. If it is not a link, it is given a
2631558Srgrimes	 * temporary name in anticipation that it will be renamed
2641558Srgrimes	 * when it is later found by inode number.
2651558Srgrimes	 */
2661558Srgrimes	if (((key & (INOFND|NAMEFND)) == (INOFND|NAMEFND)) && ip != np) {
2671558Srgrimes		if (lookuptype == LINK) {
2681558Srgrimes			removeleaf(np);
2691558Srgrimes			freeentry(np);
2701558Srgrimes		} else {
2711558Srgrimes			dprintf(stdout, "name/inode conflict, mktempname %s\n",
2721558Srgrimes				myname(np));
2731558Srgrimes			mktempname(np);
2741558Srgrimes		}
2751558Srgrimes		np = NULL;
2761558Srgrimes		key &= ~NAMEFND;
2771558Srgrimes	}
2781558Srgrimes	if ((key & ONTAPE) &&
2791558Srgrimes	  (((key & INOFND) && ip->e_type != type) ||
2801558Srgrimes	   ((key & NAMEFND) && np->e_type != type)))
2811558Srgrimes		key |= MODECHG;
2821558Srgrimes
2831558Srgrimes	/*
2841558Srgrimes	 * Decide on the disposition of the file based on its flags.
2851558Srgrimes	 * Note that we have already handled the case in which
2861558Srgrimes	 * a name and inode are found that correspond to different files.
2871558Srgrimes	 * Thus if both NAMEFND and INOFND are set then ip == np.
2881558Srgrimes	 */
2891558Srgrimes	switch (key) {
2901558Srgrimes
2911558Srgrimes	/*
2921558Srgrimes	 * A previously existing file has been found.
2931558Srgrimes	 * Mark it as KEEP so that other links to the inode can be
2941558Srgrimes	 * detected, and so that it will not be reclaimed by the search
2951558Srgrimes	 * for unreferenced names.
2961558Srgrimes	 */
2971558Srgrimes	case INOFND|NAMEFND:
2981558Srgrimes		ip->e_flags |= KEEP;
2991558Srgrimes		dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
3001558Srgrimes			flagvalues(ip));
3011558Srgrimes		break;
3021558Srgrimes
3031558Srgrimes	/*
3041558Srgrimes	 * A file on the tape has a name which is the same as a name
3051558Srgrimes	 * corresponding to a different file in the previous dump.
3061558Srgrimes	 * Since all files to be deleted have already been removed,
3071558Srgrimes	 * this file is either a now unneeded link, or it must live
3081558Srgrimes	 * under a new name in this dump level. If it is a link, it
3091558Srgrimes	 * can simply be removed. If it is not a link, it is given a
3101558Srgrimes	 * temporary name in anticipation that it will be renamed
3111558Srgrimes	 * when it is later found by inode number (see INOFND case
3121558Srgrimes	 * below). The entry is then treated as a new file.
3131558Srgrimes	 */
3141558Srgrimes	case ONTAPE|NAMEFND:
3151558Srgrimes	case ONTAPE|NAMEFND|MODECHG:
3161558Srgrimes		if (lookuptype == LINK) {
3171558Srgrimes			removeleaf(np);
3181558Srgrimes			freeentry(np);
3191558Srgrimes		} else {
3201558Srgrimes			mktempname(np);
3211558Srgrimes		}
322102411Scharnier		/* FALLTHROUGH */
3231558Srgrimes
3241558Srgrimes	/*
3251558Srgrimes	 * A previously non-existent file.
326102231Strhodes	 * Add it to the file system, and request its extraction.
3271558Srgrimes	 * If it is a directory, create it immediately.
3281558Srgrimes	 * (Since the name is unused there can be no conflict)
3291558Srgrimes	 */
3301558Srgrimes	case ONTAPE:
3311558Srgrimes		ep = addentry(name, ino, type);
3321558Srgrimes		if (type == NODE)
3331558Srgrimes			newnode(ep);
3341558Srgrimes		ep->e_flags |= NEW|KEEP;
3351558Srgrimes		dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
3361558Srgrimes			flagvalues(ep));
3371558Srgrimes		break;
3381558Srgrimes
3391558Srgrimes	/*
3401558Srgrimes	 * A file with the same inode number, but a different
3411558Srgrimes	 * name has been found. If the other name has not already
3421558Srgrimes	 * been found (indicated by the KEEP flag, see above) then
3431558Srgrimes	 * this must be a new name for the file, and it is renamed.
3441558Srgrimes	 * If the other name has been found then this must be a
3451558Srgrimes	 * link to the file. Hard links to directories are not
3461558Srgrimes	 * permitted, and are either deleted or converted to
3471558Srgrimes	 * symbolic links. Finally, if the file is on the tape,
3481558Srgrimes	 * a request is made to extract it.
3491558Srgrimes	 */
3501558Srgrimes	case ONTAPE|INOFND:
3511558Srgrimes		if (type == LEAF && (ip->e_flags & KEEP) == 0)
3521558Srgrimes			ip->e_flags |= EXTRACT;
353102411Scharnier		/* FALLTHROUGH */
3541558Srgrimes	case INOFND:
3551558Srgrimes		if ((ip->e_flags & KEEP) == 0) {
3561558Srgrimes			renameit(myname(ip), name);
3571558Srgrimes			moveentry(ip, name);
3581558Srgrimes			ip->e_flags |= KEEP;
3591558Srgrimes			dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
3601558Srgrimes				flagvalues(ip));
3611558Srgrimes			break;
3621558Srgrimes		}
3631558Srgrimes		if (ip->e_type == NODE) {
3641558Srgrimes			descend = FAIL;
3651558Srgrimes			fprintf(stderr,
3661558Srgrimes				"deleted hard link %s to directory %s\n",
3671558Srgrimes				name, myname(ip));
3681558Srgrimes			break;
3691558Srgrimes		}
3701558Srgrimes		ep = addentry(name, ino, type|LINK);
3711558Srgrimes		ep->e_flags |= NEW;
3721558Srgrimes		dprintf(stdout, "[%s] %s: %s|LINK\n", keyval(key), name,
3731558Srgrimes			flagvalues(ep));
3741558Srgrimes		break;
3751558Srgrimes
3761558Srgrimes	/*
3771558Srgrimes	 * A previously known file which is to be updated. If it is a link,
3781558Srgrimes	 * then all names referring to the previous file must be removed
3791558Srgrimes	 * so that the subset of them that remain can be recreated.
3801558Srgrimes	 */
3811558Srgrimes	case ONTAPE|INOFND|NAMEFND:
3821558Srgrimes		if (lookuptype == LINK) {
3831558Srgrimes			removeleaf(np);
3841558Srgrimes			freeentry(np);
3851558Srgrimes			ep = addentry(name, ino, type|LINK);
3861558Srgrimes			if (type == NODE)
3871558Srgrimes			        newnode(ep);
3881558Srgrimes			ep->e_flags |= NEW|KEEP;
3891558Srgrimes			dprintf(stdout, "[%s] %s: %s|LINK\n", keyval(key), name,
3901558Srgrimes				flagvalues(ep));
3911558Srgrimes			break;
3921558Srgrimes		}
3931558Srgrimes		if (type == LEAF && lookuptype != LINK)
3941558Srgrimes			np->e_flags |= EXTRACT;
3951558Srgrimes		np->e_flags |= KEEP;
3961558Srgrimes		dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
3971558Srgrimes			flagvalues(np));
3981558Srgrimes		break;
3991558Srgrimes
4001558Srgrimes	/*
4011558Srgrimes	 * An inode is being reused in a completely different way.
4021558Srgrimes	 * Normally an extract can simply do an "unlink" followed
4031558Srgrimes	 * by a "creat". Here we must do effectively the same
4041558Srgrimes	 * thing. The complications arise because we cannot really
4051558Srgrimes	 * delete a directory since it may still contain files
4061558Srgrimes	 * that we need to rename, so we delete it from the symbol
4071558Srgrimes	 * table, and put it on the list to be deleted eventually.
4081558Srgrimes	 * Conversely if a directory is to be created, it must be
4098871Srgrimes	 * done immediately, rather than waiting until the
4101558Srgrimes	 * extraction phase.
4111558Srgrimes	 */
4121558Srgrimes	case ONTAPE|INOFND|MODECHG:
4131558Srgrimes	case ONTAPE|INOFND|NAMEFND|MODECHG:
4141558Srgrimes		if (ip->e_flags & KEEP) {
4151558Srgrimes			badentry(ip, "cannot KEEP and change modes");
4161558Srgrimes			break;
4171558Srgrimes		}
4181558Srgrimes		if (ip->e_type == LEAF) {
4191558Srgrimes			/* changing from leaf to node */
42028034Sjoerg			for (ip = lookupino(ino); ip != NULL; ip = ip->e_links) {
42128034Sjoerg				if (ip->e_type != LEAF)
42228034Sjoerg					badentry(ip, "NODE and LEAF links to same inode");
42328034Sjoerg				removeleaf(ip);
42428034Sjoerg				freeentry(ip);
42528034Sjoerg			}
4261558Srgrimes			ip = addentry(name, ino, type);
4271558Srgrimes			newnode(ip);
4281558Srgrimes		} else {
4291558Srgrimes			/* changing from node to leaf */
4301558Srgrimes			if ((ip->e_flags & TMPNAME) == 0)
4311558Srgrimes				mktempname(ip);
4321558Srgrimes			deleteino(ip->e_ino);
4331558Srgrimes			ip->e_next = removelist;
4341558Srgrimes			removelist = ip;
4351558Srgrimes			ip = addentry(name, ino, type);
4361558Srgrimes		}
4371558Srgrimes		ip->e_flags |= NEW|KEEP;
4381558Srgrimes		dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
4391558Srgrimes			flagvalues(ip));
4401558Srgrimes		break;
4411558Srgrimes
4421558Srgrimes	/*
44337906Scharnier	 * A hard link to a directory that has been removed.
4441558Srgrimes	 * Ignore it.
4451558Srgrimes	 */
4461558Srgrimes	case NAMEFND:
4471558Srgrimes		dprintf(stdout, "[%s] %s: Extraneous name\n", keyval(key),
4481558Srgrimes			name);
4491558Srgrimes		descend = FAIL;
4501558Srgrimes		break;
4511558Srgrimes
4521558Srgrimes	/*
4531558Srgrimes	 * If we find a directory entry for a file that is not on
4541558Srgrimes	 * the tape, then we must have found a file that was created
4551558Srgrimes	 * while the dump was in progress. Since we have no contents
4561558Srgrimes	 * for it, we discard the name knowing that it will be on the
4571558Srgrimes	 * next incremental tape.
4581558Srgrimes	 */
459102232Simp	case 0:
4601558Srgrimes		fprintf(stderr, "%s: (inode %d) not found on tape\n",
4611558Srgrimes			name, ino);
4621558Srgrimes		break;
4631558Srgrimes
4641558Srgrimes	/*
4651558Srgrimes	 * If any of these arise, something is grievously wrong with
4661558Srgrimes	 * the current state of the symbol table.
4671558Srgrimes	 */
4681558Srgrimes	case INOFND|NAMEFND|MODECHG:
4691558Srgrimes	case NAMEFND|MODECHG:
4701558Srgrimes	case INOFND|MODECHG:
4711558Srgrimes		fprintf(stderr, "[%s] %s: inconsistent state\n", keyval(key),
4721558Srgrimes			name);
4731558Srgrimes		break;
4741558Srgrimes
4751558Srgrimes	/*
4761558Srgrimes	 * These states "cannot" arise for any state of the symbol table.
4771558Srgrimes	 */
4781558Srgrimes	case ONTAPE|MODECHG:
4791558Srgrimes	case MODECHG:
4801558Srgrimes	default:
4811558Srgrimes		panic("[%s] %s: impossible state\n", keyval(key), name);
4821558Srgrimes		break;
4838871Srgrimes	}
4841558Srgrimes	return (descend);
4851558Srgrimes}
4861558Srgrimes
4871558Srgrimes/*
4881558Srgrimes * Calculate the active flags in a key.
4891558Srgrimes */
4901558Srgrimesstatic char *
49192837Simpkeyval(int key)
4921558Srgrimes{
4931558Srgrimes	static char keybuf[32];
4941558Srgrimes
4951558Srgrimes	(void) strcpy(keybuf, "|NIL");
4961558Srgrimes	keybuf[0] = '\0';
4971558Srgrimes	if (key & ONTAPE)
4981558Srgrimes		(void) strcat(keybuf, "|ONTAPE");
4991558Srgrimes	if (key & INOFND)
5001558Srgrimes		(void) strcat(keybuf, "|INOFND");
5011558Srgrimes	if (key & NAMEFND)
5021558Srgrimes		(void) strcat(keybuf, "|NAMEFND");
5031558Srgrimes	if (key & MODECHG)
5041558Srgrimes		(void) strcat(keybuf, "|MODECHG");
5051558Srgrimes	return (&keybuf[1]);
5061558Srgrimes}
5071558Srgrimes
5081558Srgrimes/*
5091558Srgrimes * Find unreferenced link names.
5101558Srgrimes */
5111558Srgrimesvoid
51292837Simpfindunreflinks(void)
5131558Srgrimes{
51492806Sobrien	struct entry *ep, *np;
51592806Sobrien	ino_t i;
5161558Srgrimes
5171558Srgrimes	vprintf(stdout, "Find unreferenced names.\n");
5181558Srgrimes	for (i = ROOTINO; i < maxino; i++) {
5191558Srgrimes		ep = lookupino(i);
5201558Srgrimes		if (ep == NULL || ep->e_type == LEAF || TSTINO(i, dumpmap) == 0)
5211558Srgrimes			continue;
5221558Srgrimes		for (np = ep->e_entries; np != NULL; np = np->e_sibling) {
5231558Srgrimes			if (np->e_flags == 0) {
5241558Srgrimes				dprintf(stdout,
5251558Srgrimes				    "%s: remove unreferenced name\n",
5261558Srgrimes				    myname(np));
5271558Srgrimes				removeleaf(np);
5281558Srgrimes				freeentry(np);
5291558Srgrimes			}
5301558Srgrimes		}
5311558Srgrimes	}
5321558Srgrimes	/*
5331558Srgrimes	 * Any leaves remaining in removed directories is unreferenced.
5341558Srgrimes	 */
5351558Srgrimes	for (ep = removelist; ep != NULL; ep = ep->e_next) {
5361558Srgrimes		for (np = ep->e_entries; np != NULL; np = np->e_sibling) {
5371558Srgrimes			if (np->e_type == LEAF) {
5381558Srgrimes				if (np->e_flags != 0)
5391558Srgrimes					badentry(np, "unreferenced with flags");
5401558Srgrimes				dprintf(stdout,
5411558Srgrimes				    "%s: remove unreferenced name\n",
5421558Srgrimes				    myname(np));
5431558Srgrimes				removeleaf(np);
5441558Srgrimes				freeentry(np);
5451558Srgrimes			}
5461558Srgrimes		}
5471558Srgrimes	}
5481558Srgrimes}
5491558Srgrimes
5501558Srgrimes/*
5511558Srgrimes * Remove old nodes (directories).
5521558Srgrimes * Note that this routine runs in O(N*D) where:
5531558Srgrimes *	N is the number of directory entries to be removed.
5541558Srgrimes *	D is the maximum depth of the tree.
5551558Srgrimes * If N == D this can be quite slow. If the list were
5561558Srgrimes * topologically sorted, the deletion could be done in
5571558Srgrimes * time O(N).
5581558Srgrimes */
5591558Srgrimesvoid
56092837Simpremoveoldnodes(void)
5611558Srgrimes{
56292806Sobrien	struct entry *ep, **prev;
5631558Srgrimes	long change;
5641558Srgrimes
5651558Srgrimes	vprintf(stdout, "Remove old nodes (directories).\n");
5661558Srgrimes	do	{
5671558Srgrimes		change = 0;
5681558Srgrimes		prev = &removelist;
5691558Srgrimes		for (ep = removelist; ep != NULL; ep = *prev) {
5701558Srgrimes			if (ep->e_entries != NULL) {
5711558Srgrimes				prev = &ep->e_next;
5721558Srgrimes				continue;
5731558Srgrimes			}
5741558Srgrimes			*prev = ep->e_next;
5751558Srgrimes			removenode(ep);
5761558Srgrimes			freeentry(ep);
5771558Srgrimes			change++;
5781558Srgrimes		}
5791558Srgrimes	} while (change);
5801558Srgrimes	for (ep = removelist; ep != NULL; ep = ep->e_next)
5811558Srgrimes		badentry(ep, "cannot remove, non-empty");
5821558Srgrimes}
5831558Srgrimes
5841558Srgrimes/*
5851558Srgrimes * This is the routine used to extract files for the 'r' command.
5861558Srgrimes * Extract new leaves.
5871558Srgrimes */
5881558Srgrimesvoid
58992837Simpcreateleaves(char *symtabfile)
5901558Srgrimes{
59192806Sobrien	struct entry *ep;
5921558Srgrimes	ino_t first;
5931558Srgrimes	long curvol;
5941558Srgrimes
5951558Srgrimes	if (command == 'R') {
5961558Srgrimes		vprintf(stdout, "Continue extraction of new leaves\n");
5971558Srgrimes	} else {
5981558Srgrimes		vprintf(stdout, "Extract new leaves.\n");
5991558Srgrimes		dumpsymtable(symtabfile, volno);
6001558Srgrimes	}
6011558Srgrimes	first = lowerbnd(ROOTINO);
6021558Srgrimes	curvol = volno;
6031558Srgrimes	while (curfile.ino < maxino) {
6041558Srgrimes		first = lowerbnd(first);
6051558Srgrimes		/*
6061558Srgrimes		 * If the next available file is not the one which we
6071558Srgrimes		 * expect then we have missed one or more files. Since
6081558Srgrimes		 * we do not request files that were not on the tape,
6091558Srgrimes		 * the lost files must have been due to a tape read error,
6101558Srgrimes		 * or a file that was removed while the dump was in progress.
6111558Srgrimes		 */
6121558Srgrimes		while (first < curfile.ino) {
6131558Srgrimes			ep = lookupino(first);
6141558Srgrimes			if (ep == NULL)
6151558Srgrimes				panic("%d: bad first\n", first);
6161558Srgrimes			fprintf(stderr, "%s: not found on tape\n", myname(ep));
6171558Srgrimes			ep->e_flags &= ~(NEW|EXTRACT);
6181558Srgrimes			first = lowerbnd(first);
6191558Srgrimes		}
6201558Srgrimes		/*
6211558Srgrimes		 * If we find files on the tape that have no corresponding
6221558Srgrimes		 * directory entries, then we must have found a file that
6238871Srgrimes		 * was created while the dump was in progress. Since we have
6241558Srgrimes		 * no name for it, we discard it knowing that it will be
6251558Srgrimes		 * on the next incremental tape.
6261558Srgrimes		 */
6271558Srgrimes		if (first != curfile.ino) {
6281558Srgrimes			fprintf(stderr, "expected next file %d, got %d\n",
6291558Srgrimes				first, curfile.ino);
6301558Srgrimes			skipfile();
6311558Srgrimes			goto next;
6321558Srgrimes		}
6331558Srgrimes		ep = lookupino(curfile.ino);
6341558Srgrimes		if (ep == NULL)
6351558Srgrimes			panic("unknown file on tape\n");
6361558Srgrimes		if ((ep->e_flags & (NEW|EXTRACT)) == 0)
6371558Srgrimes			badentry(ep, "unexpected file on tape");
6381558Srgrimes		/*
6391558Srgrimes		 * If the file is to be extracted, then the old file must
6401558Srgrimes		 * be removed since its type may change from one leaf type
64137906Scharnier		 * to another (e.g. "file" to "character special").
6421558Srgrimes		 */
6431558Srgrimes		if ((ep->e_flags & EXTRACT) != 0) {
6441558Srgrimes			removeleaf(ep);
6451558Srgrimes			ep->e_flags &= ~REMOVED;
6461558Srgrimes		}
6471558Srgrimes		(void) extractfile(myname(ep));
6481558Srgrimes		ep->e_flags &= ~(NEW|EXTRACT);
6491558Srgrimes		/*
6501558Srgrimes		 * We checkpoint the restore after every tape reel, so
65137906Scharnier		 * as to simplify the amount of work required by the
6521558Srgrimes		 * 'R' command.
6531558Srgrimes		 */
6541558Srgrimes	next:
6551558Srgrimes		if (curvol != volno) {
6561558Srgrimes			dumpsymtable(symtabfile, volno);
6571558Srgrimes			skipmaps();
6581558Srgrimes			curvol = volno;
6591558Srgrimes		}
6601558Srgrimes	}
6611558Srgrimes}
6621558Srgrimes
6631558Srgrimes/*
6641558Srgrimes * This is the routine used to extract files for the 'x' and 'i' commands.
6651558Srgrimes * Efficiently extract a subset of the files on a tape.
6661558Srgrimes */
6671558Srgrimesvoid
66892837Simpcreatefiles(void)
6691558Srgrimes{
67092806Sobrien	ino_t first, next, last;
67192806Sobrien	struct entry *ep;
6721558Srgrimes	long curvol;
6731558Srgrimes
6741558Srgrimes	vprintf(stdout, "Extract requested files\n");
6751558Srgrimes	curfile.action = SKIP;
6761558Srgrimes	getvol((long)1);
6771558Srgrimes	skipmaps();
6781558Srgrimes	skipdirs();
6791558Srgrimes	first = lowerbnd(ROOTINO);
6801558Srgrimes	last = upperbnd(maxino - 1);
6811558Srgrimes	for (;;) {
68290642Siedowse		curvol = volno;
6831558Srgrimes		first = lowerbnd(first);
6841558Srgrimes		last = upperbnd(last);
6851558Srgrimes		/*
6861558Srgrimes		 * Check to see if any files remain to be extracted
6871558Srgrimes		 */
6881558Srgrimes		if (first > last)
6891558Srgrimes			return;
690164911Sdwmalone		if (Dflag) {
691164911Sdwmalone			if (curfile.ino == maxino)
692164911Sdwmalone				return;
693164911Sdwmalone			if((ep = lookupino(curfile.ino)) != NULL &&
694164911Sdwmalone			    (ep->e_flags & (NEW|EXTRACT))) {
695164911Sdwmalone				goto justgetit;
696164911Sdwmalone			} else {
697164911Sdwmalone				skipfile();
698164911Sdwmalone				continue;
699164911Sdwmalone			}
700164911Sdwmalone		}
7011558Srgrimes		/*
70290642Siedowse		 * Reject any volumes with inodes greater than the last
70390642Siedowse		 * one needed, so that we can quickly skip backwards to
70490642Siedowse		 * a volume containing useful inodes. We can't do this
70590642Siedowse		 * if there are no further volumes available (curfile.ino
70690642Siedowse		 * >= maxino) or if we are already at the first tape.
7071558Srgrimes		 */
70890642Siedowse		if (curfile.ino > last && curfile.ino < maxino && volno > 1) {
7091558Srgrimes			curfile.action = SKIP;
7101558Srgrimes			getvol((long)0);
7111558Srgrimes			skipmaps();
7121558Srgrimes			skipdirs();
71390642Siedowse			continue;
7141558Srgrimes		}
7151558Srgrimes		/*
7161558Srgrimes		 * Decide on the next inode needed.
7171558Srgrimes		 * Skip across the inodes until it is found
71890642Siedowse		 * or a volume change is encountered
7191558Srgrimes		 */
72090642Siedowse		if (curfile.ino < maxino) {
72190642Siedowse			next = lowerbnd(curfile.ino);
7221558Srgrimes			while (next > curfile.ino && volno == curvol)
7231558Srgrimes				skipfile();
72490642Siedowse			if (volno != curvol) {
72590642Siedowse				skipmaps();
72690642Siedowse				skipdirs();
72790642Siedowse				continue;
72890642Siedowse			}
72990642Siedowse		} else {
73090642Siedowse			/*
73190642Siedowse			 * No further volumes or inodes available. Set
73290642Siedowse			 * `next' to the first inode, so that a warning
73390642Siedowse			 * is emitted below for each missing file.
73490642Siedowse			 */
73590642Siedowse			next = first;
73690642Siedowse		}
7371558Srgrimes		/*
7381558Srgrimes		 * If the current inode is greater than the one we were
7391558Srgrimes		 * looking for then we missed the one we were looking for.
7401558Srgrimes		 * Since we only attempt to extract files listed in the
7411558Srgrimes		 * dump map, the lost files must have been due to a tape
7421558Srgrimes		 * read error, or a file that was removed while the dump
7431558Srgrimes		 * was in progress. Thus we report all requested files
7441558Srgrimes		 * between the one we were looking for, and the one we
7451558Srgrimes		 * found as missing, and delete their request flags.
7461558Srgrimes		 */
7471558Srgrimes		while (next < curfile.ino) {
7481558Srgrimes			ep = lookupino(next);
7491558Srgrimes			if (ep == NULL)
7501558Srgrimes				panic("corrupted symbol table\n");
7511558Srgrimes			fprintf(stderr, "%s: not found on tape\n", myname(ep));
7521558Srgrimes			ep->e_flags &= ~NEW;
7531558Srgrimes			next = lowerbnd(next);
7541558Srgrimes		}
7551558Srgrimes		/*
7561558Srgrimes		 * The current inode is the one that we are looking for,
7571558Srgrimes		 * so extract it per its requested name.
7581558Srgrimes		 */
7591558Srgrimes		if (next == curfile.ino && next <= last) {
7601558Srgrimes			ep = lookupino(next);
7611558Srgrimes			if (ep == NULL)
7621558Srgrimes				panic("corrupted symbol table\n");
763164911Sdwmalonejustgetit:
7641558Srgrimes			(void) extractfile(myname(ep));
7651558Srgrimes			ep->e_flags &= ~NEW;
7661558Srgrimes			if (volno != curvol)
7671558Srgrimes				skipmaps();
7681558Srgrimes		}
7691558Srgrimes	}
7701558Srgrimes}
7711558Srgrimes
7721558Srgrimes/*
7731558Srgrimes * Add links.
7741558Srgrimes */
7751558Srgrimesvoid
77692837Simpcreatelinks(void)
7771558Srgrimes{
77892806Sobrien	struct entry *np, *ep;
77992806Sobrien	ino_t i;
7801558Srgrimes	char name[BUFSIZ];
7811558Srgrimes
78237906Scharnier	if ((ep = lookupino(WINO))) {
78323685Speter		vprintf(stdout, "Add whiteouts\n");
78423685Speter		for ( ; ep != NULL; ep = ep->e_links) {
78523685Speter			if ((ep->e_flags & NEW) == 0)
78623685Speter				continue;
78723685Speter			(void) addwhiteout(myname(ep));
78823685Speter			ep->e_flags &= ~NEW;
78923685Speter		}
79023685Speter	}
7911558Srgrimes	vprintf(stdout, "Add links\n");
7921558Srgrimes	for (i = ROOTINO; i < maxino; i++) {
7931558Srgrimes		ep = lookupino(i);
7941558Srgrimes		if (ep == NULL)
7951558Srgrimes			continue;
7961558Srgrimes		for (np = ep->e_links; np != NULL; np = np->e_links) {
7971558Srgrimes			if ((np->e_flags & NEW) == 0)
7981558Srgrimes				continue;
7991558Srgrimes			(void) strcpy(name, myname(ep));
8001558Srgrimes			if (ep->e_type == NODE) {
8011558Srgrimes				(void) linkit(name, myname(np), SYMLINK);
8021558Srgrimes			} else {
8031558Srgrimes				(void) linkit(name, myname(np), HARDLINK);
8041558Srgrimes			}
8051558Srgrimes			np->e_flags &= ~NEW;
8061558Srgrimes		}
8071558Srgrimes	}
8081558Srgrimes}
8091558Srgrimes
8101558Srgrimes/*
8111558Srgrimes * Check the symbol table.
8121558Srgrimes * We do this to insure that all the requested work was done, and
8131558Srgrimes * that no temporary names remain.
8141558Srgrimes */
8151558Srgrimesvoid
81692837Simpcheckrestore(void)
8171558Srgrimes{
81892806Sobrien	struct entry *ep;
81992806Sobrien	ino_t i;
8201558Srgrimes
8211558Srgrimes	vprintf(stdout, "Check the symbol table.\n");
82223685Speter	for (i = WINO; i < maxino; i++) {
8231558Srgrimes		for (ep = lookupino(i); ep != NULL; ep = ep->e_links) {
8241558Srgrimes			ep->e_flags &= ~KEEP;
8251558Srgrimes			if (ep->e_type == NODE)
8261558Srgrimes				ep->e_flags &= ~(NEW|EXISTED);
82729574Sphk			if (ep->e_flags != 0)
8281558Srgrimes				badentry(ep, "incomplete operations");
8291558Srgrimes		}
8301558Srgrimes	}
8311558Srgrimes}
8321558Srgrimes
8331558Srgrimes/*
8341558Srgrimes * Compare with the directory structure on the tape
8351558Srgrimes * A paranoid check that things are as they should be.
8361558Srgrimes */
8371558Srgrimeslong
83892837Simpverifyfile(char *name, ino_t ino, int type)
8391558Srgrimes{
8401558Srgrimes	struct entry *np, *ep;
8411558Srgrimes	long descend = GOOD;
8421558Srgrimes
8431558Srgrimes	ep = lookupname(name);
8441558Srgrimes	if (ep == NULL) {
8451558Srgrimes		fprintf(stderr, "Warning: missing name %s\n", name);
8461558Srgrimes		return (FAIL);
8471558Srgrimes	}
8481558Srgrimes	np = lookupino(ino);
8491558Srgrimes	if (np != ep)
8501558Srgrimes		descend = FAIL;
8511558Srgrimes	for ( ; np != NULL; np = np->e_links)
8521558Srgrimes		if (np == ep)
8531558Srgrimes			break;
8541558Srgrimes	if (np == NULL)
8551558Srgrimes		panic("missing inumber %d\n", ino);
8561558Srgrimes	if (ep->e_type == LEAF && type != LEAF)
8571558Srgrimes		badentry(ep, "type should be LEAF");
8581558Srgrimes	return (descend);
8591558Srgrimes}
860