geom_disk / scsi_da: deny opening write-protected disks for writing

Ths change consists of two parts.

geom_disk: deny opening a disk for writing if it's marked as
write-protected.  A new disk(9) flag is added to mark write protected
disks.  A possible alternative could be to add another parameter to d_open,
so that the open mode could be passed to it and the disk drivers could
make the decision internally, but the flag required less churn.

scsi_da: add a new phase of disk probing to query the all pages mode
sense page.  We can determine if the disk is write protected using bit 7
of the device specific field in the mode parameter header returned by
MODE SENSE.

PR:		224037
Reviewed by:	mav
MFC after:	4 weeks
Differential Revision: https://reviews.freebsd.org/D13360
This commit is contained in:
Andriy Gapon 2018-01-15 11:20:00 +00:00
parent a826eb5a41
commit 6ce374aa94
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=327996
3 changed files with 101 additions and 22 deletions

View File

@ -81,6 +81,7 @@ __FBSDID("$FreeBSD$");
* ATA -> LOGDIR -> IDDIR -> SUP -> ATA_ZONE
*/
typedef enum {
DA_STATE_PROBE_WP,
DA_STATE_PROBE_RC,
DA_STATE_PROBE_RC16,
DA_STATE_PROBE_LBP,
@ -157,6 +158,7 @@ typedef enum {
DA_CCB_PROBE_ATA_IDDIR = 0x0F,
DA_CCB_PROBE_ATA_SUP = 0x10,
DA_CCB_PROBE_ATA_ZONE = 0x11,
DA_CCB_PROBE_WP = 0x12,
DA_CCB_TYPE_MASK = 0x1F,
DA_CCB_RETRY_UA = 0x20
} da_ccb_state;
@ -2427,7 +2429,7 @@ daregister(struct cam_periph *periph, void *arg)
}
LIST_INIT(&softc->pending_ccbs);
softc->state = DA_STATE_PROBE_RC;
softc->state = DA_STATE_PROBE_WP;
bioq_init(&softc->delete_run_queue);
if (SID_IS_REMOVABLE(&cgd->inq_data))
softc->flags |= DA_FLAG_PACK_REMOVABLE;
@ -2526,7 +2528,6 @@ daregister(struct cam_periph *periph, void *arg)
if (SID_ANSI_REV(&cgd->inq_data) >= SCSI_REV_SPC3 &&
(softc->quirks & DA_Q_NO_RC16) == 0) {
softc->flags |= DA_FLAG_CAN_RC16;
softc->state = DA_STATE_PROBE_RC16;
}
/*
@ -3095,6 +3096,36 @@ dastart(struct cam_periph *periph, union ccb *start_ccb)
daschedule(periph);
break;
}
case DA_STATE_PROBE_WP:
{
void *mode_buf;
int mode_buf_len;
mode_buf_len = 192;
mode_buf = malloc(mode_buf_len, M_SCSIDA, M_NOWAIT);
if (mode_buf == NULL) {
xpt_print(periph->path, "Unable to send mode sense - "
"malloc failure\n");
softc->state = DA_STATE_PROBE_RC;
goto skipstate;
}
scsi_mode_sense_len(&start_ccb->csio,
/*retries*/ da_retry_count,
/*cbfcnp*/ dadone,
/*tag_action*/ MSG_SIMPLE_Q_TAG,
/*dbd*/ FALSE,
/*pc*/ SMS_PAGE_CTRL_CURRENT,
/*page*/ SMS_ALL_PAGES_PAGE,
/*param_buf*/ mode_buf,
/*param_len*/ mode_buf_len,
/*minimum_cmd_size*/ softc->minimum_cmd_size,
/*sense_len*/ SSD_FULL_SIZE,
/*timeout*/ da_default_timeout * 1000);
start_ccb->ccb_h.ccb_bp = NULL;
start_ccb->ccb_h.ccb_state = DA_CCB_PROBE_WP;
xpt_action(start_ccb);
break;
}
case DA_STATE_PROBE_RC:
{
struct scsi_read_capacity_data *rcap;
@ -4255,6 +4286,52 @@ dadone(struct cam_periph *periph, union ccb *done_ccb)
biodone(bp);
return;
}
case DA_CCB_PROBE_WP:
{
struct scsi_mode_header_6 *mode_hdr6;
struct scsi_mode_header_10 *mode_hdr10;
uint8_t dev_spec;
if (softc->minimum_cmd_size > 6) {
mode_hdr10 = (struct scsi_mode_header_10 *)csio->data_ptr;
dev_spec = mode_hdr10->dev_spec;
} else {
mode_hdr6 = (struct scsi_mode_header_6 *)csio->data_ptr;
dev_spec = mode_hdr6->dev_spec;
}
if (cam_ccb_status(done_ccb) == CAM_REQ_CMP) {
if ((dev_spec & 0x80) != 0)
softc->disk->d_flags |= DISKFLAG_WRITE_PROTECT;
else
softc->disk->d_flags &= ~DISKFLAG_WRITE_PROTECT;
} else {
int error;
error = daerror(done_ccb, CAM_RETRY_SELTO,
SF_RETRY_UA|SF_NO_PRINT);
if (error == ERESTART)
return;
else if (error != 0) {
if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
/* Don't wedge this device's queue */
cam_release_devq(done_ccb->ccb_h.path,
/*relsim_flags*/0,
/*reduction*/0,
/*timeout*/0,
/*getcount_only*/0);
}
}
}
free(csio->data_ptr, M_SCSIDA);
xpt_release_ccb(done_ccb);
if ((softc->flags & DA_FLAG_CAN_RC16) != 0)
softc->state = DA_STATE_PROBE_RC16;
else
softc->state = DA_STATE_PROBE_RC;
xpt_schedule(periph, priority);
return;
}
case DA_CCB_PROBE_RC:
case DA_CCB_PROBE_RC16:
{
@ -5340,11 +5417,7 @@ dareprobe(struct cam_periph *periph)
KASSERT(status == CAM_REQ_CMP,
("dareprobe: cam_periph_acquire failed"));
if (softc->flags & DA_FLAG_CAN_RC16)
softc->state = DA_STATE_PROBE_RC16;
else
softc->state = DA_STATE_PROBE_RC;
softc->state = DA_STATE_PROBE_WP;
xpt_schedule(periph, CAM_PRIORITY_DEV);
}

