Skip to content

Commit 6ae66a7

Browse files
daandemeyerkawasaki
authored andcommitted
cdrom, scsi: sr: propagate read-only status to block layer via set_disk_ro()
The cdrom core never calls set_disk_ro() for a registered device, so BLKROGET on a CD-ROM device always returns 0 (writable), even when the drive has no write capabilities and writes will inevitably fail. This causes problems for userspace that relies on BLKROGET to determine whether a block device is read-only. For example, systemd's loop device setup uses BLKROGET to decide whether to create a loop device with LO_FLAGS_READ_ONLY. Without the read-only flag, writes pass through the loop device to the CD-ROM and fail with I/O errors. systemd-fsck similarly checks BLKROGET to decide whether to run fsck in no-repair mode (-n). The write-capability bits in cdi->mask come from two different sources: CDC_DVD_RAM and CDC_CD_RW are populated by the driver from the MODE SENSE capabilities page (page 0x2A) before register_cdrom() is called, while CDC_MRW_W and CDC_RAM require the MMC GET CONFIGURATION command and were only probed by cdrom_open_write() at device open time. This meant that any attempt to compute the writable state from the full mask at probe time was incorrect, because the GET CONFIGURATION bits were still unset (and cdi->mask is initialized such that capabilities are assumed present). Fix this by factoring the GET CONFIGURATION probing out of cdrom_open_write() into a new exported helper, cdrom_probe_write_features(), and having sr call it from sr_probe() right after get_capabilities() has populated the MODE SENSE bits. register_cdrom() then calls set_disk_ro() based on the full write-capability mask (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW) so the block layer reflects the drive's actual write support. The feature queries used (CDF_MRW and CDF_RWRT via GET CONFIGURATION with RT=00) report drive-level capabilities that are persistent across media, so a single probe before register_cdrom() is sufficient and the redundant probe at open time is dropped. With set_disk_ro() now accurate, the long-vestigial cd->writeable flag in sr can go: get_capabilities() used to set cd->writeable based on the same four mask bits, but because CDC_MRW_W and CDC_RAM default to "capability present" in cdi->mask and aren't touched by MODE SENSE, the condition that gated cd->writeable was always true, making it unconditionally 1. Replace the corresponding gate in sr_init_command() with get_disk_ro(cd->disk), which turns a previously no-op check into a real one and also catches kernel-internal bio writers that bypass blkdev_write_iter()'s bdev_read_only() check. The sd driver (SCSI disks) does not have this problem because it checks the MODE SENSE Write Protect bit and calls set_disk_ro() accordingly. The sr driver cannot use the same approach because the MMC specification does not define the WP bit in the MODE SENSE device-specific parameter byte for CD-ROM devices. Fixes: 1da177e ("Linux-2.6.12-rc2") Signed-off-by: Daan De Meyer <[email protected]>
1 parent 3b54e52 commit 6ae66a7

4 files changed

Lines changed: 51 additions & 35 deletions

File tree

drivers/cdrom/cdrom.c

Lines changed: 48 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,16 @@ int register_cdrom(struct gendisk *disk, struct cdrom_device_info *cdi)
631631

632632
WARN_ON(!cdo->generic_packet);
633633

634+
/*
635+
* Propagate the drive's write support to the block layer so BLKROGET
636+
* reflects actual write capability. Drivers that use GET CONFIGURATION
637+
* features (CDC_MRW_W, CDC_RAM) must have called
638+
* cdrom_probe_write_features() before register_cdrom() so the mask is
639+
* complete here.
640+
*/
641+
set_disk_ro(disk, !CDROM_CAN(CDC_DVD_RAM | CDC_MRW_W | CDC_RAM |
642+
CDC_CD_RW));
643+
634644
cd_dbg(CD_REG_UNREG, "drive \"/dev/%s\" registered\n", cdi->name);
635645
mutex_lock(&cdrom_mutex);
636646
list_add(&cdi->list, &cdrom_list);
@@ -742,6 +752,44 @@ static int cdrom_is_random_writable(struct cdrom_device_info *cdi, int *write)
742752
return 0;
743753
}
744754

