Index: head/sys/ufs/ffs/ffs_extern.h =================================================================== --- head/sys/ufs/ffs/ffs_extern.h +++ head/sys/ufs/ffs/ffs_extern.h @@ -61,7 +61,6 @@ struct ucred *a_cred, int a_flags, struct buf **a_bpp); int ffs_balloc_ufs2(struct vnode *a_vp, off_t a_startoffset, int a_size, struct ucred *a_cred, int a_flags, struct buf **a_bpp); -int ffs_blkatoff(struct vnode *, off_t, char **, struct buf **); void ffs_blkfree(struct ufsmount *, struct fs *, struct vnode *, ufs2_daddr_t, long, ino_t, enum vtype, struct workhead *, u_long); ufs2_daddr_t ffs_blkpref_ufs1(struct inode *, ufs_lbn_t, int, ufs1_daddr_t *); @@ -69,7 +68,6 @@ void ffs_blkrelease_finish(struct ufsmount *, u_long); u_long ffs_blkrelease_start(struct ufsmount *, struct vnode *, ino_t); uint32_t ffs_calc_sbhash(struct fs *); -int ffs_check_blkno(struct mount *, ino_t, ufs2_daddr_t, int); 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); @@ -84,7 +82,6 @@ struct cg **); int ffs_isblock(struct fs *, u_char *, ufs1_daddr_t); int ffs_isfreeblock(struct fs *, u_char *, ufs1_daddr_t); -int ffs_load_inode(struct buf *, struct inode *, struct fs *, ino_t); void ffs_oldfscompat_write(struct fs *, struct ufsmount *); int ffs_own_mount(const struct mount *mp); int ffs_reallocblks(struct vop_reallocblks_args *); Index: head/sys/ufs/ffs/ffs_subr.c =================================================================== --- head/sys/ufs/ffs/ffs_subr.c +++ head/sys/ufs/ffs/ffs_subr.c @@ -67,7 +67,6 @@ #include #include #include -#include #include #include @@ -81,216 +80,6 @@ #define UFS_FREE(ptr, type) free(ptr, type) #define UFS_TIME time_second -/* - * Return buffer with the contents of block "offset" from the beginning of - * directory "ip". If "res" is non-zero, fill it in with a pointer to the - * remaining space in the directory. - */ -int -ffs_blkatoff(struct vnode *vp, off_t offset, char **res, struct buf **bpp) -{ - struct inode *ip; - struct fs *fs; - struct buf *bp; - ufs_lbn_t lbn; - int bsize, error; - - ip = VTOI(vp); - fs = ITOFS(ip); - lbn = lblkno(fs, offset); - bsize = blksize(fs, ip, lbn); - - *bpp = NULL; - error = bread(vp, lbn, bsize, NOCRED, &bp); - if (error) { - return (error); - } - if (res) - *res = (char *)bp->b_data + blkoff(fs, offset); - *bpp = bp; - return (0); -} - -/* - * Load up the contents of an inode and copy the appropriate pieces - * to the incore copy. - */ -int -ffs_load_inode(struct buf *bp, struct inode *ip, struct fs *fs, ino_t ino) -{ - struct ufs1_dinode *dip1; - struct ufs2_dinode *dip2; - int error; - - if (I_IS_UFS1(ip)) { - dip1 = ip->i_din1; - *dip1 = - *((struct ufs1_dinode *)bp->b_data + ino_to_fsbo(fs, ino)); - ip->i_mode = dip1->di_mode; - ip->i_nlink = dip1->di_nlink; - ip->i_effnlink = dip1->di_nlink; - ip->i_size = dip1->di_size; - ip->i_flags = dip1->di_flags; - ip->i_gen = dip1->di_gen; - ip->i_uid = dip1->di_uid; - ip->i_gid = dip1->di_gid; - return (0); - } - dip2 = ((struct ufs2_dinode *)bp->b_data + ino_to_fsbo(fs, ino)); - if ((error = ffs_verify_dinode_ckhash(fs, dip2)) != 0 && - !ffs_fsfail_cleanup(ITOUMP(ip), error)) { - printf("%s: inode %jd: check-hash failed\n", fs->fs_fsmnt, - (intmax_t)ino); - return (error); - } - *ip->i_din2 = *dip2; - dip2 = ip->i_din2; - ip->i_mode = dip2->di_mode; - ip->i_nlink = dip2->di_nlink; - ip->i_effnlink = dip2->di_nlink; - ip->i_size = dip2->di_size; - ip->i_flags = dip2->di_flags; - ip->i_gen = dip2->di_gen; - ip->i_uid = dip2->di_uid; - ip->i_gid = dip2->di_gid; - return (0); -} - -/* - * Verify that a filesystem block number is a valid data block. - * This routine is only called on untrusted filesystems. - */ -int -ffs_check_blkno(struct mount *mp, ino_t inum, ufs2_daddr_t daddr, int blksize) -{ - struct fs *fs; - struct ufsmount *ump; - ufs2_daddr_t end_daddr; - int cg, havemtx; - - KASSERT((mp->mnt_flag & MNT_UNTRUSTED) != 0, - ("ffs_check_blkno called on a trusted file system")); - ump = VFSTOUFS(mp); - fs = ump->um_fs; - cg = dtog(fs, daddr); - end_daddr = daddr + numfrags(fs, blksize); - /* - * Verify that the block number is a valid data block. Also check - * that it does not point to an inode block or a superblock. Accept - * blocks that are unalloacted (0) or part of snapshot metadata - * (BLK_NOCOPY or BLK_SNAP). - * - * Thus, the block must be in a valid range for the filesystem and - * either in the space before a backup superblock (except the first - * cylinder group where that space is used by the bootstrap code) or - * after the inode blocks and before the end of the cylinder group. - */ - if ((uint64_t)daddr <= BLK_SNAP || - ((uint64_t)end_daddr <= fs->fs_size && - ((cg > 0 && end_daddr <= cgsblock(fs, cg)) || - (daddr >= cgdmin(fs, cg) && - end_daddr <= cgbase(fs, cg) + fs->fs_fpg)))) - return (0); - if ((havemtx = mtx_owned(UFS_MTX(ump))) == 0) - UFS_LOCK(ump); - if (ppsratecheck(&ump->um_last_integritymsg, - &ump->um_secs_integritymsg, 1)) { - UFS_UNLOCK(ump); - uprintf("\n%s: inode %jd, out-of-range indirect block " - "number %jd\n", mp->mnt_stat.f_mntonname, inum, daddr); - if (havemtx) - UFS_LOCK(ump); - } else if (!havemtx) - UFS_UNLOCK(ump); - return (EINTEGRITY); -} - -/* - * Initiate a forcible unmount. - * Used to unmount filesystems whose underlying media has gone away. - */ -static void -ffs_fsfail_unmount(void *v, int pending) -{ - struct fsfail_task *etp; - struct mount *mp; - - etp = v; - - /* - * Find our mount and get a ref on it, then try to unmount. - */ - mp = vfs_getvfs(&etp->fsid); - if (mp != NULL) - dounmount(mp, MNT_FORCE, curthread); - free(etp, M_UFSMNT); -} - -/* - * On first ENXIO error, start a task that forcibly unmounts the filesystem. - * - * Return true if a cleanup is in progress. - */ -int -ffs_fsfail_cleanup(struct ufsmount *ump, int error) -{ - int retval; - - UFS_LOCK(ump); - retval = ffs_fsfail_cleanup_locked(ump, error); - UFS_UNLOCK(ump); - return (retval); -} - -int -ffs_fsfail_cleanup_locked(struct ufsmount *ump, int error) -{ - struct fsfail_task *etp; - struct task *tp; - - mtx_assert(UFS_MTX(ump), MA_OWNED); - if (error == ENXIO && (ump->um_flags & UM_FSFAIL_CLEANUP) == 0) { - ump->um_flags |= UM_FSFAIL_CLEANUP; - /* - * Queue an async forced unmount. - */ - etp = ump->um_fsfail_task; - ump->um_fsfail_task = NULL; - if (etp != NULL) { - tp = &etp->task; - TASK_INIT(tp, 0, ffs_fsfail_unmount, etp); - taskqueue_enqueue(taskqueue_thread, tp); - printf("UFS: forcibly unmounting %s from %s\n", - ump->um_mountp->mnt_stat.f_mntfromname, - ump->um_mountp->mnt_stat.f_mntonname); - } - } - return ((ump->um_flags & UM_FSFAIL_CLEANUP) != 0); -} - -/* - * Wrapper used during ENXIO cleanup to allocate empty buffers when - * the kernel is unable to read the real one. They are needed so that - * the soft updates code can use them to unwind its dependencies. - */ -int -ffs_breadz(struct ufsmount *ump, struct vnode *vp, daddr_t lblkno, - daddr_t dblkno, int size, daddr_t *rablkno, int *rabsize, int cnt, - struct ucred *cred, int flags, void (*ckhashfunc)(struct buf *), - struct buf **bpp) -{ - int error; - - flags |= GB_CVTENXIO; - error = breadn_flags(vp, lblkno, dblkno, size, rablkno, rabsize, cnt, - cred, flags, ckhashfunc, bpp); - if (error != 0 && ffs_fsfail_cleanup(ump, error)) { - error = getblkx(vp, lblkno, dblkno, size, 0, 0, flags, bpp); - KASSERT(error == 0, ("getblkx failed")); - vfs_bio_bzero_buf(*bpp, 0, size); - } - return (error); -} #endif /* _KERNEL */ /* Index: head/sys/ufs/ffs/ffs_vfsops.c =================================================================== --- head/sys/ufs/ffs/ffs_vfsops.c +++ head/sys/ufs/ffs/ffs_vfsops.c @@ -155,6 +155,217 @@ &ffs_enxio_enable, 0, "enable mapping of other disk I/O errors to ENXIO"); +/* + * Return buffer with the contents of block "offset" from the beginning of + * directory "ip". If "res" is non-zero, fill it in with a pointer to the + * remaining space in the directory. + */ +static int +ffs_blkatoff(struct vnode *vp, off_t offset, char **res, struct buf **bpp) +{ + struct inode *ip; + struct fs *fs; + struct buf *bp; + ufs_lbn_t lbn; + int bsize, error; + + ip = VTOI(vp); + fs = ITOFS(ip); + lbn = lblkno(fs, offset); + bsize = blksize(fs, ip, lbn); + + *bpp = NULL; + error = bread(vp, lbn, bsize, NOCRED, &bp); + if (error) { + return (error); + } + if (res) + *res = (char *)bp->b_data + blkoff(fs, offset); + *bpp = bp; + return (0); +} + +/* + * Load up the contents of an inode and copy the appropriate pieces + * to the incore copy. + */ +static int +ffs_load_inode(struct buf *bp, struct inode *ip, struct fs *fs, ino_t ino) +{ + struct ufs1_dinode *dip1; + struct ufs2_dinode *dip2; + int error; + + if (I_IS_UFS1(ip)) { + dip1 = ip->i_din1; + *dip1 = + *((struct ufs1_dinode *)bp->b_data + ino_to_fsbo(fs, ino)); + ip->i_mode = dip1->di_mode; + ip->i_nlink = dip1->di_nlink; + ip->i_effnlink = dip1->di_nlink; + ip->i_size = dip1->di_size; + ip->i_flags = dip1->di_flags; + ip->i_gen = dip1->di_gen; + ip->i_uid = dip1->di_uid; + ip->i_gid = dip1->di_gid; + return (0); + } + dip2 = ((struct ufs2_dinode *)bp->b_data + ino_to_fsbo(fs, ino)); + if ((error = ffs_verify_dinode_ckhash(fs, dip2)) != 0 && + !ffs_fsfail_cleanup(ITOUMP(ip), error)) { + printf("%s: inode %jd: check-hash failed\n", fs->fs_fsmnt, + (intmax_t)ino); + return (error); + } + *ip->i_din2 = *dip2; + dip2 = ip->i_din2; + ip->i_mode = dip2->di_mode; + ip->i_nlink = dip2->di_nlink; + ip->i_effnlink = dip2->di_nlink; + ip->i_size = dip2->di_size; + ip->i_flags = dip2->di_flags; + ip->i_gen = dip2->di_gen; + ip->i_uid = dip2->di_uid; + ip->i_gid = dip2->di_gid; + return (0); +} + +/* + * Verify that a filesystem block number is a valid data block. + * This routine is only called on untrusted filesystems. + */ +static int +ffs_check_blkno(struct mount *mp, ino_t inum, ufs2_daddr_t daddr, int blksize) +{ + struct fs *fs; + struct ufsmount *ump; + ufs2_daddr_t end_daddr; + int cg, havemtx; + + KASSERT((mp->mnt_flag & MNT_UNTRUSTED) != 0, + ("ffs_check_blkno called on a trusted file system")); + ump = VFSTOUFS(mp); + fs = ump->um_fs; + cg = dtog(fs, daddr); + end_daddr = daddr + numfrags(fs, blksize); + /* + * Verify that the block number is a valid data block. Also check + * that it does not point to an inode block or a superblock. Accept + * blocks that are unalloacted (0) or part of snapshot metadata + * (BLK_NOCOPY or BLK_SNAP). + * + * Thus, the block must be in a valid range for the filesystem and + * either in the space before a backup superblock (except the first + * cylinder group where that space is used by the bootstrap code) or + * after the inode blocks and before the end of the cylinder group. + */ + if ((uint64_t)daddr <= BLK_SNAP || + ((uint64_t)end_daddr <= fs->fs_size && + ((cg > 0 && end_daddr <= cgsblock(fs, cg)) || + (daddr >= cgdmin(fs, cg) && + end_daddr <= cgbase(fs, cg) + fs->fs_fpg)))) + return (0); + if ((havemtx = mtx_owned(UFS_MTX(ump))) == 0) + UFS_LOCK(ump); + if (ppsratecheck(&ump->um_last_integritymsg, + &ump->um_secs_integritymsg, 1)) { + UFS_UNLOCK(ump); + uprintf("\n%s: inode %jd, out-of-range indirect block " + "number %jd\n", mp->mnt_stat.f_mntonname, inum, daddr); + if (havemtx) + UFS_LOCK(ump); + } else if (!havemtx) + UFS_UNLOCK(ump); + return (EINTEGRITY); +} + +/* + * Initiate a forcible unmount. + * Used to unmount filesystems whose underlying media has gone away. + */ +static void +ffs_fsfail_unmount(void *v, int pending) +{ + struct fsfail_task *etp; + struct mount *mp; + + etp = v; + + /* + * Find our mount and get a ref on it, then try to unmount. + */ + mp = vfs_getvfs(&etp->fsid); + if (mp != NULL) + dounmount(mp, MNT_FORCE, curthread); + free(etp, M_UFSMNT); +} + +/* + * On first ENXIO error, start a task that forcibly unmounts the filesystem. + * + * Return true if a cleanup is in progress. + */ +int +ffs_fsfail_cleanup(struct ufsmount *ump, int error) +{ + int retval; + + UFS_LOCK(ump); + retval = ffs_fsfail_cleanup_locked(ump, error); + UFS_UNLOCK(ump); + return (retval); +} + +int +ffs_fsfail_cleanup_locked(struct ufsmount *ump, int error) +{ + struct fsfail_task *etp; + struct task *tp; + + mtx_assert(UFS_MTX(ump), MA_OWNED); + if (error == ENXIO && (ump->um_flags & UM_FSFAIL_CLEANUP) == 0) { + ump->um_flags |= UM_FSFAIL_CLEANUP; + /* + * Queue an async forced unmount. + */ + etp = ump->um_fsfail_task; + ump->um_fsfail_task = NULL; + if (etp != NULL) { + tp = &etp->task; + TASK_INIT(tp, 0, ffs_fsfail_unmount, etp); + taskqueue_enqueue(taskqueue_thread, tp); + printf("UFS: forcibly unmounting %s from %s\n", + ump->um_mountp->mnt_stat.f_mntfromname, + ump->um_mountp->mnt_stat.f_mntonname); + } + } + return ((ump->um_flags & UM_FSFAIL_CLEANUP) != 0); +} + +/* + * Wrapper used during ENXIO cleanup to allocate empty buffers when + * the kernel is unable to read the real one. They are needed so that + * the soft updates code can use them to unwind its dependencies. + */ +int +ffs_breadz(struct ufsmount *ump, struct vnode *vp, daddr_t lblkno, + daddr_t dblkno, int size, daddr_t *rablkno, int *rabsize, int cnt, + struct ucred *cred, int flags, void (*ckhashfunc)(struct buf *), + struct buf **bpp) +{ + int error; + + flags |= GB_CVTENXIO; + error = breadn_flags(vp, lblkno, dblkno, size, rablkno, rabsize, cnt, + cred, flags, ckhashfunc, bpp); + if (error != 0 && ffs_fsfail_cleanup(ump, error)) { + error = getblkx(vp, lblkno, dblkno, size, 0, 0, flags, bpp); + KASSERT(error == 0, ("getblkx failed")); + vfs_bio_bzero_buf(*bpp, 0, size); + } + return (error); +} + static int ffs_mount(struct mount *mp) { Index: head/sys/ufs/ufs/ufs_vnops.c =================================================================== --- head/sys/ufs/ufs/ufs_vnops.c +++ head/sys/ufs/ufs/ufs_vnops.c @@ -2184,7 +2184,7 @@ error = 0; while (error == 0 && uio->uio_resid > 0 && uio->uio_offset < ip->i_size) { - error = ffs_blkatoff(vp, uio->uio_offset, NULL, &bp); + error = UFS_BLKATOFF(vp, uio->uio_offset, NULL, &bp); if (error) break; if (bp->b_offset + bp->b_bcount > ip->i_size)