View File

@ -122,14 +122,18 @@ g_disk_access(struct g_provider *pp, int r, int w, int e)
e += pp->ace;
error = 0;
if ((pp->acr + pp->acw + pp->ace) == 0 && (r + w + e) > 0) {
if (dp->d_open != NULL) {
/*
* It would be better to defer this decision to d_open if
* it was able to take flags.
*/
if (w > 0 && (dp->d_flags & DISKFLAG_WRITE_PROTECT) != 0)
error = EROFS;
if (error == 0 && dp->d_open != NULL)
error = dp->d_open(dp);
if (bootverbose && error != 0)
printf("Opened disk %s -> %d\n",
pp->name, error);
if (error != 0)
return (error);
}
if (bootverbose && error != 0)
printf("Opened disk %s -> %d\n", pp->name, error);
if (error != 0)
return (error);
pp->sectorsize = dp->d_sectorsize;
if (dp->d_maxsize == 0) {
printf("WARNING: Disk drive %s%d has no d_maxsize\n",
@ -1043,7 +1047,8 @@ g_disk_sysctl_flags(SYSCTL_HANDLER_ARGS)
"\4CANFLUSHCACHE"
"\5UNMAPPEDBIO"
"\6DIRECTCOMPLETION"
"\10CANZONE");
"\10CANZONE"
"\11WRITEPROTECT");
sbuf_finish(sb);
error = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1);

View File

@ -126,13 +126,14 @@ struct disk {
LIST_HEAD(,disk_alias) d_aliases;
};
#define DISKFLAG_RESERVED 0x1 /* Was NEEDSGIANT */
#define DISKFLAG_OPEN 0x2
#define DISKFLAG_CANDELETE 0x4
#define DISKFLAG_CANFLUSHCACHE 0x8
#define DISKFLAG_UNMAPPED_BIO 0x10
#define DISKFLAG_DIRECT_COMPLETION 0x20
#define DISKFLAG_CANZONE 0x80
#define DISKFLAG_RESERVED 0x0001 /* Was NEEDSGIANT */
#define DISKFLAG_OPEN 0x0002
#define DISKFLAG_CANDELETE 0x0004
#define DISKFLAG_CANFLUSHCACHE 0x0008
#define DISKFLAG_UNMAPPED_BIO 0x0010
#define DISKFLAG_DIRECT_COMPLETION 0x0020
#define DISKFLAG_CANZONE 0x0080
#define DISKFLAG_WRITE_PROTECT 0x0100
struct disk *disk_alloc(void);
void disk_create(struct disk *disk, int version);