diff options
author | mckusick <mckusick@FreeBSD.org> | 2001-03-21 04:09:01 +0000 |
---|---|---|
committer | mckusick <mckusick@FreeBSD.org> | 2001-03-21 04:09:01 +0000 |
commit | 69603157dedb4757ce880ddfc5157558441cf985 (patch) | |
tree | 6378d524f9c6f5c077153375f319c0f93a7682cf /sys/ufs/ffs/ffs_alloc.c | |
parent | 39275d892cc0291136bb750a2f1191137371de42 (diff) | |
download | FreeBSD-src-69603157dedb4757ce880ddfc5157558441cf985.zip FreeBSD-src-69603157dedb4757ce880ddfc5157558441cf985.tar.gz |
Add kernel support for running fsck on active filesystems.
Diffstat (limited to 'sys/ufs/ffs/ffs_alloc.c')
-rw-r--r-- | sys/ufs/ffs/ffs_alloc.c | 233 |
1 files changed, 219 insertions, 14 deletions
diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c index cea935b..9166083 100644 --- a/sys/ufs/ffs/ffs_alloc.c +++ b/sys/ufs/ffs/ffs_alloc.c @@ -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 @@ nospace: 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); +} |