add defines to provide compatibility with 4.3BSD-Tahoe systems
[unix-history] / usr / src / sbin / dump / traverse.c
index 4dea6b8..b20847a 100644 (file)
-static char *sccsid = "@(#)traverse.c  1.3 (Berkeley) %G%";
+/*
+ * Copyright (c) 1980, 1988 Regents of the University of California.
+ * All rights reserved.  The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)traverse.c 5.9 (Berkeley) %G%";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <ufs/dir.h>
+#include <ufs/dinode.h>
+#include <ufs/fs.h>
+#include <protocols/dumprestore.h>
+#ifdef __STDC__
+#include <unistd.h>
+#include <string.h>
+#endif
 #include "dump.h"
 
 #include "dump.h"
 
-struct fs      sblock;
+void   dmpindir();
+#define        HASDUMPEDFILE   0x1
+#define        HASSUBDIRS      0x2
 
 
-pass(fn, map)
-       int (*fn)();
-       short *map;
+/*
+ * 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(ip)
+       struct dinode *ip;
 {
 {
-       struct dinode *dp;
-       int bits;
-       ino_t maxino;
+       long blkest, sizeest;
 
 
-       sync();
-       bread(SBLOCK, &sblock, sizeof sblock);
-       if (sblock.fs_magic != FS_MAGIC) {
-               msg("bad sblock magic number\n");
-               dumpabort();
-       }
-       maxino = sblock.fs_ipg * sblock.fs_ncg;
-       for (ino = 0; ino < maxino; ) {
-               if((ino % MLEN) == 0) {
-                       bits = ~0;
-                       if(map != NULL)
-                               bits = *map++;
-               }
-               ino++;
-               if(bits & 1) {
-                       dp = getino(ino);
-                       (*fn)(dp);
-               }
-               bits >>= 1;
+       /*
+        * ip->di_size is the size of the file in bytes.
+        * ip->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(ip->di_blocks), TP_BSIZE);
+       sizeest = howmany(ip->di_size, TP_BSIZE);
+       if (blkest > sizeest)
+               blkest = sizeest;
+       if (ip->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);
 }
 
 }
 
-icat(dp, fn1, fn2)
-       register struct dinode  *dp;
-       int (*fn1)(), (*fn2)();
+/*
+ * 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.
+ */
+mapfiles(maxino, tapesize)
+       ino_t maxino;
+       long *tapesize;
 {
 {
-       register int i;
+       register int mode;
+       register ino_t ino;
+       register struct dinode *dp;
+       int anydirskipped = 0;
 
 
-       (*fn2)(dp->di_db, NDADDR);
-       for (i = 0; i < NDADDR; i++) {
-               if (dp->di_db[i] != 0)
-                       (*fn1)(dp->di_db[i]);
-       }
-       for (i = 0; i < NIADDR; i++) {
-               if (dp->di_ib[i] != 0)
-                       indir(dp->di_ib[i], fn1, fn2, i);
+       for (ino = 0; ino < maxino; ino++) {
+               dp = getino(ino);
+               if ((mode = (dp->di_mode & IFMT)) == 0)
+                       continue;
+               SETINO(ino, usedinomap);
+               if (mode == IFDIR)
+                       SETINO(ino, dumpdirmap);
+               if (dp->di_mtime >= spcl.c_ddate ||
+                   dp->di_ctime >= spcl.c_ddate) {
+                       SETINO(ino, dumpinomap);
+                       if (mode != IFREG && mode != IFDIR && mode != IFLNK) {
+                               *tapesize += 1;
+                               continue;
+                       }
+                       *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, usedinomap);
+       return (anydirskipped);
 }
 
 }
 
-indir(d, fn1, fn2, n)
-daddr_t d;
-int (*fn1)(), (*fn2)();
+/*
+ * 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.
+ */
+mapdirs(maxino, tapesize)
+       ino_t maxino;
+       long *tapesize;
 {
 {
-       register i;
-       daddr_t idblk[NINDIR];
+       register struct dinode *dp;
+       register int i, bits;
+       register char *map;
+       register ino_t ino;
+       long filesize, blkcnt = 0;
+       int ret, change = 0;
 
 
-       bread(d, (char *)idblk, sizeof(idblk));
-       if(n <= 0) {
-               spcl.c_type = TS_ADDR;
-               (*fn2)(idblk, NINDIR);
-               for(i=0; i<NINDIR; i++) {
-                       d = idblk[i];
-                       if(d != 0)
-                               (*fn1)(d);
+       for (map = dumpdirmap, ino = 0; ino < maxino; ) {
+               if ((ino % NBBY) == 0)
+                       bits = *map++;
+               else
+                       bits >>= 1;
+               ino++;
+               if ((bits & 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],
+                                       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) {
+                       if (!TSTINO(ino, dumpinomap)) {
+                               SETINO(ino, dumpinomap);
+                               *tapesize += blockest(dp);
+                       }
+                       change = 1;
+                       continue;
                }
                }
-       } else {
-               n--;
-               for(i=0; i<NINDIR; i++) {
-                       d = idblk[i];
-                       if(d != 0)
-                               indir(d, fn1, fn2, n);
+               if ((ret & HASSUBDIRS) == 0) {
+                       if (!TSTINO(ino, dumpinomap)) {
+                               CLRINO(ino, dumpdirmap);
+                               change = 1;
+                       }
                }
        }
                }
        }
+       return (change);
 }
 
 }
 
-mark(ip)
-struct dinode *ip;
+/*
+ * 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.
+ */
+dirindir(ino, blkno, level, filesize)
+       ino_t ino;
+       daddr_t blkno;
+       int level, *filesize;
 {
 {
-       register f;
+       int ret = 0;
+       register int i;
+       daddr_t idblk[MAXNINDIR];
 
 
-       f = ip->di_mode & IFMT;
-       if(f == 0)
-               return;
-       BIS(ino, clrmap);
-       if(f == IFDIR)
-               BIS(ino, dirmap);
-       if(ip->di_mtime >= spcl.c_ddate ||
-          ip->di_ctime >= spcl.c_ddate) {
-               BIS(ino, nodmap);
-               if (f != IFREG){
-                       esize += 1;
-                       return;
+       bread(fsbtodb(sblock, blkno), (char *)idblk, sblock->fs_bsize);
+       if (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;
                }
                }
-               est(ip);
+               return (ret);
+       }
+       level--;
+       for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
+               blkno = idblk[i];
+               if (blkno != 0)
+                       ret |= dirindir(ino, blkno, level, filesize);
        }
        }
+       return (ret);
 }
 
 }
 
