summaryrefslogtreecommitdiffstats
path: root/sys/ufs
diff options
context:
space:
mode:
authormckusick <mckusick@FreeBSD.org>2003-02-22 00:29:51 +0000
committermckusick <mckusick@FreeBSD.org>2003-02-22 00:29:51 +0000
commit5340f28b154d4fa0a65fbe142b2d7dec10966348 (patch)
tree90f2dbda7eaecc3abfd965b963c9862a47d2bf81 /sys/ufs
parente6aeffdeb5cd5ab10425b1d33b16e6c79b54c859 (diff)
downloadFreeBSD-src-5340f28b154d4fa0a65fbe142b2d7dec10966348.zip
FreeBSD-src-5340f28b154d4fa0a65fbe142b2d7dec10966348.tar.gz
This patch fixes a bug on an active filesystem on which a snapshot
is being taken from panicing with either "freeing free block" or "freeing free inode". The problem arises when the snapshot code is scanning the filesystem looking for inodes with a reference count of zero (e.g., unlinked but still open) so that it can expunge them from its view. If it encounters a reclaimed vnode and has to restart its scan, then it will panic if it encounters and tries to free an inode that it has already processed. The fix is to check each candidate inode to see if it has already been processed before trying to delete it from the snapshot image. Sponsored by: DARPA & NAI Labs.
Diffstat (limited to 'sys/ufs')
-rw-r--r--sys/ufs/ffs/ffs_alloc.c41
-rw-r--r--sys/ufs/ffs/ffs_extern.h3
-rw-r--r--sys/ufs/ffs/ffs_snapshot.c4
3 files changed, 47 insertions, 1 deletions
diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c
index 745050c..881fdca 100644
--- a/sys/ufs/ffs/ffs_alloc.c
+++ b/sys/ufs/ffs/ffs_alloc.c
@@ -1934,6 +1934,47 @@ ffs_freefile(fs, devvp, ino, mode)
}
/*
+ * Check to see if a file is free.
+ */
+int
+ffs_checkfreefile(fs, devvp, ino)
+ struct fs *fs;
+ struct vnode *devvp;
+ ino_t ino;
+{
+ struct cg *cgp;
+ struct buf *bp;
+ ufs2_daddr_t cgbno;
+ int error, ret, cg;
+ u_int8_t *inosused;
+
+ cg = ino_to_cg(fs, ino);
+ if (devvp->v_type != VCHR) {
+ /* devvp is a snapshot */
+ cgbno = fragstoblks(fs, cgtod(fs, cg));
+ } else {
+ /* devvp is a normal disk device */
+ cgbno = fsbtodb(fs, cgtod(fs, cg));
+ }
+ if ((u_int)ino >= fs->fs_ipg * fs->fs_ncg)
+ return (1);
+ if ((error = bread(devvp, cgbno, (int)fs->fs_cgsize, NOCRED, &bp))) {
+ brelse(bp);
+ return (1);
+ }
+ cgp = (struct cg *)bp->b_data;
+ if (!cg_chkmagic(cgp)) {
+ brelse(bp);
+ return (1);
+ }
+ inosused = cg_inosused(cgp);
+ ino %= fs->fs_ipg;
+ ret = isclr(inosused, ino);
+ brelse(bp);
+ return (ret);
+}
+
+/*
* Find a block of the specified size in the specified cylinder group.
*
* It is a panic if a request is made to find a block if none are
diff --git a/sys/ufs/ffs/ffs_extern.h b/sys/ufs/ffs/ffs_extern.h
index 3c9c885..8618123 100644
--- a/sys/ufs/ffs/ffs_extern.h
+++ b/sys/ufs/ffs/ffs_extern.h
@@ -63,12 +63,13 @@ int ffs_blkatoff(struct vnode *, off_t, char **, struct buf **);
void ffs_blkfree(struct fs *, struct vnode *, ufs2_daddr_t, long, ino_t);
ufs2_daddr_t ffs_blkpref_ufs1(struct inode *, ufs_lbn_t, int, ufs1_daddr_t *);
ufs2_daddr_t ffs_blkpref_ufs2(struct inode *, ufs_lbn_t, int, ufs2_daddr_t *);
+int ffs_checkfreefile(struct fs *, struct vnode *, ino_t);
void ffs_clrblock(struct fs *, u_char *, ufs1_daddr_t);
void ffs_clusteracct (struct fs *, struct cg *, ufs1_daddr_t, int);
vfs_fhtovp_t ffs_fhtovp;
int ffs_flushfiles(struct mount *, int, struct thread *);
void ffs_fragacct(struct fs *, int, int32_t [], int);
-int ffs_freefile(struct fs *, struct vnode *, ino_t, int );
+int ffs_freefile(struct fs *, struct vnode *, ino_t, int);
int ffs_isblock(struct fs *, u_char *, ufs1_daddr_t);
void ffs_load_inode(struct buf *, struct inode *, struct fs *, ino_t);
int ffs_mountroot(void);
diff --git a/sys/ufs/ffs/ffs_snapshot.c b/sys/ufs/ffs/ffs_snapshot.c
index 6c6fe5e..3bfc1b9 100644
--- a/sys/ufs/ffs/ffs_snapshot.c
+++ b/sys/ufs/ffs/ffs_snapshot.c
@@ -420,6 +420,10 @@ loop:
if (vn_lock(xvp, LK_EXCLUSIVE, td) != 0)
goto loop;
xp = VTOI(xvp);
+ if (ffs_checkfreefile(copy_fs, vp, xp->i_number)) {
+ VOP_UNLOCK(xvp, 0, td);
+ continue;
+ }
/*
* If there is a fragment, clear it here.
*/
OpenPOWER on IntegriCloud