755+
/*
756+
* Probe write-related MMC features via GET CONFIGURATION and update
757+
* cdi->mask accordingly. Drivers that populate cdi->mask from the MODE SENSE
758+
* capabilities page (e.g. sr) should call this after those MODE SENSE bits
759+
* have been set but before register_cdrom(), so that the full set of
760+
* write-capability bits is known by the time register_cdrom() decides on the
761+
* initial read-only state of the disk.
762+
*/
763+
void cdrom_probe_write_features(struct cdrom_device_info *cdi)
764+
{
765+
int mrw, mrw_write, ram_write;
766+
767+
mrw = 0;
768+
if (!cdrom_is_mrw(cdi, &mrw_write))
769+
mrw = 1;
770+
771+
if (CDROM_CAN(CDC_MO_DRIVE))
772+
ram_write = 1;
773+
else
774+
(void) cdrom_is_random_writable(cdi, &ram_write);
775+
776+
if (mrw)
777+
cdi->mask &= ~CDC_MRW;
778+
else
779+
cdi->mask |= CDC_MRW;
780+
781+
if (mrw_write)
782+
cdi->mask &= ~CDC_MRW_W;
783+
else
784+
cdi->mask |= CDC_MRW_W;
785+
786+
if (ram_write)
787+
cdi->mask &= ~CDC_RAM;
788+
else
789+
cdi->mask |= CDC_RAM;
790+
}
791+
EXPORT_SYMBOL(cdrom_probe_write_features);
792+
745793
static int cdrom_media_erasable(struct cdrom_device_info *cdi)
746794
{
747795
disc_information di;
@@ -894,33 +942,8 @@ static int cdrom_is_dvd_rw(struct cdrom_device_info *cdi)
894942
*/
895943
static int cdrom_open_write(struct cdrom_device_info *cdi)
896944
{
897-
int mrw, mrw_write, ram_write;
898945
int ret = 1;
899946

900-
mrw = 0;
901-
if (!cdrom_is_mrw(cdi, &mrw_write))
902-
mrw = 1;
903-
904-
if (CDROM_CAN(CDC_MO_DRIVE))
905-
ram_write = 1;
906-
else
907-
(void) cdrom_is_random_writable(cdi, &ram_write);
908-
909-
if (mrw)
910-
cdi->mask &= ~CDC_MRW;
911-
else
912-
cdi->mask |= CDC_MRW;
913-
914-
if (mrw_write)
915-
cdi->mask &= ~CDC_MRW_W;
916-
else
917-
cdi->mask |= CDC_MRW_W;
918-
919-
if (ram_write)
920-
cdi->mask &= ~CDC_RAM;
921-
else
922-
cdi->mask |= CDC_RAM;
923-
924947
if (CDROM_CAN(CDC_MRW_W))
925948
ret = cdrom_mrw_open_write(cdi);
926949
else if (CDROM_CAN(CDC_DVD_RAM))

drivers/scsi/sr.c

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ static blk_status_t sr_init_command(struct scsi_cmnd *SCpnt)
395395

396396
switch (req_op(rq)) {
397397
case REQ_OP_WRITE:
398-
if (!cd->writeable)
398+
if (get_disk_ro(cd->disk))
399399
goto out;
400400
SCpnt->cmnd[0] = WRITE_10;
401401
cd->cdi.media_written = 1;
@@ -681,6 +681,7 @@ static int sr_probe(struct scsi_device *sdev)
681681
error = -ENOMEM;
682682
if (get_capabilities(cd))
683683
goto fail_minor;
684+
cdrom_probe_write_features(&cd->cdi);
684685
sr_vendor_init(cd);
685686

686687
set_capacity(disk, cd->capacity);
@@ -899,14 +900,6 @@ static int get_capabilities(struct scsi_cd *cd)
899900
/*else I don't think it can close its tray
900901
cd->cdi.mask |= CDC_CLOSE_TRAY; */
901902

902-
/*
903-
* if DVD-RAM, MRW-W or CD-RW, we are randomly writable
904-
*/
905-
if ((cd->cdi.mask & (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) !=
906-
(CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) {
907-
cd->writeable = 1;
908-
}
909-
910903
kfree(buffer);
911904
return 0;
912905
}

drivers/scsi/sr.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ typedef struct scsi_cd {
3535
struct scsi_device *device;
3636
unsigned int vendor; /* vendor code, see sr_vendor.c */
3737
unsigned long ms_offset; /* for reading multisession-CD's */
38-
unsigned writeable : 1;
3938
unsigned use:1; /* is this device still supportable */
4039
unsigned xa_flag:1; /* CD has XA sectors ? */
4140
unsigned readcd_known:1; /* drive supports READ_CD (0xbe) */

include/linux/cdrom.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ int cdrom_ioctl(struct cdrom_device_info *cdi, struct block_device *bdev,
108108
extern unsigned int cdrom_check_events(struct cdrom_device_info *cdi,
109109
unsigned int clearing);
110110

111+
extern void cdrom_probe_write_features(struct cdrom_device_info *cdi);
111112
extern int register_cdrom(struct gendisk *disk, struct cdrom_device_info *cdi);
112113
extern void unregister_cdrom(struct cdrom_device_info *cdi);
113114

0 commit comments

Comments
 (0)