-add(ip)
-struct dinode *ip;
+/*
+ * 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.
+ */
+searchdir(ino, blkno, size, filesize)
+       ino_t ino;
+       daddr_t blkno;
+       register int size;
+       int filesize;
 {
 {
+       register struct direct *dp;
+       register long loc;
+       char dblk[MAXBSIZE];
 
 
-       if(BIT(ino, nodmap))
-               return;
-       nsubdir = 0;
-       dadded = 0;
-       icat(ip, dsrch, nullf);
-       if(dadded) {
-               BIS(ino, nodmap);
-               est(ip);
-               nadded++;
+       bread(fsbtodb(sblock, blkno), dblk, 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))
+                       return (HASDUMPEDFILE);
+               if (TSTINO(dp->d_ino, dumpdirmap))
+                       return (HASSUBDIRS);
        }
        }
-       if(nsubdir == 0)
-               if(!BIT(ino, nodmap))
-                       BIC(ino, dirmap);
+       return (0);
 }
 
 }
 
-dump(ip)
-struct dinode *ip;
+/*
+ * Dump passes 3 and 4.
+ *
+ * Dump the contents of an inode to tape.
+ */
+void
+dumpino(ip, ino)
+       struct dinode *ip;
+       ino_t ino;
 {
 {
-       register i;
+       int mode, level, cnt;
+       long size;
 
 
-       if(newtape) {
+       if (newtape) {
                newtape = 0;
                newtape = 0;
-               bitmap(nodmap, TS_BITS);
+               dumpmap(dumpinomap, TS_BITS, ino);
        }
        }
-       BIC(ino, nodmap);
+       CLRINO(ino, dumpinomap);
        spcl.c_dinode = *ip;
        spcl.c_type = TS_INODE;
        spcl.c_count = 0;
        spcl.c_dinode = *ip;
        spcl.c_type = TS_INODE;
        spcl.c_count = 0;
-       i = ip->di_mode & IFMT;
-       if(i != IFDIR && i != IFREG) {
-               spclrec();
+       /*
+        * Check for freed inode.
+        */
+       if ((mode = (ip->di_mode & IFMT)) == 0)
+               return;
+       if ((mode != IFDIR && mode != IFREG && mode != IFLNK) ||
+           ip->di_size == 0) {
+               writeheader(ino);
                return;
        }
                return;
        }
-       icat(ip, tapsrec, dmpspc);
+       if (ip->di_size > NDADDR * sblock->fs_bsize)
+               cnt = NDADDR * sblock->fs_frag;
+       else
+               cnt = howmany(ip->di_size, sblock->fs_fsize);
+       blksout(&ip->di_db[0], cnt, ino);
+       if ((size = ip->di_size - NDADDR * sblock->fs_bsize) <= 0)
+               return;
+       for (level = 0; level < NIADDR; level++) {
+               dmpindir(ino, ip->di_ib[level], level, &size);
+               if (size <= 0)
+                       return;
+       }
 }
 
 }
 
