Commit cf0310df authored by Kirk McKusick's avatar Kirk McKusick
Browse files

Fix bug 253158 - Panic: snapacct_ufs2: bad block - mksnap_ffs(8) crash

PR:           253158

(cherry picked from commit 8563de2f)
(cherry picked from commit c31480a1)
parent ffdcad75
...@@ -58,6 +58,9 @@ __FBSDID("$FreeBSD$"); ...@@ -58,6 +58,9 @@ __FBSDID("$FreeBSD$");
#include <sys/rwlock.h> #include <sys/rwlock.h>
#include <sys/vnode.h> #include <sys/vnode.h>
#include <vm/vm.h>
#include <vm/vm_extern.h>
#include <geom/geom.h> #include <geom/geom.h>
#include <ufs/ufs/extattr.h> #include <ufs/ufs/extattr.h>
...@@ -307,21 +310,21 @@ ffs_snapshot(mp, snapfile) ...@@ -307,21 +310,21 @@ ffs_snapshot(mp, snapfile)
ip = VTOI(vp); ip = VTOI(vp);
devvp = ITODEVVP(ip); devvp = ITODEVVP(ip);
/* /*
* Allocate and copy the last block contents so as to be able * Calculate the size of the filesystem then allocate the block
* to set size to that of the filesystem. * immediately following the last block of the filesystem that
* will contain the snapshot list. This operation allows us to
* set the size of the snapshot.
*/ */
numblks = howmany(fs->fs_size, fs->fs_frag); numblks = howmany(fs->fs_size, fs->fs_frag);
error = UFS_BALLOC(vp, lblktosize(fs, (off_t)(numblks - 1)), error = UFS_BALLOC(vp, lblktosize(fs, (off_t)numblks),
fs->fs_bsize, KERNCRED, BA_CLRBUF, &bp); fs->fs_bsize, KERNCRED, BA_CLRBUF, &bp);
if (error) if (error)
goto out; goto out;
ip->i_size = lblktosize(fs, (off_t)numblks); bawrite(bp);
ip->i_size = lblktosize(fs, (off_t)(numblks + 1));
vnode_pager_setsize(vp, ip->i_size);
DIP_SET(ip, i_size, ip->i_size); DIP_SET(ip, i_size, ip->i_size);
ip->i_flag |= IN_SIZEMOD | IN_CHANGE | IN_UPDATE; ip->i_flag |= IN_SIZEMOD | IN_CHANGE | IN_UPDATE;
error = readblock(vp, bp, numblks - 1);
bawrite(bp);
if (error != 0)
goto out;
/* /*
* Preallocate critical data structures so that we can copy * Preallocate critical data structures so that we can copy
* them in without further allocation after we suspend all * them in without further allocation after we suspend all
...@@ -366,8 +369,11 @@ ffs_snapshot(mp, snapfile) ...@@ -366,8 +369,11 @@ ffs_snapshot(mp, snapfile)
if (error) if (error)
goto out; goto out;
bawrite(nbp); bawrite(nbp);
if (cg % 10 == 0) if (cg % 10 == 0) {
ffs_syncvnode(vp, MNT_WAIT, 0); error = ffs_syncvnode(vp, MNT_WAIT, 0);
if (error != 0)
goto out;
}
} }
/* /*
* Copy all the cylinder group maps. Although the * Copy all the cylinder group maps. Although the
...@@ -439,21 +445,11 @@ ffs_snapshot(mp, snapfile) ...@@ -439,21 +445,11 @@ ffs_snapshot(mp, snapfile)
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if (ip->i_effnlink == 0) { if (ip->i_effnlink == 0) {
error = ENOENT; /* Snapshot file unlinked */ error = ENOENT; /* Snapshot file unlinked */
goto out1; goto resumefs;
} }
if (collectsnapstats) if (collectsnapstats)
nanotime(&starttime); nanotime(&starttime);
/* The last block might have changed. Copy it again to be sure. */
error = UFS_BALLOC(vp, lblktosize(fs, (off_t)(numblks - 1)),
fs->fs_bsize, KERNCRED, BA_CLRBUF, &bp);
if (error != 0)
goto out1;
error = readblock(vp, bp, numblks - 1);
bp->b_flags |= B_VALIDSUSPWRT;
bawrite(bp);
if (error != 0)
goto out1;
/* /*
* First, copy all the cylinder group maps that have changed. * First, copy all the cylinder group maps that have changed.
*/ */
...@@ -464,11 +460,11 @@ ffs_snapshot(mp, snapfile) ...@@ -464,11 +460,11 @@ ffs_snapshot(mp, snapfile)
error = UFS_BALLOC(vp, lfragtosize(fs, cgtod(fs, cg)), error = UFS_BALLOC(vp, lfragtosize(fs, cgtod(fs, cg)),
fs->fs_bsize, KERNCRED, 0, &nbp); fs->fs_bsize, KERNCRED, 0, &nbp);
if (error) if (error)
goto out1; goto resumefs;
error = cgaccount(cg, vp, nbp, 2); error = cgaccount(cg, vp, nbp, 2);
bawrite(nbp); bawrite(nbp);
if (error) if (error)
goto out1; goto resumefs;
} }
/* /*
* Grab a copy of the superblock and its summary information. * Grab a copy of the superblock and its summary information.
...@@ -496,10 +492,7 @@ ffs_snapshot(mp, snapfile) ...@@ -496,10 +492,7 @@ ffs_snapshot(mp, snapfile)
if ((error = bread(devvp, fsbtodb(fs, fs->fs_csaddr + loc), if ((error = bread(devvp, fsbtodb(fs, fs->fs_csaddr + loc),
len, KERNCRED, &bp)) != 0) { len, KERNCRED, &bp)) != 0) {
brelse(bp); brelse(bp);
free(copy_fs->fs_csp, M_UFSMNT); goto resumefs;
free(copy_fs, M_UFSMNT);
copy_fs = NULL;
goto out1;
} }
bcopy(bp->b_data, space, (u_int)len); bcopy(bp->b_data, space, (u_int)len);
space = (char *)space + len; space = (char *)space + len;
...@@ -521,10 +514,27 @@ ffs_snapshot(mp, snapfile) ...@@ -521,10 +514,27 @@ ffs_snapshot(mp, snapfile)
* Note that we skip unlinked snapshot files as they will * Note that we skip unlinked snapshot files as they will
* be handled separately below. * be handled separately below.
* *
* We also calculate the needed size for the snapshot list. * We also calculate the size needed for the snapshot list.
* Initial number of entries is composed of:
* - one for each cylinder group map
* - one for each block used by superblock summary table
* - one for each snapshot inode block
* - one for the superblock
* - one for the snapshot list
* The direct block entries in the snapshot are always
* copied (see reason below). Note that the superblock and
* the first cylinder group will almost always be allocated
* in the direct blocks, but we add the slop for them in case
* they do not end up there. The snapshot list size may get
* expanded by one because of an update of an inode block for
* an unlinked but still open file when it is expunged.
*
* Because the direct block pointers are always copied, they
* are not added to the list. Instead ffs_copyonwrite()
* explicitly checks for them before checking the snapshot list.
*/ */
snaplistsize = fs->fs_ncg + howmany(fs->fs_cssize, fs->fs_bsize) + snaplistsize = fs->fs_ncg + howmany(fs->fs_cssize, fs->fs_bsize) +
FSMAXSNAP + 1 /* superblock */ + 1 /* last block */ + 1 /* size */; FSMAXSNAP + /* superblock */ 1 + /* snaplist */ 1;
MNT_ILOCK(mp); MNT_ILOCK(mp);
mp->mnt_kern_flag &= ~MNTK_SUSPENDED; mp->mnt_kern_flag &= ~MNTK_SUSPENDED;
MNT_IUNLOCK(mp); MNT_IUNLOCK(mp);
...@@ -604,11 +614,8 @@ ffs_snapshot(mp, snapfile) ...@@ -604,11 +614,8 @@ ffs_snapshot(mp, snapfile)
VOP_UNLOCK(xvp, 0); VOP_UNLOCK(xvp, 0);
vdrop(xvp); vdrop(xvp);
if (error) { if (error) {
free(copy_fs->fs_csp, M_UFSMNT);
free(copy_fs, M_UFSMNT);
copy_fs = NULL;
MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp); MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp);
goto out1; goto resumefs;
} }
} }
/* /*
...@@ -616,12 +623,8 @@ ffs_snapshot(mp, snapfile) ...@@ -616,12 +623,8 @@ ffs_snapshot(mp, snapfile)
*/ */
if (fs->fs_flags & FS_SUJ) { if (fs->fs_flags & FS_SUJ) {
error = softdep_journal_lookup(mp, &xvp); error = softdep_journal_lookup(mp, &xvp);
if (error) { if (error)
free(copy_fs->fs_csp, M_UFSMNT); goto resumefs;
free(copy_fs, M_UFSMNT);
copy_fs = NULL;
goto out1;
}
xp = VTOI(xvp); xp = VTOI(xvp);
if (I_IS_UFS1(xp)) if (I_IS_UFS1(xp))
error = expunge_ufs1(vp, xp, copy_fs, fullacct_ufs1, error = expunge_ufs1(vp, xp, copy_fs, fullacct_ufs1,
...@@ -672,6 +675,27 @@ ffs_snapshot(mp, snapfile) ...@@ -672,6 +675,27 @@ ffs_snapshot(mp, snapfile)
sn->sn_listsize = blkp - snapblklist; sn->sn_listsize = blkp - snapblklist;
VI_UNLOCK(devvp); VI_UNLOCK(devvp);
} }
/*
* Preallocate all the direct blocks in the snapshot inode so
* that we never have to write the inode itself to commit an
* update to the contents of the snapshot. Note that once
* created, the size of the snapshot will never change, so
* there will never be a need to write the inode except to
* update the non-integrity-critical time fields and
* allocated-block count.
*/
for (blockno = 0; blockno < UFS_NDADDR; blockno++) {
if (DIP(ip, i_db[blockno]) != 0)
continue;
error = UFS_BALLOC(vp, lblktosize(fs, blockno),
fs->fs_bsize, KERNCRED, BA_CLRBUF, &bp);
if (error)
goto resumefs;
error = readblock(vp, bp, blockno);
bawrite(bp);
if (error != 0)
goto resumefs;
}
/* /*
* Record snapshot inode. Since this is the newest snapshot, * Record snapshot inode. Since this is the newest snapshot,
* it must be placed at the end of the list. * it must be placed at the end of the list.
...@@ -684,11 +708,15 @@ ffs_snapshot(mp, snapfile) ...@@ -684,11 +708,15 @@ ffs_snapshot(mp, snapfile)
TAILQ_INSERT_TAIL(&sn->sn_head, ip, i_nextsnap); TAILQ_INSERT_TAIL(&sn->sn_head, ip, i_nextsnap);
devvp->v_vflag |= VV_COPYONWRITE; devvp->v_vflag |= VV_COPYONWRITE;
VI_UNLOCK(devvp); VI_UNLOCK(devvp);
resumefs:
ASSERT_VOP_LOCKED(vp, "ffs_snapshot vp"); ASSERT_VOP_LOCKED(vp, "ffs_snapshot vp");
out1: if (error != 0 && copy_fs != NULL) {
KASSERT((sn != NULL && copy_fs != NULL && error == 0) || free(copy_fs->fs_csp, M_UFSMNT);
(sn == NULL && copy_fs == NULL && error != 0), free(copy_fs, M_UFSMNT);
("email phk@ and mckusick@")); copy_fs = NULL;
}
KASSERT(error != 0 || (sn != NULL && copy_fs != NULL),
("missing snapshot setup parameters"));
/* /*
* Resume operation on filesystem. * Resume operation on filesystem.
*/ */
...@@ -762,7 +790,7 @@ ffs_snapshot(mp, snapfile) ...@@ -762,7 +790,7 @@ ffs_snapshot(mp, snapfile)
aiov.iov_base = (void *)snapblklist; aiov.iov_base = (void *)snapblklist;
aiov.iov_len = snaplistsize * sizeof(daddr_t); aiov.iov_len = snaplistsize * sizeof(daddr_t);
auio.uio_resid = aiov.iov_len; auio.uio_resid = aiov.iov_len;
auio.uio_offset = ip->i_size; auio.uio_offset = lblktosize(fs, (off_t)numblks);
auio.uio_segflg = UIO_SYSSPACE; auio.uio_segflg = UIO_SYSSPACE;
auio.uio_rw = UIO_WRITE; auio.uio_rw = UIO_WRITE;
auio.uio_td = td; auio.uio_td = td;
...@@ -781,7 +809,6 @@ ffs_snapshot(mp, snapfile) ...@@ -781,7 +809,6 @@ ffs_snapshot(mp, snapfile)
for (loc = 0; loc < len; loc++) { for (loc = 0; loc < len; loc++) {
error = bread(vp, blkno + loc, fs->fs_bsize, KERNCRED, &nbp); error = bread(vp, blkno + loc, fs->fs_bsize, KERNCRED, &nbp);
if (error) { if (error) {
brelse(nbp);
fs->fs_snapinum[snaploc] = 0; fs->fs_snapinum[snaploc] = 0;
free(snapblklist, M_UFSMNT); free(snapblklist, M_UFSMNT);
goto done; goto done;
...@@ -810,27 +837,6 @@ ffs_snapshot(mp, snapfile) ...@@ -810,27 +837,6 @@ ffs_snapshot(mp, snapfile)
VI_UNLOCK(devvp); VI_UNLOCK(devvp);
if (space != NULL) if (space != NULL)
free(space, M_UFSMNT); free(space, M_UFSMNT);
/*
* Preallocate all the direct blocks in the snapshot inode so
* that we never have to write the inode itself to commit an
* update to the contents of the snapshot. Note that once
* created, the size of the snapshot will never change, so
* there will never be a need to write the inode except to
* update the non-integrity-critical time fields and
* allocated-block count.
*/
for (blockno = 0; blockno < UFS_NDADDR; blockno++) {
if (DIP(ip, i_db[blockno]) != 0)
continue;
error = UFS_BALLOC(vp, lblktosize(fs, blockno),
fs->fs_bsize, KERNCRED, BA_CLRBUF, &bp);
if (error)
break;
error = readblock(vp, bp, blockno);
bawrite(bp);
if (error != 0)
break;
}
done: done:
free(copy_fs->fs_csp, M_UFSMNT); free(copy_fs->fs_csp, M_UFSMNT);
free(copy_fs, M_UFSMNT); free(copy_fs, M_UFSMNT);
...@@ -1545,7 +1551,8 @@ mapacct_ufs2(vp, oldblkp, lastblkp, fs, lblkno, expungetype) ...@@ -1545,7 +1551,8 @@ mapacct_ufs2(vp, oldblkp, lastblkp, fs, lblkno, expungetype)
blkno = *oldblkp; blkno = *oldblkp;
if (blkno == 0 || blkno == BLK_NOCOPY) if (blkno == 0 || blkno == BLK_NOCOPY)
continue; continue;
if (acctit && expungetype == BLK_SNAP && blkno != BLK_SNAP) if (acctit && expungetype == BLK_SNAP && blkno != BLK_SNAP &&
lblkno >= UFS_NDADDR)
*ip->i_snapblklist++ = lblkno; *ip->i_snapblklist++ = lblkno;
if (blkno == BLK_SNAP) if (blkno == BLK_SNAP)
blkno = blkstofrags(fs, lblkno); blkno = blkstofrags(fs, lblkno);
...@@ -2281,6 +2288,10 @@ ffs_copyonwrite(devvp, bp) ...@@ -2281,6 +2288,10 @@ ffs_copyonwrite(devvp, bp)
ip = TAILQ_FIRST(&sn->sn_head); ip = TAILQ_FIRST(&sn->sn_head);
fs = ITOFS(ip); fs = ITOFS(ip);
lbn = fragstoblks(fs, dbtofsb(fs, bp->b_blkno)); lbn = fragstoblks(fs, dbtofsb(fs, bp->b_blkno));
if (lbn < UFS_NDADDR) {
VI_UNLOCK(devvp);
return (0); /* Direct blocks are always copied */
}
snapblklist = sn->sn_blklist; snapblklist = sn->sn_blklist;
upper = sn->sn_listsize - 1; upper = sn->sn_listsize - 1;
lower = 1; lower = 1;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment