122041Sdist /* 234359Skarels * Copyright (c) 1980, 1988 Regents of the University of California. 322041Sdist * All rights reserved. The Berkeley software License Agreement 422041Sdist * specifies the terms and conditions for redistribution. 522041Sdist */ 65329Smckusic 722041Sdist #ifndef lint 8*46796Smckusick static char sccsid[] = "@(#)traverse.c 5.9 (Berkeley) 02/28/91"; 946584Storek #endif /* not lint */ 1022041Sdist 1146795Sbostic #include <sys/param.h> 1246795Sbostic #include <ufs/dir.h> 1346795Sbostic #include <ufs/dinode.h> 1446795Sbostic #include <ufs/fs.h> 1546795Sbostic #include <protocols/dumprestore.h> 1646795Sbostic #ifdef __STDC__ 1746795Sbostic #include <unistd.h> 1846795Sbostic #include <string.h> 1946795Sbostic #endif 201426Sroot #include "dump.h" 211426Sroot 2246792Smckusick void dmpindir(); 2346792Smckusick #define HASDUMPEDFILE 0x1 2446792Smckusick #define HASSUBDIRS 0x2 2546584Storek 2646584Storek /* 2746584Storek * This is an estimation of the number of TP_BSIZE blocks in the file. 2846584Storek * It estimates the number of blocks in files with holes by assuming 2946584Storek * that all of the blocks accounted for by di_blocks are data blocks 3046584Storek * (when some of the blocks are usually used for indirect pointers); 3146584Storek * hence the estimate may be high. 3246584Storek */ 3346792Smckusick long 3446792Smckusick blockest(ip) 3546584Storek struct dinode *ip; 3646584Storek { 3746792Smckusick long blkest, sizeest; 3846584Storek 3946584Storek /* 4046584Storek * ip->di_size is the size of the file in bytes. 4146584Storek * ip->di_blocks stores the number of sectors actually in the file. 4246584Storek * If there are more sectors than the size would indicate, this just 4346584Storek * means that there are indirect blocks in the file or unused 4446584Storek * sectors in the last file block; we can safely ignore these 4546792Smckusick * (blkest = sizeest below). 4646584Storek * If the file is bigger than the number of sectors would indicate, 4746584Storek * then the file has holes in it. In this case we must use the 4846584Storek * block count to estimate the number of data blocks used, but 4946584Storek * we use the actual size for estimating the number of indirect 5046792Smckusick * dump blocks (sizeest vs. blkest in the indirect block 5146792Smckusick * calculation). 5246584Storek */ 5346792Smckusick blkest = howmany(dbtob(ip->di_blocks), TP_BSIZE); 5446792Smckusick sizeest = howmany(ip->di_size, TP_BSIZE); 5546792Smckusick if (blkest > sizeest) 5646792Smckusick blkest = sizeest; 5746584Storek if (ip->di_size > sblock->fs_bsize * NDADDR) { 5846584Storek /* calculate the number of indirect blocks on the dump tape */ 5946792Smckusick blkest += 6046792Smckusick howmany(sizeest - NDADDR * sblock->fs_bsize / TP_BSIZE, 6146584Storek TP_NINDIR); 6246584Storek } 6346792Smckusick return (blkest + 1); 6446584Storek } 6546584Storek 6646792Smckusick /* 6746792Smckusick * Dump pass 1. 6846792Smckusick * 6946792Smckusick * Walk the inode list for a filesystem to find all allocated inodes 7046792Smckusick * that have been modified since the previous dump time. Also, find all 7146792Smckusick * the directories in the filesystem. 7246792Smckusick */ 7346792Smckusick mapfiles(maxino, tapesize) 7446792Smckusick ino_t maxino; 7546792Smckusick long *tapesize; 7646584Storek { 7746792Smckusick register int mode; 7846792Smckusick register ino_t ino; 7946792Smckusick register struct dinode *dp; 8046792Smckusick int anydirskipped = 0; 8146584Storek 8246792Smckusick for (ino = 0; ino < maxino; ino++) { 8346792Smckusick dp = getino(ino); 8446792Smckusick if ((mode = (dp->di_mode & IFMT)) == 0) 8546792Smckusick continue; 8646792Smckusick SETINO(ino, usedinomap); 8746792Smckusick if (mode == IFDIR) 8846792Smckusick SETINO(ino, dumpdirmap); 8946792Smckusick if (dp->di_mtime >= spcl.c_ddate || 9046792Smckusick dp->di_ctime >= spcl.c_ddate) { 9146792Smckusick SETINO(ino, dumpinomap); 9246792Smckusick if (mode != IFREG && mode != IFDIR && mode != IFLNK) { 9346792Smckusick *tapesize += 1; 9446792Smckusick continue; 9546792Smckusick } 9646792Smckusick *tapesize += blockest(dp); 9746792Smckusick continue; 9846792Smckusick } 9946792Smckusick if (mode == IFDIR) 10046792Smckusick anydirskipped = 1; 10146792Smckusick } 10246792Smckusick /* 10346792Smckusick * Restore gets very upset if the root is not dumped, 10446792Smckusick * so ensure that it always is dumped. 10546792Smckusick */ 10646792Smckusick SETINO(ROOTINO, usedinomap); 10746792Smckusick return (anydirskipped); 10846584Storek } 10946584Storek 11046792Smckusick /* 11146792Smckusick * Dump pass 2. 11246792Smckusick * 11346792Smckusick * Scan each directory on the filesystem to see if it has any modified 11446792Smckusick * files in it. If it does, and has not already been added to the dump 11546792Smckusick * list (because it was itself modified), then add it. If a directory 11646792Smckusick * has not been modified itself, contains no modified files and has no 11746792Smckusick * subdirectories, then it can be deleted from the dump list and from 11846792Smckusick * the list of directories. By deleting it from the list of directories, 11946792Smckusick * its parent may now qualify for the same treatment on this or a later 12046792Smckusick * pass using this algorithm. 12146792Smckusick */ 12246792Smckusick mapdirs(maxino, tapesize) 12346792Smckusick ino_t maxino; 12446792Smckusick long *tapesize; 12546792Smckusick { 12646792Smckusick register struct dinode *dp; 12746792Smckusick register int i, bits; 12825797Smckusick register char *map; 12946792Smckusick register ino_t ino; 13046792Smckusick long filesize, blkcnt = 0; 13146792Smckusick int ret, change = 0; 1321426Sroot 13346792Smckusick for (map = dumpdirmap, ino = 0; ino < maxino; ) { 13446584Storek if ((ino % NBBY) == 0) 13546792Smckusick bits = *map++; 13646792Smckusick else 13746792Smckusick bits >>= 1; 1384702Smckusic ino++; 13946792Smckusick if ((bits & 1) == 0 || TSTINO(ino, dumpinomap)) 14046792Smckusick continue; 14146792Smckusick dp = getino(ino); 14246792Smckusick filesize = dp->di_size; 14346792Smckusick for (ret = 0, i = 0; filesize > 0 && i < NDADDR; i++) { 14446792Smckusick if (dp->di_db[i] != 0) 14546792Smckusick ret |= searchdir(ino, dp->di_db[i], 146*46796Smckusick dblksize(sblock, dp, i), filesize); 14746792Smckusick if (ret & HASDUMPEDFILE) 14846792Smckusick filesize = 0; 14946792Smckusick else 15046792Smckusick filesize -= sblock->fs_bsize; 15146792Smckusick } 15246792Smckusick for (i = 0; filesize > 0 && i < NIADDR; i++) { 15346792Smckusick if (dp->di_ib[i] == 0) 15446792Smckusick continue; 15546792Smckusick ret |= dirindir(ino, dp->di_ib[i], i, &filesize); 15646792Smckusick } 15746792Smckusick if (ret & HASDUMPEDFILE) { 15846792Smckusick if (!TSTINO(ino, dumpinomap)) { 15946792Smckusick SETINO(ino, dumpinomap); 16046792Smckusick *tapesize += blockest(dp); 16146792Smckusick } 16246792Smckusick change = 1; 16346792Smckusick continue; 16446792Smckusick } 16546792Smckusick if ((ret & HASSUBDIRS) == 0) { 16646792Smckusick if (!TSTINO(ino, dumpinomap)) { 16746792Smckusick CLRINO(ino, dumpdirmap); 16846792Smckusick change = 1; 16946792Smckusick } 17046792Smckusick } 1711426Sroot } 17246792Smckusick return (change); 1731426Sroot } 1741426Sroot 17546792Smckusick /* 17646792Smckusick * Read indirect blocks, and pass the data blocks to be searched 17746792Smckusick * as directories. Quit as soon as any entry is found that will 17846792Smckusick * require the directory to be dumped. 17946792Smckusick */ 18046792Smckusick dirindir(ino, blkno, level, filesize) 18146792Smckusick ino_t ino; 18246792Smckusick daddr_t blkno; 18346792Smckusick int level, *filesize; 1841426Sroot { 18546792Smckusick int ret = 0; 18646792Smckusick register int i; 18746792Smckusick daddr_t idblk[MAXNINDIR]; 1881426Sroot 18946792Smckusick bread(fsbtodb(sblock, blkno), (char *)idblk, sblock->fs_bsize); 19046792Smckusick if (level <= 0) { 19146792Smckusick for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) { 19246792Smckusick blkno = idblk[i]; 19346792Smckusick if (blkno != 0) 194*46796Smckusick ret |= searchdir(ino, blkno, sblock->fs_bsize, 195*46796Smckusick filesize); 19646792Smckusick if (ret & HASDUMPEDFILE) 19746792Smckusick *filesize = 0; 19846792Smckusick else 19946792Smckusick *filesize -= sblock->fs_bsize; 2001426Sroot } 20146792Smckusick return (ret); 2025329Smckusic } 20346792Smckusick level--; 20446792Smckusick for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) { 20546792Smckusick blkno = idblk[i]; 20646792Smckusick if (blkno != 0) 20746792Smckusick ret |= dirindir(ino, blkno, level, filesize); 2085329Smckusic } 20946792Smckusick return (ret); 2101426Sroot } 2111426Sroot 21246792Smckusick /* 21346792Smckusick * Scan a disk block containing directory information looking to see if 21446792Smckusick * any of the entries are on the dump list and to see if the directory 21546792Smckusick * contains any subdirectories. 21646792Smckusick */ 217*46796Smckusick searchdir(ino, blkno, size, filesize) 21846792Smckusick ino_t ino; 21946792Smckusick daddr_t blkno; 22046792Smckusick register int size; 221*46796Smckusick int filesize; 2225329Smckusic { 22346792Smckusick register struct direct *dp; 22446792Smckusick register long loc; 22546792Smckusick char dblk[MAXBSIZE]; 2265329Smckusic 22746792Smckusick bread(fsbtodb(sblock, blkno), dblk, size); 228*46796Smckusick if (filesize < size) 229*46796Smckusick size = filesize; 23046792Smckusick for (loc = 0; loc < size; ) { 23146792Smckusick dp = (struct direct *)(dblk + loc); 23246792Smckusick if (dp->d_reclen == 0) { 23346792Smckusick msg("corrupted directory, inumber %d\n", ino); 23446792Smckusick break; 2355329Smckusic } 23646792Smckusick loc += dp->d_reclen; 23746792Smckusick if (dp->d_ino == 0) 23846792Smckusick continue; 23946792Smckusick if (dp->d_name[0] == '.') { 24046792Smckusick if (dp->d_name[1] == '\0') 24146792Smckusick continue; 24246792Smckusick if (dp->d_name[1] == '.' && dp->d_name[2] == '\0') 24346792Smckusick continue; 2445329Smckusic } 24546792Smckusick if (TSTINO(dp->d_ino, dumpinomap)) 24646792Smckusick return (HASDUMPEDFILE); 24746792Smckusick if (TSTINO(dp->d_ino, dumpdirmap)) 24846792Smckusick return (HASSUBDIRS); 2495329Smckusic } 25046792Smckusick return (0); 2515329Smckusic } 2525329Smckusic 25346792Smckusick /* 25446792Smckusick * Dump passes 3 and 4. 25546792Smckusick * 25646792Smckusick * Dump the contents of an inode to tape. 25746792Smckusick */ 25846584Storek void 25946792Smckusick dumpino(ip, ino) 26017234Smckusick struct dinode *ip; 26146792Smckusick ino_t ino; 26217234Smckusick { 26346792Smckusick int mode, level, cnt; 2644777Smckusic long size; 2651426Sroot 26646792Smckusick if (newtape) { 2671426Sroot newtape = 0; 26846792Smckusick dumpmap(dumpinomap, TS_BITS, ino); 2691426Sroot } 27046792Smckusick CLRINO(ino, dumpinomap); 2711426Sroot spcl.c_dinode = *ip; 2721426Sroot spcl.c_type = TS_INODE; 2731426Sroot spcl.c_count = 0; 27446792Smckusick /* 27546792Smckusick * Check for freed inode. 27646792Smckusick */ 27746792Smckusick if ((mode = (ip->di_mode & IFMT)) == 0) 27817234Smckusick return; 27946792Smckusick if ((mode != IFDIR && mode != IFREG && mode != IFLNK) || 28046792Smckusick ip->di_size == 0) { 28146792Smckusick writeheader(ino); 2821426Sroot return; 2831426Sroot } 2845329Smckusic if (ip->di_size > NDADDR * sblock->fs_bsize) 28546792Smckusick cnt = NDADDR * sblock->fs_frag; 2864777Smckusic else 28746792Smckusick cnt = howmany(ip->di_size, sblock->fs_fsize); 28846792Smckusick blksout(&ip->di_db[0], cnt, ino); 28946792Smckusick if ((size = ip->di_size - NDADDR * sblock->fs_bsize) <= 0) 2904777Smckusic return; 29146792Smckusick for (level = 0; level < NIADDR; level++) { 29246792Smckusick dmpindir(ino, ip->di_ib[level], level, &size); 2934777Smckusic if (size <= 0) 2944777Smckusic return; 2954777Smckusic } 2961426Sroot } 2971426Sroot 29846792Smckusick /* 29946792Smckusick * Read indirect blocks, and pass the data blocks to be dumped. 30046792Smckusick */ 30146584Storek void 30246792Smckusick dmpindir(ino, blk, level, size) 30346792Smckusick ino_t ino; 3044777Smckusic daddr_t blk; 30546792Smckusick int level; 3064777Smckusic long *size; 3071426Sroot { 3084777Smckusic int i, cnt; 3095329Smckusic daddr_t idblk[MAXNINDIR]; 3101426Sroot 3114777Smckusic if (blk != 0) 3125329Smckusic bread(fsbtodb(sblock, blk), (char *)idblk, sblock->fs_bsize); 3134777Smckusic else 31446584Storek bzero((char *)idblk, sblock->fs_bsize); 31546792Smckusick if (level <= 0) { 3165329Smckusic if (*size < NINDIR(sblock) * sblock->fs_bsize) 3175329Smckusic cnt = howmany(*size, sblock->fs_fsize); 3184777Smckusic else 3195329Smckusic cnt = NINDIR(sblock) * sblock->fs_frag; 3205329Smckusic *size -= NINDIR(sblock) * sblock->fs_bsize; 32146792Smckusick blksout(&idblk[0], cnt, ino); 3224777Smckusic return; 3231426Sroot } 32446792Smckusick level--; 3255329Smckusic for (i = 0; i < NINDIR(sblock); i++) { 32646792Smckusick dmpindir(ino, idblk[i], level, size); 3274777Smckusic if (*size <= 0) 3284777Smckusic return; 3294777Smckusic } 3301426Sroot } 3311426Sroot 33246792Smckusick /* 33346792Smckusick * Collect up the data into tape record sized buffers and output them. 33446792Smckusick */ 33546584Storek void 33646792Smckusick blksout(blkp, frags, ino) 3374777Smckusic daddr_t *blkp; 3384777Smckusic int frags; 33946792Smckusick ino_t ino; 3404777Smckusic { 34146584Storek register daddr_t *bp; 3425329Smckusic int i, j, count, blks, tbperdb; 3434777Smckusic 3449403Smckusick blks = howmany(frags * sblock->fs_fsize, TP_BSIZE); 34546584Storek tbperdb = sblock->fs_bsize >> tp_bshift; 3464777Smckusic for (i = 0; i < blks; i += TP_NINDIR) { 3474777Smckusic if (i + TP_NINDIR > blks) 3484777Smckusic count = blks; 3494777Smckusic else 3504777Smckusic count = i + TP_NINDIR; 3514777Smckusic for (j = i; j < count; j++) 3525329Smckusic if (blkp[j / tbperdb] != 0) 3534777Smckusic spcl.c_addr[j - i] = 1; 3544777Smckusic else 3554777Smckusic spcl.c_addr[j - i] = 0; 3564777Smckusic spcl.c_count = count - i; 35746792Smckusick writeheader(ino); 35846584Storek bp = &blkp[i / tbperdb]; 35946584Storek for (j = i; j < count; j += tbperdb, bp++) 36046584Storek if (*bp != 0) 3615329Smckusic if (j + tbperdb <= count) 36246792Smckusick dumpblock(*bp, sblock->fs_bsize); 3634777Smckusic else 36446792Smckusick dumpblock(*bp, (count - j) * TP_BSIZE); 3654777Smckusic spcl.c_type = TS_ADDR; 3664777Smckusic } 3674777Smckusic } 3684777Smckusic 36946792Smckusick /* 37046792Smckusick * Dump a map to the tape. 37146792Smckusick */ 37246584Storek void 37346792Smckusick dumpmap(map, type, ino) 3745329Smckusic char *map; 37546792Smckusick int type; 37646792Smckusick ino_t ino; 3771426Sroot { 37846792Smckusick register int i; 3791426Sroot char *cp; 3801426Sroot 38146792Smckusick spcl.c_type = type; 38246792Smckusick spcl.c_count = howmany(mapsize * sizeof(char), TP_BSIZE); 38346792Smckusick writeheader(ino); 3845329Smckusic for (i = 0, cp = map; i < spcl.c_count; i++, cp += TP_BSIZE) 38546792Smckusick writerec(cp); 3861426Sroot } 3871426Sroot 38846792Smckusick /* 38946792Smckusick * Write a header record to the dump tape. 39046792Smckusick */ 39146584Storek void 39246792Smckusick writeheader(ino) 39346792Smckusick ino_t ino; 3941426Sroot { 39546792Smckusick register long sum, cnt, *lp; 3961426Sroot 3971426Sroot spcl.c_inumber = ino; 3988368Smckusick spcl.c_magic = NFS_MAGIC; 3991426Sroot spcl.c_checksum = 0; 40046792Smckusick lp = (long *)&spcl; 40146792Smckusick sum = 0; 40246792Smckusick cnt = sizeof(union u_spcl) / (4 * sizeof(long)); 40346792Smckusick while (--cnt >= 0) { 40446792Smckusick sum += *lp++; 40546792Smckusick sum += *lp++; 40646792Smckusick sum += *lp++; 40746792Smckusick sum += *lp++; 40824168Smckusick } 40946792Smckusick spcl.c_checksum = CHECKSUM - sum; 41046792Smckusick writerec((char *)&spcl); 4111426Sroot } 4121426Sroot 4134702Smckusic struct dinode * 41446792Smckusick getino(inum) 41546792Smckusick ino_t inum; 4164702Smckusic { 4174702Smckusic static daddr_t minino, maxino; 41846792Smckusick static struct dinode inoblock[MAXINOPB]; 4194702Smckusic 42046792Smckusick curino = inum; 42146792Smckusick if (inum >= minino && inum < maxino) 42246792Smckusick return (&inoblock[inum - minino]); 42346792Smckusick bread(fsbtodb(sblock, itod(sblock, inum)), inoblock, sblock->fs_bsize); 42446792Smckusick minino = inum - (inum % INOPB(sblock)); 4255329Smckusic maxino = minino + INOPB(sblock); 42646792Smckusick return (&inoblock[inum - minino]); 4274702Smckusic } 4284702Smckusic 42946792Smckusick /* 43046792Smckusick * Read a chunk of data from the disk. 43146792Smckusick * Try to recover from hard errors by reading in sector sized pieces. 43246792Smckusick * Error recovery is attempted at most BREADEMAX times before seeking 43346792Smckusick * consent from the operator to continue. 43446792Smckusick */ 4351426Sroot int breaderrors = 0; 4361426Sroot #define BREADEMAX 32 4371426Sroot 43846584Storek void 43946792Smckusick bread(blkno, buf, size) 44046792Smckusick daddr_t blkno; 44146792Smckusick char *buf; 44246792Smckusick int size; 4431426Sroot { 44446792Smckusick int cnt, i; 44534359Skarels extern int errno; 4461426Sroot 44715095Smckusick loop: 44846792Smckusick if (lseek(diskfd, (long)(blkno << dev_bshift), 0) < 0) 4491426Sroot msg("bread: lseek fails\n"); 45046792Smckusick if ((cnt = read(diskfd, buf, size)) == size) 45115095Smckusick return; 45246792Smckusick if (blkno + (size / dev_bsize) > fsbtodb(sblock, sblock->fs_size)) { 45315095Smckusick /* 45415095Smckusick * Trying to read the final fragment. 45515095Smckusick * 45615095Smckusick * NB - dump only works in TP_BSIZE blocks, hence 45730560Smckusick * rounds `dev_bsize' fragments up to TP_BSIZE pieces. 45815095Smckusick * It should be smarter about not actually trying to 45915095Smckusick * read more than it can get, but for the time being 46015095Smckusick * we punt and scale back the read only when it gets 46115095Smckusick * us into trouble. (mkm 9/25/83) 46215095Smckusick */ 46346792Smckusick size -= dev_bsize; 46415095Smckusick goto loop; 4651426Sroot } 46646584Storek msg("read error from %s [block %d]: count=%d, got=%d, errno=%d (%s)\n", 46746792Smckusick disk, blkno, size, cnt, errno, strerror(errno)); 46846584Storek if (++breaderrors > BREADEMAX) { 46915095Smckusick msg("More than %d block read errors from %d\n", 47015095Smckusick BREADEMAX, disk); 47115095Smckusick broadcast("DUMP IS AILING!\n"); 47215095Smckusick msg("This is an unrecoverable error.\n"); 47315095Smckusick if (!query("Do you want to attempt to continue?")){ 47415095Smckusick dumpabort(); 47515095Smckusick /*NOTREACHED*/ 47615095Smckusick } else 47715095Smckusick breaderrors = 0; 47815095Smckusick } 47934359Skarels /* 48034359Skarels * Zero buffer, then try to read each sector of buffer separately. 48134359Skarels */ 48246792Smckusick bzero(buf, size); 48346792Smckusick for (i = 0; i < size; i += dev_bsize, buf += dev_bsize, blkno++) { 48446792Smckusick if (lseek(diskfd, (long)(blkno << dev_bshift), 0) < 0) 48534359Skarels msg("bread: lseek2 fails!\n"); 48646792Smckusick if ((cnt = read(diskfd, buf, dev_bsize)) != dev_bsize) 48734359Skarels msg(" read error from %s [sector %d, errno %d]\n", 48846792Smckusick disk, blkno, errno); 48934359Skarels } 4901426Sroot } 491