From 142d8d2f406cdf3f3bb9ff73c2f09b6cc6f669a4 Mon Sep 17 00:00:00 2001 From: Kirk McKusick Date: Thu, 6 Jul 2000 02:03:11 +0000 Subject: [PATCH] Teach fsck about snapshot files. These changes should have no effect on operation of fsck on filesystems without snapshots. If you get compilation errors, be sure that you have copies of /usr/include/sys/mount.h (1.94), /usr/include/sys/stat.h (1.21), and /usr/include/ufs/ffs/fs.h (1.16) as of July 4, 2000 or later. --- sbin/fsck/dir.c | 14 ++++++--- sbin/fsck/fsck.h | 7 +++-- sbin/fsck/inode.c | 11 +++++-- sbin/fsck/pass1.c | 21 ++++++++++++- sbin/fsck/pass5.c | 70 +++++++++++++++++++++++++++++++++++++++++-- sbin/fsck/setup.c | 13 ++++++-- sbin/fsck_ffs/dir.c | 14 ++++++--- sbin/fsck_ffs/fsck.h | 7 +++-- sbin/fsck_ffs/inode.c | 11 +++++-- sbin/fsck_ffs/pass1.c | 21 ++++++++++++- sbin/fsck_ffs/pass5.c | 70 +++++++++++++++++++++++++++++++++++++++++-- sbin/fsck_ffs/setup.c | 13 ++++++-- sbin/fsck_ifs/dir.c | 14 ++++++--- sbin/fsck_ifs/fsck.h | 7 +++-- sbin/fsck_ifs/inode.c | 11 +++++-- sbin/fsck_ifs/pass1.c | 21 ++++++++++++- sbin/fsck_ifs/pass5.c | 70 +++++++++++++++++++++++++++++++++++++++++-- sbin/fsck_ifs/setup.c | 13 ++++++-- 18 files changed, 363 insertions(+), 45 deletions(-) diff --git a/sbin/fsck/dir.c b/sbin/fsck/dir.c index f5fb6a0ef30a..5ce99f6b7b54 100644 --- a/sbin/fsck/dir.c +++ b/sbin/fsck/dir.c @@ -241,7 +241,7 @@ dircheck(idesc, dp) if (dp->d_reclen == 0 || dp->d_reclen > spaceleft || (dp->d_reclen & 0x3) != 0) - return (0); + goto bad; if (dp->d_ino == 0) return (1); size = DIRSIZ(!newinofmt, dp); @@ -261,13 +261,19 @@ dircheck(idesc, dp) idesc->id_filesize < size || namlen > MAXNAMLEN || type > 15) - return (0); + goto bad; for (cp = dp->d_name, size = 0; size < namlen; size++) if (*cp == '\0' || (*cp++ == '/')) - return (0); + goto bad; if (*cp != '\0') - return (0); + goto bad; return (1); +bad: + if (debug) + printf("Bad dir: ino %d reclen %d namlen %d type %d name %s\n", + dp->d_ino, dp->d_reclen, dp->d_namlen, dp->d_type, + dp->d_name); + return (0); } void diff --git a/sbin/fsck/fsck.h b/sbin/fsck/fsck.h index 9d59c8ff2526..f0280fe11fe6 100644 --- a/sbin/fsck/fsck.h +++ b/sbin/fsck/fsck.h @@ -114,12 +114,14 @@ struct bufarea *pbp; /* current inode block */ #define cgrp (*cgblk.b_un.b_cg) enum fixstate {DONTKNOW, NOFIX, FIX, IGNORE}; +ino_t cursnapshot; struct inodesc { enum fixstate id_fix; /* policy on fixing errors */ int (*id_func)(); /* function to be applied to blocks of inode */ ino_t id_number; /* inode number described */ ino_t id_parent; /* for DATA nodes, their parent */ + int id_lbn; /* logical block number of current block */ ufs_daddr_t id_blkno; /* current block number being examined */ int id_numfrags; /* number of frags contained in block */ quad_t id_filesize; /* for DATA nodes, the size of the directory */ @@ -130,8 +132,9 @@ struct inodesc { char id_type; /* type of descriptor, DATA or ADDR */ }; /* file types */ -#define DATA 1 -#define ADDR 2 +#define DATA 1 /* a directory */ +#define SNAP 2 /* a snapshot */ +#define ADDR 3 /* anything but a directory or a snapshot */ /* * Linked list of duplicate blocks. diff --git a/sbin/fsck/inode.c b/sbin/fsck/inode.c index dbf86f760f81..e241cdbbfeb5 100644 --- a/sbin/fsck/inode.c +++ b/sbin/fsck/inode.c @@ -71,6 +71,7 @@ ckinode(dp, idesc) if (idesc->id_fix != IGNORE) idesc->id_fix = DONTKNOW; + idesc->id_lbn = -1; idesc->id_entryno = 0; idesc->id_filesize = dp->di_size; mode = dp->di_mode & IFMT; @@ -80,6 +81,7 @@ ckinode(dp, idesc) dino = *dp; ndb = howmany(dino.di_size, sblock.fs_bsize); for (ap = &dino.di_db[0]; ap < &dino.di_db[NDADDR]; ap++) { + idesc->id_lbn++; if (--ndb == 0 && (offset = blkoff(&sblock, dino.di_size)) != 0) idesc->id_numfrags = numfrags(&sblock, fragroundup(&sblock, offset)); @@ -106,7 +108,7 @@ ckinode(dp, idesc) continue; } idesc->id_blkno = *ap; - if (idesc->id_type == ADDR) + if (idesc->id_type != DATA) ret = (*idesc->id_func)(idesc); else ret = dirscan(idesc); @@ -117,12 +119,14 @@ ckinode(dp, idesc) remsize = dino.di_size - sblock.fs_bsize * NDADDR; sizepb = sblock.fs_bsize; for (ap = &dino.di_ib[0], n = 1; n <= NIADDR; ap++, n++) { + sizepb *= NINDIR(&sblock); if (*ap) { idesc->id_blkno = *ap; ret = iblock(idesc, n, remsize); if (ret & STOP) return (ret); } else { + idesc->id_lbn += sizepb / sblock.fs_bsize; if (idesc->id_type == DATA && remsize > 0) { /* An empty block in a directory XXX */ getpathname(pathbuf, idesc->id_number, @@ -141,7 +145,6 @@ ckinode(dp, idesc) } } } - sizepb *= NINDIR(&sblock); remsize -= sizepb; } return (KEEPON); @@ -162,7 +165,7 @@ iblock(idesc, ilevel, isize) char pathbuf[MAXPATHLEN + 1]; struct dinode *dp; - if (idesc->id_type == ADDR) { + if (idesc->id_type != DATA) { func = idesc->id_func; if (((n = (*func)(idesc)) & KEEPON) == 0) return (n); @@ -193,6 +196,8 @@ iblock(idesc, ilevel, isize) } aplim = &bp->b_un.b_indir[nif]; for (ap = bp->b_un.b_indir; ap < aplim; ap++) { + if (ilevel == 0) + idesc->id_lbn++; if (*ap) { idesc->id_blkno = *ap; if (ilevel == 0) diff --git a/sbin/fsck/pass1.c b/sbin/fsck/pass1.c index dd09b009705e..59826a7e2fc2 100644 --- a/sbin/fsck/pass1.c +++ b/sbin/fsck/pass1.c @@ -40,6 +40,7 @@ static const char rcsid[] = #endif /* not lint */ #include +#include #include #include @@ -82,7 +83,6 @@ pass1() * Find all allocated blocks. */ memset(&idesc, 0, sizeof(struct inodesc)); - idesc.id_type = ADDR; idesc.id_func = pass1check; n_files = n_blks = 0; for (c = 0; c < sblock.fs_ncg; c++) { @@ -306,6 +306,10 @@ checkinode(inumber, idesc) } badblk = dupblk = 0; idesc->id_number = inumber; + if (dp->di_flags & SF_SNAPSHOT) + idesc->id_type = SNAP; + else + idesc->id_type = ADDR; (void)ckinode(dp, idesc); idesc->id_entryno *= btodb(sblock.fs_fsize); if (dp->di_blocks != idesc->id_entryno) { @@ -341,6 +345,21 @@ pass1check(idesc) register struct dups *dlp; struct dups *new; + if (idesc->id_type == SNAP) { + if (blkno == BLK_NOCOPY) + return (KEEPON); + if (idesc->id_number == cursnapshot) { + if (blkno == blkstofrags(&sblock, idesc->id_lbn)) + return (KEEPON); + if (blkno == BLK_SNAP) { + blkno = blkstofrags(&sblock, idesc->id_lbn); + idesc->id_entryno -= idesc->id_numfrags; + } + } else { + if (blkno == BLK_SNAP) + return (KEEPON); + } + } if ((anyout = chkrange(blkno, idesc->id_numfrags)) != 0) { blkerror(idesc->id_number, "BAD", blkno); if (badblk++ >= MAXBAD) { diff --git a/sbin/fsck/pass5.c b/sbin/fsck/pass5.c index d5edaace1690..e39562579771 100644 --- a/sbin/fsck/pass5.c +++ b/sbin/fsck/pass5.c @@ -53,12 +53,12 @@ void pass5() { int c, blk, frags, basesize, sumsize, mapsize, savednrpos = 0; - int inomapsize, blkmapsize; + int inomapsize, blkmapsize, astart, aend, ustart, uend; struct fs *fs = &sblock; struct cg *cg = &cgrp; ufs_daddr_t dbase, dmax; ufs_daddr_t d; - long i, j, k; + long i, j, k, l, m, n; struct csum *cs; struct csum cstotal; struct inodesc idesc[3]; @@ -314,6 +314,72 @@ pass5() &cg_blktot(newcg)[0], (size_t)sumsize); cgdirty(); } + if (debug) { + for (i = 0; i < inomapsize; i++) { + j = cg_inosused(newcg)[i]; + k = cg_inosused(cg)[i]; + if (j == k) + continue; + for (m = 0, l = 1; m < NBBY; m++, l <<= 1) { + if ((j & l) == (k & l)) + continue; + n = c * fs->fs_ipg + i * NBBY + m; + if ((j & l) != 0) + pwarn("%s INODE %d MARKED %s\n", + "ALLOCATED", n, "FREE"); + else + pwarn("%s INODE %d MARKED %s\n", + "UNALLOCATED", n, "USED"); + } + } + astart = ustart = -1; + for (i = 0; i < blkmapsize; i++) { + j = cg_blksfree(cg)[i]; + k = cg_blksfree(newcg)[i]; + if (j == k) + continue; + for (m = 0, l = 1; m < NBBY; m++, l <<= 1) { + if ((j & l) == (k & l)) + continue; + n = c * fs->fs_fpg + i * NBBY + m; + if ((j & l) != 0) { + if (astart == -1) { + astart = aend = n; + continue; + } + if (aend + 1 == n) { + aend = n; + continue; + } + pwarn("%s FRAGS %d-%d %s\n", + "ALLOCATED", astart, aend, + "MARKED FREE"); + astart = aend = n; + } else { + if (ustart == -1) { + ustart = uend = n; + continue; + } + if (uend + 1 == n) { + uend = n; + continue; + } + pwarn("%s FRAGS %d-%d %s\n", + "UNALLOCATED", ustart, uend, + "MARKED USED"); + ustart = uend = n; + } + } + } + if (astart != -1) + pwarn("%s FRAGS %d-%d %s\n", + "ALLOCATED", astart, aend, + "MARKED FREE"); + if (ustart != -1) + pwarn("%s FRAGS %d-%d %s\n", + "UNALLOCATED", ustart, uend, + "MARKED USED"); + } if (usedsoftdep) { for (i = 0; i < inomapsize; i++) { j = cg_inosused(newcg)[i]; diff --git a/sbin/fsck/setup.c b/sbin/fsck/setup.c index 49bbc8d99883..6c4098c11bc7 100644 --- a/sbin/fsck/setup.c +++ b/sbin/fsck/setup.c @@ -82,6 +82,7 @@ setup(dev) havesb = 0; fswritefd = -1; + cursnapshot = 0; skipclean = fflag ? 0 : preen; if (stat(dev, &statb) < 0) { printf("Can't stat %s: %s\n", dev, strerror(errno)); @@ -89,9 +90,13 @@ setup(dev) } if ((statb.st_mode & S_IFMT) != S_IFCHR && (statb.st_mode & S_IFMT) != S_IFBLK) { - pfatal("%s is not a disk device", dev); - if (reply("CONTINUE") == 0) - return (0); + if ((statb.st_flags & SF_SNAPSHOT) != 0) { + cursnapshot = statb.st_ino; + } else { + pfatal("%s is not a disk device", dev); + if (reply("CONTINUE") == 0) + return (0); + } } if ((fsreadfd = open(dev, O_RDONLY)) < 0) { printf("Can't open %s: %s\n", dev, strerror(errno)); @@ -378,6 +383,8 @@ readsb(listerr) memmove(altsblock.fs_csp, sblock.fs_csp, sizeof sblock.fs_csp); altsblock.fs_maxcluster = sblock.fs_maxcluster; memmove(altsblock.fs_fsmnt, sblock.fs_fsmnt, sizeof sblock.fs_fsmnt); + memmove(altsblock.fs_snapinum, sblock.fs_snapinum, + sizeof sblock.fs_snapinum); memmove(altsblock.fs_sparecon, sblock.fs_sparecon, sizeof sblock.fs_sparecon); /* diff --git a/sbin/fsck_ffs/dir.c b/sbin/fsck_ffs/dir.c index f5fb6a0ef30a..5ce99f6b7b54 100644 --- a/sbin/fsck_ffs/dir.c +++ b/sbin/fsck_ffs/dir.c @@ -241,7 +241,7 @@ dircheck(idesc, dp) if (dp->d_reclen == 0 || dp->d_reclen > spaceleft || (dp->d_reclen & 0x3) != 0) - return (0); + goto bad; if (dp->d_ino == 0) return (1); size = DIRSIZ(!newinofmt, dp); @@ -261,13 +261,19 @@ dircheck(idesc, dp) idesc->id_filesize < size || namlen > MAXNAMLEN || type > 15) - return (0); + goto bad; for (cp = dp->d_name, size = 0; size < namlen; size++) if (*cp == '\0' || (*cp++ == '/')) - return (0); + goto bad; if (*cp != '\0') - return (0); + goto bad; return (1); +bad: + if (debug) + printf("Bad dir: ino %d reclen %d namlen %d type %d name %s\n", + dp->d_ino, dp->d_reclen, dp->d_namlen, dp->d_type, + dp->d_name); + return (0); } void diff --git a/sbin/fsck_ffs/fsck.h b/sbin/fsck_ffs/fsck.h index 9d59c8ff2526..f0280fe11fe6 100644 --- a/sbin/fsck_ffs/fsck.h +++ b/sbin/fsck_ffs/fsck.h @@ -114,12 +114,14 @@ struct bufarea *pbp; /* current inode block */ #define cgrp (*cgblk.b_un.b_cg) enum fixstate {DONTKNOW, NOFIX, FIX, IGNORE}; +ino_t cursnapshot; struct inodesc { enum fixstate id_fix; /* policy on fixing errors */ int (*id_func)(); /* function to be applied to blocks of inode */ ino_t id_number; /* inode number described */ ino_t id_parent; /* for DATA nodes, their parent */ + int id_lbn; /* logical block number of current block */ ufs_daddr_t id_blkno; /* current block number being examined */ int id_numfrags; /* number of frags contained in block */ quad_t id_filesize; /* for DATA nodes, the size of the directory */ @@ -130,8 +132,9 @@ struct inodesc { char id_type; /* type of descriptor, DATA or ADDR */ }; /* file types */ -#define DATA 1 -#define ADDR 2 +#define DATA 1 /* a directory */ +#define SNAP 2 /* a snapshot */ +#define ADDR 3 /* anything but a directory or a snapshot */ /* * Linked list of duplicate blocks. diff --git a/sbin/fsck_ffs/inode.c b/sbin/fsck_ffs/inode.c index dbf86f760f81..e241cdbbfeb5 100644 --- a/sbin/fsck_ffs/inode.c +++ b/sbin/fsck_ffs/inode.c @@ -71,6 +71,7 @@ ckinode(dp, idesc) if (idesc->id_fix != IGNORE) idesc->id_fix = DONTKNOW; + idesc->id_lbn = -1; idesc->id_entryno = 0; idesc->id_filesize = dp->di_size; mode = dp->di_mode & IFMT; @@ -80,6 +81,7 @@ ckinode(dp, idesc) dino = *dp; ndb = howmany(dino.di_size, sblock.fs_bsize); for (ap = &dino.di_db[0]; ap < &dino.di_db[NDADDR]; ap++) { + idesc->id_lbn++; if (--ndb == 0 && (offset = blkoff(&sblock, dino.di_size)) != 0) idesc->id_numfrags = numfrags(&sblock, fragroundup(&sblock, offset)); @@ -106,7 +108,7 @@ ckinode(dp, idesc) continue; } idesc->id_blkno = *ap; - if (idesc->id_type == ADDR) + if (idesc->id_type != DATA) ret = (*idesc->id_func)(idesc); else ret = dirscan(idesc); @@ -117,12 +119,14 @@ ckinode(dp, idesc) remsize = dino.di_size - sblock.fs_bsize * NDADDR; sizepb = sblock.fs_bsize; for (ap = &dino.di_ib[0], n = 1; n <= NIADDR; ap++, n++) { + sizepb *= NINDIR(&sblock); if (*ap) { idesc->id_blkno = *ap; ret = iblock(idesc, n, remsize); if (ret & STOP) return (ret); } else { + idesc->id_lbn += sizepb / sblock.fs_bsize; if (idesc->id_type == DATA && remsize > 0) { /* An empty block in a directory XXX */ getpathname(pathbuf, idesc->id_number, @@ -141,7 +145,6 @@ ckinode(dp, idesc) } } } - sizepb *= NINDIR(&sblock); remsize -= sizepb; } return (KEEPON); @@ -162,7 +165,7 @@ iblock(idesc, ilevel, isize) char pathbuf[MAXPATHLEN + 1]; struct dinode *dp; - if (idesc->id_type == ADDR) { + if (idesc->id_type != DATA) { func = idesc->id_func; if (((n = (*func)(idesc)) & KEEPON) == 0) return (n); @@ -193,6 +196,8 @@ iblock(idesc, ilevel, isize) } aplim = &bp->b_un.b_indir[nif]; for (ap = bp->b_un.b_indir; ap < aplim; ap++) { + if (ilevel == 0) + idesc->id_lbn++; if (*ap) { idesc->id_blkno = *ap; if (ilevel == 0) diff --git a/sbin/fsck_ffs/pass1.c b/sbin/fsck_ffs/pass1.c index dd09b009705e..59826a7e2fc2 100644 --- a/sbin/fsck_ffs/pass1.c +++ b/sbin/fsck_ffs/pass1.c @@ -40,6 +40,7 @@ static const char rcsid[] = #endif /* not lint */ #include +#include #include #include @@ -82,7 +83,6 @@ pass1() * Find all allocated blocks. */ memset(&idesc, 0, sizeof(struct inodesc)); - idesc.id_type = ADDR; idesc.id_func = pass1check; n_files = n_blks = 0; for (c = 0; c < sblock.fs_ncg; c++) { @@ -306,6 +306,10 @@ checkinode(inumber, idesc) } badblk = dupblk = 0; idesc->id_number = inumber; + if (dp->di_flags & SF_SNAPSHOT) + idesc->id_type = SNAP; + else + idesc->id_type = ADDR; (void)ckinode(dp, idesc); idesc->id_entryno *= btodb(sblock.fs_fsize); if (dp->di_blocks != idesc->id_entryno) { @@ -341,6 +345,21 @@ pass1check(idesc) register struct dups *dlp; struct dups *new; + if (idesc->id_type == SNAP) { + if (blkno == BLK_NOCOPY) + return (KEEPON); + if (idesc->id_number == cursnapshot) { + if (blkno == blkstofrags(&sblock, idesc->id_lbn)) + return (KEEPON); + if (blkno == BLK_SNAP) { + blkno = blkstofrags(&sblock, idesc->id_lbn); + idesc->id_entryno -= idesc->id_numfrags; + } + } else { + if (blkno == BLK_SNAP) + return (KEEPON); + } + } if ((anyout = chkrange(blkno, idesc->id_numfrags)) != 0) { blkerror(idesc->id_number, "BAD", blkno); if (badblk++ >= MAXBAD) { diff --git a/sbin/fsck_ffs/pass5.c b/sbin/fsck_ffs/pass5.c index d5edaace1690..e39562579771 100644 --- a/sbin/fsck_ffs/pass5.c +++ b/sbin/fsck_ffs/pass5.c @@ -53,12 +53,12 @@ void pass5() { int c, blk, frags, basesize, sumsize, mapsize, savednrpos = 0; - int inomapsize, blkmapsize; + int inomapsize, blkmapsize, astart, aend, ustart, uend; struct fs *fs = &sblock; struct cg *cg = &cgrp; ufs_daddr_t dbase, dmax; ufs_daddr_t d; - long i, j, k; + long i, j, k, l, m, n; struct csum *cs; struct csum cstotal; struct inodesc idesc[3]; @@ -314,6 +314,72 @@ pass5() &cg_blktot(newcg)[0], (size_t)sumsize); cgdirty(); } + if (debug) { + for (i = 0; i < inomapsize; i++) { + j = cg_inosused(newcg)[i]; + k = cg_inosused(cg)[i]; + if (j == k) + continue; + for (m = 0, l = 1; m < NBBY; m++, l <<= 1) { + if ((j & l) == (k & l)) + continue; + n = c * fs->fs_ipg + i * NBBY + m; + if ((j & l) != 0) + pwarn("%s INODE %d MARKED %s\n", + "ALLOCATED", n, "FREE"); + else + pwarn("%s INODE %d MARKED %s\n", + "UNALLOCATED", n, "USED"); + } + } + astart = ustart = -1; + for (i = 0; i < blkmapsize; i++) { + j = cg_blksfree(cg)[i]; + k = cg_blksfree(newcg)[i]; + if (j == k) + continue; + for (m = 0, l = 1; m < NBBY; m++, l <<= 1) { + if ((j & l) == (k & l)) + continue; + n = c * fs->fs_fpg + i * NBBY + m; + if ((j & l) != 0) { + if (astart == -1) { + astart = aend = n; + continue; + } + if (aend + 1 == n) { + aend = n; + continue; + } + pwarn("%s FRAGS %d-%d %s\n", + "ALLOCATED", astart, aend, + "MARKED FREE"); + astart = aend = n; + } else { + if (ustart == -1) { + ustart = uend = n; + continue; + } + if (uend + 1 == n) { + uend = n; + continue; + } + pwarn("%s FRAGS %d-%d %s\n", + "UNALLOCATED", ustart, uend, + "MARKED USED"); + ustart = uend = n; + } + } + } + if (astart != -1) + pwarn("%s FRAGS %d-%d %s\n", + "ALLOCATED", astart, aend, + "MARKED FREE"); + if (ustart != -1) + pwarn("%s FRAGS %d-%d %s\n", + "UNALLOCATED", ustart, uend, + "MARKED USED"); + } if (usedsoftdep) { for (i = 0; i < inomapsize; i++) { j = cg_inosused(newcg)[i]; diff --git a/sbin/fsck_ffs/setup.c b/sbin/fsck_ffs/setup.c index 49bbc8d99883..6c4098c11bc7 100644 --- a/sbin/fsck_ffs/setup.c +++ b/sbin/fsck_ffs/setup.c @@ -82,6 +82,7 @@ setup(dev) havesb = 0; fswritefd = -1; + cursnapshot = 0; skipclean = fflag ? 0 : preen; if (stat(dev, &statb) < 0) { printf("Can't stat %s: %s\n", dev, strerror(errno)); @@ -89,9 +90,13 @@ setup(dev) } if ((statb.st_mode & S_IFMT) != S_IFCHR && (statb.st_mode & S_IFMT) != S_IFBLK) { - pfatal("%s is not a disk device", dev); - if (reply("CONTINUE") == 0) - return (0); + if ((statb.st_flags & SF_SNAPSHOT) != 0) { + cursnapshot = statb.st_ino; + } else { + pfatal("%s is not a disk device", dev); + if (reply("CONTINUE") == 0) + return (0); + } } if ((fsreadfd = open(dev, O_RDONLY)) < 0) { printf("Can't open %s: %s\n", dev, strerror(errno)); @@ -378,6 +383,8 @@ readsb(listerr) memmove(altsblock.fs_csp, sblock.fs_csp, sizeof sblock.fs_csp); altsblock.fs_maxcluster = sblock.fs_maxcluster; memmove(altsblock.fs_fsmnt, sblock.fs_fsmnt, sizeof sblock.fs_fsmnt); + memmove(altsblock.fs_snapinum, sblock.fs_snapinum, + sizeof sblock.fs_snapinum); memmove(altsblock.fs_sparecon, sblock.fs_sparecon, sizeof sblock.fs_sparecon); /* diff --git a/sbin/fsck_ifs/dir.c b/sbin/fsck_ifs/dir.c index f5fb6a0ef30a..5ce99f6b7b54 100644 --- a/sbin/fsck_ifs/dir.c +++ b/sbin/fsck_ifs/dir.c @@ -241,7 +241,7 @@ dircheck(idesc, dp) if (dp->d_reclen == 0 || dp->d_reclen > spaceleft || (dp->d_reclen & 0x3) != 0) - return (0); + goto bad; if (dp->d_ino == 0) return (1); size = DIRSIZ(!newinofmt, dp); @@ -261,13 +261,19 @@ dircheck(idesc, dp) idesc->id_filesize < size || namlen > MAXNAMLEN || type > 15) - return (0); + goto bad; for (cp = dp->d_name, size = 0; size < namlen; size++) if (*cp == '\0' || (*cp++ == '/')) - return (0); + goto bad; if (*cp != '\0') - return (0); + goto bad; return (1); +bad: + if (debug) + printf("Bad dir: ino %d reclen %d namlen %d type %d name %s\n", + dp->d_ino, dp->d_reclen, dp->d_namlen, dp->d_type, + dp->d_name); + return (0); } void diff --git a/sbin/fsck_ifs/fsck.h b/sbin/fsck_ifs/fsck.h index 9d59c8ff2526..f0280fe11fe6 100644 --- a/sbin/fsck_ifs/fsck.h +++ b/sbin/fsck_ifs/fsck.h @@ -114,12 +114,14 @@ struct bufarea *pbp; /* current inode block */ #define cgrp (*cgblk.b_un.b_cg) enum fixstate {DONTKNOW, NOFIX, FIX, IGNORE}; +ino_t cursnapshot; struct inodesc { enum fixstate id_fix; /* policy on fixing errors */ int (*id_func)(); /* function to be applied to blocks of inode */ ino_t id_number; /* inode number described */ ino_t id_parent; /* for DATA nodes, their parent */ + int id_lbn; /* logical block number of current block */ ufs_daddr_t id_blkno; /* current block number being examined */ int id_numfrags; /* number of frags contained in block */ quad_t id_filesize; /* for DATA nodes, the size of the directory */ @@ -130,8 +132,9 @@ struct inodesc { char id_type; /* type of descriptor, DATA or ADDR */ }; /* file types */ -#define DATA 1 -#define ADDR 2 +#define DATA 1 /* a directory */ +#define SNAP 2 /* a snapshot */ +#define ADDR 3 /* anything but a directory or a snapshot */ /* * Linked list of duplicate blocks. diff --git a/sbin/fsck_ifs/inode.c b/sbin/fsck_ifs/inode.c index dbf86f760f81..e241cdbbfeb5 100644 --- a/sbin/fsck_ifs/inode.c +++ b/sbin/fsck_ifs/inode.c @@ -71,6 +71,7 @@ ckinode(dp, idesc) if (idesc->id_fix != IGNORE) idesc->id_fix = DONTKNOW; + idesc->id_lbn = -1; idesc->id_entryno = 0; idesc->id_filesize = dp->di_size; mode = dp->di_mode & IFMT; @@ -80,6 +81,7 @@ ckinode(dp, idesc) dino = *dp; ndb = howmany(dino.di_size, sblock.fs_bsize); for (ap = &dino.di_db[0]; ap < &dino.di_db[NDADDR]; ap++) { + idesc->id_lbn++; if (--ndb == 0 && (offset = blkoff(&sblock, dino.di_size)) != 0) idesc->id_numfrags = numfrags(&sblock, fragroundup(&sblock, offset)); @@ -106,7 +108,7 @@ ckinode(dp, idesc) continue; } idesc->id_blkno = *ap; - if (idesc->id_type == ADDR) + if (idesc->id_type != DATA) ret = (*idesc->id_func)(idesc); else ret = dirscan(idesc); @@ -117,12 +119,14 @@ ckinode(dp, idesc) remsize = dino.di_size - sblock.fs_bsize * NDADDR; sizepb = sblock.fs_bsize; for (ap = &dino.di_ib[0], n = 1; n <= NIADDR; ap++, n++) { + sizepb *= NINDIR(&sblock); if (*ap) { idesc->id_blkno = *ap; ret = iblock(idesc, n, remsize); if (ret & STOP) return (ret); } else { + idesc->id_lbn += sizepb / sblock.fs_bsize; if (idesc->id_type == DATA && remsize > 0) { /* An empty block in a directory XXX */ getpathname(pathbuf, idesc->id_number, @@ -141,7 +145,6 @@ ckinode(dp, idesc) } } } - sizepb *= NINDIR(&sblock); remsize -= sizepb; } return (KEEPON); @@ -162,7 +165,7 @@ iblock(idesc, ilevel, isize) char pathbuf[MAXPATHLEN + 1]; struct dinode *dp; - if (idesc->id_type == ADDR) { + if (idesc->id_type != DATA) { func = idesc->id_func; if (((n = (*func)(idesc)) & KEEPON) == 0) return (n); @@ -193,6 +196,8 @@ iblock(idesc, ilevel, isize) } aplim = &bp->b_un.b_indir[nif]; for (ap = bp->b_un.b_indir; ap < aplim; ap++) { + if (ilevel == 0) + idesc->id_lbn++; if (*ap) { idesc->id_blkno = *ap; if (ilevel == 0) diff --git a/sbin/fsck_ifs/pass1.c b/sbin/fsck_ifs/pass1.c index dd09b009705e..59826a7e2fc2 100644 --- a/sbin/fsck_ifs/pass1.c +++ b/sbin/fsck_ifs/pass1.c @@ -40,6 +40,7 @@ static const char rcsid[] = #endif /* not lint */ #include +#include #include #include @@ -82,7 +83,6 @@ pass1() * Find all allocated blocks. */ memset(&idesc, 0, sizeof(struct inodesc)); - idesc.id_type = ADDR; idesc.id_func = pass1check; n_files = n_blks = 0; for (c = 0; c < sblock.fs_ncg; c++) { @@ -306,6 +306,10 @@ checkinode(inumber, idesc) } badblk = dupblk = 0; idesc->id_number = inumber; + if (dp->di_flags & SF_SNAPSHOT) + idesc->id_type = SNAP; + else + idesc->id_type = ADDR; (void)ckinode(dp, idesc); idesc->id_entryno *= btodb(sblock.fs_fsize); if (dp->di_blocks != idesc->id_entryno) { @@ -341,6 +345,21 @@ pass1check(idesc) register struct dups *dlp; struct dups *new; + if (idesc->id_type == SNAP) { + if (blkno == BLK_NOCOPY) + return (KEEPON); + if (idesc->id_number == cursnapshot) { + if (blkno == blkstofrags(&sblock, idesc->id_lbn)) + return (KEEPON); + if (blkno == BLK_SNAP) { + blkno = blkstofrags(&sblock, idesc->id_lbn); + idesc->id_entryno -= idesc->id_numfrags; + } + } else { + if (blkno == BLK_SNAP) + return (KEEPON); + } + } if ((anyout = chkrange(blkno, idesc->id_numfrags)) != 0) { blkerror(idesc->id_number, "BAD", blkno); if (badblk++ >= MAXBAD) { diff --git a/sbin/fsck_ifs/pass5.c b/sbin/fsck_ifs/pass5.c index d5edaace1690..e39562579771 100644 --- a/sbin/fsck_ifs/pass5.c +++ b/sbin/fsck_ifs/pass5.c @@ -53,12 +53,12 @@ void pass5() { int c, blk, frags, basesize, sumsize, mapsize, savednrpos = 0; - int inomapsize, blkmapsize; + int inomapsize, blkmapsize, astart, aend, ustart, uend; struct fs *fs = &sblock; struct cg *cg = &cgrp; ufs_daddr_t dbase, dmax; ufs_daddr_t d; - long i, j, k; + long i, j, k, l, m, n; struct csum *cs; struct csum cstotal; struct inodesc idesc[3]; @@ -314,6 +314,72 @@ pass5() &cg_blktot(newcg)[0], (size_t)sumsize); cgdirty(); } + if (debug) { + for (i = 0; i < inomapsize; i++) { + j = cg_inosused(newcg)[i]; + k = cg_inosused(cg)[i]; + if (j == k) + continue; + for (m = 0, l = 1; m < NBBY; m++, l <<= 1) { + if ((j & l) == (k & l)) + continue; + n = c * fs->fs_ipg + i * NBBY + m; + if ((j & l) != 0) + pwarn("%s INODE %d MARKED %s\n", + "ALLOCATED", n, "FREE"); + else + pwarn("%s INODE %d MARKED %s\n", + "UNALLOCATED", n, "USED"); + } + } + astart = ustart = -1; + for (i = 0; i < blkmapsize; i++) { + j = cg_blksfree(cg)[i]; + k = cg_blksfree(newcg)[i]; + if (j == k) + continue; + for (m = 0, l = 1; m < NBBY; m++, l <<= 1) { + if ((j & l) == (k & l)) + continue; + n = c * fs->fs_fpg + i * NBBY + m; + if ((j & l) != 0) { + if (astart == -1) { + astart = aend = n; + continue; + } + if (aend + 1 == n) { + aend = n; + continue; + } + pwarn("%s FRAGS %d-%d %s\n", + "ALLOCATED", astart, aend, + "MARKED FREE"); + astart = aend = n; + } else { + if (ustart == -1) { + ustart = uend = n; + continue; + } + if (uend + 1 == n) { + uend = n; + continue; + } + pwarn("%s FRAGS %d-%d %s\n", + "UNALLOCATED", ustart, uend, + "MARKED USED"); + ustart = uend = n; + } + } + } + if (astart != -1) + pwarn("%s FRAGS %d-%d %s\n", + "ALLOCATED", astart, aend, + "MARKED FREE"); + if (ustart != -1) + pwarn("%s FRAGS %d-%d %s\n", + "UNALLOCATED", ustart, uend, + "MARKED USED"); + } if (usedsoftdep) { for (i = 0; i < inomapsize; i++) { j = cg_inosused(newcg)[i]; diff --git a/sbin/fsck_ifs/setup.c b/sbin/fsck_ifs/setup.c index 49bbc8d99883..6c4098c11bc7 100644 --- a/sbin/fsck_ifs/setup.c +++ b/sbin/fsck_ifs/setup.c @@ -82,6 +82,7 @@ setup(dev) havesb = 0; fswritefd = -1; + cursnapshot = 0; skipclean = fflag ? 0 : preen; if (stat(dev, &statb) < 0) { printf("Can't stat %s: %s\n", dev, strerror(errno)); @@ -89,9 +90,13 @@ setup(dev) } if ((statb.st_mode & S_IFMT) != S_IFCHR && (statb.st_mode & S_IFMT) != S_IFBLK) { - pfatal("%s is not a disk device", dev); - if (reply("CONTINUE") == 0) - return (0); + if ((statb.st_flags & SF_SNAPSHOT) != 0) { + cursnapshot = statb.st_ino; + } else { + pfatal("%s is not a disk device", dev); + if (reply("CONTINUE") == 0) + return (0); + } } if ((fsreadfd = open(dev, O_RDONLY)) < 0) { printf("Can't open %s: %s\n", dev, strerror(errno)); @@ -378,6 +383,8 @@ readsb(listerr) memmove(altsblock.fs_csp, sblock.fs_csp, sizeof sblock.fs_csp); altsblock.fs_maxcluster = sblock.fs_maxcluster; memmove(altsblock.fs_fsmnt, sblock.fs_fsmnt, sizeof sblock.fs_fsmnt); + memmove(altsblock.fs_snapinum, sblock.fs_snapinum, + sizeof sblock.fs_snapinum); memmove(altsblock.fs_sparecon, sblock.fs_sparecon, sizeof sblock.fs_sparecon); /*