1*47082Smckusick /*- 2*47082Smckusick * Copyright (c) 1980, 1988, 1991 The Regents of the University of California. 3*47082Smckusick * All rights reserved. 4*47082Smckusick * 5*47082Smckusick * %sccs.include.redist.c% 622041Sdist */ 75329Smckusic 822041Sdist #ifndef lint 9*47082Smckusick static char sccsid[] = "@(#)traverse.c 5.11 (Berkeley) 03/07/91"; 1046584Storek #endif /* not lint */ 1122041Sdist 1246795Sbostic #include <sys/param.h> 1346795Sbostic #include <ufs/dir.h> 1446795Sbostic #include <ufs/dinode.h> 1546795Sbostic #include <ufs/fs.h> 1646795Sbostic #include <protocols/dumprestore.h> 1746795Sbostic #ifdef __STDC__ 1846795Sbostic #include <unistd.h> 1946795Sbostic #include <string.h> 2046795Sbostic #endif 211426Sroot #include "dump.h" 221426Sroot 2346792Smckusick void dmpindir(); 2446792Smckusick #define HASDUMPEDFILE 0x1 2546792Smckusick #define HASSUBDIRS 0x2 2646584Storek 2746584Storek /* 2846584Storek * This is an estimation of the number of TP_BSIZE blocks in the file. 2946584Storek * It estimates the number of blocks in files with holes by assuming 3046584Storek * that all of the blocks accounted for by di_blocks are data blocks 3146584Storek * (when some of the blocks are usually used for indirect pointers); 3246584Storek * hence the estimate may be high. 3346584Storek */ 3446792Smckusick long 3547058Smckusick blockest(dp) 3647058Smckusick register struct dinode *dp; 3746584Storek { 3846792Smckusick long blkest, sizeest; 3946584Storek 4046584Storek /* 4147058Smckusick * dp->di_size is the size of the file in bytes. 4247058Smckusick * dp->di_blocks stores the number of sectors actually in the file. 4346584Storek * If there are more sectors than the size would indicate, this just 4446584Storek * means that there are indirect blocks in the file or unused 4546584Storek * sectors in the last file block; we can safely ignore these 4646792Smckusick * (blkest = sizeest below). 4746584Storek * If the file is bigger than the number of sectors would indicate, 4846584Storek * then the file has holes in it. In this case we must use the 4946584Storek * block count to estimate the number of data blocks used, but 5046584Storek * we use the actual size for estimating the number of indirect 5146792Smckusick * dump blocks (sizeest vs. blkest in the indirect block 5246792Smckusick * calculation). 5346584Storek */ 5447058Smckusick blkest = howmany(dbtob(dp->di_blocks), TP_BSIZE); 5547058Smckusick sizeest = howmany(dp->di_size, TP_BSIZE); 5646792Smckusick if (blkest > sizeest) 5746792Smckusick blkest = sizeest; 5847058Smckusick if (dp->di_size > sblock->fs_bsize * NDADDR) { 5946584Storek /* calculate the number of indirect blocks on the dump tape */ 6046792Smckusick blkest += 6146792Smckusick howmany(sizeest - NDADDR * sblock->fs_bsize / TP_BSIZE, 6246584Storek TP_NINDIR); 6346584Storek } 6446792Smckusick return (blkest + 1); 6546584Storek } 6646584Storek 6746792Smckusick /* 6846792Smckusick * Dump pass 1. 6946792Smckusick * 7046792Smckusick * Walk the inode list for a filesystem to find all allocated inodes 7146792Smckusick * that have been modified since the previous dump time. Also, find all 7246792Smckusick * the directories in the filesystem. 7346792Smckusick */ 7446792Smckusick mapfiles(maxino, tapesize) 7546792Smckusick ino_t maxino; 7646792Smckusick long *tapesize; 7746584Storek { 7846792Smckusick register int mode; 7946792Smckusick register ino_t ino; 8046792Smckusick register struct dinode *dp; 8146792Smckusick int anydirskipped = 0; 8246584Storek 8346792Smckusick for (ino = 0; ino < maxino; ino++) { 8446792Smckusick dp = getino(ino); 8546792Smckusick if ((mode = (dp->di_mode & IFMT)) == 0) 8646792Smckusick continue; 8746792Smckusick SETINO(ino, usedinomap); 8846792Smckusick if (mode == IFDIR) 8946792Smckusick SETINO(ino, dumpdirmap); 9046792Smckusick if (dp->di_mtime >= spcl.c_ddate || 9146792Smckusick dp->di_ctime >= spcl.c_ddate) { 9246792Smckusick SETINO(ino, dumpinomap); 9346792Smckusick if (mode != IFREG && mode != IFDIR && mode != IFLNK) { 9446792Smckusick *tapesize += 1; 9546792Smckusick continue; 9646792Smckusick } 9746792Smckusick *tapesize += blockest(dp); 9846792Smckusick continue; 9946792Smckusick } 10046792Smckusick if (mode == IFDIR) 10146792Smckusick anydirskipped = 1; 10246792Smckusick } 10346792Smckusick /* 10446792Smckusick * Restore gets very upset if the root is not dumped, 10546792Smckusick * so ensure that it always is dumped. 10646792Smckusick */ 10746792Smckusick SETINO(ROOTINO, usedinomap); 10846792Smckusick return (anydirskipped); 10946584Storek } 11046584Storek 11146792Smckusick /* 11246792Smckusick * Dump pass 2. 11346792Smckusick * 11446792Smckusick * Scan each directory on the filesystem to see if it has any modified 11546792Smckusick * files in it. If it does, and has not already been added to the dump 11646792Smckusick * list (because it was itself modified), then add it. If a directory 11746792Smckusick * has not been modified itself, contains no modified files and has no 11846792Smckusick * subdirectories, then it can be deleted from the dump list and from 11946792Smckusick * the list of directories. By deleting it from the list of directories, 12046792Smckusick * its parent may now qualify for the same treatment on this or a later 12146792Smckusick * pass using this algorithm. 12246792Smckusick */ 12346792Smckusick mapdirs(maxino, tapesize) 12446792Smckusick ino_t maxino; 12546792Smckusick long *tapesize; 12646792Smckusick { 12746792Smckusick register struct dinode *dp; 12847058Smckusick register int i, dirty; 12925797Smckusick register char *map; 13046792Smckusick register ino_t ino; 13146792Smckusick long filesize, blkcnt = 0; 13246792Smckusick int ret, change = 0; 1331426Sroot 13446792Smckusick for (map = dumpdirmap, ino = 0; ino < maxino; ) { 13546584Storek if ((ino % NBBY) == 0) 13647058Smckusick dirty = *map++; 13746792Smckusick else 13847058Smckusick dirty >>= 1; 1394702Smckusic ino++; 14047058Smckusick if ((dirty & 1) == 0 || TSTINO(ino, dumpinomap)) 14146792Smckusick continue; 14246792Smckusick dp = getino(ino); 14346792Smckusick filesize = dp->di_size; 14446792Smckusick for (ret = 0, i = 0; filesize > 0 && i < NDADDR; i++) { 14546792Smckusick if (dp->di_db[i] != 0) 14646792Smckusick ret |= searchdir(ino, dp->di_db[i], 14746796Smckusick dblksize(sblock, dp, i), filesize); 14846792Smckusick if (ret & HASDUMPEDFILE) 14946792Smckusick filesize = 0; 15046792Smckusick else 15146792Smckusick filesize -= sblock->fs_bsize; 15246792Smckusick } 15346792Smckusick for (i = 0; filesize > 0 && i < NIADDR; i++) { 15446792Smckusick if (dp->di_ib[i] == 0) 15546792Smckusick continue; 15646792Smckusick ret |= dirindir(ino, dp->di_ib[i], i, &filesize); 15746792Smckusick } 15846792Smckusick if (ret & HASDUMPEDFILE) { 15946792Smckusick if (!TSTINO(ino, dumpinomap)) { 16046792Smckusick SETINO(ino, dumpinomap); 16146792Smckusick *tapesize += blockest(dp); 16246792Smckusick } 16346792Smckusick change = 1; 16446792Smckusick continue; 16546792Smckusick } 16646792Smckusick if ((ret & HASSUBDIRS) == 0) { 16746792Smckusick if (!TSTINO(ino, dumpinomap)) { 16846792Smckusick CLRINO(ino, dumpdirmap); 16946792Smckusick change = 1; 17046792Smckusick } 17146792Smckusick } 1721426Sroot } 17346792Smckusick return (change); 1741426Sroot } 1751426Sroot 17646792Smckusick /* 17746792Smckusick * Read indirect blocks, and pass the data blocks to be searched 17846792Smckusick * as directories. Quit as soon as any entry is found that will 17946792Smckusick * require the directory to be dumped. 18046792Smckusick */ 18146792Smckusick dirindir(ino, blkno, level, filesize) 18246792Smckusick ino_t ino; 18346792Smckusick daddr_t blkno; 18446792Smckusick int level, *filesize; 1851426Sroot { 18646792Smckusick int ret = 0; 18746792Smckusick register int i; 18846792Smckusick daddr_t idblk[MAXNINDIR]; 1891426Sroot 19046792Smckusick bread(fsbtodb(sblock, blkno), (char *)idblk, sblock->fs_bsize); 19146792Smckusick if (level <= 0) { 19246792Smckusick for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) { 19346792Smckusick blkno = idblk[i]; 19446792Smckusick if (blkno != 0) 19546796Smckusick ret |= searchdir(ino, blkno, sblock->fs_bsize, 19646796Smckusick filesize); 19746792Smckusick if (ret & HASDUMPEDFILE) 19846792Smckusick *filesize = 0; 19946792Smckusick else 20046792Smckusick *filesize -= sblock->fs_bsize; 2011426Sroot } 20246792Smckusick return (ret); 2035329Smckusic } 20446792Smckusick level--; 20546792Smckusick for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) { 20646792Smckusick blkno = idblk[i]; 20746792Smckusick if (blkno != 0) 20846792Smckusick ret |= dirindir(ino, blkno, level, filesize); 2095329Smckusic } 21046792Smckusick return (ret); 2111426Sroot } 2121426Sroot 21346792Smckusick /* 21446792Smckusick * Scan a disk block containing directory information looking to see if 21546792Smckusick * any of the entries are on the dump list and to see if the directory 21646792Smckusick * contains any subdirectories. 21746792Smckusick */ 21846796Smckusick searchdir(ino, blkno, size, filesize) 21946792Smckusick ino_t ino; 22046792Smckusick daddr_t blkno; 22146792Smckusick register int size; 22246796Smckusick int filesize; 2235329Smckusic { 22446792Smckusick register struct direct *dp; 22546792Smckusick register long loc; 22646792Smckusick char dblk[MAXBSIZE]; 2275329Smckusic 22846792Smckusick bread(fsbtodb(sblock, blkno), dblk, size); 22946796Smckusick if (filesize < size) 23046796Smckusick size = filesize; 23146792Smckusick for (loc = 0; loc < size; ) { 23246792Smckusick dp = (struct direct *)(dblk + loc); 23346792Smckusick if (dp->d_reclen == 0) { 23446792Smckusick msg("corrupted directory, inumber %d\n", ino); 23546792Smckusick break; 2365329Smckusic } 23746792Smckusick loc += dp->d_reclen; 23846792Smckusick if (dp->d_ino == 0) 23946792Smckusick continue; 24046792Smckusick if (dp->d_name[0] == '.') { 24146792Smckusick if (dp->d_name[1] == '\0') 24246792Smckusick continue; 24346792Smckusick if (dp->d_name[1] == '.' && dp->d_name[2] == '\0') 24446792Smckusick continue; 2455329Smckusic } 24646792Smckusick if (TSTINO(dp->d_ino, dumpinomap)) 24746792Smckusick return (HASDUMPEDFILE); 24846792Smckusick if (TSTINO(dp->d_ino, dumpdirmap)) 24946792Smckusick return (HASSUBDIRS); 2505329Smckusic } 25146792Smckusick return (0); 2525329Smckusic } 2535329Smckusic 25446792Smckusick /* 25546792Smckusick * Dump passes 3 and 4. 25646792Smckusick * 25746792Smckusick * Dump the contents of an inode to tape. 25846792Smckusick */ 25946584Storek void 26047058Smckusick dumpino(dp, ino) 26147058Smckusick register struct dinode *dp; 26246792Smckusick ino_t ino; 26317234Smckusick { 26446792Smckusick int mode, level, cnt; 2654777Smckusic long size; 2661426Sroot 26746792Smckusick if (newtape) { 2681426Sroot newtape = 0; 26946792Smckusick dumpmap(dumpinomap, TS_BITS, ino); 2701426Sroot } 27146792Smckusick CLRINO(ino, dumpinomap); 27247058Smckusick spcl.c_dinode = *dp; 2731426Sroot spcl.c_type = TS_INODE; 2741426Sroot spcl.c_count = 0; 27546792Smckusick /* 27646792Smckusick * Check for freed inode. 27746792Smckusick */ 27847058Smckusick if ((mode = (dp->di_mode & IFMT)) == 0) 27917234Smckusick return; 28046792Smckusick if ((mode != IFDIR && mode != IFREG && mode != IFLNK) || 28147058Smckusick dp->di_size == 0) { 28246792Smckusick writeheader(ino); 2831426Sroot return; 2841426Sroot } 28547058Smckusick if (dp->di_size > NDADDR * sblock->fs_bsize) 28646792Smckusick cnt = NDADDR * sblock->fs_frag; 2874777Smckusic else 28847058Smckusick cnt = howmany(dp->di_size, sblock->fs_fsize); 28947058Smckusick blksout(&dp->di_db[0], cnt, ino); 29047058Smckusick if ((size = dp->di_size - NDADDR * sblock->fs_bsize) <= 0) 2914777Smckusic return; 29246792Smckusick for (level = 0; level < NIADDR; level++) { 29347058Smckusick dmpindir(ino, dp->di_ib[level], level, &size); 2944777Smckusic if (size <= 0) 2954777Smckusic return; 2964777Smckusic } 2971426Sroot } 2981426Sroot 29946792Smckusick /* 30046792Smckusick * Read indirect blocks, and pass the data blocks to be dumped. 30146792Smckusick */ 30246584Storek void 30346792Smckusick dmpindir(ino, blk, level, size) 30446792Smckusick ino_t ino; 3054777Smckusic daddr_t blk; 30646792Smckusick int level; 3074777Smckusic long *size; 3081426Sroot { 3094777Smckusic int i, cnt; 3105329Smckusic daddr_t idblk[MAXNINDIR]; 3111426Sroot 3124777Smckusic if (blk != 0) 3135329Smckusic bread(fsbtodb(sblock, blk), (char *)idblk, sblock->fs_bsize); 3144777Smckusic else 31546584Storek bzero((char *)idblk, sblock->fs_bsize); 31646792Smckusick if (level <= 0) { 3175329Smckusic if (*size < NINDIR(sblock) * sblock->fs_bsize) 3185329Smckusic cnt = howmany(*size, sblock->fs_fsize); 3194777Smckusic else 3205329Smckusic cnt = NINDIR(sblock) * sblock->fs_frag; 3215329Smckusic *size -= NINDIR(sblock) * sblock->fs_bsize; 32246792Smckusick blksout(&idblk[0], cnt, ino); 3234777Smckusic return; 3241426Sroot } 32546792Smckusick level--; 3265329Smckusic for (i = 0; i < NINDIR(sblock); i++) { 32746792Smckusick dmpindir(ino, idblk[i], level, size); 3284777Smckusic if (*size <= 0) 3294777Smckusic return; 3304777Smckusic } 3311426Sroot } 3321426Sroot 33346792Smckusick /* 33446792Smckusick * Collect up the data into tape record sized buffers and output them. 33546792Smckusick */ 33646584Storek void 33746792Smckusick blksout(blkp, frags, ino) 3384777Smckusic daddr_t *blkp; 3394777Smckusic int frags; 34046792Smckusick ino_t ino; 3414777Smckusic { 34246584Storek register daddr_t *bp; 3435329Smckusic int i, j, count, blks, tbperdb; 3444777Smckusic 3459403Smckusick blks = howmany(frags * sblock->fs_fsize, TP_BSIZE); 34646584Storek tbperdb = sblock->fs_bsize >> tp_bshift; 3474777Smckusic for (i = 0; i < blks; i += TP_NINDIR) { 3484777Smckusic if (i + TP_NINDIR > blks) 3494777Smckusic count = blks; 3504777Smckusic else 3514777Smckusic count = i + TP_NINDIR; 3524777Smckusic for (j = i; j < count; j++) 3535329Smckusic if (blkp[j / tbperdb] != 0) 3544777Smckusic spcl.c_addr[j - i] = 1; 3554777Smckusic else 3564777Smckusic spcl.c_addr[j - i] = 0; 3574777Smckusic spcl.c_count = count - i; 35846792Smckusick writeheader(ino); 35946584Storek bp = &blkp[i / tbperdb]; 36046584Storek for (j = i; j < count; j += tbperdb, bp++) 36146584Storek if (*bp != 0) 3625329Smckusic if (j + tbperdb <= count) 36346792Smckusick dumpblock(*bp, sblock->fs_bsize); 3644777Smckusic else 36546792Smckusick dumpblock(*bp, (count - j) * TP_BSIZE); 3664777Smckusic spcl.c_type = TS_ADDR; 3674777Smckusic } 3684777Smckusic } 3694777Smckusic 37046792Smckusick /* 37146792Smckusick * Dump a map to the tape. 37246792Smckusick */ 37346584Storek void 37446792Smckusick dumpmap(map, type, ino) 3755329Smckusic char *map; 37646792Smckusick int type; 37746792Smckusick ino_t ino; 3781426Sroot { 37946792Smckusick register int i; 3801426Sroot char *cp; 3811426Sroot 38246792Smckusick spcl.c_type = type; 38346792Smckusick spcl.c_count = howmany(mapsize * sizeof(char), TP_BSIZE); 38446792Smckusick writeheader(ino); 3855329Smckusic for (i = 0, cp = map; i < spcl.c_count; i++, cp += TP_BSIZE) 38646792Smckusick writerec(cp); 3871426Sroot } 3881426Sroot 38946792Smckusick /* 39046792Smckusick * Write a header record to the dump tape. 39146792Smckusick */ 39246584Storek void 39346792Smckusick writeheader(ino) 39446792Smckusick ino_t ino; 3951426Sroot { 39646792Smckusick register long sum, cnt, *lp; 3971426Sroot 3981426Sroot spcl.c_inumber = ino; 3998368Smckusick spcl.c_magic = NFS_MAGIC; 4001426Sroot spcl.c_checksum = 0; 40146792Smckusick lp = (long *)&spcl; 40246792Smckusick sum = 0; 40346792Smckusick cnt = sizeof(union u_spcl) / (4 * sizeof(long)); 40446792Smckusick while (--cnt >= 0) { 40546792Smckusick sum += *lp++; 40646792Smckusick sum += *lp++; 40746792Smckusick sum += *lp++; 40846792Smckusick sum += *lp++; 40924168Smckusick } 41046792Smckusick spcl.c_checksum = CHECKSUM - sum; 41146792Smckusick writerec((char *)&spcl); 4121426Sroot } 4131426Sroot 4144702Smckusic struct dinode * 41546792Smckusick getino(inum) 41646792Smckusick ino_t inum; 4174702Smckusic { 4184702Smckusic static daddr_t minino, maxino; 41946792Smckusick static struct dinode inoblock[MAXINOPB]; 4204702Smckusic 42146792Smckusick curino = inum; 42246792Smckusick if (inum >= minino && inum < maxino) 42346792Smckusick return (&inoblock[inum - minino]); 42446792Smckusick bread(fsbtodb(sblock, itod(sblock, inum)), inoblock, sblock->fs_bsize); 42546792Smckusick minino = inum - (inum % INOPB(sblock)); 4265329Smckusic maxino = minino + INOPB(sblock); 42746792Smckusick return (&inoblock[inum - minino]); 4284702Smckusic } 4294702Smckusic 43046792Smckusick /* 43146792Smckusick * Read a chunk of data from the disk. 43246792Smckusick * Try to recover from hard errors by reading in sector sized pieces. 43346792Smckusick * Error recovery is attempted at most BREADEMAX times before seeking 43446792Smckusick * consent from the operator to continue. 43546792Smckusick */ 4361426Sroot int breaderrors = 0; 4371426Sroot #define BREADEMAX 32 4381426Sroot 43946584Storek void 44046792Smckusick bread(blkno, buf, size) 44146792Smckusick daddr_t blkno; 44246792Smckusick char *buf; 44346792Smckusick int size; 4441426Sroot { 44546792Smckusick int cnt, i; 44634359Skarels extern int errno; 4471426Sroot 44815095Smckusick loop: 44946792Smckusick if (lseek(diskfd, (long)(blkno << dev_bshift), 0) < 0) 4501426Sroot msg("bread: lseek fails\n"); 45146792Smckusick if ((cnt = read(diskfd, buf, size)) == size) 45215095Smckusick return; 45346792Smckusick if (blkno + (size / dev_bsize) > fsbtodb(sblock, sblock->fs_size)) { 45415095Smckusick /* 45515095Smckusick * Trying to read the final fragment. 45615095Smckusick * 45715095Smckusick * NB - dump only works in TP_BSIZE blocks, hence 45830560Smckusick * rounds `dev_bsize' fragments up to TP_BSIZE pieces. 45915095Smckusick * It should be smarter about not actually trying to 46015095Smckusick * read more than it can get, but for the time being 46115095Smckusick * we punt and scale back the read only when it gets 46215095Smckusick * us into trouble. (mkm 9/25/83) 46315095Smckusick */ 46446792Smckusick size -= dev_bsize; 46515095Smckusick goto loop; 4661426Sroot } 46746584Storek msg("read error from %s [block %d]: count=%d, got=%d, errno=%d (%s)\n", 46846792Smckusick disk, blkno, size, cnt, errno, strerror(errno)); 46946584Storek if (++breaderrors > BREADEMAX) { 47015095Smckusick msg("More than %d block read errors from %d\n", 47115095Smckusick BREADEMAX, disk); 47215095Smckusick broadcast("DUMP IS AILING!\n"); 47315095Smckusick msg("This is an unrecoverable error.\n"); 47415095Smckusick if (!query("Do you want to attempt to continue?")){ 47515095Smckusick dumpabort(); 47615095Smckusick /*NOTREACHED*/ 47715095Smckusick } else 47815095Smckusick breaderrors = 0; 47915095Smckusick } 48034359Skarels /* 48134359Skarels * Zero buffer, then try to read each sector of buffer separately. 48234359Skarels */ 48346792Smckusick bzero(buf, size); 48446792Smckusick for (i = 0; i < size; i += dev_bsize, buf += dev_bsize, blkno++) { 48546792Smckusick if (lseek(diskfd, (long)(blkno << dev_bshift), 0) < 0) 48634359Skarels msg("bread: lseek2 fails!\n"); 48746792Smckusick if ((cnt = read(diskfd, buf, dev_bsize)) != dev_bsize) 48834359Skarels msg(" read error from %s [sector %d, errno %d]\n", 48946792Smckusick disk, blkno, errno); 49034359Skarels } 4911426Sroot } 492