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); /*