-dmpspc(dp, n)
-daddr_t *dp;
+/*
+ * Read indirect blocks, and pass the data blocks to be dumped.
+ */
+void
+dmpindir(ino, blk, level, size)
+       ino_t ino;
+       daddr_t blk;
+       int level;
+       long *size;
 {
 {
-       register i, t;
+       int i, cnt;
+       daddr_t idblk[MAXNINDIR];
 
 
-       spcl.c_count = n;
-       for(i=0; i<n; i++) {
-               t = 0;
-               if(dp[i] != 0)
-                       t++;
-               spcl.c_addr[i] = t;
+       if (blk != 0)
+               bread(fsbtodb(sblock, blk), (char *)idblk, sblock->fs_bsize);
+       else
+               bzero((char *)idblk, sblock->fs_bsize);
+       if (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;
+       }
+       level--;
+       for (i = 0; i < NINDIR(sblock); i++) {
+               dmpindir(ino, idblk[i], level, size);
+               if (*size <= 0)
+                       return;
        }
        }
-       spclrec();
 }
 
 }
 
-bitmap(map, typ)
-short *map;
+/*
+ * 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 i, n;
-       char *cp;
+       register daddr_t *bp;
+       int i, j, count, blks, tbperdb;
 
 
-       n = -1;
-       for(i=0; i<MSIZ; i++)
-               if(map[i])
-                       n = i;
-       if(n < 0)
-               return;
-       spcl.c_type = typ;
-       spcl.c_count = (n*sizeof(map[0]) + BSIZE)/BSIZE;
-       spclrec();
-       cp = (char *)map;
-       for(i=0; i<spcl.c_count; i++) {
-               taprec(cp);
-               cp += BSIZE;
+       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, sblock->fs_bsize);
+                               else
+                                       dumpblock(*bp, (count - j) * TP_BSIZE);
+               spcl.c_type = TS_ADDR;
        }
 }
 
        }
 }
 
-spclrec()
+/*
+ * Dump a map to the tape.
+ */
+void
+dumpmap(map, type, ino)
+       char *map;
+       int type;
+       ino_t ino;
 {
 {
-       register i, *ip, s;
+       register int i;
+       char *cp;
 
 
-       spcl.c_inumber = ino;
-       spcl.c_magic = MAGIC;
-       spcl.c_checksum = 0;
-       ip = (int *)&spcl;
-       s = 0;
-       for(i=0; i<BSIZE/sizeof(*ip); i++)
-               s += *ip++;
-       spcl.c_checksum = CHECKSUM - s;
-       taprec((char *)&spcl);
+       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);
 }
 
 }
 
