Add kernel support for running fsck on active filesystems.

This commit is contained in:
Kirk McKusick 2001-03-21 04:09:01 +00:00
parent 31c6ce0aed
commit 812b1d416c
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=74548
6 changed files with 278 additions and 43 deletions

View File

@ -41,6 +41,7 @@
#include <sys/bio.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/mount.h>
@ -327,8 +328,6 @@ ffs_realloccg(ip, lbprev, bpref, osize, nsize, cred, bpp)
return (ENOSPC);
}
SYSCTL_NODE(_vfs, OID_AUTO, ffs, CTLFLAG_RW, 0, "FFS filesystem");
/*
* Reallocate a sequence of blocks into a contiguous sequence of blocks.
*
@ -343,11 +342,14 @@ SYSCTL_NODE(_vfs, OID_AUTO, ffs, CTLFLAG_RW, 0, "FFS filesystem");
* Note that the error return is not reflected back to the user. Rather
* the previous block allocation will be used.
*/
SYSCTL_NODE(_vfs, OID_AUTO, ffs, CTLFLAG_RW, 0, "FFS filesystem");
static int doasyncfree = 1;
SYSCTL_INT(_vfs_ffs, FFS_ASYNCFREE, doasyncfree, CTLFLAG_RW, &doasyncfree, 0, "");
SYSCTL_INT(_vfs_ffs, OID_AUTO, doasyncfree, CTLFLAG_RW, &doasyncfree, 0, "");
static int doreallocblks = 1;
SYSCTL_INT(_vfs_ffs, FFS_REALLOCBLKS, doreallocblks, CTLFLAG_RW, &doreallocblks, 0, "");
SYSCTL_INT(_vfs_ffs, OID_AUTO, doreallocblks, CTLFLAG_RW, &doreallocblks, 0, "");
#ifdef DEBUG
static volatile int prtrealloc = 0;
@ -612,7 +614,7 @@ ffs_valloc(pvp, mode, cred, vpp)
ip->i_mode, (u_long)ip->i_number, fs->fs_fsmnt);
panic("ffs_valloc: dup alloc");
}
if (ip->i_blocks) { /* XXX */
if (ip->i_blocks && (fs->fs_flags & FS_UNCLEAN) == 0) { /* XXX */
printf("free inode %s/%lu had %ld blocks\n",
fs->fs_fsmnt, (u_long)ino, (long)ip->i_blocks);
ip->i_blocks = 0;
@ -1478,7 +1480,7 @@ ffs_checkblk(ip, bno, size)
* Free an inode.
*/
int
ffs_vfree( pvp, ino, mode)
ffs_vfree(pvp, ino, mode)
struct vnode *pvp;
ino_t ino;
int mode;
@ -1487,27 +1489,25 @@ ffs_vfree( pvp, ino, mode)
softdep_freefile(pvp, ino, mode);
return (0);
}
return (ffs_freefile(pvp, ino, mode));
return (ffs_freefile(VTOI(pvp), ino, mode));
}
/*
* Do the actual free operation.
* The specified inode is placed back in the free map.
*/
int
ffs_freefile( pvp, ino, mode)
struct vnode *pvp;
int
ffs_freefile(pip, ino, mode)
struct inode *pip;
ino_t ino;
int mode;
{
register struct fs *fs;
register struct cg *cgp;
register struct inode *pip;
struct buf *bp;
int error, cg;
u_int8_t *inosused;
pip = VTOI(pvp);
fs = pip->i_fs;
if ((u_int)ino >= fs->fs_ipg * fs->fs_ncg)
panic("ffs_vfree: range: dev = (%d,%d), ino = %d, fs = %s",
@ -1529,8 +1529,8 @@ ffs_vfree( pvp, ino, mode)
inosused = cg_inosused(cgp);
ino %= fs->fs_ipg;
if (isclr(inosused, ino)) {
printf("dev = %s, ino = %lu, fs = %s\n",
devtoname(pip->i_dev), (u_long)ino, fs->fs_fsmnt);
printf("dev = %s, ino = %lu, fs = %s\n", devtoname(pip->i_dev),
(u_long)ino + cg * fs->fs_ipg, fs->fs_fsmnt);
if (fs->fs_ronly == 0)
panic("ffs_vfree: freeing free inode");
}
@ -1726,3 +1726,208 @@ ffs_fserr(fs, uid, cp)
log(LOG_ERR, "pid %d (%s), uid %d on %s: %s\n", p ? p->p_pid : -1,
p ? p->p_comm : "-", uid, fs->fs_fsmnt, cp);
}
/*
* This function provides the capability for the fsck program to
* update an active filesystem. Six operations are provided:
*
* adjrefcnt(inode, amt) - adjusts the reference count on the
* specified inode by the specified amount. Under normal
* operation the count should always go down. Decrementing
* the count to zero will cause the inode to be freed.
* adjblkcnt(inode, amt) - adjust the number of blocks used to
* by the specifed amount.
* freedirs(inode, count) - directory inodes [inode..inode + count - 1]
* are marked as free. Inodes should never have to be marked
* as in use.
* freefiles(inode, count) - file inodes [inode..inode + count - 1]
* are marked as free. Inodes should never have to be marked
* as in use.
* freeblks(blockno, size) - blocks [blockno..blockno + size - 1]
* are marked as free. Blocks should never have to be marked
* as in use.
* setflags(flags, set/clear) - the fs_flags field has the specified
* flags set (second parameter +1) or cleared (second parameter -1).
*/
static int sysctl_ffs_fsck __P((SYSCTL_HANDLER_ARGS));
SYSCTL_PROC(_vfs_ffs, FFS_ADJ_REFCNT, adjrefcnt, CTLFLAG_WR|CTLTYPE_STRUCT,
0, 0, sysctl_ffs_fsck, "S,fsck", "Adjust Inode Reference Count");
SYSCTL_NODE(_vfs_ffs, FFS_ADJ_BLKCNT, adjblkcnt, CTLFLAG_WR,
sysctl_ffs_fsck, "Adjust Inode Used Blocks Count");
SYSCTL_NODE(_vfs_ffs, FFS_DIR_FREE, freedirs, CTLFLAG_WR,
sysctl_ffs_fsck, "Free Range of Directory Inodes");
SYSCTL_NODE(_vfs_ffs, FFS_FILE_FREE, freefiles, CTLFLAG_WR,
sysctl_ffs_fsck, "Free Range of File Inodes");
SYSCTL_NODE(_vfs_ffs, FFS_BLK_FREE, freeblks, CTLFLAG_WR,
sysctl_ffs_fsck, "Free Range of Blocks");
SYSCTL_NODE(_vfs_ffs, FFS_SET_FLAGS, setflags, CTLFLAG_WR,
sysctl_ffs_fsck, "Change Filesystem Flags");
#ifdef DEBUG
static int fsckcmds = 0;
SYSCTL_INT(_debug, OID_AUTO, fsckcmds, CTLFLAG_RW, &fsckcmds, 0, "");
#endif /* DEBUG */
static int
sysctl_ffs_fsck(SYSCTL_HANDLER_ARGS)
{
struct fsck_cmd cmd;
struct inode tip;
struct ufsmount *ump;
struct vnode *vp;
struct inode *ip;
struct mount *mp;
struct fs *fs;
ufs_daddr_t blkno;
long blkcnt, blksize;
struct file *fp;
int filetype, error;
if (req->newlen > sizeof cmd)
return (EBADRPC);
if ((error = SYSCTL_IN(req, &cmd, sizeof cmd)) != 0)
return (error);
if (cmd.version != FFS_CMD_VERSION)
return (ERPCMISMATCH);
if ((error = getvnode(curproc->p_fd, cmd.handle, &fp)) != 0)
return (error);
mp = ((struct vnode *)fp->f_data)->v_mount;
if (mp->mnt_flag & MNT_RDONLY)
return (EROFS);
ump = VFSTOUFS(mp);
fs = ump->um_fs;
filetype = IFREG;
switch (oidp->oid_number) {
case FFS_SET_FLAGS:
#ifdef DEBUG
if (fsckcmds)
printf("%s: %s flags\n", mp->mnt_stat.f_mntonname,
cmd.size > 0 ? "set" : "clear");
#endif /* DEBUG */
if (cmd.size > 0)
fs->fs_flags |= (long)cmd.value;
else
fs->fs_flags &= ~(long)cmd.value;
break;
case FFS_ADJ_REFCNT:
#ifdef DEBUG
if (fsckcmds) {
printf("%s: adjust inode %d count by %ld\n",
mp->mnt_stat.f_mntonname, (ino_t)cmd.value,
cmd.size);
}
#endif /* DEBUG */
if ((error = VFS_VGET(mp, (ino_t)cmd.value, &vp)) != 0)
return (error);
ip = VTOI(vp);
ip->i_nlink += cmd.size;
ip->i_effnlink += cmd.size;
ip->i_flag |= IN_CHANGE;
if (DOINGSOFTDEP(vp))
softdep_change_linkcnt(ip);
vput(vp);
break;
case FFS_ADJ_BLKCNT:
#ifdef DEBUG
if (fsckcmds) {
printf("%s: adjust inode %d block count by %ld\n",
mp->mnt_stat.f_mntonname, (ino_t)cmd.value,
cmd.size);
}
#endif /* DEBUG */
if ((error = VFS_VGET(mp, (ino_t)cmd.value, &vp)) != 0)
return (error);
ip = VTOI(vp);
ip->i_blocks += cmd.size;
ip->i_flag |= IN_CHANGE;
vput(vp);
break;
case FFS_DIR_FREE:
filetype = IFDIR;
/* fall through */
case FFS_FILE_FREE:
#ifdef DEBUG
if (fsckcmds) {
if (cmd.size == 1)
printf("%s: free %s inode %d\n",
mp->mnt_stat.f_mntonname,
filetype == IFDIR ? "directory" : "file",
(ino_t)cmd.value);
else
printf("%s: free %s inodes %d-%d\n",
mp->mnt_stat.f_mntonname,
filetype == IFDIR ? "directory" : "file",
(ino_t)cmd.value);
(ino_t)cmd.value + cmd.size - 1);
}
#endif /* DEBUG */
tip.i_devvp = ump->um_devvp;
tip.i_dev = ump->um_dev;
tip.i_fs = fs;
while (cmd.size > 0) {
if ((error = ffs_freefile(&tip, cmd.value, filetype)))
return (error);
cmd.size -= 1;
cmd.value += 1;
}
break;
case FFS_BLK_FREE:
#ifdef DEBUG
if (fsckcmds) {
if (cmd.size == 1)
printf("%s: free block %d\n",
mp->mnt_stat.f_mntonname,
(ufs_daddr_t)cmd.value);
else
printf("%s: free blocks %d-%ld\n",
mp->mnt_stat.f_mntonname,
(ufs_daddr_t)cmd.value,
(ufs_daddr_t)cmd.value + cmd.size - 1);
}
#endif /* DEBUG */
tip.i_number = ROOTINO;
tip.i_devvp = ump->um_devvp;
tip.i_dev = ump->um_dev;
tip.i_fs = fs;
tip.i_size = cmd.size * fs->fs_fsize;
tip.i_uid = 0;
tip.i_vnode = NULL;
blkno = (ufs_daddr_t)cmd.value;
blkcnt = cmd.size;
blksize = fs->fs_frag - (blkno % fs->fs_frag);
while (blkcnt > 0) {
if (blksize > blkcnt)
blksize = blkcnt;
ffs_blkfree(&tip, blkno, blksize * fs->fs_fsize);
blkno += blksize;
blkcnt -= blksize;
blksize = fs->fs_frag;
}
break;
default:
#ifdef DEBUG
if (fsckcmds) {
printf("Invalid request %d from fsck\n",
oidp->oid_number);
}
#endif /* DEBUG */
return(EINVAL);
}
return (0);
}

