ufs_error() sets SB_RDONLY on every error, whatever onerror= says, and ufs_panic() does too, both without holding s_umount. If that happens while the filesystem is frozen through bdev_freeze() thaw_super() sees a read-only superblock and leaves the s_writers rwsems locked so every writer hangs. It also leaked the cylinder group info because ufs_put_super() doesn't free it for a read-only superblock, and a remount to read-write read it in again over the old copy. So call sb_set_emergency_ro() instead and let the VFS refuse modifications. ufs_error() and ufs_panic() still mark the superblock UFS_FSBAD but don't queue ufs_sync_fs() anymore which does nothing in that state. ufs_put_cylinder() doesn't store the allocation rotors and ufs_write_inode() refuses. ufs_handle_dirsync() treats EROFS as success like ext2 because the directory entry was already changed. A remount to read-write fails until umount. A remount to read-only works and keeps the cylinder group info because evicting inodes that were unlinked while open still loads cylinder groups. Writes through file descriptors opened before the error fail with EROFS. Signed-off-by: Christian Brauner (Amutable) --- fs/ufs/cylinder.c | 12 +++++++----- fs/ufs/dir.c | 3 +++ fs/ufs/inode.c | 2 ++ fs/ufs/super.c | 56 ++++++++++++++++++++++++++++++++++++++----------------- 4 files changed, 51 insertions(+), 22 deletions(-) diff --git a/fs/ufs/cylinder.c b/fs/ufs/cylinder.c index b930ee1cf853..e0360873a7f8 100644 --- a/fs/ufs/cylinder.c +++ b/fs/ufs/cylinder.c @@ -117,12 +117,14 @@ void ufs_put_cylinder (struct super_block * sb, unsigned bitmap_nr) } /* * rotor is not so important data, so we put it to disk - * at the end of working with cylinder + * at the end of working with cylinder, but not after an error */ - ucg->cg_rotor = cpu_to_fs32(sb, ucpi->c_rotor); - ucg->cg_frotor = cpu_to_fs32(sb, ucpi->c_frotor); - ucg->cg_irotor = cpu_to_fs32(sb, ucpi->c_irotor); - ubh_mark_buffer_dirty (UCPI_UBH(ucpi)); + if (!sb_emergency_ro(sb)) { + ucg->cg_rotor = cpu_to_fs32(sb, ucpi->c_rotor); + ucg->cg_frotor = cpu_to_fs32(sb, ucpi->c_frotor); + ucg->cg_irotor = cpu_to_fs32(sb, ucpi->c_irotor); + ubh_mark_buffer_dirty (UCPI_UBH(ucpi)); + } for (i = 1; i < UCPI_UBH(ucpi)->count; i++) { brelse (UCPI_UBH(ucpi)->bh[i]); } diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c index e96174b738b4..78c883921115 100644 --- a/fs/ufs/dir.c +++ b/fs/ufs/dir.c @@ -64,6 +64,9 @@ static int ufs_handle_dirsync(struct inode *dir) err = filemap_write_and_wait(dir->i_mapping); if (!err) err = sync_inode_metadata(dir, 1); + /* The change is done, an error made us read-only in the meantime. */ + if (err == -EROFS && sb_emergency_ro(dir->i_sb)) + err = 0; return err; } diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index c9ff8673fa66..ed5d98c8bfca 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -828,6 +828,8 @@ static int ufs_update_inode(struct inode * inode, int do_sync) int ufs_write_inode(struct inode *inode, struct writeback_control *wbc) { + if (sb_emergency_ro(inode->i_sb)) + return -EROFS; return ufs_update_inode(inode, wbc->sync_mode == WB_SYNC_ALL); } diff --git a/fs/ufs/super.c b/fs/ufs/super.c index 3569ac92b065..d82cf73360a7 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -280,11 +280,13 @@ void ufs_error (struct super_block * sb, const char * function, uspi = UFS_SB(sb)->s_uspi; usb1 = ubh_get_usb_first(uspi); - if (!sb_rdonly(sb)) { + /* + * SB_RDONLY needs s_umount, see sb_set_emergency_ro(). The superblock + * marked bad goes out with the block device. + */ + if (!sb_rdonly(sb) && sb_set_emergency_ro(sb)) { usb1->fs_clean = UFS_FSBAD; ubh_mark_buffer_dirty(USPI_UBH(uspi)); - ufs_mark_sb_dirty(sb); - sb->s_flags |= SB_RDONLY; } va_start(args, fmt); vaf.fmt = fmt; @@ -314,15 +316,14 @@ void ufs_panic (struct super_block * sb, const char * function, uspi = UFS_SB(sb)->s_uspi; usb1 = ubh_get_usb_first(uspi); - if (!sb_rdonly(sb)) { + /* See ufs_error(). */ + if (!sb_rdonly(sb) && sb_set_emergency_ro(sb)) { usb1->fs_clean = UFS_FSBAD; ubh_mark_buffer_dirty(USPI_UBH(uspi)); - ufs_mark_sb_dirty(sb); } va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; - sb->s_flags |= SB_RDONLY; pr_crit("panic (device %s): %s: %pV\n", sb->s_id, function, &vaf); va_end(args); @@ -512,11 +513,13 @@ static int ufs_read_cylinder_structures(struct super_block *sb) failed: kfree (base); + sbi->s_csp = NULL; if (sbi->s_ucg) { for (i = 0; i < uspi->s_ncg; i++) if (sbi->s_ucg[i]) brelse (sbi->s_ucg[i]); kfree (sbi->s_ucg); + sbi->s_ucg = NULL; for (i = 0; i < UFS_MAX_GROUP_LOADED; i++) kfree (sbi->s_ucpi[i]); } @@ -579,7 +582,8 @@ static void ufs_put_cstotal(struct super_block *sb) * ufs_put_super_internal() - put on-disk intrenal structures * @sb: pointer to super_block structure * Put on-disk structures associated with cylinder groups - * and write them back to disk, also update cs_total on disk + * and write them back to disk, also update cs_total on disk. + * Nothing is written back after an error. */ static void ufs_put_super_internal(struct super_block *sb) { @@ -591,10 +595,14 @@ static void ufs_put_super_internal(struct super_block *sb) UFSD("ENTER\n"); + base = space = (char*) sbi->s_csp; + /* Don't write anything back after an error, see ufs_error(). */ + if (sb_emergency_ro(sb)) + goto release; + ufs_put_cstotal(sb); size = uspi->s_cssize; blks = (size + uspi->s_fsize - 1) >> uspi->s_fshift; - base = space = (char*) sbi->s_csp; for (i = 0; i < blks; i++, space += uspi->s_fsize) { struct buffer_head *bh = sb_bread(sb, uspi->s_csaddr + i); @@ -607,6 +615,7 @@ static void ufs_put_super_internal(struct super_block *sb) mark_buffer_dirty(bh); brelse(bh); } +release: for (i = 0; i < sbi->s_cg_loaded; i++) { ufs_put_cylinder (sb, i); kfree (sbi->s_ucpi[i]); @@ -616,7 +625,9 @@ static void ufs_put_super_internal(struct super_block *sb) for (i = 0; i < uspi->s_ncg; i++) brelse (sbi->s_ucg[i]); kfree (sbi->s_ucg); + sbi->s_ucg = NULL; kfree (base); + sbi->s_csp = NULL; UFSD("EXIT\n"); } @@ -628,6 +639,10 @@ static int ufs_sync_fs(struct super_block *sb, int wait) struct ufs_super_block_third * usb3; unsigned flags; + /* Leave the superblock alone after an error, see ufs_error(). */ + if (sb_emergency_ro(sb)) + return 0; + mutex_lock(&UFS_SB(sb)->s_lock); UFSD("ENTER\n"); @@ -684,7 +699,8 @@ static void ufs_put_super(struct super_block *sb) UFSD("ENTER\n"); - if (!sb_rdonly(sb)) + /* NULL if a remount to read-only freed the cylinder groups */ + if (sbi->s_ucg) ufs_put_super_internal(sb); cancel_delayed_work_sync(&sbi->sync_work); @@ -1265,14 +1281,20 @@ static int ufs_reconfigure(struct fs_context *fc) * fs was mouted as rw, remounting ro */ if (fc->sb_flags & SB_RDONLY) { - ufs_put_super_internal(sb); - usb1->fs_time = ufs_get_seconds(sb); - if ((flags & UFS_ST_MASK) == UFS_ST_SUN - || (flags & UFS_ST_MASK) == UFS_ST_SUNOS - || (flags & UFS_ST_MASK) == UFS_ST_SUNx86) - ufs_set_fs_state(sb, usb1, usb3, - UFS_FSOK - fs32_to_cpu(sb, usb1->fs_time)); - ubh_mark_buffer_dirty (USPI_UBH(uspi)); + /* + * After an error leave the superblock alone and keep the + * cylinder groups, evict still needs them. + */ + if (!sb_emergency_ro(sb)) { + ufs_put_super_internal(sb); + usb1->fs_time = ufs_get_seconds(sb); + if ((flags & UFS_ST_MASK) == UFS_ST_SUN + || (flags & UFS_ST_MASK) == UFS_ST_SUNOS + || (flags & UFS_ST_MASK) == UFS_ST_SUNx86) + ufs_set_fs_state(sb, usb1, usb3, + UFS_FSOK - fs32_to_cpu(sb, usb1->fs_time)); + ubh_mark_buffer_dirty (USPI_UBH(uspi)); + } sb->s_flags |= SB_RDONLY; } else { /* -- 2.53.0