usb_storage: report read-only drives as write protected

A target that defines HAVE_STORAGE_READONLY provides storage_readonly(),
and USB mass storage reports such a drive write protected: MODE SENSE
sets the WP bit, and WRITE(10) and WRITE(16) fail with DATA PROTECT /
WRITE PROTECTED before taking data, so a host mounts it read-only
instead of failing each write as a medium error. Only NAND storage wires
it up so far.

The iPod Nano 3G, added in the changes that follow, defines it: its FTL
mounts read-only in the bootloader and after a failed commit, and the
NAND check image is always read-only.

Built for the Nano 3G (bootloader, check image, firmware) without new
warnings. On hardware, Linux and Windows hosts have read the
always-read-only check image over USB; no host write has been made to a
read-only drive.

AI provenance: developed with Claude Opus 5 (Anthropic), used through
Claude Code. The model wrote most of the code and this message under
Andrew Rice's direction. Any hardware testing described above was
carried out by Andrew Rice, who is responsible for this change.

Change-Id: Idb34def2d588047e5dced9bac1521c00b37dd5e5
This commit is contained in:
Andrew Rice 2026-09-17 13:18:58 +10:00
parent 0a0b877dc2
commit 6958f6638e

View file

@ -127,6 +127,7 @@
#define SENSE_MEDIUM_ERROR 0x03
#define SENSE_ILLEGAL_REQUEST 0x05
#define SENSE_UNIT_ATTENTION 0x06
#define SENSE_DATA_PROTECT 0x07
#define ASC_MEDIUM_NOT_PRESENT 0x3a
#define ASC_INVALID_FIELD_IN_CBD 0x24
@ -135,6 +136,14 @@
#define ASC_READ_ERROR 0x11
#define ASC_NOT_READY 0x04
#define ASC_INVALID_COMMAND 0x20
#define ASC_WRITE_PROTECTED 0x27
/* A drive whose driver can only read it is reported write protected */
#ifdef HAVE_STORAGE_READONLY
#define LUN_READONLY(lun) storage_readonly(lun)
#else
#define LUN_READONLY(lun) false
#endif
#define ASCQ_BECOMING_READY 0x01
@ -905,7 +914,8 @@ static void handle_scsi(struct command_block_wrapper* cbw)
tb.ms_data_10->mode_data_length =
htobe16(sizeof(struct mode_sense_data_10)-2);
tb.ms_data_10->medium_type = 0;
tb.ms_data_10->device_specific = 0;
tb.ms_data_10->device_specific =
LUN_READONLY(lun) ? 0x80 : 0;
tb.ms_data_10->reserved = 0;
tb.ms_data_10->longlba = 1;
tb.ms_data_10->block_descriptor_length =
@ -969,7 +979,8 @@ static void handle_scsi(struct command_block_wrapper* cbw)
tb.ms_data_6->mode_data_length =
sizeof(struct mode_sense_data_6)-1;
tb.ms_data_6->medium_type = 0;
tb.ms_data_6->device_specific = 0;
tb.ms_data_6->device_specific =
LUN_READONLY(lun) ? 0x80 : 0;
tb.ms_data_6->block_descriptor_length =
sizeof(struct mode_sense_bdesc_shortlba);
tb.ms_data_6->block_descriptor.density_code = 0;
@ -1249,6 +1260,13 @@ static void handle_scsi(struct command_block_wrapper* cbw)
cur_sense_data.ascq=0;
break;
}
if(LUN_READONLY(lun)) {
send_csw(UMS_STATUS_FAIL);
cur_sense_data.sense_key=SENSE_DATA_PROTECT;
cur_sense_data.asc=ASC_WRITE_PROTECTED;
cur_sense_data.ascq=0;
break;
}
cur_cmd.data[0] = tb.transfer_buffer;
cur_cmd.data[1] = &tb.transfer_buffer[WRITE_BUFFER_SIZE];
cur_cmd.data_select=0;
@ -1284,6 +1302,13 @@ static void handle_scsi(struct command_block_wrapper* cbw)
cur_sense_data.ascq=0;
break;
}
if(LUN_READONLY(lun)) {
send_csw(UMS_STATUS_FAIL);
cur_sense_data.sense_key=SENSE_DATA_PROTECT;
cur_sense_data.asc=ASC_WRITE_PROTECTED;
cur_sense_data.ascq=0;
break;
}
cur_cmd.data[0] = tb.transfer_buffer;
cur_cmd.data[1] = &tb.transfer_buffer[WRITE_BUFFER_SIZE];
cur_cmd.data_select=0;