View File

@ -37,21 +37,6 @@
#ifndef _UFS_FFS_EXTERN_H
#define _UFS_FFS_EXTERN_H
/*
* Sysctl values for the fast filesystem.
*/
#define FFS_REALLOCBLKS 3 /* block reallocation enabled */
#define FFS_ASYNCFREE 4 /* asynchronous block freeing enabled */
#define FFS_MAXID 5 /* number of valid ffs ids */
#define FFS_NAMES { \
{ 0, 0 }, \
{ 0, 0 }, \
{ 0, 0 }, \
{ "doreallocblks", CTLTYPE_INT }, \
{ "doasyncfree", CTLTYPE_INT }, \
}
struct buf;
struct fid;
struct fs;
@ -80,7 +65,7 @@ void ffs_clrblock __P((struct fs *, u_char *, ufs_daddr_t));
int ffs_fhtovp __P((struct mount *, struct fid *, struct vnode **));
int ffs_flushfiles __P((struct mount *, int, struct proc *));
void ffs_fragacct __P((struct fs *, int, int32_t [], int));
int ffs_freefile __P(( struct vnode *, ino_t, int ));
int ffs_freefile __P((struct inode *, ino_t, int ));
int ffs_isblock __P((struct fs *, u_char *, ufs_daddr_t));
int ffs_isfreeblock __P((struct fs *, unsigned char *, ufs_daddr_t));
int ffs_mountfs __P((struct vnode *, struct mount *, struct proc *,

View File

@ -171,7 +171,7 @@ static void free_allocdirect __P((struct allocdirectlst *,
struct allocdirect *, int));
static int check_inode_unwritten __P((struct inodedep *));
static int free_inodedep __P((struct inodedep *));
static void handle_workitem_freeblocks __P((struct freeblks *));
static void handle_workitem_freeblocks __P((struct freeblks *, int));
static void merge_inode_lists __P((struct inodedep *));
static void setup_allocindir_phase2 __P((struct buf *, struct inode *,
struct allocindir *));
@ -684,7 +684,7 @@ process_worklist_item(matchmnt, flags)
"process_worklist_item");
if (mp == matchmnt)
matchcnt += 1;
handle_workitem_freeblocks(WK_FREEBLKS(wk));
handle_workitem_freeblocks(WK_FREEBLKS(wk), flags & LK_NOWAIT);
break;
case D_FREEFRAG:
@ -1113,7 +1113,7 @@ softdep_mount(devvp, mp, fs, cred)
}
#ifdef DEBUG
if (bcmp(&cstotal, &fs->fs_cstotal, sizeof cstotal))
printf("ffs_mountfs: superblock updated for soft updates\n");
printf("%s: superblock summary recomputed\n", fs->fs_fsmnt);
#endif
bcopy(&cstotal, &fs->fs_cstotal, sizeof cstotal);
return (0);
@ -1819,7 +1819,7 @@ softdep_setup_freeblocks(ip, length)
* the dependencies.
*/
if (!delay)
handle_workitem_freeblocks(freeblks);
handle_workitem_freeblocks(freeblks, 0);
}
/*
@ -2082,10 +2082,12 @@ free_inodedep(inodedep)
* performed in this function.
*/
static void
handle_workitem_freeblocks(freeblks)
handle_workitem_freeblocks(freeblks, flags)
struct freeblks *freeblks;
int flags;
{
struct inode tip;
struct inode tip, *ip;
struct vnode *vp;
ufs_daddr_t bn;
struct fs *fs;
int i, level, bsize;
@ -2130,13 +2132,27 @@ handle_workitem_freeblocks(freeblks)
ffs_blkfree(&tip, bn, bsize);
blocksreleased += btodb(bsize);
}
/*
* If we still have not finished background cleanup, then check
* to see if the block count needs to be adjusted.
*/
if (freeblks->fb_chkcnt != blocksreleased &&
(fs->fs_flags & FS_UNCLEAN) != 0 && (flags & LK_NOWAIT) == 0 &&
VFS_VGET(freeblks->fb_mnt, freeblks->fb_previousinum, &vp) == 0) {
ip = VTOI(vp);
ip->i_blocks += freeblks->fb_chkcnt - blocksreleased;
ip->i_flag |= IN_CHANGE;
vput(vp);
}
#ifdef DIAGNOSTIC
if (freeblks->fb_chkcnt != blocksreleased)
if (freeblks->fb_chkcnt != blocksreleased &&
((fs->fs_flags & FS_UNCLEAN) == 0 || (flags & LK_NOWAIT) != 0))
printf("handle_workitem_freeblocks: block count");
if (allerror)
softdep_error("handle_workitem_freeblks", allerror);
#endif /* DIAGNOSTIC */
WORKITEM_FREE(freeblks, D_FREEBLKS);
}
@ -2876,7 +2892,6 @@ handle_workitem_freefile(freefile)
struct freefile *freefile;
{
struct fs *fs;
struct vnode vp;
struct inode tip;
struct inodedep *idp;
int error;
@ -2892,9 +2907,7 @@ handle_workitem_freefile(freefile)
tip.i_devvp = freefile->fx_devvp;
tip.i_dev = freefile->fx_devvp->v_rdev;
tip.i_fs = fs;
tip.i_vnode = &vp;
vp.v_data = &tip;
if ((error = ffs_freefile(&vp, freefile->fx_oldinum, freefile->fx_mode)) != 0)
if ((error = ffs_freefile(&tip, freefile->fx_oldinum, freefile->fx_mode)) != 0)
softdep_error("handle_workitem_freefile", error);
WORKITEM_FREE(freefile, D_FREEFILE);
}