-dsrch(d)
-daddr_t d;
+/*
+ * Write a header record to the dump tape.
+ */
+void
+writeheader(ino)
+       ino_t ino;
 {
 {
-       register char *cp;
-       register i;
-       register ino_t in;
-       struct direct dblk[DIRPB];
+       register long sum, cnt, *lp;
 
 
-       if(dadded)
-               return;
-       bread(d, (char *)dblk, sizeof(dblk));
-       for(i=0; i<DIRPB; i++) {
-               in = dblk[i].d_ino;
-               if(in == 0)
-                       continue;
-               cp = dblk[i].d_name;
-               if(cp[0] == '.') {
-                       if(cp[1] == '\0')
-                               continue;
-                       if(cp[1] == '.' && cp[2] == '\0')
-                               continue;
-               }
-               if(BIT(in, nodmap)) {
-                       dadded++;
-                       return;
-               }
-               if(BIT(in, dirmap))
-                       nsubdir++;
+       spcl.c_inumber = ino;
+       spcl.c_magic = NFS_MAGIC;
+       spcl.c_checksum = 0;
+       lp = (long *)&spcl;
+       sum = 0;
+       cnt = sizeof(union u_spcl) / (4 * sizeof(long));
+       while (--cnt >= 0) {
+               sum += *lp++;
+               sum += *lp++;
+               sum += *lp++;
+               sum += *lp++;
        }
        }
-}
-
-nullf()
-{
+       spcl.c_checksum = CHECKSUM - sum;
+       writerec((char *)&spcl);
 }
 
 struct dinode *
 }
 
 struct dinode *
-getino(ino)
-       daddr_t ino;
+getino(inum)
+       ino_t inum;
 {
        static daddr_t minino, maxino;
 {
        static daddr_t minino, maxino;
-       static struct dinode itab[INOPB];
+       static struct dinode inoblock[MAXINOPB];
 
 
-       if (ino >= minino && ino < maxino) {
-               return (&itab[ino - minino]);
-       }
-       bread(itod(ino, &sblock), itab, BSIZE);
-       minino = ino - (ino % INOPB);
-       maxino = minino + INOPB;
-       return (&itab[ino - minino]);
+       curino = inum;
+       if (inum >= minino && inum < maxino)
+               return (&inoblock[inum - minino]);
+       bread(fsbtodb(sblock, itod(sblock, inum)), inoblock, 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
 
 int    breaderrors = 0;                
 #define        BREADEMAX 32
 
-bread(da, ba, c)
-       daddr_t da;
-       char *ba;
-       int     c;      
+void
+bread(blkno, buf, size)
+       daddr_t blkno;
+       char *buf;
+       int size;       
 {
 {
-       register n;
-       register        regc;
+       int cnt, i;
+       extern int errno;
 
 
-       if (lseek(fi, (long)(da*FSIZE), 0) < 0){
+loop:
+       if (lseek(diskfd, (long)(blkno << dev_bshift), 0) < 0)
                msg("bread: lseek fails\n");
                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;
        }
        }
-       regc = c;       /* put c someplace safe; it gets clobbered */
-       n = read(fi, ba, c);
-       if(n != c || regc != c){
-               msg("(This should not happen)bread from %s [block %d]: c=0x%x, regc=0x%x, &c=0x%x, n=0x%x\n",
-                       disk, da, c, regc, &c, n);
-#ifdef ERNIE
-               msg("Notify Robert Henry of this error.\n");
-#endif
-               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();
-                               /*NOTREACHED*/
-                       } else
-                               breaderrors = 0;
-               }
+       msg("read error from %s [block %d]: count=%d, got=%d, errno=%d (%s)\n",
+               disk, blkno, size, cnt, errno, strerror(errno));
+       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();
+                       /*NOTREACHED*/
+               } else
+                       breaderrors = 0;
+       }
+       /*
+        * Zero buffer, then try to read each sector of buffer separately.
+        */
+       bzero(buf, size);
+       for (i = 0; i < size; i += dev_bsize, buf += dev_bsize, blkno++) {
+               if (lseek(diskfd, (long)(blkno << dev_bshift), 0) < 0)
+                       msg("bread: lseek2 fails!\n");
+               if ((cnt = read(diskfd, buf, dev_bsize)) != dev_bsize)
+                       msg("    read error from %s [sector %d, errno %d]\n",
+                           disk, blkno, errno);
        }
 }
        }
 }
-
-CLR(map)
-register short *map;
-{
-       register n;
-
-       n = MSIZ;
-       do
-               *map++ = 0;
-       while(--n);
-}
-