Index: head/include/protocols/dumprestore.h =================================================================== --- head/include/protocols/dumprestore.h (revision 40667) +++ head/include/protocols/dumprestore.h (revision 40668) @@ -1,112 +1,112 @@ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)dumprestore.h 8.2 (Berkeley) 1/21/94 */ #ifndef _PROTOCOLS_DUMPRESTORE_H_ #define _PROTOCOLS_DUMPRESTORE_H_ /* * TP_BSIZE is the size of file blocks on the dump tapes. * Note that TP_BSIZE must be a multiple of DEV_BSIZE. * * NTREC is the number of TP_BSIZE blocks that are written * in each tape record. HIGHDENSITYTREC is the number of * TP_BSIZE blocks that are written in each tape record on * 6250 BPI or higher density tapes. * * TP_NINDIR is the number of indirect pointers in a TS_INODE * or TS_ADDR record. Note that it must be a power of two. */ #define TP_BSIZE 1024 #define NTREC 10 #define HIGHDENSITYTREC 32 #define TP_NINDIR (TP_BSIZE/2) #define LBLSIZE 16 #define NAMELEN 64 #define OFS_MAGIC (int)60011 #define NFS_MAGIC (int)60012 #define CHECKSUM (int)84446 union u_spcl { char dummy[TP_BSIZE]; struct s_spcl { - long c_type; /* record type (see below) */ + int32_t c_type; /* record type (see below) */ time_t c_date; /* date of this dump */ time_t c_ddate; /* date of previous dump */ - long c_volume; /* dump volume number */ + int32_t c_volume; /* dump volume number */ daddr_t c_tapea; /* logical block of this record */ ino_t c_inumber; /* number of inode */ - long c_magic; /* magic number (see above) */ - long c_checksum; /* record checksum */ + int32_t c_magic; /* magic number (see above) */ + int32_t c_checksum; /* record checksum */ struct dinode c_dinode; /* ownership and mode of inode */ - long c_count; /* number of valid c_addr entries */ + int32_t c_count; /* number of valid c_addr entries */ char c_addr[TP_NINDIR]; /* 1 => data; 0 => hole in inode */ char c_label[LBLSIZE]; /* dump label */ - long c_level; /* level of this dump */ + int32_t c_level; /* level of this dump */ char c_filesys[NAMELEN]; /* name of dumpped file system */ char c_dev[NAMELEN]; /* name of dumpped device */ char c_host[NAMELEN]; /* name of dumpped host */ - long c_flags; /* additional information */ - long c_firstrec; /* first record on volume */ - long c_spare[32]; /* reserved for future uses */ + int32_t c_flags; /* additional information */ + int32_t c_firstrec; /* first record on volume */ + int32_t c_spare[32]; /* reserved for future uses */ } s_spcl; } u_spcl; #define spcl u_spcl.s_spcl /* * special record types */ #define TS_TAPE 1 /* dump tape header */ #define TS_INODE 2 /* beginning of file record */ #define TS_ADDR 4 /* continuation of file record */ #define TS_BITS 3 /* map of inodes on tape */ #define TS_CLRI 6 /* map of inodes deleted since last dump */ #define TS_END 5 /* end of volume marker */ /* * flag values */ #define DR_NEWHEADER 0x0001 /* new format tape header */ #define DR_NEWINODEFMT 0x0002 /* new format inodes on tape */ #define DUMPOUTFMT "%-16s %c %s" /* for printf */ /* name, level, ctime(date) */ #define DUMPINFMT "%16s %c %[^\n]\n" /* inverse for scanf */ #endif /* !_DUMPRESTORE_H_ */ Index: head/sbin/dump/traverse.c =================================================================== --- head/sbin/dump/traverse.c (revision 40667) +++ head/sbin/dump/traverse.c (revision 40668) @@ -1,612 +1,612 @@ /*- * Copyright (c) 1980, 1988, 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)traverse.c 8.7 (Berkeley) 6/15/95"; #endif static const char rcsid[] = - "$Id$"; + "$Id: traverse.c,v 1.7 1998/06/15 06:58:12 charnier Exp $"; #endif /* not lint */ #include #include #ifdef sunos #include #include #include #include #else #include #include #include #endif #include #include #include #ifdef __STDC__ #include #include #endif #include "dump.h" #define HASDUMPEDFILE 0x1 #define HASSUBDIRS 0x2 #ifdef FS_44INODEFMT typedef quad_t fsizeT; #else typedef long fsizeT; #endif static int dirindir __P((ino_t ino, daddr_t blkno, int level, long *size)); static void dmpindir __P((ino_t ino, daddr_t blk, int level, fsizeT *size)); static int searchdir __P((ino_t ino, daddr_t blkno, long size, long filesize)); /* * This is an estimation of the number of TP_BSIZE blocks in the file. * It estimates the number of blocks in files with holes by assuming * that all of the blocks accounted for by di_blocks are data blocks * (when some of the blocks are usually used for indirect pointers); * hence the estimate may be high. */ long blockest(dp) register struct dinode *dp; { long blkest, sizeest; /* * dp->di_size is the size of the file in bytes. * dp->di_blocks stores the number of sectors actually in the file. * If there are more sectors than the size would indicate, this just * means that there are indirect blocks in the file or unused * sectors in the last file block; we can safely ignore these * (blkest = sizeest below). * If the file is bigger than the number of sectors would indicate, * then the file has holes in it. In this case we must use the * block count to estimate the number of data blocks used, but * we use the actual size for estimating the number of indirect * dump blocks (sizeest vs. blkest in the indirect block * calculation). */ blkest = howmany(dbtob(dp->di_blocks), TP_BSIZE); sizeest = howmany(dp->di_size, TP_BSIZE); if (blkest > sizeest) blkest = sizeest; if (dp->di_size > sblock->fs_bsize * NDADDR) { /* calculate the number of indirect blocks on the dump tape */ blkest += howmany(sizeest - NDADDR * sblock->fs_bsize / TP_BSIZE, TP_NINDIR); } return (blkest + 1); } /* Auxiliary macro to pick up files changed since previous dump. */ #define CHANGEDSINCE(dp, t) \ ((dp)->di_mtime >= (t) || (dp)->di_ctime >= (t)) /* The WANTTODUMP macro decides whether a file should be dumped. */ #ifdef UF_NODUMP #define WANTTODUMP(dp) \ (CHANGEDSINCE(dp, spcl.c_ddate) && \ (nonodump || ((dp)->di_flags & UF_NODUMP) != UF_NODUMP)) #else #define WANTTODUMP(dp) CHANGEDSINCE(dp, spcl.c_ddate) #endif /* * Dump pass 1. * * Walk the inode list for a filesystem to find all allocated inodes * that have been modified since the previous dump time. Also, find all * the directories in the filesystem. */ int mapfiles(maxino, tapesize) ino_t maxino; long *tapesize; { register int mode; register ino_t ino; register struct dinode *dp; int anydirskipped = 0; for (ino = ROOTINO; ino < maxino; ino++) { dp = getino(ino); if ((mode = (dp->di_mode & IFMT)) == 0) continue; SETINO(ino, usedinomap); if (mode == IFDIR) SETINO(ino, dumpdirmap); if (WANTTODUMP(dp)) { SETINO(ino, dumpinomap); if (mode != IFREG && mode != IFDIR && mode != IFLNK) *tapesize += 1; else *tapesize += blockest(dp); continue; } if (mode == IFDIR) anydirskipped = 1; } /* * Restore gets very upset if the root is not dumped, * so ensure that it always is dumped. */ SETINO(ROOTINO, dumpinomap); return (anydirskipped); } /* * Dump pass 2. * * Scan each directory on the filesystem to see if it has any modified * files in it. If it does, and has not already been added to the dump * list (because it was itself modified), then add it. If a directory * has not been modified itself, contains no modified files and has no * subdirectories, then it can be deleted from the dump list and from * the list of directories. By deleting it from the list of directories, * its parent may now qualify for the same treatment on this or a later * pass using this algorithm. */ int mapdirs(maxino, tapesize) ino_t maxino; long *tapesize; { register struct dinode *dp; register int i, isdir; register char *map; register ino_t ino; long filesize; int ret, change = 0; isdir = 0; /* XXX just to get gcc to shut up */ for (map = dumpdirmap, ino = 1; ino < maxino; ino++) { if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */ isdir = *map++; else isdir >>= 1; if ((isdir & 1) == 0 || TSTINO(ino, dumpinomap)) continue; dp = getino(ino); filesize = dp->di_size; for (ret = 0, i = 0; filesize > 0 && i < NDADDR; i++) { if (dp->di_db[i] != 0) ret |= searchdir(ino, dp->di_db[i], (long)dblksize(sblock, dp, i), filesize); if (ret & HASDUMPEDFILE) filesize = 0; else filesize -= sblock->fs_bsize; } for (i = 0; filesize > 0 && i < NIADDR; i++) { if (dp->di_ib[i] == 0) continue; ret |= dirindir(ino, dp->di_ib[i], i, &filesize); } if (ret & HASDUMPEDFILE) { SETINO(ino, dumpinomap); *tapesize += blockest(dp); change = 1; continue; } if ((ret & HASSUBDIRS) == 0) { if (!TSTINO(ino, dumpinomap)) { CLRINO(ino, dumpdirmap); change = 1; } } } return (change); } /* * Read indirect blocks, and pass the data blocks to be searched * as directories. Quit as soon as any entry is found that will * require the directory to be dumped. */ static int dirindir(ino, blkno, ind_level, filesize) ino_t ino; daddr_t blkno; int ind_level; long *filesize; { int ret = 0; register int i; daddr_t idblk[MAXNINDIR]; bread(fsbtodb(sblock, blkno), (char *)idblk, (int)sblock->fs_bsize); if (ind_level <= 0) { for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) { blkno = idblk[i]; if (blkno != 0) ret |= searchdir(ino, blkno, sblock->fs_bsize, *filesize); if (ret & HASDUMPEDFILE) *filesize = 0; else *filesize -= sblock->fs_bsize; } return (ret); } ind_level--; for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) { blkno = idblk[i]; if (blkno != 0) ret |= dirindir(ino, blkno, ind_level, filesize); } return (ret); } /* * Scan a disk block containing directory information looking to see if * any of the entries are on the dump list and to see if the directory * contains any subdirectories. */ static int searchdir(ino, blkno, size, filesize) ino_t ino; daddr_t blkno; register long size; long filesize; { register struct direct *dp; register long loc, ret = 0; char dblk[MAXBSIZE]; bread(fsbtodb(sblock, blkno), dblk, (int)size); if (filesize < size) size = filesize; for (loc = 0; loc < size; ) { dp = (struct direct *)(dblk + loc); if (dp->d_reclen == 0) { msg("corrupted directory, inumber %d\n", ino); break; } loc += dp->d_reclen; if (dp->d_ino == 0) continue; if (dp->d_name[0] == '.') { if (dp->d_name[1] == '\0') continue; if (dp->d_name[1] == '.' && dp->d_name[2] == '\0') continue; } if (TSTINO(dp->d_ino, dumpinomap)) { ret |= HASDUMPEDFILE; if (ret & HASSUBDIRS) break; } if (TSTINO(dp->d_ino, dumpdirmap)) { ret |= HASSUBDIRS; if (ret & HASDUMPEDFILE) break; } } return (ret); } /* * Dump passes 3 and 4. * * Dump the contents of an inode to tape. */ void dumpino(dp, ino) register struct dinode *dp; ino_t ino; { int ind_level, cnt; fsizeT size; char buf[TP_BSIZE]; if (newtape) { newtape = 0; dumpmap(dumpinomap, TS_BITS, ino); } CLRINO(ino, dumpinomap); spcl.c_dinode = *dp; spcl.c_type = TS_INODE; spcl.c_count = 0; switch (dp->di_mode & S_IFMT) { case 0: /* * Freed inode. */ return; case S_IFLNK: /* * Check for short symbolic link. */ #ifdef FS_44INODEFMT if (dp->di_size > 0 && dp->di_size < sblock->fs_maxsymlinklen) { spcl.c_addr[0] = 1; spcl.c_count = 1; writeheader(ino); memmove(buf, dp->di_shortlink, (u_long)dp->di_size); buf[dp->di_size] = '\0'; writerec(buf, 0); return; } #endif /* fall through */ case S_IFDIR: case S_IFREG: if (dp->di_size > 0) break; /* fall through */ case S_IFIFO: case S_IFSOCK: case S_IFCHR: case S_IFBLK: writeheader(ino); return; default: msg("Warning: undefined file type 0%o\n", dp->di_mode & IFMT); return; } if (dp->di_size > NDADDR * sblock->fs_bsize) cnt = NDADDR * sblock->fs_frag; else cnt = howmany(dp->di_size, sblock->fs_fsize); blksout(&dp->di_db[0], cnt, ino); if ((size = dp->di_size - NDADDR * sblock->fs_bsize) <= 0) return; for (ind_level = 0; ind_level < NIADDR; ind_level++) { dmpindir(ino, dp->di_ib[ind_level], ind_level, &size); if (size <= 0) return; } } /* * Read indirect blocks, and pass the data blocks to be dumped. */ static void dmpindir(ino, blk, ind_level, size) ino_t ino; daddr_t blk; int ind_level; fsizeT *size; { int i, cnt; daddr_t idblk[MAXNINDIR]; if (blk != 0) bread(fsbtodb(sblock, blk), (char *)idblk, (int) sblock->fs_bsize); else memset(idblk, 0, (int)sblock->fs_bsize); if (ind_level <= 0) { if (*size < NINDIR(sblock) * sblock->fs_bsize) cnt = howmany(*size, sblock->fs_fsize); else cnt = NINDIR(sblock) * sblock->fs_frag; *size -= NINDIR(sblock) * sblock->fs_bsize; blksout(&idblk[0], cnt, ino); return; } ind_level--; for (i = 0; i < NINDIR(sblock); i++) { dmpindir(ino, idblk[i], ind_level, size); if (*size <= 0) return; } } /* * Collect up the data into tape record sized buffers and output them. */ void blksout(blkp, frags, ino) daddr_t *blkp; int frags; ino_t ino; { register daddr_t *bp; int i, j, count, blks, tbperdb; blks = howmany(frags * sblock->fs_fsize, TP_BSIZE); tbperdb = sblock->fs_bsize >> tp_bshift; for (i = 0; i < blks; i += TP_NINDIR) { if (i + TP_NINDIR > blks) count = blks; else count = i + TP_NINDIR; for (j = i; j < count; j++) if (blkp[j / tbperdb] != 0) spcl.c_addr[j - i] = 1; else spcl.c_addr[j - i] = 0; spcl.c_count = count - i; writeheader(ino); bp = &blkp[i / tbperdb]; for (j = i; j < count; j += tbperdb, bp++) if (*bp != 0) if (j + tbperdb <= count) dumpblock(*bp, (int)sblock->fs_bsize); else dumpblock(*bp, (count - j) * TP_BSIZE); spcl.c_type = TS_ADDR; } } /* * Dump a map to the tape. */ void dumpmap(map, type, ino) char *map; int type; ino_t ino; { register int i; char *cp; spcl.c_type = type; spcl.c_count = howmany(mapsize * sizeof(char), TP_BSIZE); writeheader(ino); for (i = 0, cp = map; i < spcl.c_count; i++, cp += TP_BSIZE) writerec(cp, 0); } /* * Write a header record to the dump tape. */ void writeheader(ino) ino_t ino; { - register long sum, cnt, *lp; + register int32_t sum, cnt, *lp; spcl.c_inumber = ino; spcl.c_magic = NFS_MAGIC; spcl.c_checksum = 0; - lp = (long *)&spcl; + lp = (int32_t *)&spcl; sum = 0; - cnt = sizeof(union u_spcl) / (4 * sizeof(long)); + cnt = sizeof(union u_spcl) / (4 * sizeof(int32_t)); while (--cnt >= 0) { sum += *lp++; sum += *lp++; sum += *lp++; sum += *lp++; } spcl.c_checksum = CHECKSUM - sum; writerec((char *)&spcl, 1); } struct dinode * getino(inum) ino_t inum; { static daddr_t minino, maxino; static struct dinode inoblock[MAXINOPB]; curino = inum; if (inum >= minino && inum < maxino) return (&inoblock[inum - minino]); bread(fsbtodb(sblock, ino_to_fsba(sblock, inum)), (char *)inoblock, (int)sblock->fs_bsize); minino = inum - (inum % INOPB(sblock)); maxino = minino + INOPB(sblock); return (&inoblock[inum - minino]); } /* * Read a chunk of data from the disk. * Try to recover from hard errors by reading in sector sized pieces. * Error recovery is attempted at most BREADEMAX times before seeking * consent from the operator to continue. */ int breaderrors = 0; #define BREADEMAX 32 void bread(blkno, buf, size) daddr_t blkno; char *buf; int size; { int cnt, i; extern int errno; loop: if (lseek(diskfd, ((off_t)blkno << dev_bshift), 0) != ((off_t)blkno << dev_bshift)) msg("bread: lseek fails\n"); if ((cnt = read(diskfd, buf, size)) == size) return; if (blkno + (size / dev_bsize) > fsbtodb(sblock, sblock->fs_size)) { /* * Trying to read the final fragment. * * NB - dump only works in TP_BSIZE blocks, hence * rounds `dev_bsize' fragments up to TP_BSIZE pieces. * It should be smarter about not actually trying to * read more than it can get, but for the time being * we punt and scale back the read only when it gets * us into trouble. (mkm 9/25/83) */ size -= dev_bsize; goto loop; } if (cnt == -1) msg("read error from %s: %s: [block %d]: count=%d\n", disk, strerror(errno), blkno, size); else msg("short read error from %s: [block %d]: count=%d, got=%d\n", disk, blkno, size, cnt); if (++breaderrors > BREADEMAX) { msg("More than %d block read errors from %d\n", BREADEMAX, disk); broadcast("DUMP IS AILING!\n"); msg("This is an unrecoverable error.\n"); if (!query("Do you want to attempt to continue?")){ dumpabort(0); /*NOTREACHED*/ } else breaderrors = 0; } /* * Zero buffer, then try to read each sector of buffer separately. */ memset(buf, 0, size); for (i = 0; i < size; i += dev_bsize, buf += dev_bsize, blkno++) { if (lseek(diskfd, ((off_t)blkno << dev_bshift), 0) != ((off_t)blkno << dev_bshift)) msg("bread: lseek2 fails!\n"); if ((cnt = read(diskfd, buf, (int)dev_bsize)) == dev_bsize) continue; if (cnt == -1) { msg("read error from %s: %s: [sector %d]: count=%d\n", disk, strerror(errno), blkno, dev_bsize); continue; } msg("short read error from %s: [sector %d]: count=%d, got=%d\n", disk, blkno, dev_bsize, cnt); } } Index: head/sbin/restore/dirs.c =================================================================== --- head/sbin/restore/dirs.c (revision 40667) +++ head/sbin/restore/dirs.c (revision 40668) @@ -1,768 +1,768 @@ /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)dirs.c 8.7 (Berkeley) 5/1/95"; #endif static const char rcsid[] = - "$Id$"; + "$Id: dirs.c,v 1.12 1998/07/28 06:20:05 charnier Exp $"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include "pathnames.h" #include "restore.h" #include "extern.h" /* * Symbol table of directories read from tape. */ #define HASHSIZE 1000 #define INOHASH(val) (val % HASHSIZE) struct inotab { struct inotab *t_next; ino_t t_ino; - long t_seekpt; - long t_size; + int32_t t_seekpt; + int32_t t_size; }; static struct inotab *inotab[HASHSIZE]; /* * Information retained about directories. */ struct modeinfo { ino_t ino; struct timeval timep[2]; mode_t mode; uid_t uid; gid_t gid; int flags; }; /* * Definitions for library routines operating on directories. */ #undef DIRBLKSIZ #define DIRBLKSIZ 1024 struct rstdirdesc { int dd_fd; - long dd_loc; - long dd_size; + int32_t dd_loc; + int32_t dd_size; char dd_buf[DIRBLKSIZ]; }; /* * Global variables for this file. */ static long seekpt; static FILE *df, *mf; static RST_DIR *dirp; static char dirfile[MAXPATHLEN] = "#"; /* No file */ static char modefile[MAXPATHLEN] = "#"; /* No file */ static char dot[2] = "."; /* So it can be modified */ /* * Format of old style directories. */ #define ODIRSIZ 14 struct odirect { u_short d_ino; char d_name[ODIRSIZ]; }; static struct inotab *allocinotab __P((ino_t, struct dinode *, long)); static void dcvt __P((struct odirect *, struct direct *)); static void flushent __P((void)); static struct inotab *inotablookup __P((ino_t)); static RST_DIR *opendirfile __P((const char *)); static void putdir __P((char *, long)); static void putent __P((struct direct *)); static void rst_seekdir __P((RST_DIR *, long, long)); static long rst_telldir __P((RST_DIR *)); static struct direct *searchdir __P((ino_t, char *)); /* * Extract directory contents, building up a directory structure * on disk for extraction by name. * If genmode is requested, save mode, owner, and times for all * directories on the tape. */ void extractdirs(genmode) int genmode; { register int i; register struct dinode *ip; struct inotab *itp; struct direct nulldir; int fd; vprintf(stdout, "Extract directories from tape\n"); (void) sprintf(dirfile, "%srstdir%d", _PATH_TMP, dumpdate); if (command != 'r' && command != 'R') { (void *) strcat(dirfile, "-XXXXXX"); fd = mkstemp(dirfile); } else fd = open(dirfile, O_RDWR|O_CREAT|O_EXCL, 0666); if (fd == -1 || (df = fdopen(fd, "w")) == NULL) { if (fd != -1) close(fd); warn("%s - cannot create directory temporary\nfopen", dirfile); done(1); } if (genmode != 0) { (void) sprintf(modefile, "%srstmode%d", _PATH_TMP, dumpdate); if (command != 'r' && command != 'R') { (void *) strcat(modefile, "-XXXXXX"); fd = mkstemp(modefile); } else fd = open(modefile, O_RDWR|O_CREAT|O_EXCL, 0666); if (fd == -1 || (mf = fdopen(fd, "w")) == NULL) { if (fd != -1) close(fd); warn("%s - cannot create modefile\nfopen", modefile); done(1); } } nulldir.d_ino = 0; nulldir.d_type = DT_DIR; nulldir.d_namlen = 1; (void) strcpy(nulldir.d_name, "/"); nulldir.d_reclen = DIRSIZ(0, &nulldir); for (;;) { curfile.name = ""; curfile.action = USING; ip = curfile.dip; if (ip == NULL || (ip->di_mode & IFMT) != IFDIR) { (void) fclose(df); dirp = opendirfile(dirfile); if (dirp == NULL) fprintf(stderr, "opendirfile: %s\n", strerror(errno)); if (mf != NULL) (void) fclose(mf); i = dirlookup(dot); if (i == 0) panic("Root directory is not on tape\n"); return; } itp = allocinotab(curfile.ino, ip, seekpt); getfile(putdir, xtrnull); putent(&nulldir); flushent(); itp->t_size = seekpt - itp->t_seekpt; } } /* * skip over all the directories on the tape */ void skipdirs() { while (curfile.dip && (curfile.dip->di_mode & IFMT) == IFDIR) { skipfile(); } } /* * Recursively find names and inumbers of all files in subtree * pname and pass them off to be processed. */ void treescan(pname, ino, todo) char *pname; ino_t ino; long (*todo) __P((char *, ino_t, int)); { register struct inotab *itp; register struct direct *dp; int namelen; long bpt; char locname[MAXPATHLEN + 1]; itp = inotablookup(ino); if (itp == NULL) { /* * Pname is name of a simple file or an unchanged directory. */ (void) (*todo)(pname, ino, LEAF); return; } /* * Pname is a dumped directory name. */ if ((*todo)(pname, ino, NODE) == FAIL) return; /* * begin search through the directory * skipping over "." and ".." */ (void) strncpy(locname, pname, sizeof(locname) - 1); locname[sizeof(locname) - 1] = '\0'; (void) strncat(locname, "/", sizeof(locname) - strlen(locname)); namelen = strlen(locname); rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); dp = rst_readdir(dirp); /* "." */ if (dp != NULL && strcmp(dp->d_name, ".") == 0) dp = rst_readdir(dirp); /* ".." */ else fprintf(stderr, "Warning: `.' missing from directory %s\n", pname); if (dp != NULL && strcmp(dp->d_name, "..") == 0) dp = rst_readdir(dirp); /* first real entry */ else fprintf(stderr, "Warning: `..' missing from directory %s\n", pname); bpt = rst_telldir(dirp); /* * a zero inode signals end of directory */ while (dp != NULL) { locname[namelen] = '\0'; if (namelen + dp->d_namlen >= sizeof(locname)) { fprintf(stderr, "%s%s: name exceeds %d char\n", locname, dp->d_name, sizeof(locname) - 1); } else { (void) strncat(locname, dp->d_name, (int)dp->d_namlen); treescan(locname, dp->d_ino, todo); rst_seekdir(dirp, bpt, itp->t_seekpt); } dp = rst_readdir(dirp); bpt = rst_telldir(dirp); } } /* * Lookup a pathname which is always assumed to start from the ROOTINO. */ struct direct * pathsearch(pathname) const char *pathname; { ino_t ino; struct direct *dp; char *path, *name, buffer[MAXPATHLEN]; strcpy(buffer, pathname); path = buffer; ino = ROOTINO; while (*path == '/') path++; dp = NULL; while ((name = strsep(&path, "/")) != NULL && *name != '\0') { if ((dp = searchdir(ino, name)) == NULL) return (NULL); ino = dp->d_ino; } return (dp); } /* * Lookup the requested name in directory inum. * Return its inode number if found, zero if it does not exist. */ static struct direct * searchdir(inum, name) ino_t inum; char *name; { register struct direct *dp; register struct inotab *itp; int len; itp = inotablookup(inum); if (itp == NULL) return (NULL); rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); len = strlen(name); do { dp = rst_readdir(dirp); if (dp == NULL) return (NULL); } while (dp->d_namlen != len || strncmp(dp->d_name, name, len) != 0); return (dp); } /* * Put the directory entries in the directory file */ static void putdir(buf, size) char *buf; long size; { struct direct cvtbuf; register struct odirect *odp; struct odirect *eodp; register struct direct *dp; long loc, i; if (cvtflag) { eodp = (struct odirect *)&buf[size]; for (odp = (struct odirect *)buf; odp < eodp; odp++) if (odp->d_ino != 0) { dcvt(odp, &cvtbuf); putent(&cvtbuf); } } else { for (loc = 0; loc < size; ) { dp = (struct direct *)(buf + loc); if (Bcvt) swabst((u_char *)"ls", (u_char *) dp); if (oldinofmt && dp->d_ino != 0) { # if BYTE_ORDER == BIG_ENDIAN if (Bcvt) dp->d_namlen = dp->d_type; # else if (!Bcvt) dp->d_namlen = dp->d_type; # endif dp->d_type = DT_UNKNOWN; } i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1)); if ((dp->d_reclen & 0x3) != 0 || dp->d_reclen > i || dp->d_reclen < DIRSIZ(0, dp) || dp->d_namlen > NAME_MAX) { vprintf(stdout, "Mangled directory: "); if ((dp->d_reclen & 0x3) != 0) vprintf(stdout, "reclen not multiple of 4 "); if (dp->d_reclen < DIRSIZ(0, dp)) vprintf(stdout, "reclen less than DIRSIZ (%d < %d) ", dp->d_reclen, DIRSIZ(0, dp)); if (dp->d_namlen > NAME_MAX) vprintf(stdout, "reclen name too big (%d > %d) ", dp->d_namlen, NAME_MAX); vprintf(stdout, "\n"); loc += i; continue; } loc += dp->d_reclen; if (dp->d_ino != 0) { putent(dp); } } } } /* * These variables are "local" to the following two functions. */ char dirbuf[DIRBLKSIZ]; long dirloc = 0; long prev = 0; /* * add a new directory entry to a file. */ static void putent(dp) struct direct *dp; { dp->d_reclen = DIRSIZ(0, dp); if (dirloc + dp->d_reclen > DIRBLKSIZ) { ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev; (void) fwrite(dirbuf, 1, DIRBLKSIZ, df); dirloc = 0; } memmove(dirbuf + dirloc, dp, (long)dp->d_reclen); prev = dirloc; dirloc += dp->d_reclen; } /* * flush out a directory that is finished. */ static void flushent() { ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev; (void) fwrite(dirbuf, (int)dirloc, 1, df); seekpt = ftell(df); dirloc = 0; } static void dcvt(odp, ndp) register struct odirect *odp; register struct direct *ndp; { memset(ndp, 0, (long)(sizeof *ndp)); ndp->d_ino = odp->d_ino; ndp->d_type = DT_UNKNOWN; (void) strncpy(ndp->d_name, odp->d_name, ODIRSIZ); ndp->d_namlen = strlen(ndp->d_name); ndp->d_reclen = DIRSIZ(0, ndp); } /* * Seek to an entry in a directory. * Only values returned by rst_telldir should be passed to rst_seekdir. * This routine handles many directories in a single file. * It takes the base of the directory in the file, plus * the desired seek offset into it. */ static void rst_seekdir(dirp, loc, base) register RST_DIR *dirp; long loc, base; { if (loc == rst_telldir(dirp)) return; loc -= base; if (loc < 0) fprintf(stderr, "bad seek pointer to rst_seekdir %ld\n", loc); (void) lseek(dirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), SEEK_SET); dirp->dd_loc = loc & (DIRBLKSIZ - 1); if (dirp->dd_loc != 0) dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ); } /* * get next entry in a directory. */ struct direct * rst_readdir(dirp) register RST_DIR *dirp; { register struct direct *dp; for (;;) { if (dirp->dd_loc == 0) { dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ); if (dirp->dd_size <= 0) { dprintf(stderr, "error reading directory\n"); return (NULL); } } if (dirp->dd_loc >= dirp->dd_size) { dirp->dd_loc = 0; continue; } dp = (struct direct *)(dirp->dd_buf + dirp->dd_loc); if (dp->d_reclen == 0 || dp->d_reclen > DIRBLKSIZ + 1 - dirp->dd_loc) { dprintf(stderr, "corrupted directory: bad reclen %d\n", dp->d_reclen); return (NULL); } dirp->dd_loc += dp->d_reclen; if (dp->d_ino == 0 && strcmp(dp->d_name, "/") == 0) return (NULL); if (dp->d_ino >= maxino) { dprintf(stderr, "corrupted directory: bad inum %d\n", dp->d_ino); continue; } return (dp); } } /* * Simulate the opening of a directory */ RST_DIR * rst_opendir(name) const char *name; { struct inotab *itp; RST_DIR *dirp; ino_t ino; if ((ino = dirlookup(name)) > 0 && (itp = inotablookup(ino)) != NULL) { dirp = opendirfile(dirfile); rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); return (dirp); } return (NULL); } /* * In our case, there is nothing to do when closing a directory. */ void rst_closedir(dirp) RST_DIR *dirp; { (void)close(dirp->dd_fd); free(dirp); return; } /* * Simulate finding the current offset in the directory. */ static long rst_telldir(dirp) RST_DIR *dirp; { return ((long)lseek(dirp->dd_fd, (off_t)0, SEEK_CUR) - dirp->dd_size + dirp->dd_loc); } /* * Open a directory file. */ static RST_DIR * opendirfile(name) const char *name; { register RST_DIR *dirp; register int fd; if ((fd = open(name, O_RDONLY)) == -1) return (NULL); if ((dirp = malloc(sizeof(RST_DIR))) == NULL) { (void)close(fd); return (NULL); } dirp->dd_fd = fd; dirp->dd_loc = 0; return (dirp); } /* * Set the mode, owner, and times for all new or changed directories */ void setdirmodes(flags) int flags; { FILE *mf; struct modeinfo node; struct entry *ep; char *cp; vprintf(stdout, "Set directory mode, owner, and times.\n"); if (command == 'r' || command == 'R') (void) sprintf(modefile, "%srstmode%d", _PATH_TMP, dumpdate); if (modefile[0] == '#') { panic("modefile not defined\n"); fprintf(stderr, "directory mode, owner, and times not set\n"); return; } mf = fopen(modefile, "r"); if (mf == NULL) { fprintf(stderr, "fopen: %s\n", strerror(errno)); fprintf(stderr, "cannot open mode file %s\n", modefile); fprintf(stderr, "directory mode, owner, and times not set\n"); return; } clearerr(mf); for (;;) { (void) fread((char *)&node, 1, sizeof(struct modeinfo), mf); if (feof(mf)) break; ep = lookupino(node.ino); if (command == 'i' || command == 'x') { if (ep == NULL) continue; if ((flags & FORCE) == 0 && ep->e_flags & EXISTED) { ep->e_flags &= ~NEW; continue; } if (node.ino == ROOTINO && reply("set owner/mode for '.'") == FAIL) continue; } if (ep == NULL) { panic("cannot find directory inode %d\n", node.ino); } else { cp = myname(ep); (void) chown(cp, node.uid, node.gid); (void) chmod(cp, node.mode); (void) chflags(cp, node.flags); utimes(cp, node.timep); ep->e_flags &= ~NEW; } } if (ferror(mf)) panic("error setting directory modes\n"); (void) fclose(mf); } /* * Generate a literal copy of a directory. */ int genliteraldir(name, ino) char *name; ino_t ino; { register struct inotab *itp; int ofile, dp, i, size; char buf[BUFSIZ]; itp = inotablookup(ino); if (itp == NULL) panic("Cannot find directory inode %d named %s\n", ino, name); if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) { fprintf(stderr, "%s: ", name); (void) fflush(stderr); fprintf(stderr, "cannot create file: %s\n", strerror(errno)); return (FAIL); } rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); dp = dup(dirp->dd_fd); for (i = itp->t_size; i > 0; i -= BUFSIZ) { size = i < BUFSIZ ? i : BUFSIZ; if (read(dp, buf, (int) size) == -1) { fprintf(stderr, "write error extracting inode %d, name %s\n", curfile.ino, curfile.name); fprintf(stderr, "read: %s\n", strerror(errno)); done(1); } if (!Nflag && write(ofile, buf, (int) size) == -1) { fprintf(stderr, "write error extracting inode %d, name %s\n", curfile.ino, curfile.name); fprintf(stderr, "write: %s\n", strerror(errno)); done(1); } } (void) close(dp); (void) close(ofile); return (GOOD); } /* * Determine the type of an inode */ int inodetype(ino) ino_t ino; { struct inotab *itp; itp = inotablookup(ino); if (itp == NULL) return (LEAF); return (NODE); } /* * Allocate and initialize a directory inode entry. * If requested, save its pertinent mode, owner, and time info. */ static struct inotab * allocinotab(ino, dip, seekpt) ino_t ino; struct dinode *dip; long seekpt; { register struct inotab *itp; struct modeinfo node; itp = calloc(1, sizeof(struct inotab)); if (itp == NULL) panic("no memory directory table\n"); itp->t_next = inotab[INOHASH(ino)]; inotab[INOHASH(ino)] = itp; itp->t_ino = ino; itp->t_seekpt = seekpt; if (mf == NULL) return (itp); node.ino = ino; node.timep[0].tv_sec = dip->di_atime; node.timep[0].tv_usec = dip->di_atimensec / 1000; node.timep[1].tv_sec = dip->di_mtime; node.timep[1].tv_usec = dip->di_mtimensec / 1000; node.mode = dip->di_mode; node.flags = dip->di_flags; node.uid = dip->di_uid; node.gid = dip->di_gid; (void) fwrite((char *)&node, 1, sizeof(struct modeinfo), mf); return (itp); } /* * Look up an inode in the table of directories */ static struct inotab * inotablookup(ino) ino_t ino; { register struct inotab *itp; for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next) if (itp->t_ino == ino) return (itp); return (NULL); } /* * Clean up and exit */ void done(exitcode) int exitcode; { closemt(); if (modefile[0] != '#') (void) unlink(modefile); if (dirfile[0] != '#') (void) unlink(dirfile); exit(exitcode); } Index: head/sbin/restore/symtab.c =================================================================== --- head/sbin/restore/symtab.c (revision 40667) +++ head/sbin/restore/symtab.c (revision 40668) @@ -1,635 +1,635 @@ /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)symtab.c 8.3 (Berkeley) 4/28/95"; #endif static const char rcsid[] = - "$Id$"; + "$Id: symtab.c,v 1.5 1998/07/28 06:20:13 charnier Exp $"; #endif /* not lint */ /* * These routines maintain the symbol table which tracks the state * of the file system being restored. They provide lookup by either * name or inode number. They also provide for creation, deletion, * and renaming of entries. Because of the dynamic nature of pathnames, * names should not be saved, but always constructed just before they * are needed, by calling "myname". */ #include #include #include #include #include #include #include #include #include #include "restore.h" #include "extern.h" /* * The following variables define the inode symbol table. * The primary hash table is dynamically allocated based on * the number of inodes in the file system (maxino), scaled by * HASHFACTOR. The variable "entry" points to the hash table; * the variable "entrytblsize" indicates its size (in entries). */ #define HASHFACTOR 5 static struct entry **entry; static long entrytblsize; static void addino __P((ino_t, struct entry *)); static struct entry *lookupparent __P((char *)); static void removeentry __P((struct entry *)); /* * Look up an entry by inode number */ struct entry * lookupino(inum) ino_t inum; { register struct entry *ep; if (inum < WINO || inum >= maxino) return (NULL); for (ep = entry[inum % entrytblsize]; ep != NULL; ep = ep->e_next) if (ep->e_ino == inum) return (ep); return (NULL); } /* * Add an entry into the entry table */ static void addino(inum, np) ino_t inum; struct entry *np; { struct entry **epp; if (inum < WINO || inum >= maxino) panic("addino: out of range %d\n", inum); epp = &entry[inum % entrytblsize]; np->e_ino = inum; np->e_next = *epp; *epp = np; if (dflag) for (np = np->e_next; np != NULL; np = np->e_next) if (np->e_ino == inum) badentry(np, "duplicate inum"); } /* * Delete an entry from the entry table */ void deleteino(inum) ino_t inum; { register struct entry *next; struct entry **prev; if (inum < WINO || inum >= maxino) panic("deleteino: out of range %d\n", inum); prev = &entry[inum % entrytblsize]; for (next = *prev; next != NULL; next = next->e_next) { if (next->e_ino == inum) { next->e_ino = 0; *prev = next->e_next; return; } prev = &next->e_next; } panic("deleteino: %d not found\n", inum); } /* * Look up an entry by name */ struct entry * lookupname(name) char *name; { register struct entry *ep; register char *np, *cp; char buf[MAXPATHLEN]; cp = name; for (ep = lookupino(ROOTINO); ep != NULL; ep = ep->e_entries) { for (np = buf; *cp != '/' && *cp != '\0' && np < &buf[sizeof(buf)]; ) *np++ = *cp++; if (np == &buf[sizeof(buf)]) break; *np = '\0'; for ( ; ep != NULL; ep = ep->e_sibling) if (strcmp(ep->e_name, buf) == 0) break; if (ep == NULL) break; if (*cp++ == '\0') return (ep); } return (NULL); } /* * Look up the parent of a pathname */ static struct entry * lookupparent(name) char *name; { struct entry *ep; char *tailindex; tailindex = strrchr(name, '/'); if (tailindex == NULL) return (NULL); *tailindex = '\0'; ep = lookupname(name); *tailindex = '/'; if (ep == NULL) return (NULL); if (ep->e_type != NODE) panic("%s is not a directory\n", name); return (ep); } /* * Determine the current pathname of a node or leaf */ char * myname(ep) register struct entry *ep; { register char *cp; static char namebuf[MAXPATHLEN]; for (cp = &namebuf[MAXPATHLEN - 2]; cp > &namebuf[ep->e_namlen]; ) { cp -= ep->e_namlen; memmove(cp, ep->e_name, (long)ep->e_namlen); if (ep == lookupino(ROOTINO)) return (cp); *(--cp) = '/'; ep = ep->e_parent; } panic("%s: pathname too long\n", cp); return(cp); } /* * Unused symbol table entries are linked together on a free list * headed by the following pointer. */ static struct entry *freelist = NULL; /* * add an entry to the symbol table */ struct entry * addentry(name, inum, type) char *name; ino_t inum; int type; { register struct entry *np, *ep; if (freelist != NULL) { np = freelist; freelist = np->e_next; memset(np, 0, (long)sizeof(struct entry)); } else { np = (struct entry *)calloc(1, sizeof(struct entry)); if (np == NULL) panic("no memory to extend symbol table\n"); } np->e_type = type & ~LINK; ep = lookupparent(name); if (ep == NULL) { if (inum != ROOTINO || lookupino(ROOTINO) != NULL) panic("bad name to addentry %s\n", name); np->e_name = savename(name); np->e_namlen = strlen(name); np->e_parent = np; addino(ROOTINO, np); return (np); } np->e_name = savename(strrchr(name, '/') + 1); np->e_namlen = strlen(np->e_name); np->e_parent = ep; np->e_sibling = ep->e_entries; ep->e_entries = np; if (type & LINK) { ep = lookupino(inum); if (ep == NULL) panic("link to non-existent name\n"); np->e_ino = inum; np->e_links = ep->e_links; ep->e_links = np; } else if (inum != 0) { if (lookupino(inum) != NULL) panic("duplicate entry\n"); addino(inum, np); } return (np); } /* * delete an entry from the symbol table */ void freeentry(ep) register struct entry *ep; { register struct entry *np; ino_t inum; if (ep->e_flags != REMOVED) badentry(ep, "not marked REMOVED"); if (ep->e_type == NODE) { if (ep->e_links != NULL) badentry(ep, "freeing referenced directory"); if (ep->e_entries != NULL) badentry(ep, "freeing non-empty directory"); } if (ep->e_ino != 0) { np = lookupino(ep->e_ino); if (np == NULL) badentry(ep, "lookupino failed"); if (np == ep) { inum = ep->e_ino; deleteino(inum); if (ep->e_links != NULL) addino(inum, ep->e_links); } else { for (; np != NULL; np = np->e_links) { if (np->e_links == ep) { np->e_links = ep->e_links; break; } } if (np == NULL) badentry(ep, "link not found"); } } removeentry(ep); freename(ep->e_name); ep->e_next = freelist; freelist = ep; } /* * Relocate an entry in the tree structure */ void moveentry(ep, newname) register struct entry *ep; char *newname; { struct entry *np; char *cp; np = lookupparent(newname); if (np == NULL) badentry(ep, "cannot move ROOT"); if (np != ep->e_parent) { removeentry(ep); ep->e_parent = np; ep->e_sibling = np->e_entries; np->e_entries = ep; } cp = strrchr(newname, '/') + 1; freename(ep->e_name); ep->e_name = savename(cp); ep->e_namlen = strlen(cp); if (strcmp(gentempname(ep), ep->e_name) == 0) ep->e_flags |= TMPNAME; else ep->e_flags &= ~TMPNAME; } /* * Remove an entry in the tree structure */ static void removeentry(ep) register struct entry *ep; { register struct entry *np; np = ep->e_parent; if (np->e_entries == ep) { np->e_entries = ep->e_sibling; } else { for (np = np->e_entries; np != NULL; np = np->e_sibling) { if (np->e_sibling == ep) { np->e_sibling = ep->e_sibling; break; } } if (np == NULL) badentry(ep, "cannot find entry in parent list"); } } /* * Table of unused string entries, sorted by length. * * Entries are allocated in STRTBLINCR sized pieces so that names * of similar lengths can use the same entry. The value of STRTBLINCR * is chosen so that every entry has at least enough space to hold * a "struct strtbl" header. Thus every entry can be linked onto an * appropriate free list. * * NB. The macro "allocsize" below assumes that "struct strhdr" * has a size that is a power of two. */ struct strhdr { struct strhdr *next; }; #define STRTBLINCR (sizeof(struct strhdr)) #define allocsize(size) (((size) + 1 + STRTBLINCR - 1) & ~(STRTBLINCR - 1)) static struct strhdr strtblhdr[allocsize(NAME_MAX) / STRTBLINCR]; /* * Allocate space for a name. It first looks to see if it already * has an appropriate sized entry, and if not allocates a new one. */ char * savename(name) char *name; { struct strhdr *np; long len; char *cp; if (name == NULL) panic("bad name\n"); len = strlen(name); np = strtblhdr[len / STRTBLINCR].next; if (np != NULL) { strtblhdr[len / STRTBLINCR].next = np->next; cp = (char *)np; } else { cp = malloc((unsigned)allocsize(len)); if (cp == NULL) panic("no space for string table\n"); } (void) strcpy(cp, name); return (cp); } /* * Free space for a name. The resulting entry is linked onto the * appropriate free list. */ void freename(name) char *name; { struct strhdr *tp, *np; tp = &strtblhdr[strlen(name) / STRTBLINCR]; np = (struct strhdr *)name; np->next = tp->next; tp->next = np; } /* * Useful quantities placed at the end of a dumped symbol table. */ struct symtableheader { - long volno; - long stringsize; - long entrytblsize; + int32_t volno; + int32_t stringsize; + int32_t entrytblsize; time_t dumptime; time_t dumpdate; ino_t maxino; - long ntrec; + int32_t ntrec; }; /* * dump a snapshot of the symbol table */ void dumpsymtable(filename, checkpt) char *filename; long checkpt; { register struct entry *ep, *tep; register ino_t i; struct entry temp, *tentry; long mynum = 1, stroff = 0; FILE *fd; struct symtableheader hdr; vprintf(stdout, "Check pointing the restore\n"); if (Nflag) return; if ((fd = fopen(filename, "w")) == NULL) { fprintf(stderr, "fopen: %s\n", strerror(errno)); panic("cannot create save file %s for symbol table\n", filename); } clearerr(fd); /* * Assign indices to each entry * Write out the string entries */ for (i = WINO; i <= maxino; i++) { for (ep = lookupino(i); ep != NULL; ep = ep->e_links) { ep->e_index = mynum++; (void) fwrite(ep->e_name, sizeof(char), (int)allocsize(ep->e_namlen), fd); } } /* * Convert pointers to indexes, and output */ tep = &temp; stroff = 0; for (i = WINO; i <= maxino; i++) { for (ep = lookupino(i); ep != NULL; ep = ep->e_links) { memmove(tep, ep, (long)sizeof(struct entry)); tep->e_name = (char *)stroff; stroff += allocsize(ep->e_namlen); tep->e_parent = (struct entry *)ep->e_parent->e_index; if (ep->e_links != NULL) tep->e_links = (struct entry *)ep->e_links->e_index; if (ep->e_sibling != NULL) tep->e_sibling = (struct entry *)ep->e_sibling->e_index; if (ep->e_entries != NULL) tep->e_entries = (struct entry *)ep->e_entries->e_index; if (ep->e_next != NULL) tep->e_next = (struct entry *)ep->e_next->e_index; (void) fwrite((char *)tep, sizeof(struct entry), 1, fd); } } /* * Convert entry pointers to indexes, and output */ for (i = 0; i < entrytblsize; i++) { if (entry[i] == NULL) tentry = NULL; else tentry = (struct entry *)entry[i]->e_index; (void) fwrite((char *)&tentry, sizeof(struct entry *), 1, fd); } hdr.volno = checkpt; hdr.maxino = maxino; hdr.entrytblsize = entrytblsize; hdr.stringsize = stroff; hdr.dumptime = dumptime; hdr.dumpdate = dumpdate; hdr.ntrec = ntrec; (void) fwrite((char *)&hdr, sizeof(struct symtableheader), 1, fd); if (ferror(fd)) { fprintf(stderr, "fwrite: %s\n", strerror(errno)); panic("output error to file %s writing symbol table\n", filename); } (void) fclose(fd); } /* * Initialize a symbol table from a file */ void initsymtable(filename) char *filename; { char *base; long tblsize; register struct entry *ep; struct entry *baseep, *lep; struct symtableheader hdr; struct stat stbuf; register long i; int fd; vprintf(stdout, "Initialize symbol table.\n"); if (filename == NULL) { entrytblsize = maxino / HASHFACTOR; entry = (struct entry **) calloc((unsigned)entrytblsize, sizeof(struct entry *)); if (entry == (struct entry **)NULL) panic("no memory for entry table\n"); ep = addentry(".", ROOTINO, NODE); ep->e_flags |= NEW; return; } if ((fd = open(filename, O_RDONLY, 0)) < 0) { fprintf(stderr, "open: %s\n", strerror(errno)); panic("cannot open symbol table file %s\n", filename); } if (fstat(fd, &stbuf) < 0) { fprintf(stderr, "stat: %s\n", strerror(errno)); panic("cannot stat symbol table file %s\n", filename); } tblsize = stbuf.st_size - sizeof(struct symtableheader); base = calloc(sizeof(char), (unsigned)tblsize); if (base == NULL) panic("cannot allocate space for symbol table\n"); if (read(fd, base, (int)tblsize) < 0 || read(fd, (char *)&hdr, sizeof(struct symtableheader)) < 0) { fprintf(stderr, "read: %s\n", strerror(errno)); panic("cannot read symbol table file %s\n", filename); } switch (command) { case 'r': /* * For normal continuation, insure that we are using * the next incremental tape */ if (hdr.dumpdate != dumptime) { if (hdr.dumpdate < dumptime) fprintf(stderr, "Incremental tape too low\n"); else fprintf(stderr, "Incremental tape too high\n"); done(1); } break; case 'R': /* * For restart, insure that we are using the same tape */ curfile.action = SKIP; dumptime = hdr.dumptime; dumpdate = hdr.dumpdate; if (!bflag) newtapebuf(hdr.ntrec); getvol(hdr.volno); break; default: panic("initsymtable called from command %c\n", command); break; } maxino = hdr.maxino; entrytblsize = hdr.entrytblsize; entry = (struct entry **) (base + tblsize - (entrytblsize * sizeof(struct entry *))); baseep = (struct entry *)(base + hdr.stringsize - sizeof(struct entry)); lep = (struct entry *)entry; for (i = 0; i < entrytblsize; i++) { if (entry[i] == NULL) continue; entry[i] = &baseep[(long)entry[i]]; } for (ep = &baseep[1]; ep < lep; ep++) { ep->e_name = base + (long)ep->e_name; ep->e_parent = &baseep[(long)ep->e_parent]; if (ep->e_sibling != NULL) ep->e_sibling = &baseep[(long)ep->e_sibling]; if (ep->e_links != NULL) ep->e_links = &baseep[(long)ep->e_links]; if (ep->e_entries != NULL) ep->e_entries = &baseep[(long)ep->e_entries]; if (ep->e_next != NULL) ep->e_next = &baseep[(long)ep->e_next]; } } Index: head/sbin/restore/tape.c =================================================================== --- head/sbin/restore/tape.c (revision 40667) +++ head/sbin/restore/tape.c (revision 40668) @@ -1,1403 +1,1403 @@ /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)tape.c 8.9 (Berkeley) 5/1/95"; #endif static const char rcsid[] = - "$Id: tape.c,v 1.13 1998/07/28 06:20:15 charnier Exp $"; + "$Id: tape.c,v 1.14 1998/07/28 18:50:01 imp Exp $"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include "restore.h" #include "extern.h" #include "pathnames.h" static long fssize = MAXBSIZE; static int mt = -1; static int pipein = 0; static char *magtape; static int blkcnt; static int numtrec; static char *tapebuf; static union u_spcl endoftapemark; static long blksread; /* blocks read since last header */ static long tpblksread = 0; /* TP_BSIZE blocks read */ static long tapesread; static jmp_buf restart; static int gettingfile = 0; /* restart has a valid frame */ static char *host = NULL; static int ofile; static char *map; static char lnkbuf[MAXPATHLEN + 1]; static int pathlen; int oldinofmt; /* old inode format conversion required */ int Bcvt; /* Swap Bytes (for CCI or sun) */ static int Qcvt; /* Swap quads (for sun) */ #define FLUSHTAPEBUF() blkcnt = ntrec + 1 static void accthdr __P((struct s_spcl *)); static int checksum __P((int *)); static void findinode __P((struct s_spcl *)); static void findtapeblksize __P((void)); static int gethead __P((struct s_spcl *)); static void readtape __P((char *)); static void setdumpnum __P((void)); static u_long swabl __P((u_long)); static u_char *swablong __P((u_char *, int)); static u_char *swabshort __P((u_char *, int)); static void terminateinput __P((void)); static void xtrfile __P((char *, long)); static void xtrlnkfile __P((char *, long)); static void xtrlnkskip __P((char *, long)); static void xtrmap __P((char *, long)); static void xtrmapskip __P((char *, long)); static void xtrskip __P((char *, long)); static int readmapflag; /* * Set up an input source */ void setinput(source) char *source; { FLUSHTAPEBUF(); if (bflag) newtapebuf(ntrec); else newtapebuf(NTREC > HIGHDENSITYTREC ? NTREC : HIGHDENSITYTREC); terminal = stdin; #ifdef RRESTORE if (strchr(source, ':')) { host = source; source = strchr(host, ':'); *source++ = '\0'; if (rmthost(host) == 0) done(1); } else #endif if (strcmp(source, "-") == 0) { /* * Since input is coming from a pipe we must establish * our own connection to the terminal. */ terminal = fopen(_PATH_TTY, "r"); if (terminal == NULL) { (void)fprintf(stderr, "cannot open %s: %s\n", _PATH_TTY, strerror(errno)); terminal = fopen(_PATH_DEVNULL, "r"); if (terminal == NULL) { (void)fprintf(stderr, "cannot open %s: %s\n", _PATH_DEVNULL, strerror(errno)); done(1); } } pipein++; } setuid(getuid()); /* no longer need or want root privileges */ magtape = strdup(source); if (magtape == NULL) { fprintf(stderr, "Cannot allocate space for magtape buffer\n"); done(1); } } void newtapebuf(size) long size; { static tapebufsize = -1; ntrec = size; if (size <= tapebufsize) return; if (tapebuf != NULL) free(tapebuf); tapebuf = malloc(size * TP_BSIZE); if (tapebuf == NULL) { fprintf(stderr, "Cannot allocate space for tape buffer\n"); done(1); } tapebufsize = size; } /* * Verify that the tape drive can be accessed and * that it actually is a dump tape. */ void setup() { int i, j, *ip; struct stat stbuf; vprintf(stdout, "Verify tape and initialize maps\n"); #ifdef RRESTORE if (host) mt = rmtopen(magtape, 0); else #endif if (pipein) mt = 0; else mt = open(magtape, O_RDONLY, 0); if (mt < 0) { fprintf(stderr, "%s: %s\n", magtape, strerror(errno)); done(1); } volno = 1; setdumpnum(); FLUSHTAPEBUF(); if (!pipein && !bflag) findtapeblksize(); if (gethead(&spcl) == FAIL) { blkcnt--; /* push back this block */ blksread--; tpblksread--; cvtflag++; if (gethead(&spcl) == FAIL) { fprintf(stderr, "Tape is not a dump tape\n"); done(1); } fprintf(stderr, "Converting to new file system format.\n"); } if (pipein) { endoftapemark.s_spcl.c_magic = cvtflag ? OFS_MAGIC : NFS_MAGIC; endoftapemark.s_spcl.c_type = TS_END; ip = (int *)&endoftapemark; j = sizeof(union u_spcl) / sizeof(int); i = 0; do i += *ip++; while (--j); endoftapemark.s_spcl.c_checksum = CHECKSUM - i; } if (vflag || command == 't') printdumpinfo(); dumptime = spcl.c_ddate; dumpdate = spcl.c_date; if (stat(".", &stbuf) < 0) { fprintf(stderr, "cannot stat .: %s\n", strerror(errno)); done(1); } if (stbuf.st_blksize > 0 && stbuf.st_blksize < TP_BSIZE ) fssize = TP_BSIZE; if (stbuf.st_blksize >= TP_BSIZE && stbuf.st_blksize <= MAXBSIZE) fssize = stbuf.st_blksize; if (((fssize - 1) & fssize) != 0) { fprintf(stderr, "bad block size %ld\n", fssize); done(1); } if (spcl.c_volume != 1) { fprintf(stderr, "Tape is not volume 1 of the dump\n"); done(1); } if (gethead(&spcl) == FAIL) { dprintf(stdout, "header read failed at %ld blocks\n", blksread); panic("no header after volume mark!\n"); } findinode(&spcl); if (spcl.c_type != TS_CLRI) { fprintf(stderr, "Cannot find file removal list\n"); done(1); } maxino = (spcl.c_count * TP_BSIZE * NBBY) + 1; dprintf(stdout, "maxino = %d\n", maxino); map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY)); if (map == NULL) panic("no memory for active inode map\n"); usedinomap = map; curfile.action = USING; getfile(xtrmap, xtrmapskip); if (spcl.c_type != TS_BITS) { fprintf(stderr, "Cannot find file dump list\n"); done(1); } map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY)); if (map == (char *)NULL) panic("no memory for file dump list\n"); dumpmap = map; curfile.action = USING; getfile(xtrmap, xtrmapskip); /* * If there may be whiteout entries on the tape, pretend that the * whiteout inode exists, so that the whiteout entries can be * extracted. */ if (oldinofmt == 0) SETINO(WINO, dumpmap); } /* * Prompt user to load a new dump volume. * "Nextvol" is the next suggested volume to use. * This suggested volume is enforced when doing full * or incremental restores, but can be overridden by * the user when only extracting a subset of the files. */ void getvol(nextvol) long nextvol; { long newvol, savecnt, wantnext, i; union u_spcl tmpspcl; # define tmpbuf tmpspcl.s_spcl char buf[TP_BSIZE]; if (nextvol == 1) { tapesread = 0; gettingfile = 0; } if (pipein) { if (nextvol != 1) panic("Changing volumes on pipe input?\n"); if (volno == 1) return; goto gethdr; } savecnt = blksread; again: if (pipein) done(1); /* pipes do not get a second chance */ if (command == 'R' || command == 'r' || curfile.action != SKIP) { newvol = nextvol; wantnext = 1; } else { newvol = 0; wantnext = 0; } while (newvol <= 0) { if (tapesread == 0) { fprintf(stderr, "%s%s%s%s%s", "You have not read any tapes yet.\n", "Unless you know which volume your", " file(s) are on you should start\n", "with the last volume and work", " towards the first.\n"); } else { fprintf(stderr, "You have read volumes"); strcpy(buf, ": "); for (i = 1; i < 32; i++) if (tapesread & (1 << i)) { fprintf(stderr, "%s%ld", buf, i); strcpy(buf, ", "); } fprintf(stderr, "\n"); } do { fprintf(stderr, "Specify next volume #: "); (void) fflush(stderr); (void) fgets(buf, BUFSIZ, terminal); } while (!feof(terminal) && buf[0] == '\n'); if (feof(terminal)) done(1); newvol = atoi(buf); if (newvol <= 0) { fprintf(stderr, "Volume numbers are positive numerics\n"); } } if (newvol == volno) { tapesread |= 1 << volno; return; } closemt(); fprintf(stderr, "Mount tape volume %ld\n", newvol); fprintf(stderr, "Enter ``none'' if there are no more tapes\n"); fprintf(stderr, "otherwise enter tape name (default: %s) ", magtape); (void) fflush(stderr); (void) fgets(buf, BUFSIZ, terminal); if (feof(terminal)) done(1); if (!strcmp(buf, "none\n")) { terminateinput(); return; } if (buf[0] != '\n') { (void) strcpy(magtape, buf); magtape[strlen(magtape) - 1] = '\0'; } #ifdef RRESTORE if (host) mt = rmtopen(magtape, 0); else #endif mt = open(magtape, O_RDONLY, 0); if (mt == -1) { fprintf(stderr, "Cannot open %s\n", magtape); volno = -1; goto again; } gethdr: volno = newvol; setdumpnum(); FLUSHTAPEBUF(); if (gethead(&tmpbuf) == FAIL) { dprintf(stdout, "header read failed at %ld blocks\n", blksread); fprintf(stderr, "tape is not dump tape\n"); volno = 0; goto again; } if (tmpbuf.c_volume != volno) { fprintf(stderr, "Wrong volume (%ld)\n", tmpbuf.c_volume); volno = 0; goto again; } if (tmpbuf.c_date != dumpdate || tmpbuf.c_ddate != dumptime) { fprintf(stderr, "Wrong dump date\n\tgot: %s", ctime(&tmpbuf.c_date)); fprintf(stderr, "\twanted: %s", ctime(&dumpdate)); volno = 0; goto again; } tapesread |= 1 << volno; blksread = savecnt; /* * If continuing from the previous volume, skip over any * blocks read already at the end of the previous volume. * * If coming to this volume at random, skip to the beginning * of the next record. */ dprintf(stdout, "read %ld recs, tape starts with %ld\n", tpblksread, tmpbuf.c_firstrec); if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER)) { if (!wantnext) { tpblksread = tmpbuf.c_firstrec; for (i = tmpbuf.c_count; i > 0; i--) readtape(buf); } else if (tmpbuf.c_firstrec > 0 && tmpbuf.c_firstrec < tpblksread - 1) { /* * -1 since we've read the volume header */ i = tpblksread - tmpbuf.c_firstrec - 1; dprintf(stderr, "Skipping %ld duplicate record%s.\n", i, i > 1 ? "s" : ""); while (--i >= 0) readtape(buf); } } if (curfile.action == USING) { if (volno == 1) panic("active file into volume 1\n"); return; } /* * Skip up to the beginning of the next record */ if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER)) for (i = tmpbuf.c_count; i > 0; i--) readtape(buf); (void) gethead(&spcl); findinode(&spcl); if (gettingfile) { gettingfile = 0; longjmp(restart, 1); } } /* * Handle unexpected EOF. */ static void terminateinput() { if (gettingfile && curfile.action == USING) { printf("Warning: %s %s\n", "End-of-input encountered while extracting", curfile.name); } curfile.name = ""; curfile.action = UNKNOWN; curfile.dip = NULL; curfile.ino = maxino; if (gettingfile) { gettingfile = 0; longjmp(restart, 1); } } /* * handle multiple dumps per tape by skipping forward to the * appropriate one. */ static void setdumpnum() { struct mtop tcom; if (dumpnum == 1 || volno != 1) return; if (pipein) { fprintf(stderr, "Cannot have multiple dumps on pipe input\n"); done(1); } tcom.mt_op = MTFSF; tcom.mt_count = dumpnum - 1; #ifdef RRESTORE if (host) rmtioctl(MTFSF, dumpnum - 1); else #endif if (ioctl(mt, (int)MTIOCTOP, (char *)&tcom) < 0) fprintf(stderr, "ioctl MTFSF: %s\n", strerror(errno)); } void printdumpinfo() { fprintf(stdout, "Dump date: %s", ctime(&spcl.c_date)); fprintf(stdout, "Dumped from: %s", (spcl.c_ddate == 0) ? "the epoch\n" : ctime(&spcl.c_ddate)); if (spcl.c_host[0] == '\0') return; fprintf(stderr, "Level %ld dump of %s on %s:%s\n", spcl.c_level, spcl.c_filesys, spcl.c_host, spcl.c_dev); fprintf(stderr, "Label: %s\n", spcl.c_label); } int extractfile(name) char *name; { int flags; mode_t mode; struct timeval timep[2]; struct entry *ep; curfile.name = name; curfile.action = USING; timep[0].tv_sec = curfile.dip->di_atime; timep[0].tv_usec = curfile.dip->di_atimensec / 1000; timep[1].tv_sec = curfile.dip->di_mtime; timep[1].tv_usec = curfile.dip->di_mtimensec / 1000; mode = curfile.dip->di_mode; flags = curfile.dip->di_flags; switch (mode & IFMT) { default: fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode); skipfile(); return (FAIL); case IFSOCK: vprintf(stdout, "skipped socket %s\n", name); skipfile(); return (GOOD); case IFDIR: if (mflag) { ep = lookupname(name); if (ep == NULL || ep->e_flags & EXTRACT) panic("unextracted directory %s\n", name); skipfile(); return (GOOD); } vprintf(stdout, "extract file %s\n", name); return (genliteraldir(name, curfile.ino)); case IFLNK: lnkbuf[0] = '\0'; pathlen = 0; getfile(xtrlnkfile, xtrlnkskip); if (pathlen == 0) { vprintf(stdout, "%s: zero length symbolic link (ignored)\n", name); return (GOOD); } return (linkit(lnkbuf, name, SYMLINK)); case IFIFO: vprintf(stdout, "extract fifo %s\n", name); if (Nflag) { skipfile(); return (GOOD); } if (uflag && !Nflag) (void)unlink(name); if (mkfifo(name, mode) < 0) { fprintf(stderr, "%s: cannot create fifo: %s\n", name, strerror(errno)); skipfile(); return (FAIL); } (void) chown(name, curfile.dip->di_uid, curfile.dip->di_gid); (void) chmod(name, mode); (void) chflags(name, flags); skipfile(); utimes(name, timep); return (GOOD); case IFCHR: case IFBLK: vprintf(stdout, "extract special file %s\n", name); if (Nflag) { skipfile(); return (GOOD); } if (uflag) (void)unlink(name); if (mknod(name, mode, (int)curfile.dip->di_rdev) < 0) { fprintf(stderr, "%s: cannot create special file: %s\n", name, strerror(errno)); skipfile(); return (FAIL); } (void) chown(name, curfile.dip->di_uid, curfile.dip->di_gid); (void) chmod(name, mode); (void) chflags(name, flags); skipfile(); utimes(name, timep); return (GOOD); case IFREG: vprintf(stdout, "extract file %s\n", name); if (Nflag) { skipfile(); return (GOOD); } if (uflag) (void)unlink(name); if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) { fprintf(stderr, "%s: cannot create file: %s\n", name, strerror(errno)); skipfile(); return (FAIL); } (void) fchown(ofile, curfile.dip->di_uid, curfile.dip->di_gid); (void) fchmod(ofile, mode); (void) fchflags(ofile, flags); getfile(xtrfile, xtrskip); (void) close(ofile); utimes(name, timep); return (GOOD); } /* NOTREACHED */ } /* * skip over bit maps on the tape */ void skipmaps() { while (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI) skipfile(); } /* * skip over a file on the tape */ void skipfile() { curfile.action = SKIP; getfile(xtrnull, xtrnull); } /* * Extract a file from the tape. * When an allocated block is found it is passed to the fill function; * when an unallocated block (hole) is found, a zeroed buffer is passed * to the skip function. */ void getfile(fill, skip) void (*fill) __P((char *, long)); void (*skip) __P((char *, long)); { register int i; int curblk = 0; quad_t size = spcl.c_dinode.di_size; static char clearedbuf[MAXBSIZE]; char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE]; char junk[TP_BSIZE]; if (spcl.c_type == TS_END) panic("ran off end of tape\n"); if (spcl.c_magic != NFS_MAGIC) panic("not at beginning of a file\n"); if (!gettingfile && setjmp(restart) != 0) return; gettingfile++; loop: for (i = 0; i < spcl.c_count; i++) { if (readmapflag || spcl.c_addr[i]) { readtape(&buf[curblk++][0]); if (curblk == fssize / TP_BSIZE) { (*fill)((char *)buf, (long)(size > TP_BSIZE ? fssize : (curblk - 1) * TP_BSIZE + size)); curblk = 0; } } else { if (curblk > 0) { (*fill)((char *)buf, (long)(size > TP_BSIZE ? curblk * TP_BSIZE : (curblk - 1) * TP_BSIZE + size)); curblk = 0; } (*skip)(clearedbuf, (long)(size > TP_BSIZE ? TP_BSIZE : size)); } if ((size -= TP_BSIZE) <= 0) { for (i++; i < spcl.c_count; i++) if (readmapflag || spcl.c_addr[i]) readtape(junk); break; } } if (gethead(&spcl) == GOOD && size > 0) { if (spcl.c_type == TS_ADDR) goto loop; dprintf(stdout, "Missing address (header) block for %s at %ld blocks\n", curfile.name, blksread); } if (curblk > 0) (*fill)((char *)buf, (long)((curblk * TP_BSIZE) + size)); findinode(&spcl); gettingfile = 0; } /* * Write out the next block of a file. */ static void xtrfile(buf, size) char *buf; long size; { if (Nflag) return; if (write(ofile, buf, (int) size) == -1) { fprintf(stderr, "write error extracting inode %d, name %s\nwrite: %s\n", curfile.ino, curfile.name, strerror(errno)); done(1); } } /* * Skip over a hole in a file. */ /* ARGSUSED */ static void xtrskip(buf, size) char *buf; long size; { if (lseek(ofile, size, SEEK_CUR) == -1) { fprintf(stderr, "seek error extracting inode %d, name %s\nlseek: %s\n", curfile.ino, curfile.name, strerror(errno)); done(1); } } /* * Collect the next block of a symbolic link. */ static void xtrlnkfile(buf, size) char *buf; long size; { pathlen += size; if (pathlen > MAXPATHLEN) { fprintf(stderr, "symbolic link name: %s->%s%s; too long %d\n", curfile.name, lnkbuf, buf, pathlen); done(1); } (void) strcat(lnkbuf, buf); } /* * Skip over a hole in a symbolic link (should never happen). */ /* ARGSUSED */ static void xtrlnkskip(buf, size) char *buf; long size; { fprintf(stderr, "unallocated block in symbolic link %s\n", curfile.name); done(1); } /* * Collect the next block of a bit map. */ static void xtrmap(buf, size) char *buf; long size; { memmove(map, buf, size); map += size; } /* * Skip over a hole in a bit map (should never happen). */ /* ARGSUSED */ static void xtrmapskip(buf, size) char *buf; long size; { panic("hole in map\n"); map += size; } /* * Noop, when an extraction function is not needed. */ /* ARGSUSED */ void xtrnull(buf, size) char *buf; long size; { return; } /* * Read TP_BSIZE blocks from the input. * Handle read errors, and end of media. */ static void readtape(buf) char *buf; { long rd, newvol, i; int cnt, seek_failed; if (blkcnt < numtrec) { memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], (long)TP_BSIZE); blksread++; tpblksread++; return; } for (i = 0; i < ntrec; i++) ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0; if (numtrec == 0) numtrec = ntrec; cnt = ntrec * TP_BSIZE; rd = 0; getmore: #ifdef RRESTORE if (host) i = rmtread(&tapebuf[rd], cnt); else #endif i = read(mt, &tapebuf[rd], cnt); /* * Check for mid-tape short read error. * If found, skip rest of buffer and start with the next. */ if (!pipein && numtrec < ntrec && i > 0) { dprintf(stdout, "mid-media short read error.\n"); numtrec = ntrec; } /* * Handle partial block read. */ if (pipein && i == 0 && rd > 0) i = rd; else if (i > 0 && i != ntrec * TP_BSIZE) { if (pipein) { rd += i; cnt -= i; if (cnt > 0) goto getmore; i = rd; } else { /* * Short read. Process the blocks read. */ if (i % TP_BSIZE != 0) vprintf(stdout, "partial block read: %ld should be %ld\n", i, ntrec * TP_BSIZE); numtrec = i / TP_BSIZE; } } /* * Handle read error. */ if (i < 0) { fprintf(stderr, "Tape read error while "); switch (curfile.action) { default: fprintf(stderr, "trying to set up tape\n"); break; case UNKNOWN: fprintf(stderr, "trying to resynchronize\n"); break; case USING: fprintf(stderr, "restoring %s\n", curfile.name); break; case SKIP: fprintf(stderr, "skipping over inode %d\n", curfile.ino); break; } if (!yflag && !reply("continue")) done(1); i = ntrec * TP_BSIZE; memset(tapebuf, 0, i); #ifdef RRESTORE if (host) seek_failed = (rmtseek(i, 1) < 0); else #endif seek_failed = (lseek(mt, i, SEEK_CUR) == (off_t)-1); if (seek_failed) { fprintf(stderr, "continuation failed: %s\n", strerror(errno)); done(1); } } /* * Handle end of tape. */ if (i == 0) { vprintf(stdout, "End-of-tape encountered\n"); if (!pipein) { newvol = volno + 1; volno = 0; numtrec = 0; getvol(newvol); readtape(buf); return; } if (rd % TP_BSIZE != 0) panic("partial block read: %d should be %d\n", rd, ntrec * TP_BSIZE); terminateinput(); memmove(&tapebuf[rd], &endoftapemark, (long)TP_BSIZE); } blkcnt = 0; memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], (long)TP_BSIZE); blksread++; tpblksread++; } static void findtapeblksize() { register long i; for (i = 0; i < ntrec; i++) ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0; blkcnt = 0; #ifdef RRESTORE if (host) i = rmtread(tapebuf, ntrec * TP_BSIZE); else #endif i = read(mt, tapebuf, ntrec * TP_BSIZE); if (i <= 0) { fprintf(stderr, "tape read error: %s\n", strerror(errno)); done(1); } if (i % TP_BSIZE != 0) { fprintf(stderr, "Tape block size (%ld) %s (%d)\n", i, "is not a multiple of dump block size", TP_BSIZE); done(1); } ntrec = i / TP_BSIZE; numtrec = ntrec; vprintf(stdout, "Tape block size is %ld\n", ntrec); } void closemt() { if (mt < 0) return; #ifdef RRESTORE if (host) rmtclose(); else #endif (void) close(mt); } /* * Read the next block from the tape. * Check to see if it is one of several vintage headers. * If it is an old style header, convert it to a new style header. * If it is not any valid header, return an error. */ static int gethead(buf) struct s_spcl *buf; { long i; union { quad_t qval; - long val[2]; + int32_t val[2]; } qcvt; union u_ospcl { char dummy[TP_BSIZE]; struct s_ospcl { - long c_type; - long c_date; - long c_ddate; - long c_volume; - long c_tapea; + int32_t c_type; + int32_t c_date; + int32_t c_ddate; + int32_t c_volume; + int32_t c_tapea; u_short c_inumber; - long c_magic; - long c_checksum; + int32_t c_magic; + int32_t c_checksum; struct odinode { unsigned short odi_mode; u_short odi_nlink; u_short odi_uid; u_short odi_gid; - long odi_size; - long odi_rdev; + int32_t odi_size; + int32_t odi_rdev; char odi_addr[36]; - long odi_atime; - long odi_mtime; - long odi_ctime; + int32_t odi_atime; + int32_t odi_mtime; + int32_t odi_ctime; } c_dinode; - long c_count; + int32_t c_count; char c_addr[256]; } s_ospcl; } u_ospcl; if (!cvtflag) { readtape((char *)buf); if (buf->c_magic != NFS_MAGIC) { if (swabl(buf->c_magic) != NFS_MAGIC) return (FAIL); if (!Bcvt) { vprintf(stdout, "Note: Doing Byte swapping\n"); Bcvt = 1; } } if (checksum((int *)buf) == FAIL) return (FAIL); if (Bcvt) { swabst((u_char *)"8l4s31l", (u_char *)buf); swabst((u_char *)"l",(u_char *) &buf->c_level); swabst((u_char *)"2l",(u_char *) &buf->c_flags); } goto good; } readtape((char *)(&u_ospcl.s_ospcl)); memset(buf, 0, (long)TP_BSIZE); buf->c_type = u_ospcl.s_ospcl.c_type; buf->c_date = u_ospcl.s_ospcl.c_date; buf->c_ddate = u_ospcl.s_ospcl.c_ddate; buf->c_volume = u_ospcl.s_ospcl.c_volume; buf->c_tapea = u_ospcl.s_ospcl.c_tapea; buf->c_inumber = u_ospcl.s_ospcl.c_inumber; buf->c_checksum = u_ospcl.s_ospcl.c_checksum; buf->c_magic = u_ospcl.s_ospcl.c_magic; buf->c_dinode.di_mode = u_ospcl.s_ospcl.c_dinode.odi_mode; buf->c_dinode.di_nlink = u_ospcl.s_ospcl.c_dinode.odi_nlink; buf->c_dinode.di_uid = u_ospcl.s_ospcl.c_dinode.odi_uid; buf->c_dinode.di_gid = u_ospcl.s_ospcl.c_dinode.odi_gid; buf->c_dinode.di_size = u_ospcl.s_ospcl.c_dinode.odi_size; buf->c_dinode.di_rdev = u_ospcl.s_ospcl.c_dinode.odi_rdev; buf->c_dinode.di_atime = u_ospcl.s_ospcl.c_dinode.odi_atime; buf->c_dinode.di_mtime = u_ospcl.s_ospcl.c_dinode.odi_mtime; buf->c_dinode.di_ctime = u_ospcl.s_ospcl.c_dinode.odi_ctime; buf->c_count = u_ospcl.s_ospcl.c_count; memmove(buf->c_addr, u_ospcl.s_ospcl.c_addr, (long)256); if (u_ospcl.s_ospcl.c_magic != OFS_MAGIC || checksum((int *)(&u_ospcl.s_ospcl)) == FAIL) return(FAIL); buf->c_magic = NFS_MAGIC; good: if ((buf->c_dinode.di_size == 0 || buf->c_dinode.di_size > 0xfffffff) && (buf->c_dinode.di_mode & IFMT) == IFDIR && Qcvt == 0) { qcvt.qval = buf->c_dinode.di_size; if (qcvt.val[0] || qcvt.val[1]) { printf("Note: Doing Quad swapping\n"); Qcvt = 1; } } if (Qcvt) { qcvt.qval = buf->c_dinode.di_size; i = qcvt.val[1]; qcvt.val[1] = qcvt.val[0]; qcvt.val[0] = i; buf->c_dinode.di_size = qcvt.qval; } readmapflag = 0; switch (buf->c_type) { case TS_CLRI: case TS_BITS: /* * Have to patch up missing information in bit map headers */ buf->c_inumber = 0; buf->c_dinode.di_size = buf->c_count * TP_BSIZE; if (buf->c_count > TP_NINDIR) readmapflag = 1; else for (i = 0; i < buf->c_count; i++) buf->c_addr[i]++; break; case TS_TAPE: if ((buf->c_flags & DR_NEWINODEFMT) == 0) oldinofmt = 1; /* fall through */ case TS_END: buf->c_inumber = 0; break; case TS_INODE: case TS_ADDR: break; default: panic("gethead: unknown inode type %d\n", buf->c_type); break; } /* * If we are restoring a filesystem with old format inodes, * copy the uid/gid to the new location. */ if (oldinofmt) { buf->c_dinode.di_uid = buf->c_dinode.di_ouid; buf->c_dinode.di_gid = buf->c_dinode.di_ogid; } if (dflag) accthdr(buf); return(GOOD); } /* * Check that a header is where it belongs and predict the next header */ static void accthdr(header) struct s_spcl *header; { static ino_t previno = 0x7fffffff; static int prevtype; static long predict; long blks, i; if (header->c_type == TS_TAPE) { fprintf(stderr, "Volume header (%s inode format) ", oldinofmt ? "old" : "new"); if (header->c_firstrec) fprintf(stderr, "begins with record %ld", header->c_firstrec); fprintf(stderr, "\n"); previno = 0x7fffffff; return; } if (previno == 0x7fffffff) goto newcalc; switch (prevtype) { case TS_BITS: fprintf(stderr, "Dumped inodes map header"); break; case TS_CLRI: fprintf(stderr, "Used inodes map header"); break; case TS_INODE: fprintf(stderr, "File header, ino %d", previno); break; case TS_ADDR: fprintf(stderr, "File continuation header, ino %d", previno); break; case TS_END: fprintf(stderr, "End of tape header"); break; } if (predict != blksread - 1) fprintf(stderr, "; predicted %ld blocks, got %ld blocks", predict, blksread - 1); fprintf(stderr, "\n"); newcalc: blks = 0; if (header->c_type != TS_END) for (i = 0; i < header->c_count; i++) if (readmapflag || header->c_addr[i] != 0) blks++; predict = blks; blksread = 0; prevtype = header->c_type; previno = header->c_inumber; } /* * Find an inode header. * Complain if had to skip, and complain is set. */ static void findinode(header) struct s_spcl *header; { static long skipcnt = 0; long i; char buf[TP_BSIZE]; curfile.name = ""; curfile.action = UNKNOWN; curfile.dip = NULL; curfile.ino = 0; do { if (header->c_magic != NFS_MAGIC) { skipcnt++; while (gethead(header) == FAIL || header->c_date != dumpdate) skipcnt++; } switch (header->c_type) { case TS_ADDR: /* * Skip up to the beginning of the next record */ for (i = 0; i < header->c_count; i++) if (header->c_addr[i]) readtape(buf); while (gethead(header) == FAIL || header->c_date != dumpdate) skipcnt++; break; case TS_INODE: curfile.dip = &header->c_dinode; curfile.ino = header->c_inumber; break; case TS_END: curfile.ino = maxino; break; case TS_CLRI: curfile.name = ""; break; case TS_BITS: curfile.name = ""; break; case TS_TAPE: panic("unexpected tape header\n"); /* NOTREACHED */ default: panic("unknown tape header type %d\n", spcl.c_type); /* NOTREACHED */ } } while (header->c_type == TS_ADDR); if (skipcnt > 0) fprintf(stderr, "resync restore, skipped %ld blocks\n", skipcnt); skipcnt = 0; } static int checksum(buf) register int *buf; { register int i, j; j = sizeof(union u_spcl) / sizeof(int); i = 0; if(!Bcvt) { do i += *buf++; while (--j); } else { /* What happens if we want to read restore tapes for a 16bit int machine??? */ do i += swabl(*buf++); while (--j); } if (i != CHECKSUM) { fprintf(stderr, "Checksum error %o, inode %d file %s\n", i, curfile.ino, curfile.name); return(FAIL); } return(GOOD); } #ifdef RRESTORE #if __STDC__ #include #else #include #endif void #if __STDC__ msg(const char *fmt, ...) #else msg(fmt, va_alist) char *fmt; va_dcl #endif { va_list ap; #if __STDC__ va_start(ap, fmt); #else va_start(ap); #endif (void)vfprintf(stderr, fmt, ap); va_end(ap); } #endif /* RRESTORE */ static u_char * swabshort(sp, n) register u_char *sp; register int n; { char c; while (--n >= 0) { c = sp[0]; sp[0] = sp[1]; sp[1] = c; sp += 2; } return (sp); } static u_char * swablong(sp, n) register u_char *sp; register int n; { char c; while (--n >= 0) { c = sp[0]; sp[0] = sp[3]; sp[3] = c; c = sp[2]; sp[2] = sp[1]; sp[1] = c; sp += 4; } return (sp); } void swabst(cp, sp) register u_char *cp, *sp; { int n = 0; while (*cp) { switch (*cp) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = (n * 10) + (*cp++ - '0'); continue; case 's': case 'w': case 'h': if (n == 0) n = 1; sp = swabshort(sp, n); break; case 'l': if (n == 0) n = 1; sp = swablong(sp, n); break; default: /* Any other character, like 'b' counts as byte. */ if (n == 0) n = 1; sp += n; break; } cp++; n = 0; } } static u_long swabl(x) u_long x; { swabst((u_char *)"l", (u_char *)&x); return (x); }