View File

@ -223,7 +223,8 @@ ffs_mount(mp, path, data, ndp, p)
fs->fs_flags &= ~FS_UNCLEAN;
if (fs->fs_clean == 0) {
fs->fs_flags |= FS_UNCLEAN;
if (mp->mnt_flag & MNT_FORCE) {
if ((mp->mnt_flag & MNT_FORCE) ||
(fs->fs_flags & FS_DOSOFTDEP)) {
printf("WARNING: %s was not %s\n",
fs->fs_fsmnt, "properly dismounted");
} else {
@ -584,7 +585,8 @@ ffs_mountfs(devvp, mp, p, malloctype)
fs->fs_flags &= ~FS_UNCLEAN;
if (fs->fs_clean == 0) {
fs->fs_flags |= FS_UNCLEAN;
if (ronly || (mp->mnt_flag & MNT_FORCE)) {
if (ronly || (mp->mnt_flag & MNT_FORCE) ||
(fs->fs_flags & FS_DOSOFTDEP)) {
printf(
"WARNING: %s was not properly dismounted\n",
fs->fs_fsmnt);

View File

@ -165,6 +165,28 @@
#define BLK_NOCOPY ((ufs_daddr_t)(1))
#define BLK_SNAP ((ufs_daddr_t)(2))
/*
* Sysctl values for the fast filesystem.
*/
#define FFS_ADJ_REFCNT 1 /* adjust inode reference count */
#define FFS_ADJ_BLKCNT 2 /* adjust inode used block count */
#define FFS_BLK_FREE 3 /* free range of blocks in map */
#define FFS_DIR_FREE 4 /* free specified dir inodes in map */
#define FFS_FILE_FREE 5 /* free specified file inodes in map */
#define FFS_SET_FLAGS 6 /* set filesystem flags */
#define FFS_MAXID 7 /* number of valid ffs ids */
/*
* Command structure passed in to the filesystem to adjust filesystem values.
*/
#define FFS_CMD_VERSION 0x05181979 /* version ID */
struct fsck_cmd {
int version; /* version of command structure */
int handle; /* reference to filesystem to be changed */
off_t value; /* inode or block number to be affected */
long size; /* amount or range to be adjusted */
};
/*
* Per cylinder group information; summarized in blocks allocated
* from first cylinder group data blocks. These blocks have to be

View File

@ -87,10 +87,18 @@ ufs_inactive(ap)
ufs_extattr_vnode_inactive(ap->a_vp, ap->a_p);
#endif
error = UFS_TRUNCATE(vp, (off_t)0, 0, NOCRED, p);
/*
* Setting the mode to zero needs to wait for the inode
* to be written just as does a change to the link count.
* So, rather than creating a new entry point to do the
* same thing, we just use softdep_change_linkcnt().
*/
ip->i_rdev = 0;
mode = ip->i_mode;
ip->i_mode = 0;
ip->i_flag |= IN_CHANGE | IN_UPDATE;
if (DOINGSOFTDEP(vp))
softdep_change_linkcnt(ip);
UFS_VFREE(vp, ip->i_number, mode);
}
if (ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) {