Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

floppy: bail out in open() if drive is not responding to block0 read

In case reading of block 0 during open() fails, it is not the right thing
to let open() succeed.

Fix this by introducing FD_OPEN_SHOULD_FAIL_BIT flag, and setting it in
case the bio callback encounters an error while trying to read block 0.

As a bonus, this works around certain broken userspace (blkid), which is
not able to properly handle read()s returning IO errors. Hence be nice to
those, and bail out during open() already; if block 0 is not readable,
read()s are not going to provide any meaningful data anyway.

Signed-off-by: Jiri Kosina <jkosina@suse.cz>

+29 -10
+27 -9
drivers/block/floppy.c
··· 3691 3691 if (!(mode & FMODE_NDELAY)) { 3692 3692 if (mode & (FMODE_READ|FMODE_WRITE)) { 3693 3693 UDRS->last_checked = 0; 3694 + clear_bit(FD_OPEN_SHOULD_FAIL_BIT, &UDRS->flags); 3694 3695 check_disk_change(bdev); 3695 3696 if (test_bit(FD_DISK_CHANGED_BIT, &UDRS->flags)) 3697 + goto out; 3698 + if (test_bit(FD_OPEN_SHOULD_FAIL_BIT, &UDRS->flags)) 3696 3699 goto out; 3697 3700 } 3698 3701 res = -EROFS; ··· 3749 3746 * a disk in the drive, and whether that disk is writable. 3750 3747 */ 3751 3748 3752 - static void floppy_rb0_complete(struct bio *bio, int err) 3749 + struct rb0_cbdata { 3750 + int drive; 3751 + struct completion complete; 3752 + }; 3753 + 3754 + static void floppy_rb0_cb(struct bio *bio, int err) 3753 3755 { 3754 - complete((struct completion *)bio->bi_private); 3756 + struct rb0_cbdata *cbdata = (struct rb0_cbdata *)bio->bi_private; 3757 + int drive = cbdata->drive; 3758 + 3759 + if (err) { 3760 + pr_info("floppy: error %d while reading block 0", err); 3761 + set_bit(FD_OPEN_SHOULD_FAIL_BIT, &UDRS->flags); 3762 + } 3763 + complete(&cbdata->complete); 3755 3764 } 3756 3765 3757 - static int __floppy_read_block_0(struct block_device *bdev) 3766 + static int __floppy_read_block_0(struct block_device *bdev, int drive) 3758 3767 { 3759 3768 struct bio bio; 3760 3769 struct bio_vec bio_vec; 3761 - struct completion complete; 3762 3770 struct page *page; 3771 + struct rb0_cbdata cbdata; 3763 3772 size_t size; 3764 3773 3765 3774 page = alloc_page(GFP_NOIO); ··· 3784 3769 if (!size) 3785 3770 size = 1024; 3786 3771 3772 + cbdata.drive = drive; 3773 + 3787 3774 bio_init(&bio); 3788 3775 bio.bi_io_vec = &bio_vec; 3789 3776 bio_vec.bv_page = page; ··· 3796 3779 bio.bi_bdev = bdev; 3797 3780 bio.bi_iter.bi_sector = 0; 3798 3781 bio.bi_flags = (1 << BIO_QUIET); 3799 - init_completion(&complete); 3800 - bio.bi_private = &complete; 3801 - bio.bi_end_io = floppy_rb0_complete; 3782 + bio.bi_private = &cbdata; 3783 + bio.bi_end_io = floppy_rb0_cb; 3802 3784 3803 3785 submit_bio(READ, &bio); 3804 3786 process_fd_request(); 3805 - wait_for_completion(&complete); 3787 + 3788 + init_completion(&cbdata.complete); 3789 + wait_for_completion(&cbdata.complete); 3806 3790 3807 3791 __free_page(page); 3808 3792 ··· 3845 3827 UDRS->generation++; 3846 3828 if (drive_no_geom(drive)) { 3847 3829 /* auto-sensing */ 3848 - res = __floppy_read_block_0(opened_bdev[drive]); 3830 + res = __floppy_read_block_0(opened_bdev[drive], drive); 3849 3831 } else { 3850 3832 if (cf) 3851 3833 poll_drive(false, FD_RAW_NEED_DISK);
+2 -1
include/uapi/linux/fd.h
··· 185 185 * to clear media change status */ 186 186 FD_UNUSED_BIT, 187 187 FD_DISK_CHANGED_BIT, /* disk has been changed since last i/o */ 188 - FD_DISK_WRITABLE_BIT /* disk is writable */ 188 + FD_DISK_WRITABLE_BIT, /* disk is writable */ 189 + FD_OPEN_SHOULD_FAIL_BIT 189 190 }; 190 191 191 192 #define FDSETDRVPRM _IOW(2, 0x90, struct floppy_drive_params)