restore.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* Copyright 1996, 1998, 2001, 2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Copyright (c) 1983 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "restore.h"
/* undef MAXNAMLEN to prevent compiler warnings about redef in dirent.h */
#include <dirent.h>
#ifdef __STDC__
static char *keyval(int);
static void removexattrs(struct entry *);
static void movexattrs(char *, char *);
#else
static char *keyval();
static void removexattrs();
static void movexattrs();
#endif
/*
* This implements the 't' option.
* List entries on the tape.
*/
long
char *name;
int type;
{
return (descend);
}
return (descend);
}
/*
* This implements the 'x' option.
* Request that new entries be extracted.
*/
long
char *name;
int type;
{
char buf[100];
/* Don't know if ino_t is long or long long, so be safe w/ *printf() */
if (mflag) {
"%s: not on the volume\n"), name);
} else {
"inode %llu: not on the volume\n"),
(u_longlong_t)ino);
}
return (descend);
}
if (!mflag) {
return (descend);
}
}
/* LINTED: result fits into a short */
return (descend);
}
}
/* LINTED: result fits into a short */
return (descend);
}
/*
* This is used by the 'i' option to undo previous requests made by addfile.
* Delete entries from the request queue.
*/
/* ARGSUSED */
long
char *name;
int type;
{
return (descend);
}
/* LINTED: result fits into a short */
}
return (descend);
}
/*
* The following four routines implement the incremental
* restore algorithm. The first removes old entries, the second
* does renames and calculates the extraction list, the third
* cleans up link names missed by the first two, and the final
* one deletes old directories.
*
* Directories cannot be immediately deleted, as they may have
* other files in them which need to be moved out first. As
* directories to be deleted are found, they are put on the
* following deletion list. After all deletions and renames
* are done, this list is actually deleted.
*/
static struct entry *removelist;
/*
* Remove unneeded leaves from the old tree.
* Remove directories from the lookup chains.
*/
void
#ifdef __STDC__
removeoldleaves(void)
#else
#endif
{
ino_t i;
continue;
continue;
removeleaf(ep);
} else {
mktempname(ep);
/*
* once the inode is deleted from the symbol
* table, the e_next field is reusable
*/
removelist = ep;
}
}
}
}
/*
* For each directory entry on the incremental tape, determine which
* category it falls into as follows:
* KEEP - entries that are to be left alone.
* NEW - new entries to be added.
* EXTRACT - files that must be updated with new contents.
* LINK - new links to be added.
* Renames are done at the same time.
*/
long
char *name;
int type;
{
int lookuptype = 0;
int key = 0;
/* key values */
/*
* This routine is called once for each element in the
* directory hierarchy, with a full path name.
* The "type" value is incorrectly specified as LEAF for
* directories that are not on the dump tape.
*
* Check to see if the file is on the tape.
*/
/*
* Check to see if the name exists, and if the name is a link.
*/
gettext("corrupted symbol table\n"));
done(1);
}
lookuptype = LINK;
}
/*
* Check to see if the inode exists, and if one of its links
* corresponds to the name (if one was found).
*/
break;
}
}
}
/*
* If both a name and an inode are found, but they do not
* correspond to the same file, then both the inode that has
* been found and the inode corresponding to the name that
* has been found need to be renamed. The current pathname
* is the new name for the inode that has been found. Since
* all files to be deleted have already been removed, the
* named file is either a now-unneeded link, or it must live
* under a new name in this dump level. If it is a link, it
* can be removed. If it is not a link, it is given a
* temporary name in anticipation that it will be renamed
* when it is later found by inode number.
*/
if (lookuptype == LINK) {
removeleaf(np);
} else {
mktempname(np);
}
}
/*
* Decide on the disposition of the file based on its flags.
* Note that we have already handled the case in which
* a name and inode are found that correspond to different files.
* Thus if both NAMEFND and INOFND are set then ip == np.
*/
switch (key) {
/*
* A previously existing file has been found.
* Mark it as KEEP so that other links to the inode can be
* detected, and so that it will not be reclaimed by the search
* for unreferenced names.
*/
/* LINTED: result fits into a short */
flagvalues(ip));
break;
/*
* A file on the tape has a name which is the same as a name
* corresponding to a different file in the previous dump.
* Since all files to be deleted have already been removed,
* this file is either a now-unneeded link, or it must live
* under a new name in this dump level. If it is a link, it
* can simply be removed. If it is not a link, it is given a
* temporary name in anticipation that it will be renamed
* when it is later found by inode number (see INOFND case
* below). The entry is then treated as a new file.
*/
if (lookuptype == LINK) {
removeleaf(np);
} else {
mktempname(np);
}
/*FALLTHROUGH*/
/*
* A previously non-existent file.
* Add it to the file system, and request its extraction.
* If it is a directory, create it immediately.
* (Since the name is unused there can be no conflict)
*/
case ONTAPE:
/* LINTED: result fits into a short */
flagvalues(ep));
break;
/*
* A file with the same inode number, but a different
* name has been found. If the other name has not already
* been found (indicated by the KEEP flag, see above) then
* this must be a new name for the file, and it is renamed.
* If the other name has been found then this must be a
* link to the file. Hard links to directories are not
* permitted, and are either deleted or converted to
* symbolic links. Finally, if the file is on the tape,
* a request is made to extract it.
*/
/* LINTED: result fits into a short */
}
/*FALLTHROUGH*/
case INOFND:
/* LINTED: result fits into a short */
flagvalues(ip));
break;
}
"deleted hard link %s to directory %s\n"),
break;
}
/* LINTED: result fits into a short */
flagvalues(ep));
break;
/*
* A previously known file which is to be updated.
*/
/* LINTED: result fits into a short */
}
/* LINTED: result fits into a short */
flagvalues(np));
break;
/*
* An inode is being reused in a completely different way.
* Normally an extract can simply do an "unlink" followed
* by a "creat". Here we must do effectively the same
* thing. The complications arise because we cannot really
* delete a directory since it may still contain files
* that we need to rename, so we delete it from the symbol
* table, and put it on the list to be deleted eventually.
* Conversely if a directory is to be created, it must be
* done immediately, rather than waiting until the
* extraction phase.
*/
break;
}
/* changing from leaf to node */
removeleaf(ip);
} else {
/* changing from node to leaf */
mktempname(ip);
removelist = ip;
}
/* LINTED: result fits into a short */
flagvalues(ip));
break;
/*
* A hard link to a directory that has been removed.
* Ignore it.
*/
case NAMEFND:
name);
break;
/*
* If we find a directory entry for a file that is not on
* the tape, then we must have found a file that was created
* while the dump was in progress. Since we have no contents
* for it, we discard the name knowing that it will be on the
* next incremental tape.
*/
case 0:
gettext("%s: (inode %lu) not found on volume\n"),
break;
/*
* If any of these arise, something is grievously wrong with
* the current state of the symbol table.
*/
done(1);
/*NOTREACHED*/
/*
* These states "cannot" arise for any state of the symbol table.
*/
case MODECHG:
default:
done(1);
/*NOTREACHED*/
}
return (descend);
}
/*
* Calculate the active flags in a key.
*/
static char *
int key;
{
static char keybuf[32];
/* Note longest case is everything except |NIL */
keybuf[0] = '\0';
return (&keybuf[1]);
}
/*
* Find unreferenced link names.
*/
void
#ifdef __STDC__
findunreflinks(void)
#else
#endif
{
ino_t i;
continue;
"%s: remove unreferenced name\n"),
removeleaf(np);
}
}
}
/*
* Any leaves remaining in removed directories are unreferenced.
*/
"unreferenced with flags"));
"%s: remove unreferenced name\n"),
removeleaf(np);
}
}
}
}
/*
* Remove old nodes (directories).
* Note that this routine runs in O(N*D) where:
* N is the number of directory entries to be removed.
* D is the maximum depth of the tree.
* If N == D this can be quite slow. If the list were
* topologically sorted, the deletion could be done in
* time O(N).
*/
void
#ifdef __STDC__
removeoldnodes(void)
#else
#endif
{
long change;
do {
change = 0;
prev = &removelist;
continue;
}
removenode(ep);
change++;
}
} while (change);
}
/*
* This is the routine used to extract files for the 'r' command.
* Extract new leaves.
*/
void
char *symtabfile;
{
char name[MAXCOMPLEXLEN];
int curvol;
if (command == 'R') {
} else {
}
/*
* If the next available file is not the one which we
* expect then we have missed one or more files. Since
* we do not request files that were not on the tape,
* the lost files must have been due to a tape read error,
* or a file that was removed while the dump was in progress.
*
* The loop will terminate with first == maxino, if not
* sooner. Due to the e_flags manipulation, lowerbnd()
* will never return its argument.
*/
done(1);
}
gettext("%s: not found on volume\n"),
/* LINTED: result fits into a short */
}
/*
* If we find files on the tape that have no corresponding
* directory entries, then we must have found a file that
* was created while the dump was in progress. Since we have
* no name for it, we discard it knowing that it will be
* on the next incremental tape.
*/
gettext("expected next file %d, got %d\n"),
skipfile();
goto next;
}
gettext("unknown file on volume\n"));
done(1);
}
/*
* If the file is to be extracted, then the old file must
* be removed since its type may change from one leaf type
* to another (eg "file" to "character special"). But we
* also need to preserve any existing extended attributes;
* so first rename the file, then move its attributes, then
* remove it.
*/
mktempname(ep);
(void) extractfile(name);
removeleaf(ep);
/* LINTED: result fits into a short */
} else {
}
/* LINTED: result fits into a short */
/*
* We checkpoint the restore after every tape reel, so
* as to simplify the amount of work required by the
* 'R' command.
*/
next:
skipmaps();
}
}
}
/*
* This is the routine used to extract files for the 'x' and 'i' commands.
* Efficiently extract a subset of the files on a tape.
*/
void
#ifdef __STDC__
createfiles(void)
#else
#endif
{
if (nextvol == 0) {
getvol(1);
skipmaps();
skipdirs();
}
for (;;) {
/*
* Check to see if any files remain to be extracted
*/
return;
/*
* If a map of inode numbers to tape volumes is
* available, then select the next volume to be read.
*/
if (nextvol > 0) {
skipmaps();
}
}
/*
* Reject any volumes with inodes greater than
* the last one needed. This will only be true
* if the above code has not selected a volume.
*/
getvol(0);
skipmaps();
skipdirs();
}
/*
* Decide on the next inode needed.
* Skip across the inodes until it is found
* or an out of order volume change is encountered
*/
do {
skipfile();
skipmaps();
skipdirs();
/*
* If volume change out of order occurred the
* current state must be recalculated
*/
continue;
/*
* If the current inode is greater than the one we were
* looking for then we missed the one we were looking for.
* Since we only attempt to extract files listed in the
* dump map, the lost files must have been due to a tape
* read error, or a file that was removed while the dump
* was in progress. Thus we report all requested files
* between the one we were looking for, and the one we
* found as missing, and delete their request flags.
*/
gettext("corrupted symbol table\n"));
done(1);
}
gettext("%s: not found on volume\n"),
/* LINTED: result fits into a short */
}
/*
* The current inode is the one that we are looking for,
* so extract it per its requested name.
*/
gettext("corrupted symbol table\n"));
done(1);
}
/* LINTED: result fits into a short */
skipmaps();
}
}
}
/*
* Add links.
*/
void
#ifdef __STDC__
createlinks(void)
#else
#endif
{
ino_t i;
int dfd;
int saverr;
continue;
continue;
gettext("%s->%s: link failed: %s\n"),
continue;
}
}
} else {
}
/* LINTED: result fits into a short */
}
}
}
}
/*
* Check the symbol table.
* We do this to insure that all the requested work was done, and
* that no temporary names remain.
*/
void
#ifdef __STDC__
checkrestore(void)
#else
#endif
{
ino_t i;
/* LINTED: result fits into a short */
/* LINTED: result fits into a short */
}
}
}
}
/*
* Compare with the directory structure on the tape
* A paranoid check that things are as they should be.
*/
long
char *name;
int type;
{
return (FAIL);
}
break;
done(1);
}
return (descend);
}
/*
* This routine does not actually remove any attribute files, it
* just removes entries from the symbol table. The attribute files
* themselves are assumed to be removed automatically when the
* parent file is removed.
*/
static void
{
return;
} else {
}
}
}
/*
* Move all the extended attributes associated with orig to
* the file named by the second argument (targ).
*/
static void
char *orig;
char *targ;
{
/* no attributes to move */
return;
}
perror("");
goto out;
}
/* no attributes to move */
goto out;
}
from);
perror("");
goto out;
}
perror("");
goto out;
}
gettext("%s: cannot allocate DIR structure to attribute directory: "),
from);
perror("");
goto out;
}
continue;
gettext("%s: cannot move attribute %s: "),
goto out;
}
}
out:
if (fromfd != -1)
if (tofd != -1)
if (fromdir != -1)
if (todir != -1)
}