diff -urpN linux-2.5-export/drivers/block/Makefile pjones-2.5-export/drivers/block/Makefile
--- linux-2.5-export/drivers/block/Makefile 2004-09-10 11:54:01 -04:00
+++ pjones-2.5-export/drivers/block/Makefile 2004-09-10 12:11:50 -04:00
@@ -13,7 +13,7 @@
# kblockd threads
#
-obj-y := elevator.o ll_rw_blk.o ioctl.o genhd.o scsi_ioctl.o
+obj-y := elevator.o ll_rw_blk.o ioctl.o genhd.o scsi_ioctl.o rcf.o
obj-$(CONFIG_IOSCHED_NOOP) += noop-iosched.o
obj-$(CONFIG_IOSCHED_AS) += as-iosched.o
diff -urpN linux-2.5-export/drivers/block/genhd.c pjones-2.5-export/drivers/block/genhd.c
--- linux-2.5-export/drivers/block/genhd.c 2004-09-10 11:53:54 -04:00
+++ pjones-2.5-export/drivers/block/genhd.c 2004-09-10 12:11:43 -04:00
@@ -197,6 +197,7 @@ void add_disk(struct gendisk *disk)
disk->minors, NULL, exact_match, exact_lock, disk);
register_disk(disk);
blk_register_queue(disk);
+ blk_register_filter(disk);
}
EXPORT_SYMBOL(add_disk);
@@ -204,6 +205,7 @@ EXPORT_SYMBOL(del_gendisk); /* in partit
void unlink_gendisk(struct gendisk *disk)
{
+ blk_unregister_filter(disk);
blk_unregister_queue(disk);
blk_unregister_region(MKDEV(disk->major, disk->first_minor),
disk->minors);
@@ -382,6 +384,13 @@ static ssize_t disk_stats_read(struct ge
jiffies_to_msecs(disk_stat_read(disk, io_ticks)),
jiffies_to_msecs(disk_stat_read(disk, time_in_queue)));
}
+
+static ssize_t disk_driver_read(struct gendisk * disk, char *page)
+{
+ return sprintf(page, "%s\n", disk->disk_name);
+
+}
+
static struct disk_attribute disk_attr_dev = {
.attr = {.name = "dev", .mode = S_IRUGO },
.show = disk_dev_read
@@ -402,6 +411,10 @@ static struct disk_attribute disk_attr_s
.attr = {.name = "stat", .mode = S_IRUGO },
.show = disk_stats_read
};
+static struct disk_attribute disk_attribute_driver = {
+ .attr = {.name = "driver", .mode = S_IRUGO },
+ .show = disk_driver_read
+};
static struct attribute * default_attrs[] = {
&disk_attr_dev.attr,
@@ -409,6 +422,7 @@ static struct attribute * default_attrs[
&disk_attr_removable.attr,
&disk_attr_size.attr,
&disk_attr_stat.attr,
+ &disk_attr_driver.attr,
NULL,
};
diff -urpN linux-2.5-export/drivers/block/rcf.c pjones-2.5-export/drivers/block/rcf.c
--- linux-2.5-export/drivers/block/rcf.c 1969-12-31 19:00:00 -05:00
+++ pjones-2.5-export/drivers/block/rcf.c 2004-09-10 12:12:00 -04:00
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2004 Peter M. Jones <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public Licens
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-
+ *
+ */
+
+#include <linux/list.h>
+#include <linux/genhd.h>
+#include <linux/spinlock.h>
+#include <linux/parser.h>
+#include <asm/bitops.h>
+
+#include <scsi/scsi.h>
+#include <linux/cdrom.h>
+
+int rcf_verify_command(struct file *file, unsigned char *cmd)
+{
+ struct rawio_cmd_filter *rcf;
+ struct inode *inode;
+
+ inode = file->f_dentry->d_inode;
+ if (!inode)
+ return -EINVAL;
+
+ rcf = &inode->i_bdev->bd_disk->filter;
+
+ /* root can do any command. */
+ if (capable(CAP_SYS_RAWIO))
+ return 0;
+
+ /* if there's no filter allocated, bail out. */
+ if (unlikely(!rcf))
+ return -EPERM;
+
+ /* Anybody who can open the device can do a read-safe command */
+ if (test_bit(cmd[0], rcf->read_ok))
+ return 0;
+
+ /* Write-safe commands require a writable open */
+ if (test_bit(cmd[0], rcf->write_ok))
+ if (file->f_mode & FMODE_WRITE)
+ return 0;
+
+ /* Or else the user has no perms */
+ return -EPERM;
+}
+
+EXPORT_SYMBOL(rcf_verify_command);
+
+/* and now, the sysfs stuff */
+static ssize_t rcf_readcmds_show(struct rawio_cmd_filter *rcf, char *page)
+{
+ char *npage = page;
+ int x;
+
+ for (x=0; x < RCF_MAX_NR_CMDS; x++)
+ if (test_bit(x, rcf->read_ok)) {
+ sprintf(npage, "%02x", x);
+ npage += 2;
+ if (x < RCF_MAX_NR_CMDS-1)
+ sprintf(npage++, " ");
+ }
+ if (npage != page)
+ npage += sprintf(npage, "\n");
+
+ return (npage - page);
+}
+
+static ssize_t rcf_writecmds_show(struct rawio_cmd_filter *rcf, char *page)
+{
+ char *npage = page;
+ int x;
+
+ for (x=0; x < RCF_MAX_NR_CMDS; x++)
+ if (test_bit(x, rcf->write_ok)) {
+ sprintf(npage, "%02x", x);
+ npage += 2;
+ if (x < RCF_MAX_NR_CMDS-1)
+ sprintf(npage++, " ");
+ }
+ if (npage != page)
+ npage += sprintf(npage, "\n");
+
+ return (npage - page);
+}
+
+#define RCF_ADD 0
+#define RCF_DEL 1
+
+static ssize_t rcf_store(struct rawio_cmd_filter *rcf, const char *page,
+ size_t count)
+{
+ ssize_t ret=0;
+ int ad,rw;
+ int cmd, status;
+
+ substring_t ss;
+
+ if (!strncmp(page+ret, "add", 3)) {
+ ret += 3;
+ ad = RCF_ADD;
+ } else if (!strncmp(page+ret, "del", 3)) {
+ ret += 3;
+ ad = RCF_DEL;
+ } else
+ return -EINVAL;
+ if (page[ret++] == 0)
+ return -EINVAL;
+
+ if (!strncmp(page+ret, "read", 4)) {
+ ret += 4;
+ rw = READ;
+ } else if (!strncmp(page+ret, "write", 5)) {
+ ret += 5;
+ rw = WRITE;
+ } else
+ return -EINVAL;
+ if (page[ret++] == 0)
+ return -EINVAL;
+
+ ss.from = (char *)page+ret;
+ ss.to = ss.from + count - ret;
+ ret += count - ret;
+
+ status = match_hex(&ss, &cmd);
+ if (status)
+ return status;
+ if (cmd > 256)
+ return -EINVAL;
+
+ if (ad == RCF_ADD) {
+ if (rw == READ)
+ set_bit(cmd, rcf->read_ok);
+ else
+ set_bit(cmd, rcf->write_ok);
+ } else {
+ if (rw == READ)
+ clear_bit(cmd, rcf->read_ok);
+ else
+ clear_bit(cmd, rcf->write_ok);
+ }
+
+ return count;
+}
+
+struct rcf_sysfs_entry {
+ struct attribute attr;
+ ssize_t (*show)(struct rawio_cmd_filter *, char *);
+ ssize_t (*store)(struct rawio_cmd_filter *, const char *, size_t);
+};
+
+static struct rcf_sysfs_entry rcf_readcmds_entry = {
+ .attr = {.name = "ok_read_commands", .mode = S_IRUGO | S_IWUSR },
+ .show = rcf_readcmds_show,
+ .store = rcf_store,
+};
+
+static struct rcf_sysfs_entry rcf_writecmds_entry = {
+ .attr = {.name = "ok_write_commands", .mode = S_IRUGO | S_IWUSR },
+ .show = rcf_writecmds_show,
+ .store = rcf_store,
+};
+
+static struct attribute *default_attrs[] = {
+ &rcf_readcmds_entry.attr,
+ &rcf_writecmds_entry.attr,
+ NULL,
+};
+
+#define to_rcf(atr) container_of((atr), struct rcf_sysfs_entry, attr)
+
+static ssize_t
+rcf_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
+{
+ struct rcf_sysfs_entry *entry = to_rcf(attr);
+ struct rawio_cmd_filter *rcf;
+
+ rcf = container_of(kobj, struct rawio_cmd_filter, kobj);
+ if (!entry->show)
+ return 0;
+
+ return entry->show(rcf, page);
+}
+
+static ssize_t
+rcf_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *page, size_t length)
+{
+ struct rcf_sysfs_entry *entry = to_rcf(attr);
+ struct rawio_cmd_filter *rcf;
+
+ rcf = container_of(kobj, struct rawio_cmd_filter, kobj);
+ if (!entry->store)
+ return -EINVAL;
+
+ return entry->store(rcf, page, length);
+}
+
+static struct sysfs_ops rcf_sysfs_ops = {
+ .show = rcf_attr_show,
+ .store = rcf_attr_store,
+};
+
+static struct kobj_type rcf_ktype = {
+ .sysfs_ops = &rcf_sysfs_ops,
+ .default_attrs = default_attrs,
+};
+
+static void rcf_set_defaults(struct rawio_cmd_filter *rcf)
+{
+ /* Basic read-only commands */
+ set_bit(TEST_UNIT_READY, rcf->read_ok);
+ set_bit(REQUEST_SENSE, rcf->read_ok);
+ set_bit(READ_6, rcf->read_ok);
+ set_bit(READ_10, rcf->read_ok);
+ set_bit(READ_12, rcf->read_ok);
+ set_bit(READ_16, rcf->read_ok);
+ set_bit(READ_BUFFER, rcf->read_ok);
+ set_bit(READ_LONG, rcf->read_ok);
+ set_bit(INQUIRY, rcf->read_ok);
+ set_bit(MODE_SENSE, rcf->read_ok);
+ set_bit(MODE_SENSE_10, rcf->read_ok);
+ set_bit(START_STOP, rcf->read_ok);
+ set_bit(GPCMD_VERIFY_10, rcf->read_ok);
+ set_bit(VERIFY_16, rcf->read_ok);
+ set_bit(READ_BUFFER, rcf->read_ok);
+
+ /* Audio CD commands */
+ set_bit(GPCMD_PLAY_CD, rcf->read_ok);
+ set_bit(GPCMD_PLAY_AUDIO_10, rcf->read_ok);
+ set_bit(GPCMD_PLAY_AUDIO_MSF, rcf->read_ok);
+ set_bit(GPCMD_PLAY_AUDIO_TI, rcf->read_ok);
+ set_bit(GPCMD_PAUSE_RESUME, rcf->read_ok);
+
+ /* CD/DVD data reading */
+ set_bit(GPCMD_READ_CD, rcf->read_ok);
+ set_bit(GPCMD_READ_CD_MSF, rcf->read_ok);
+ set_bit(GPCMD_READ_DISC_INFO, rcf->read_ok);
+ set_bit(GPCMD_READ_CDVD_CAPACITY, rcf->read_ok);
+ set_bit(GPCMD_READ_DVD_STRUCTURE, rcf->read_ok);
+ set_bit(GPCMD_READ_HEADER, rcf->read_ok);
+ set_bit(GPCMD_READ_TRACK_RZONE_INFO, rcf->read_ok);
+ set_bit(GPCMD_READ_SUBCHANNEL, rcf->read_ok);
+ set_bit(GPCMD_READ_TOC_PMA_ATIP, rcf->read_ok);
+ set_bit(GPCMD_REPORT_KEY, rcf->read_ok);
+ set_bit(GPCMD_SCAN, rcf->read_ok);
+ set_bit(GPCMD_GET_CONFIGURATION, rcf->read_ok);
+ set_bit(GPCMD_READ_FORMAT_CAPACITIES, rcf->read_ok);
+ set_bit(GPCMD_GET_EVENT_STATUS_NOTIFICATION, rcf->read_ok);
+ set_bit(GPCMD_GET_PERFORMANCE, rcf->read_ok);
+ set_bit(GPCMD_SEEK, rcf->read_ok);
+ set_bit(GPCMD_STOP_PLAY_SCAN, rcf->read_ok);
+
+ /* Basic writing commands */
+ set_bit(WRITE_6, rcf->write_ok);
+ set_bit(WRITE_10, rcf->write_ok);
+ set_bit(WRITE_VERIFY, rcf->write_ok);
+ set_bit(WRITE_12, rcf->write_ok);
+ set_bit(WRITE_VERIFY_12, rcf->write_ok);
+ set_bit(WRITE_16, rcf->write_ok);
+ set_bit(WRITE_LONG, rcf->write_ok);
+ set_bit(ERASE, rcf->write_ok);
+ set_bit(GPCMD_MODE_SELECT_10, rcf->write_ok);
+ set_bit(MODE_SELECT, rcf->write_ok);
+ set_bit(GPCMD_BLANK, rcf->write_ok);
+ set_bit(GPCMD_CLOSE_TRACK, rcf->write_ok);
+ set_bit(GPCMD_FLUSH_CACHE, rcf->write_ok);
+ set_bit(GPCMD_FORMAT_UNIT, rcf->write_ok);
+ set_bit(GPCMD_REPAIR_RZONE_TRACK, rcf->write_ok);
+ set_bit(GPCMD_RESERVE_RZONE_TRACK, rcf->write_ok);
+ set_bit(GPCMD_SEND_DVD_STRUCTURE, rcf->write_ok);
+ set_bit(GPCMD_SEND_EVENT, rcf->write_ok);
+ set_bit(GPCMD_SEND_KEY, rcf->write_ok);
+ set_bit(GPCMD_SEND_OPC, rcf->write_ok);
+ set_bit(GPCMD_SEND_CUE_SHEET, rcf->write_ok);
+ set_bit(GPCMD_SET_SPEED, rcf->write_ok);
+ set_bit(GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL, rcf->write_ok);
+ set_bit(GPCMD_LOAD_UNLOAD, rcf->write_ok);
+ set_bit(GPCMD_SET_STREAMING, rcf->write_ok);
+}
+
+int blk_register_filter(struct gendisk *disk)
+{
+ int ret;
+ struct rawio_cmd_filter *rcf = &disk->filter;
+
+ rcf->kobj.parent = kobject_get(&disk->kobj);
+ if (!rcf->kobj.parent)
+ return -EBUSY;
+
+ snprintf(rcf->kobj.name, KOBJ_NAME_LEN, "%s", "filter");
+ rcf->kobj.ktype = &rcf_ktype;
+
+ ret = kobject_register(&rcf->kobj);
+ if (ret < 0)
+ return ret;
+
+ rcf_set_defaults(&disk->filter);
+
+ return 0;
+}
+
+void blk_unregister_filter(struct gendisk *disk)
+{
+ struct rawio_cmd_filter *rcf = &disk->filter;
+
+ kobject_unregister(&rcf->kobj);
+ kobject_put(&disk->kobj);
+}
diff -urpN linux-2.5-export/drivers/block/scsi_ioctl.c pjones-2.5-export/drivers/block/scsi_ioctl.c
--- linux-2.5-export/drivers/block/scsi_ioctl.c 2004-09-10 11:54:05 -04:00
+++ pjones-2.5-export/drivers/block/scsi_ioctl.c 2004-09-10 12:12:07 -04:00
@@ -105,105 +105,6 @@ static int sg_emulated_host(request_queu
return put_user(1, p);
}
-#define CMD_READ_SAFE 0x01
-#define CMD_WRITE_SAFE 0x02
-#define safe_for_read(cmd) [cmd] = CMD_READ_SAFE
-#define safe_for_write(cmd) [cmd] = CMD_WRITE_SAFE
-
-static int verify_command(struct file *file, unsigned char *cmd)
-{
- static const unsigned char cmd_type[256] = {
-
- /* Basic read-only commands */
- safe_for_read(TEST_UNIT_READY),
- safe_for_read(REQUEST_SENSE),
- safe_for_read(READ_6),
- safe_for_read(READ_10),
- safe_for_read(READ_12),
- safe_for_read(READ_16),
- safe_for_read(READ_BUFFER),
- safe_for_read(READ_LONG),
- safe_for_read(INQUIRY),
- safe_for_read(MODE_SENSE),
- safe_for_read(MODE_SENSE_10),
- safe_for_read(START_STOP),
- safe_for_read(GPCMD_VERIFY_10),
- safe_for_read(VERIFY_16),
- safe_for_read(READ_BUFFER),
-
- /* Audio CD commands */
- safe_for_read(GPCMD_PLAY_CD),
- safe_for_read(GPCMD_PLAY_AUDIO_10),
- safe_for_read(GPCMD_PLAY_AUDIO_MSF),
- safe_for_read(GPCMD_PLAY_AUDIO_TI),
- safe_for_read(GPCMD_PAUSE_RESUME),
-
- /* CD/DVD data reading */
- safe_for_read(GPCMD_READ_CD),
- safe_for_read(GPCMD_READ_CD_MSF),
- safe_for_read(GPCMD_READ_DISC_INFO),
- safe_for_read(GPCMD_READ_CDVD_CAPACITY),
- safe_for_read(GPCMD_READ_DVD_STRUCTURE),
- safe_for_read(GPCMD_READ_HEADER),
- safe_for_read(GPCMD_READ_TRACK_RZONE_INFO),
- safe_for_read(GPCMD_READ_SUBCHANNEL),
- safe_for_read(GPCMD_READ_TOC_PMA_ATIP),
- safe_for_read(GPCMD_REPORT_KEY),
- safe_for_read(GPCMD_SCAN),
- safe_for_read(GPCMD_GET_CONFIGURATION),
- safe_for_read(GPCMD_READ_FORMAT_CAPACITIES),
- safe_for_read(GPCMD_GET_EVENT_STATUS_NOTIFICATION),
- safe_for_read(GPCMD_GET_PERFORMANCE),
- safe_for_read(GPCMD_SEEK),
- safe_for_read(GPCMD_STOP_PLAY_SCAN),
-
- /* Basic writing commands */
- safe_for_write(WRITE_6),
- safe_for_write(WRITE_10),
- safe_for_write(WRITE_VERIFY),
- safe_for_write(WRITE_12),
- safe_for_write(WRITE_VERIFY_12),
- safe_for_write(WRITE_16),
- safe_for_write(WRITE_LONG),
- safe_for_write(ERASE),
- safe_for_write(GPCMD_MODE_SELECT_10),
- safe_for_write(MODE_SELECT),
- safe_for_write(GPCMD_BLANK),
- safe_for_write(GPCMD_CLOSE_TRACK),
- safe_for_write(GPCMD_FLUSH_CACHE),
- safe_for_write(GPCMD_FORMAT_UNIT),
- safe_for_write(GPCMD_REPAIR_RZONE_TRACK),
- safe_for_write(GPCMD_RESERVE_RZONE_TRACK),
- safe_for_write(GPCMD_SEND_DVD_STRUCTURE),
- safe_for_write(GPCMD_SEND_EVENT),
- safe_for_write(GPCMD_SEND_KEY),
- safe_for_write(GPCMD_SEND_OPC),
- safe_for_write(GPCMD_SEND_CUE_SHEET),
- safe_for_write(GPCMD_SET_SPEED),
- safe_for_write(GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL),
- safe_for_write(GPCMD_LOAD_UNLOAD),
- safe_for_write(GPCMD_SET_STREAMING),
- };
- unsigned char type = cmd_type[cmd[0]];
-
- /* Anybody who can open the device can do a read-safe command */
- if (type & CMD_READ_SAFE)
- return 0;
-
- /* Write-safe commands just require a writable open.. */
- if (type & CMD_WRITE_SAFE) {
- if (file->f_mode & FMODE_WRITE)
- return 0;
- }
-
- /* And root can do any command.. */
- if (capable(CAP_SYS_RAWIO))
- return 0;
-
- /* Otherwise fail it with an "Operation not permitted" */
- return -EPERM;
-}
-
static int sg_io(struct file *file, request_queue_t *q,
struct gendisk *bd_disk, struct sg_io_hdr *hdr)
{
@@ -220,7 +121,7 @@ static int sg_io(struct file *file, requ
return -EINVAL;
if (copy_from_user(cmd, hdr->cmdp, hdr->cmd_len))
return -EFAULT;
- if (verify_command(file, cmd))
+ if (rcf_verify_command(file, cmd))
return -EPERM;
/*
@@ -370,7 +271,7 @@ static int sg_scsi_ioctl(struct file *fi
if (copy_from_user(buffer, sic->data + cmdlen, in_len))
goto error;
- err = verify_command(file, rq->cmd);
+ err = rcf_verify_command(file, rq->cmd);
if (err)
goto error;
diff -urpN linux-2.5-export/include/linux/blkdev.h pjones-2.5-export/include/linux/blkdev.h
--- linux-2.5-export/include/linux/blkdev.h 2004-09-10 11:53:51 -04:00
+++ pjones-2.5-export/include/linux/blkdev.h 2004-09-10 12:22:00 -04:00
@@ -512,6 +512,8 @@ struct sec_size {
unsigned block_size_bits;
};
+extern int blk_register_filter(struct gendisk *disk);
+extern void blk_unregister_filter(struct gendisk *disk);
extern int blk_register_queue(struct gendisk *disk);
extern void blk_unregister_queue(struct gendisk *disk);
extern void register_disk(struct gendisk *dev);
diff -urpN linux-2.5-export/include/linux/genhd.h pjones-2.5-export/include/linux/genhd.h
--- linux-2.5-export/include/linux/genhd.h 2004-09-10 11:53:50 -04:00
+++ pjones-2.5-export/include/linux/genhd.h 2004-09-10 12:11:37 -04:00
@@ -78,6 +78,15 @@ struct disk_stats {
unsigned io_ticks;
unsigned time_in_queue;
};
+
+#define RCF_MAX_NR_CMDS 256
+#define RCF_CMD_BYTES (RCF_MAX_NR_CMDS / (sizeof (unsigned long) * 8))
+
+struct rawio_cmd_filter {
+ unsigned long read_ok[RCF_CMD_BYTES];
+ unsigned long write_ok[RCF_CMD_BYTES];
+ struct kobject kobj;
+};
struct gendisk {
int major; /* major number of driver */
@@ -88,6 +97,7 @@ struct gendisk {
struct hd_struct **part; /* [indexed by minor] */
struct block_device_operations *fops;
struct request_queue *queue;
+ struct rawio_cmd_filter filter;
void *private_data;
sector_t capacity;
On Tue, 14 Sep 2004 10:51:10 -0400
Peter Jones <[email protected]> wrote:
Good to see someone do this. I tried myself but didn't get very far.
> diff -urpN linux-2.5-export/drivers/block/genhd.c pjones-2.5-export/drivers/block/genhd.c
...
> };
> +static struct disk_attribute disk_attribute_driver = {
This obviously needs to be
+static struct disk_attribute disk_attr_driver = {
otherwise the following part won't work:
> @@ -409,6 +422,7 @@ static struct attribute * default_attrs[
> &disk_attr_removable.attr,
> &disk_attr_size.attr,
> &disk_attr_stat.attr,
> + &disk_attr_driver.attr,
> NULL,
> };
Additionally, the naming "rawio_cmd_filter" and RCF_* seems a bit
confusing (after all raw IO usually refers to O_DIRECT file access).
"sg_cmd_filter" and SGF_* appear to be a better choice IMHO.
How should changes to the table be made? I suppose something like "echo
add 04 > ok_read_commands". But rcf_store doesn't seem to get called. Am I
missing something?
Regards, and thanks for this patch
On Wed, 2004-09-15 at 00:43 +0200, Marc Ballarin wrote:
> > +static struct disk_attribute disk_attribute_driver = {
>
> This obviously needs to be
>
> +static struct disk_attribute disk_attr_driver = {
Absolutely right; I had this checked into my bk tree, but no cset made.
That makes it not get exported, I guess...
[...]
> Additionally, the naming "rawio_cmd_filter" and RCF_* seems a bit
> confusing (after all raw IO usually refers to O_DIRECT file access).
> "sg_cmd_filter" and SGF_* appear to be a better choice IMHO.
I could go either way on this; we use CAP_SYS_RAWIO to govern if root is
getting access, but at the same time this really is scsi commands. I
wonder Jens thinks, as he's the maintainer here...
> How should changes to the table be made? I suppose something like "echo
> add 04 > ok_read_commands". But rcf_store doesn't seem to get called. Am I
> missing something?
"add read 04", "del read 04", "add write 04", ...
rcf_store() gets called as rcf_writecmds_entry.store() and
rcf_readcommands_entry.store() . The get called from rcf_attr_store(),
which gets called by sysfs as rcf_sysfs_ops.store()
> Regards, and thanks for this patch
Quite welcome. Here's an updated version with the attribute struct name
fixed:
diff -urpN linux-2.5-export/drivers/block/Makefile pjones-2.5-export/drivers/block/Makefile
--- linux-2.5-export/drivers/block/Makefile 2004-09-15 12:42:18 -04:00
+++ pjones-2.5-export/drivers/block/Makefile 2004-09-15 12:55:13 -04:00
@@ -13,7 +13,7 @@
# kblockd threads
#
-obj-y := elevator.o ll_rw_blk.o ioctl.o genhd.o scsi_ioctl.o
+obj-y := elevator.o ll_rw_blk.o ioctl.o genhd.o scsi_ioctl.o rcf.o
obj-$(CONFIG_IOSCHED_NOOP) += noop-iosched.o
obj-$(CONFIG_IOSCHED_AS) += as-iosched.o
diff -urpN linux-2.5-export/drivers/block/genhd.c pjones-2.5-export/drivers/block/genhd.c
--- linux-2.5-export/drivers/block/genhd.c 2004-09-15 12:41:22 -04:00
+++ pjones-2.5-export/drivers/block/genhd.c 2004-09-15 12:54:26 -04:00
@@ -197,6 +197,7 @@ void add_disk(struct gendisk *disk)
disk->minors, NULL, exact_match, exact_lock, disk);
register_disk(disk);
blk_register_queue(disk);
+ blk_register_filter(disk);
}
EXPORT_SYMBOL(add_disk);
@@ -204,6 +205,7 @@ EXPORT_SYMBOL(del_gendisk); /* in partit
void unlink_gendisk(struct gendisk *disk)
{
+ blk_unregister_filter(disk);
blk_unregister_queue(disk);
blk_unregister_region(MKDEV(disk->major, disk->first_minor),
disk->minors);
@@ -382,6 +384,13 @@ static ssize_t disk_stats_read(struct ge
jiffies_to_msecs(disk_stat_read(disk, io_ticks)),
jiffies_to_msecs(disk_stat_read(disk, time_in_queue)));
}
+
+static ssize_t disk_driver_read(struct gendisk * disk, char *page)
+{
+ return sprintf(page, "%s\n", disk->disk_name);
+
+}
+
static struct disk_attribute disk_attr_dev = {
.attr = {.name = "dev", .mode = S_IRUGO },
.show = disk_dev_read
@@ -402,6 +411,10 @@ static struct disk_attribute disk_attr_s
.attr = {.name = "stat", .mode = S_IRUGO },
.show = disk_stats_read
};
+static struct disk_attribute disk_attr_driver = {
+ .attr = {.name = "driver", .mode = S_IRUGO },
+ .show = disk_driver_read
+};
static struct attribute * default_attrs[] = {
&disk_attr_dev.attr,
@@ -409,6 +422,7 @@ static struct attribute * default_attrs[
&disk_attr_removable.attr,
&disk_attr_size.attr,
&disk_attr_stat.attr,
+ &disk_attr_driver.attr,
NULL,
};
diff -urpN linux-2.5-export/drivers/block/rcf.c pjones-2.5-export/drivers/block/rcf.c
--- linux-2.5-export/drivers/block/rcf.c 1969-12-31 19:00:00 -05:00
+++ pjones-2.5-export/drivers/block/rcf.c 2004-09-15 12:55:38 -04:00
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2004 Peter M. Jones <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public Licens
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-
+ *
+ */
+
+#include <linux/list.h>
+#include <linux/genhd.h>
+#include <linux/spinlock.h>
+#include <linux/parser.h>
+#include <asm/bitops.h>
+
+#include <scsi/scsi.h>
+#include <linux/cdrom.h>
+
+int rcf_verify_command(struct file *file, unsigned char *cmd)
+{
+ struct rawio_cmd_filter *rcf;
+ struct inode *inode;
+
+ inode = file->f_dentry->d_inode;
+ if (!inode)
+ return -EINVAL;
+
+ rcf = &inode->i_bdev->bd_disk->filter;
+
+ /* root can do any command. */
+ if (capable(CAP_SYS_RAWIO))
+ return 0;
+
+ /* if there's no filter allocated, bail out. */
+ if (unlikely(!rcf))
+ return -EPERM;
+
+ /* Anybody who can open the device can do a read-safe command */
+ if (test_bit(cmd[0], rcf->read_ok))
+ return 0;
+
+ /* Write-safe commands require a writable open */
+ if (test_bit(cmd[0], rcf->write_ok))
+ if (file->f_mode & FMODE_WRITE)
+ return 0;
+
+ /* Or else the user has no perms */
+ return -EPERM;
+}
+
+EXPORT_SYMBOL(rcf_verify_command);
+
+/* and now, the sysfs stuff */
+static ssize_t rcf_readcmds_show(struct rawio_cmd_filter *rcf, char *page)
+{
+ char *npage = page;
+ int x;
+
+ for (x=0; x < RCF_MAX_NR_CMDS; x++)
+ if (test_bit(x, rcf->read_ok)) {
+ sprintf(npage, "%02x", x);
+ npage += 2;
+ if (x < RCF_MAX_NR_CMDS-1)
+ sprintf(npage++, " ");
+ }
+ if (npage != page)
+ npage += sprintf(npage, "\n");
+
+ return (npage - page);
+}
+
+static ssize_t rcf_writecmds_show(struct rawio_cmd_filter *rcf, char *page)
+{
+ char *npage = page;
+ int x;
+
+ for (x=0; x < RCF_MAX_NR_CMDS; x++)
+ if (test_bit(x, rcf->write_ok)) {
+ sprintf(npage, "%02x", x);
+ npage += 2;
+ if (x < RCF_MAX_NR_CMDS-1)
+ sprintf(npage++, " ");
+ }
+ if (npage != page)
+ npage += sprintf(npage, "\n");
+
+ return (npage - page);
+}
+
+#define RCF_ADD 0
+#define RCF_DEL 1
+
+static ssize_t rcf_store(struct rawio_cmd_filter *rcf, const char *page,
+ size_t count)
+{
+ ssize_t ret=0;
+ int ad,rw;
+ int cmd, status;
+
+ substring_t ss;
+
+ if (!strncmp(page+ret, "add", 3)) {
+ ret += 3;
+ ad = RCF_ADD;
+ } else if (!strncmp(page+ret, "del", 3)) {
+ ret += 3;
+ ad = RCF_DEL;
+ } else
+ return -EINVAL;
+ if (page[ret++] == 0)
+ return -EINVAL;
+
+ if (!strncmp(page+ret, "read", 4)) {
+ ret += 4;
+ rw = READ;
+ } else if (!strncmp(page+ret, "write", 5)) {
+ ret += 5;
+ rw = WRITE;
+ } else
+ return -EINVAL;
+ if (page[ret++] == 0)
+ return -EINVAL;
+
+ ss.from = (char *)page+ret;
+ ss.to = ss.from + count - ret;
+ ret += count - ret;
+
+ status = match_hex(&ss, &cmd);
+ if (status)
+ return status;
+ if (cmd > 256)
+ return -EINVAL;
+
+ if (ad == RCF_ADD) {
+ if (rw == READ)
+ set_bit(cmd, rcf->read_ok);
+ else
+ set_bit(cmd, rcf->write_ok);
+ } else {
+ if (rw == READ)
+ clear_bit(cmd, rcf->read_ok);
+ else
+ clear_bit(cmd, rcf->write_ok);
+ }
+
+ return count;
+}
+
+struct rcf_sysfs_entry {
+ struct attribute attr;
+ ssize_t (*show)(struct rawio_cmd_filter *, char *);
+ ssize_t (*store)(struct rawio_cmd_filter *, const char *, size_t);
+};
+
+static struct rcf_sysfs_entry rcf_readcmds_entry = {
+ .attr = {.name = "ok_read_commands", .mode = S_IRUGO | S_IWUSR },
+ .show = rcf_readcmds_show,
+ .store = rcf_store,
+};
+
+static struct rcf_sysfs_entry rcf_writecmds_entry = {
+ .attr = {.name = "ok_write_commands", .mode = S_IRUGO | S_IWUSR },
+ .show = rcf_writecmds_show,
+ .store = rcf_store,
+};
+
+static struct attribute *default_attrs[] = {
+ &rcf_readcmds_entry.attr,
+ &rcf_writecmds_entry.attr,
+ NULL,
+};
+
+#define to_rcf(atr) container_of((atr), struct rcf_sysfs_entry, attr)
+
+static ssize_t
+rcf_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
+{
+ struct rcf_sysfs_entry *entry = to_rcf(attr);
+ struct rawio_cmd_filter *rcf;
+
+ rcf = container_of(kobj, struct rawio_cmd_filter, kobj);
+ if (!entry->show)
+ return 0;
+
+ return entry->show(rcf, page);
+}
+
+static ssize_t
+rcf_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *page, size_t length)
+{
+ struct rcf_sysfs_entry *entry = to_rcf(attr);
+ struct rawio_cmd_filter *rcf;
+
+ rcf = container_of(kobj, struct rawio_cmd_filter, kobj);
+ if (!entry->store)
+ return -EINVAL;
+
+ return entry->store(rcf, page, length);
+}
+
+static struct sysfs_ops rcf_sysfs_ops = {
+ .show = rcf_attr_show,
+ .store = rcf_attr_store,
+};
+
+static struct kobj_type rcf_ktype = {
+ .sysfs_ops = &rcf_sysfs_ops,
+ .default_attrs = default_attrs,
+};
+
+static void rcf_set_defaults(struct rawio_cmd_filter *rcf)
+{
+ /* Basic read-only commands */
+ set_bit(TEST_UNIT_READY, rcf->read_ok);
+ set_bit(REQUEST_SENSE, rcf->read_ok);
+ set_bit(READ_6, rcf->read_ok);
+ set_bit(READ_10, rcf->read_ok);
+ set_bit(READ_12, rcf->read_ok);
+ set_bit(READ_16, rcf->read_ok);
+ set_bit(READ_BUFFER, rcf->read_ok);
+ set_bit(READ_LONG, rcf->read_ok);
+ set_bit(INQUIRY, rcf->read_ok);
+ set_bit(MODE_SENSE, rcf->read_ok);
+ set_bit(MODE_SENSE_10, rcf->read_ok);
+ set_bit(START_STOP, rcf->read_ok);
+ set_bit(GPCMD_VERIFY_10, rcf->read_ok);
+ set_bit(VERIFY_16, rcf->read_ok);
+ set_bit(READ_BUFFER, rcf->read_ok);
+
+ /* Audio CD commands */
+ set_bit(GPCMD_PLAY_CD, rcf->read_ok);
+ set_bit(GPCMD_PLAY_AUDIO_10, rcf->read_ok);
+ set_bit(GPCMD_PLAY_AUDIO_MSF, rcf->read_ok);
+ set_bit(GPCMD_PLAY_AUDIO_TI, rcf->read_ok);
+ set_bit(GPCMD_PAUSE_RESUME, rcf->read_ok);
+
+ /* CD/DVD data reading */
+ set_bit(GPCMD_READ_CD, rcf->read_ok);
+ set_bit(GPCMD_READ_CD_MSF, rcf->read_ok);
+ set_bit(GPCMD_READ_DISC_INFO, rcf->read_ok);
+ set_bit(GPCMD_READ_CDVD_CAPACITY, rcf->read_ok);
+ set_bit(GPCMD_READ_DVD_STRUCTURE, rcf->read_ok);
+ set_bit(GPCMD_READ_HEADER, rcf->read_ok);
+ set_bit(GPCMD_READ_TRACK_RZONE_INFO, rcf->read_ok);
+ set_bit(GPCMD_READ_SUBCHANNEL, rcf->read_ok);
+ set_bit(GPCMD_READ_TOC_PMA_ATIP, rcf->read_ok);
+ set_bit(GPCMD_REPORT_KEY, rcf->read_ok);
+ set_bit(GPCMD_SCAN, rcf->read_ok);
+ set_bit(GPCMD_GET_CONFIGURATION, rcf->read_ok);
+ set_bit(GPCMD_READ_FORMAT_CAPACITIES, rcf->read_ok);
+ set_bit(GPCMD_GET_EVENT_STATUS_NOTIFICATION, rcf->read_ok);
+ set_bit(GPCMD_GET_PERFORMANCE, rcf->read_ok);
+ set_bit(GPCMD_SEEK, rcf->read_ok);
+ set_bit(GPCMD_STOP_PLAY_SCAN, rcf->read_ok);
+
+ /* Basic writing commands */
+ set_bit(WRITE_6, rcf->write_ok);
+ set_bit(WRITE_10, rcf->write_ok);
+ set_bit(WRITE_VERIFY, rcf->write_ok);
+ set_bit(WRITE_12, rcf->write_ok);
+ set_bit(WRITE_VERIFY_12, rcf->write_ok);
+ set_bit(WRITE_16, rcf->write_ok);
+ set_bit(WRITE_LONG, rcf->write_ok);
+ set_bit(ERASE, rcf->write_ok);
+ set_bit(GPCMD_MODE_SELECT_10, rcf->write_ok);
+ set_bit(MODE_SELECT, rcf->write_ok);
+ set_bit(GPCMD_BLANK, rcf->write_ok);
+ set_bit(GPCMD_CLOSE_TRACK, rcf->write_ok);
+ set_bit(GPCMD_FLUSH_CACHE, rcf->write_ok);
+ set_bit(GPCMD_FORMAT_UNIT, rcf->write_ok);
+ set_bit(GPCMD_REPAIR_RZONE_TRACK, rcf->write_ok);
+ set_bit(GPCMD_RESERVE_RZONE_TRACK, rcf->write_ok);
+ set_bit(GPCMD_SEND_DVD_STRUCTURE, rcf->write_ok);
+ set_bit(GPCMD_SEND_EVENT, rcf->write_ok);
+ set_bit(GPCMD_SEND_KEY, rcf->write_ok);
+ set_bit(GPCMD_SEND_OPC, rcf->write_ok);
+ set_bit(GPCMD_SEND_CUE_SHEET, rcf->write_ok);
+ set_bit(GPCMD_SET_SPEED, rcf->write_ok);
+ set_bit(GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL, rcf->write_ok);
+ set_bit(GPCMD_LOAD_UNLOAD, rcf->write_ok);
+ set_bit(GPCMD_SET_STREAMING, rcf->write_ok);
+}
+
+int blk_register_filter(struct gendisk *disk)
+{
+ int ret;
+ struct rawio_cmd_filter *rcf = &disk->filter;
+
+ rcf->kobj.parent = kobject_get(&disk->kobj);
+ if (!rcf->kobj.parent)
+ return -EBUSY;
+
+ snprintf(rcf->kobj.name, KOBJ_NAME_LEN, "%s", "filter");
+ rcf->kobj.ktype = &rcf_ktype;
+
+ ret = kobject_register(&rcf->kobj);
+ if (ret < 0)
+ return ret;
+
+ rcf_set_defaults(&disk->filter);
+
+ return 0;
+}
+
+void blk_unregister_filter(struct gendisk *disk)
+{
+ struct rawio_cmd_filter *rcf = &disk->filter;
+
+ kobject_unregister(&rcf->kobj);
+ kobject_put(&disk->kobj);
+}
diff -urpN linux-2.5-export/drivers/block/scsi_ioctl.c pjones-2.5-export/drivers/block/scsi_ioctl.c
--- linux-2.5-export/drivers/block/scsi_ioctl.c 2004-09-15 12:42:48 -04:00
+++ pjones-2.5-export/drivers/block/scsi_ioctl.c 2004-09-15 12:55:46 -04:00
@@ -105,105 +105,6 @@ static int sg_emulated_host(request_queu
return put_user(1, p);
}
-#define CMD_READ_SAFE 0x01
-#define CMD_WRITE_SAFE 0x02
-#define safe_for_read(cmd) [cmd] = CMD_READ_SAFE
-#define safe_for_write(cmd) [cmd] = CMD_WRITE_SAFE
-
-static int verify_command(struct file *file, unsigned char *cmd)
-{
- static const unsigned char cmd_type[256] = {
-
- /* Basic read-only commands */
- safe_for_read(TEST_UNIT_READY),
- safe_for_read(REQUEST_SENSE),
- safe_for_read(READ_6),
- safe_for_read(READ_10),
- safe_for_read(READ_12),
- safe_for_read(READ_16),
- safe_for_read(READ_BUFFER),
- safe_for_read(READ_LONG),
- safe_for_read(INQUIRY),
- safe_for_read(MODE_SENSE),
- safe_for_read(MODE_SENSE_10),
- safe_for_read(START_STOP),
- safe_for_read(GPCMD_VERIFY_10),
- safe_for_read(VERIFY_16),
- safe_for_read(READ_BUFFER),
-
- /* Audio CD commands */
- safe_for_read(GPCMD_PLAY_CD),
- safe_for_read(GPCMD_PLAY_AUDIO_10),
- safe_for_read(GPCMD_PLAY_AUDIO_MSF),
- safe_for_read(GPCMD_PLAY_AUDIO_TI),
- safe_for_read(GPCMD_PAUSE_RESUME),
-
- /* CD/DVD data reading */
- safe_for_read(GPCMD_READ_CD),
- safe_for_read(GPCMD_READ_CD_MSF),
- safe_for_read(GPCMD_READ_DISC_INFO),
- safe_for_read(GPCMD_READ_CDVD_CAPACITY),
- safe_for_read(GPCMD_READ_DVD_STRUCTURE),
- safe_for_read(GPCMD_READ_HEADER),
- safe_for_read(GPCMD_READ_TRACK_RZONE_INFO),
- safe_for_read(GPCMD_READ_SUBCHANNEL),
- safe_for_read(GPCMD_READ_TOC_PMA_ATIP),
- safe_for_read(GPCMD_REPORT_KEY),
- safe_for_read(GPCMD_SCAN),
- safe_for_read(GPCMD_GET_CONFIGURATION),
- safe_for_read(GPCMD_READ_FORMAT_CAPACITIES),
- safe_for_read(GPCMD_GET_EVENT_STATUS_NOTIFICATION),
- safe_for_read(GPCMD_GET_PERFORMANCE),
- safe_for_read(GPCMD_SEEK),
- safe_for_read(GPCMD_STOP_PLAY_SCAN),
-
- /* Basic writing commands */
- safe_for_write(WRITE_6),
- safe_for_write(WRITE_10),
- safe_for_write(WRITE_VERIFY),
- safe_for_write(WRITE_12),
- safe_for_write(WRITE_VERIFY_12),
- safe_for_write(WRITE_16),
- safe_for_write(WRITE_LONG),
- safe_for_write(ERASE),
- safe_for_write(GPCMD_MODE_SELECT_10),
- safe_for_write(MODE_SELECT),
- safe_for_write(GPCMD_BLANK),
- safe_for_write(GPCMD_CLOSE_TRACK),
- safe_for_write(GPCMD_FLUSH_CACHE),
- safe_for_write(GPCMD_FORMAT_UNIT),
- safe_for_write(GPCMD_REPAIR_RZONE_TRACK),
- safe_for_write(GPCMD_RESERVE_RZONE_TRACK),
- safe_for_write(GPCMD_SEND_DVD_STRUCTURE),
- safe_for_write(GPCMD_SEND_EVENT),
- safe_for_write(GPCMD_SEND_KEY),
- safe_for_write(GPCMD_SEND_OPC),
- safe_for_write(GPCMD_SEND_CUE_SHEET),
- safe_for_write(GPCMD_SET_SPEED),
- safe_for_write(GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL),
- safe_for_write(GPCMD_LOAD_UNLOAD),
- safe_for_write(GPCMD_SET_STREAMING),
- };
- unsigned char type = cmd_type[cmd[0]];
-
- /* Anybody who can open the device can do a read-safe command */
- if (type & CMD_READ_SAFE)
- return 0;
-
- /* Write-safe commands just require a writable open.. */
- if (type & CMD_WRITE_SAFE) {
- if (file->f_mode & FMODE_WRITE)
- return 0;
- }
-
- /* And root can do any command.. */
- if (capable(CAP_SYS_RAWIO))
- return 0;
-
- /* Otherwise fail it with an "Operation not permitted" */
- return -EPERM;
-}
-
static int sg_io(struct file *file, request_queue_t *q,
struct gendisk *bd_disk, struct sg_io_hdr *hdr)
{
@@ -220,7 +121,7 @@ static int sg_io(struct file *file, requ
return -EINVAL;
if (copy_from_user(cmd, hdr->cmdp, hdr->cmd_len))
return -EFAULT;
- if (verify_command(file, cmd))
+ if (rcf_verify_command(file, cmd))
return -EPERM;
/*
@@ -370,7 +271,7 @@ static int sg_scsi_ioctl(struct file *fi
if (copy_from_user(buffer, sic->data + cmdlen, in_len))
goto error;
- err = verify_command(file, rq->cmd);
+ err = rcf_verify_command(file, rq->cmd);
if (err)
goto error;
diff -urpN linux-2.5-export/include/linux/blkdev.h pjones-2.5-export/include/linux/blkdev.h
--- linux-2.5-export/include/linux/blkdev.h 2004-09-15 12:40:23 -04:00
+++ pjones-2.5-export/include/linux/blkdev.h 2004-09-15 12:53:55 -04:00
@@ -513,6 +513,8 @@ struct sec_size {
unsigned block_size_bits;
};
+extern int blk_register_filter(struct gendisk *disk);
+extern void blk_unregister_filter(struct gendisk *disk);
extern int blk_register_queue(struct gendisk *disk);
extern void blk_unregister_queue(struct gendisk *disk);
extern void register_disk(struct gendisk *dev);
diff -urpN linux-2.5-export/include/linux/genhd.h pjones-2.5-export/include/linux/genhd.h
--- linux-2.5-export/include/linux/genhd.h 2004-09-15 12:39:54 -04:00
+++ pjones-2.5-export/include/linux/genhd.h 2004-09-15 12:53:44 -04:00
@@ -78,6 +78,15 @@ struct disk_stats {
unsigned io_ticks;
unsigned time_in_queue;
};
+
+#define RCF_MAX_NR_CMDS 256
+#define RCF_CMD_BYTES (RCF_MAX_NR_CMDS / (sizeof (unsigned long) * 8))
+
+struct rawio_cmd_filter {
+ unsigned long read_ok[RCF_CMD_BYTES];
+ unsigned long write_ok[RCF_CMD_BYTES];
+ struct kobject kobj;
+};
struct gendisk {
int major; /* major number of driver */
@@ -88,6 +97,7 @@ struct gendisk {
struct hd_struct **part; /* [indexed by minor] */
struct block_device_operations *fops;
struct request_queue *queue;
+ struct rawio_cmd_filter filter;
void *private_data;
sector_t capacity;
--
Peter
On Tue, 14 Sep 2004, Peter Jones wrote:
> diff -urpN linux-2.5-export/drivers/block/Makefile pjones-2.5-export/drivers/block/Makefile
> --- linux-2.5-export/drivers/block/Makefile 2004-09-10 11:54:01 -04:00
> +++ pjones-2.5-export/drivers/block/Makefile 2004-09-10 12:11:50 -04:00
> @@ -13,7 +13,7 @@
> # kblockd threads
> #
[Patch snipped]
Your patch allows updating the filters for each device. This is one aspect
of the problem. Another problem is that the command filter appropriate for
CD/DVD writers is _forced on all devices_. In your patch this is in the
defaults. In the current scsi_ioctl filter it applies to everything.
I have already commented on MODE SELECT. I still think it is dangerous.
Looking at the command list reveals a couple of other problems:
- The command GPCMD_READ_DISC_INFO is "safe_for_read". The command 0x51
has also another use: XPWRITE(10), i.e., a write command. Clearly a
problem.
- The "safe_for_read" command GPCMD_REPORT_KEY, 0xa4, has aliases
CHANGE ALIAS, SET DEVICE IDENTIFIER and SET TARGET PORT.
- The "safe_for_write" command GPCMD_SEND_DVD_STRUCTURE has alias
VOLUME SET OUT (raid configuration).
There are other aliases but they return information and don't look too
dangerous. The command code is only 8 bits and there probably will be more
aliasing in future.
My conclusion is that the filter _necessary_ for burning CD/DVDs is _not
safe for all devices_.
How to solve this problem? One idea I had was to add a sysfs-changable
attribute accessible to the filter (disk or queue) that would have a few
possible states: allow only root, allow filtered, allow all? The default
would be to "allow only root". The cdrom registering could set this to
"allow filtered". This would allow the current behaviour for CD/DVD drives
but would be safe for others. Other devices could be selectively allowed
passthrough access if necessary.
This could also be done in your approach. One possibility would be to
start with empty filter and call from CD/DVD registering a function that
sets the filter you currently have as default. This would be both flexible
and safe.
Opinions?
--
Kai
On Wed, 2004-09-15 at 22:14 +0300, Kai Makisara wrote:
> On Tue, 14 Sep 2004, Peter Jones wrote:
>
> > diff -urpN linux-2.5-export/drivers/block/Makefile pjones-2.5-export/drivers/block/Makefile
> > --- linux-2.5-export/drivers/block/Makefile 2004-09-10 11:54:01 -04:00
> > +++ pjones-2.5-export/drivers/block/Makefile 2004-09-10 12:11:50 -04:00
> > @@ -13,7 +13,7 @@
> > # kblockd threads
> > #
> [Patch snipped]
>
> Your patch allows updating the filters for each device. This is one aspect
> of the problem. Another problem is that the command filter appropriate for
> CD/DVD writers is _forced on all devices_. In your patch this is in the
> defaults. In the current scsi_ioctl filter it applies to everything.
My patch leaves the defaults as what are currently in the kernel.
> I have already commented on MODE SELECT. I still think it is dangerous.
It's only marked as being allowed if you have write access; /dev/hda
usually isn't a+w, last I checked.
I'd actually say that this isn't suitable for CD drives. To do CDDA
reads in many cases you need MODE_SELECT. As such, CD drives should
have MODE_SELECT in the list of things allowed to read. But the point
is that this gives control of the policy to the userland, where it
belongs.
> Looking at the command list reveals a couple of other problems:
> - The command GPCMD_READ_DISC_INFO is "safe_for_read". The command 0x51
> has also another use: XPWRITE(10), i.e., a write command. Clearly a
> problem.
> - The "safe_for_read" command GPCMD_REPORT_KEY, 0xa4, has aliases
> CHANGE ALIAS, SET DEVICE IDENTIFIER and SET TARGET PORT.
> - The "safe_for_write" command GPCMD_SEND_DVD_STRUCTURE has alias
> VOLUME SET OUT (raid configuration).
My intent with the patch isn't really to take any stance on which
commands should and shouldn't be allowed. In fact, it's quite the
opposite -- this allows initscripts/hotplug to install an appropriate
list of commands for each device.
> There are other aliases but they return information and don't look too
> dangerous. The command code is only 8 bits and there probably will be more
> aliasing in future.
>
> My conclusion is that the filter _necessary_ for burning CD/DVDs is _not
> safe for all devices_.
Sure. The point is that there's no reason the kernel should make that
its problem.
> How to solve this problem?
Let the distros pick the list.
> One idea I had was to add a sysfs-changable attribute accessible to the
> filter (disk or queue) that would have a few possible states: allow
> only root, allow filtered, allow all?
This is basically what my patch gives you. Here's a handy table:
owner mode explanation
root.disk 0600 allow only root
root.disk 0400 allow root read commands, and write if CAP_SYS_RAWIO
root.disk 0660 allow root and disk members to do everything
root.disk 0640 allow root everything, disk can do read commands.
root.disk 0644 allow root everything, everybody read commands.
root.disk 0664 allow root and disk everything, all else get read
etc
That's everything except "allow all", but that is easy:
for x in {0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f}\
{0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f} ; do
echo "add read $x" > /sys/block/hda/filter/ok_read_commands
echo "add write %x" > /sys/block/hda/filter/ok_write_commands
done
> The default would be to "allow only root". The cdrom registering could
> set this to "allow filtered". This would allow the current behaviour
> for CD/DVD drives but would be safe for others. Other devices could be
> selectively allowed passthrough access if necessary.
There's no point in making the cdrom registration have code for this --
just let userland do the policy stuff, like it should.
> This could also be done in your approach. One possibility would be to
> start with empty filter and call from CD/DVD registering a function that
> sets the filter you currently have as default. This would be both flexible
> and safe.
I don't want to do an empty filter, because that'll mean existing users
of SG_IO and such break for no real reason. The approach my patch takes
is not to change *any* of the existing policy, but to enable distros to
change it to their hearts' content. It's *dead* simple to make
initscripts and hotplug pick "correct" lists for each device.
--
Peter
On Wed, Sep 15 2004, Kai Makisara wrote:
> On Tue, 14 Sep 2004, Peter Jones wrote:
>
> > diff -urpN linux-2.5-export/drivers/block/Makefile pjones-2.5-export/drivers/block/Makefile
> > --- linux-2.5-export/drivers/block/Makefile 2004-09-10 11:54:01 -04:00
> > +++ pjones-2.5-export/drivers/block/Makefile 2004-09-10 12:11:50 -04:00
> > @@ -13,7 +13,7 @@
> > # kblockd threads
> > #
> [Patch snipped]
>
> Your patch allows updating the filters for each device. This is one aspect
> of the problem. Another problem is that the command filter appropriate for
> CD/DVD writers is _forced on all devices_. In your patch this is in the
> defaults. In the current scsi_ioctl filter it applies to everything.
>
> I have already commented on MODE SELECT. I still think it is dangerous.
> Looking at the command list reveals a couple of other problems:
> - The command GPCMD_READ_DISC_INFO is "safe_for_read". The command 0x51
> has also another use: XPWRITE(10), i.e., a write command. Clearly a
> problem.
> - The "safe_for_read" command GPCMD_REPORT_KEY, 0xa4, has aliases
> CHANGE ALIAS, SET DEVICE IDENTIFIER and SET TARGET PORT.
> - The "safe_for_write" command GPCMD_SEND_DVD_STRUCTURE has alias
> VOLUME SET OUT (raid configuration).
>
> There are other aliases but they return information and don't look too
> dangerous. The command code is only 8 bits and there probably will be more
> aliasing in future.
>
> My conclusion is that the filter _necessary_ for burning CD/DVDs is _not
> safe for all devices_.
>
> How to solve this problem? One idea I had was to add a sysfs-changable
> attribute accessible to the filter (disk or queue) that would have a few
> possible states: allow only root, allow filtered, allow all? The default
> would be to "allow only root". The cdrom registering could set this to
> "allow filtered". This would allow the current behaviour for CD/DVD drives
> but would be safe for others. Other devices could be selectively allowed
> passthrough access if necessary.
>
> This could also be done in your approach. One possibility would be to
> start with empty filter and call from CD/DVD registering a function that
> sets the filter you currently have as default. This would be both flexible
> and safe.
I think that's a really nice idea. I've said right from the beginning
that the command tables cannot even be per-device type, at least with
CDROMs we have examples of commands with different meanings. But at
least with a device-type default list we're a little closer.
A good reminder of why the whole thing is a mess :-)
--
Jens Axboe
On Wed, 15 Sep 2004, Peter Jones wrote:
> On Wed, 2004-09-15 at 22:14 +0300, Kai Makisara wrote:
...
> My patch leaves the defaults as what are currently in the kernel.
>
Yes but what I wanted to say the filter currently in the kernel is not
safe for all devices.
> > I have already commented on MODE SELECT. I still think it is dangerous.
>
> It's only marked as being allowed if you have write access; /dev/hda
> usually isn't a+w, last I checked.
>
I am thinking about other devices than disks. Tapes are what I know best
but there are also other device types (all that support SG_IO).
For tapes it is fairly common to allow users read/write permissions for
reading/writing tapes. MODE SELECT allows a user to mess up the drive
parameters so that the next user thinks it is broken. This is not the
purpose of giving read/write permissions in this case.
> I'd actually say that this isn't suitable for CD drives. To do CDDA
> reads in many cases you need MODE_SELECT. As such, CD drives should
> have MODE_SELECT in the list of things allowed to read. But the point
> is that this gives control of the policy to the userland, where it
> belongs.
>
This is why I like your approach.
...
> My intent with the patch isn't really to take any stance on which
> commands should and shouldn't be allowed. In fact, it's quite the
> opposite -- this allows initscripts/hotplug to install an appropriate
> list of commands for each device.
>
If you are not taking a stance, then the default list should be empty.
The starting point must be safe and it can be relaxed. I am just trying to
have a safe enough starting point.
In an ideal world the initscripts/hotplug scripts would be updated
instantaneously and the empty default would not be a problem. In the real
world this does not happen and we have decide how far we want to relax the
default security. I think this is why Linus added the filter: he wanted
to allow users to burn CDs without special permissions. This is why I
suggested that the default filter should allow the current default for
CDs. It is a compromise.
...
> > This could also be done in your approach. One possibility would be to
> > start with empty filter and call from CD/DVD registering a function that
> > sets the filter you currently have as default. This would be both flexible
> > and safe.
>
> I don't want to do an empty filter, because that'll mean existing users
> of SG_IO and such break for no real reason. The approach my patch takes
> is not to change *any* of the existing policy, but to enable distros to
> change it to their hearts' content. It's *dead* simple to make
> initscripts and hotplug pick "correct" lists for each device.
>
I don't know any other SG_IO users than CD/DVD software that try to use
passthrough without CAP_RAWIO. This is probably politically incorrect, but
no sane software developer expects to be able to send raw commands without
proper permissions. If this kind of security hole is present, it will be
plugged sooner or later.
--
Kai
Hi,
this is a modified version of the previous patch.
Changes:
- always default to an empty filter table (see comment)
- the loading mechanism is changed. It is now only possible to change the
complete list at once. What you write into the file is what you see when
doing 'cat'.
- Adding or removing single commands is no longer possible. IMHO this task
(and further fanciness) is best left to userspace tools.
- instead of defining which list is changed through a keyword, the file decides.
Writes to ok_read_commands only change the commands safe for read. The
same is true for ok_write_commands and commands allowed for write.
IMHO those changes make usage more intuitive and easier for tools/scripts.
TODO:
- an rcf_debug feature is needed. Admins need a way to find out which
commands are actually needed. (echo 0/1 > filter/rcf_debug ?)
- the new sysfs interface is probably horribly written / unreliable. Someone
with actual experience in C and kernel programming needs to review this.
- is it possible to avoid the big code duplication in rcf_store_read and
rcf_store_write?
Regards,
Marc
diff -Naur orig/linux-2.6.8.1/drivers/block/genhd.c linux-2.6.8.1/drivers/block/genhd.c
--- orig/linux-2.6.8.1/drivers/block/genhd.c 2004-08-19 13:53:46.000000000 +0200
+++ linux-2.6.8.1/drivers/block/genhd.c 2004-09-14 20:23:17.000000000 +0200
@@ -197,6 +197,7 @@
disk->minors, NULL, exact_match, exact_lock, disk);
register_disk(disk);
blk_register_queue(disk);
+ blk_register_filter(disk);
}
EXPORT_SYMBOL(add_disk);
@@ -204,6 +205,7 @@
void unlink_gendisk(struct gendisk *disk)
{
+ blk_unregister_filter(disk);
blk_unregister_queue(disk);
blk_unregister_region(MKDEV(disk->major, disk->first_minor),
disk->minors);
@@ -382,6 +384,13 @@
jiffies_to_msecs(disk_stat_read(disk, io_ticks)),
jiffies_to_msecs(disk_stat_read(disk, time_in_queue)));
}
+
+static ssize_t disk_driver_read(struct gendisk * disk, char *page)
+{
+ return sprintf(page, "%s\n", disk->disk_name);
+
+}
+
static struct disk_attribute disk_attr_dev = {
.attr = {.name = "dev", .mode = S_IRUGO },
.show = disk_dev_read
@@ -402,6 +411,10 @@
.attr = {.name = "stat", .mode = S_IRUGO },
.show = disk_stats_read
};
+static struct disk_attribute disk_attr_driver = {
+ .attr = {.name = "driver", .mode = S_IRUGO },
+ .show = disk_driver_read
+};
static struct attribute * default_attrs[] = {
&disk_attr_dev.attr,
@@ -409,6 +422,7 @@
&disk_attr_removable.attr,
&disk_attr_size.attr,
&disk_attr_stat.attr,
+ &disk_attr_driver.attr,
NULL,
};
diff -Naur orig/linux-2.6.8.1/drivers/block/Makefile linux-2.6.8.1/drivers/block/Makefile
--- orig/linux-2.6.8.1/drivers/block/Makefile 2004-09-15 22:41:39.267361912 +0200
+++ linux-2.6.8.1/drivers/block/Makefile 2004-09-14 20:09:46.000000000 +0200
@@ -13,7 +13,7 @@
# kblockd threads
#
-obj-y := elevator.o ll_rw_blk.o ioctl.o genhd.o scsi_ioctl.o
+obj-y := elevator.o ll_rw_blk.o ioctl.o genhd.o scsi_ioctl.o rcf.o
obj-$(CONFIG_IOSCHED_NOOP) += noop-iosched.o
obj-$(CONFIG_IOSCHED_AS) += as-iosched.o
diff -Naur orig/linux-2.6.8.1/drivers/block/rcf.c linux-2.6.8.1/drivers/block/rcf.c
--- orig/linux-2.6.8.1/drivers/block/rcf.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.8.1/drivers/block/rcf.c 2004-09-15 22:39:51.874688080 +0200
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2004 Peter M. Jones <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public Licens
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-
+ *
+ */
+
+#include <linux/list.h>
+#include <linux/genhd.h>
+#include <linux/spinlock.h>
+#include <linux/parser.h>
+#include <asm/bitops.h>
+
+#include <scsi/scsi.h>
+#include <linux/cdrom.h>
+
+int rcf_verify_command(struct file *file, unsigned char *cmd)
+{
+ struct rawio_cmd_filter *rcf;
+ struct inode *inode;
+
+ inode = file->f_dentry->d_inode;
+ if (!inode)
+ return -EINVAL;
+
+ rcf = &inode->i_bdev->bd_disk->filter;
+
+ /* root can do any command. */
+ if (capable(CAP_SYS_RAWIO))
+ return 0;
+
+ /* if there's no filter allocated, bail out. */
+ if (unlikely(!rcf))
+ return -EPERM;
+
+ /* Anybody who can open the device can do a read-safe command */
+ if (test_bit(cmd[0], rcf->read_ok))
+ return 0;
+
+ /* Write-safe commands require a writable open */
+ if (test_bit(cmd[0], rcf->write_ok))
+ if (file->f_mode & FMODE_WRITE)
+ return 0;
+
+ /* Or else the user has no perms */
+ return -EPERM;
+}
+
+EXPORT_SYMBOL(rcf_verify_command);
+
+/* and now, the sysfs stuff */
+static ssize_t rcf_readcmds_show(struct rawio_cmd_filter *rcf, char *page)
+{
+ char *npage = page;
+ int x;
+
+ for (x=0; x < RCF_MAX_NR_CMDS; x++)
+ if (test_bit(x, rcf->read_ok)) {
+ sprintf(npage, "%02x", x);
+ npage += 2;
+ if (x < RCF_MAX_NR_CMDS-1)
+ sprintf(npage++, " ");
+ }
+ if (npage != page)
+ npage += sprintf(npage, "\n");
+
+ return (npage - page);
+}
+
+static ssize_t rcf_writecmds_show(struct rawio_cmd_filter *rcf, char *page)
+{
+ char *npage = page;
+ int x;
+
+ for (x=0; x < RCF_MAX_NR_CMDS; x++)
+ if (test_bit(x, rcf->write_ok)) {
+ sprintf(npage, "%02x", x);
+ npage += 2;
+ if (x < RCF_MAX_NR_CMDS-1)
+ sprintf(npage++, " ");
+ }
+ if (npage != page)
+ npage += sprintf(npage, "\n");
+
+ return (npage - page);
+}
+
+#define RCF_ADD 0
+#define RCF_DEL 1
+
+static ssize_t rcf_store_read(struct rawio_cmd_filter *rcf, const char *page,
+ size_t count)
+{
+ ssize_t ret=0;
+ int i=0;
+ int cmd, status;
+
+ substring_t ss;
+
+ while(i < RCF_MAX_NR_CMDS)
+ clear_bit(i++, rcf->read_ok);
+
+ while (page[ret] != 0) {
+ ss.from = (char *)page+ret++;
+ ss.to = (char *)page+(++ret);
+ status = match_hex(&ss, &cmd);
+ if (status)
+ return status;
+ if (cmd > RCF_MAX_NR_CMDS)
+ return -EINVAL;
+ set_bit(cmd, rcf->read_ok);
+ ret++;
+ }
+
+ return count;
+}
+
+static ssize_t rcf_store_write(struct rawio_cmd_filter *rcf, const char *page,
+ size_t count)
+{
+ ssize_t ret=0;
+ int i=0;
+ int cmd, status;
+
+ substring_t ss;
+
+ while(i < RCF_MAX_NR_CMDS)
+ clear_bit(i++, rcf->write_ok);
+
+ while (page[ret] != 0) {
+ ss.from = (char *)page+ret++;
+ ss.to = (char *)page+(++ret);
+ status = match_hex(&ss, &cmd);
+ if (status)
+ return status;
+ if (cmd > RCF_MAX_NR_CMDS)
+ return -EINVAL;
+ set_bit(cmd, rcf->write_ok);
+ ret++;
+ }
+
+ return count;
+}
+
+struct rcf_sysfs_entry {
+ struct attribute attr;
+ ssize_t (*show)(struct rawio_cmd_filter *, char *);
+ ssize_t (*store)(struct rawio_cmd_filter *, const char *, size_t);
+};
+
+static struct rcf_sysfs_entry rcf_readcmds_entry = {
+ .attr = {.name = "ok_read_commands", .mode = S_IRUGO | S_IWUSR },
+ .show = rcf_readcmds_show,
+ .store = rcf_store_read,
+};
+
+static struct rcf_sysfs_entry rcf_writecmds_entry = {
+ .attr = {.name = "ok_write_commands", .mode = S_IRUGO | S_IWUSR },
+ .show = rcf_writecmds_show,
+ .store = rcf_store_write,
+};
+
+static struct attribute *default_attrs[] = {
+ &rcf_readcmds_entry.attr,
+ &rcf_writecmds_entry.attr,
+ NULL,
+};
+
+#define to_rcf(atr) container_of((atr), struct rcf_sysfs_entry, attr)
+
+static ssize_t
+rcf_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
+{
+ struct rcf_sysfs_entry *entry = to_rcf(attr);
+ struct rawio_cmd_filter *rcf;
+
+ rcf = container_of(kobj, struct rawio_cmd_filter, kobj);
+ if (!entry->show)
+ return 0;
+
+ return entry->show(rcf, page);
+}
+
+static ssize_t
+rcf_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *page, size_t length)
+{
+ struct rcf_sysfs_entry *entry = to_rcf(attr);
+ struct rawio_cmd_filter *rcf;
+
+ rcf = container_of(kobj, struct rawio_cmd_filter, kobj);
+ if (!entry->store)
+ return -EINVAL;
+
+ return entry->store(rcf, page, length);
+}
+
+static struct sysfs_ops rcf_sysfs_ops = {
+ .show = rcf_attr_show,
+ .store = rcf_attr_store,
+};
+
+static struct kobj_type rcf_ktype = {
+ .sysfs_ops = &rcf_sysfs_ops,
+ .default_attrs = default_attrs,
+};
+
+static void rcf_set_defaults(struct rawio_cmd_filter *rcf)
+{
+/*
+By default deny everything. As pointed out in the original discussion
+filtering is very tricky since the same command can be used to low-level
+format a disk or simply change read modes for audio grabbing.
+This will still annoy users, but at least they can now fix the situation
+without patching the kernel.
+*/
+}
+
+int blk_register_filter(struct gendisk *disk)
+{
+ int ret;
+ struct rawio_cmd_filter *rcf = &disk->filter;
+
+ rcf->kobj.parent = kobject_get(&disk->kobj);
+ if (!rcf->kobj.parent)
+ return -EBUSY;
+
+ snprintf(rcf->kobj.name, KOBJ_NAME_LEN, "%s", "filter");
+ rcf->kobj.ktype = &rcf_ktype;
+
+ ret = kobject_register(&rcf->kobj);
+ if (ret < 0)
+ return ret;
+
+ rcf_set_defaults(&disk->filter);
+
+ return 0;
+}
+
+void blk_unregister_filter(struct gendisk *disk)
+{
+ struct rawio_cmd_filter *rcf = &disk->filter;
+
+ kobject_unregister(&rcf->kobj);
+ kobject_put(&disk->kobj);
+}
diff -Naur orig/linux-2.6.8.1/drivers/block/scsi_ioctl.c linux-2.6.8.1/drivers/block/scsi_ioctl.c
--- orig/linux-2.6.8.1/drivers/block/scsi_ioctl.c 2004-09-15 22:41:39.275360696 +0200
+++ linux-2.6.8.1/drivers/block/scsi_ioctl.c 2004-09-14 20:09:46.000000000 +0200
@@ -105,105 +105,6 @@
return put_user(1, p);
}
-#define CMD_READ_SAFE 0x01
-#define CMD_WRITE_SAFE 0x02
-#define safe_for_read(cmd) [cmd] = CMD_READ_SAFE
-#define safe_for_write(cmd) [cmd] = CMD_WRITE_SAFE
-
-static int verify_command(struct file *file, unsigned char *cmd)
-{
- static const unsigned char cmd_type[256] = {
-
- /* Basic read-only commands */
- safe_for_read(TEST_UNIT_READY),
- safe_for_read(REQUEST_SENSE),
- safe_for_read(READ_6),
- safe_for_read(READ_10),
- safe_for_read(READ_12),
- safe_for_read(READ_16),
- safe_for_read(READ_BUFFER),
- safe_for_read(READ_LONG),
- safe_for_read(INQUIRY),
- safe_for_read(MODE_SENSE),
- safe_for_read(MODE_SENSE_10),
- safe_for_read(START_STOP),
- safe_for_read(GPCMD_VERIFY_10),
- safe_for_read(VERIFY_16),
- safe_for_read(READ_BUFFER),
-
- /* Audio CD commands */
- safe_for_read(GPCMD_PLAY_CD),
- safe_for_read(GPCMD_PLAY_AUDIO_10),
- safe_for_read(GPCMD_PLAY_AUDIO_MSF),
- safe_for_read(GPCMD_PLAY_AUDIO_TI),
- safe_for_read(GPCMD_PAUSE_RESUME),
-
- /* CD/DVD data reading */
- safe_for_read(GPCMD_READ_CD),
- safe_for_read(GPCMD_READ_CD_MSF),
- safe_for_read(GPCMD_READ_DISC_INFO),
- safe_for_read(GPCMD_READ_CDVD_CAPACITY),
- safe_for_read(GPCMD_READ_DVD_STRUCTURE),
- safe_for_read(GPCMD_READ_HEADER),
- safe_for_read(GPCMD_READ_TRACK_RZONE_INFO),
- safe_for_read(GPCMD_READ_SUBCHANNEL),
- safe_for_read(GPCMD_READ_TOC_PMA_ATIP),
- safe_for_read(GPCMD_REPORT_KEY),
- safe_for_read(GPCMD_SCAN),
- safe_for_read(GPCMD_GET_CONFIGURATION),
- safe_for_read(GPCMD_READ_FORMAT_CAPACITIES),
- safe_for_read(GPCMD_GET_EVENT_STATUS_NOTIFICATION),
- safe_for_read(GPCMD_GET_PERFORMANCE),
- safe_for_read(GPCMD_SEEK),
- safe_for_read(GPCMD_STOP_PLAY_SCAN),
-
- /* Basic writing commands */
- safe_for_write(WRITE_6),
- safe_for_write(WRITE_10),
- safe_for_write(WRITE_VERIFY),
- safe_for_write(WRITE_12),
- safe_for_write(WRITE_VERIFY_12),
- safe_for_write(WRITE_16),
- safe_for_write(WRITE_LONG),
- safe_for_write(ERASE),
- safe_for_write(GPCMD_MODE_SELECT_10),
- safe_for_write(MODE_SELECT),
- safe_for_write(GPCMD_BLANK),
- safe_for_write(GPCMD_CLOSE_TRACK),
- safe_for_write(GPCMD_FLUSH_CACHE),
- safe_for_write(GPCMD_FORMAT_UNIT),
- safe_for_write(GPCMD_REPAIR_RZONE_TRACK),
- safe_for_write(GPCMD_RESERVE_RZONE_TRACK),
- safe_for_write(GPCMD_SEND_DVD_STRUCTURE),
- safe_for_write(GPCMD_SEND_EVENT),
- safe_for_write(GPCMD_SEND_KEY),
- safe_for_write(GPCMD_SEND_OPC),
- safe_for_write(GPCMD_SEND_CUE_SHEET),
- safe_for_write(GPCMD_SET_SPEED),
- safe_for_write(GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL),
- safe_for_write(GPCMD_LOAD_UNLOAD),
- safe_for_write(GPCMD_SET_STREAMING),
- };
- unsigned char type = cmd_type[cmd[0]];
-
- /* Anybody who can open the device can do a read-safe command */
- if (type & CMD_READ_SAFE)
- return 0;
-
- /* Write-safe commands just require a writable open.. */
- if (type & CMD_WRITE_SAFE) {
- if (file->f_mode & FMODE_WRITE)
- return 0;
- }
-
- /* And root can do any command.. */
- if (capable(CAP_SYS_RAWIO))
- return 0;
-
- /* Otherwise fail it with an "Operation not permitted" */
- return -EPERM;
-}
-
static int sg_io(struct file *file, request_queue_t *q,
struct gendisk *bd_disk, struct sg_io_hdr *hdr)
{
@@ -220,7 +121,7 @@
return -EINVAL;
if (copy_from_user(cmd, hdr->cmdp, hdr->cmd_len))
return -EFAULT;
- if (verify_command(file, cmd))
+ if (rcf_verify_command(file, cmd))
return -EPERM;
/*
@@ -370,7 +271,7 @@
if (copy_from_user(buffer, sic->data + cmdlen, in_len))
goto error;
- err = verify_command(file, rq->cmd);
+ err = rcf_verify_command(file, rq->cmd);
if (err)
goto error;
diff -Naur orig/linux-2.6.8.1/include/linux/blkdev.h linux-2.6.8.1/include/linux/blkdev.h
--- orig/linux-2.6.8.1/include/linux/blkdev.h 2004-09-15 22:41:48.930892832 +0200
+++ linux-2.6.8.1/include/linux/blkdev.h 2004-09-14 20:09:46.000000000 +0200
@@ -512,6 +512,8 @@
unsigned block_size_bits;
};
+extern int blk_register_filter(struct gendisk *disk);
+extern void blk_unregister_filter(struct gendisk *disk);
extern int blk_register_queue(struct gendisk *disk);
extern void blk_unregister_queue(struct gendisk *disk);
extern void register_disk(struct gendisk *dev);
diff -Naur orig/linux-2.6.8.1/include/linux/genhd.h linux-2.6.8.1/include/linux/genhd.h
--- orig/linux-2.6.8.1/include/linux/genhd.h 2004-09-15 22:41:48.945890552 +0200
+++ linux-2.6.8.1/include/linux/genhd.h 2004-09-14 20:09:46.000000000 +0200
@@ -78,6 +78,15 @@
unsigned io_ticks;
unsigned time_in_queue;
};
+
+#define RCF_MAX_NR_CMDS 256
+#define RCF_CMD_BYTES (RCF_MAX_NR_CMDS / (sizeof (unsigned long) * 8))
+
+struct rawio_cmd_filter {
+ unsigned long read_ok[RCF_CMD_BYTES];
+ unsigned long write_ok[RCF_CMD_BYTES];
+ struct kobject kobj;
+};
struct gendisk {
int major; /* major number of driver */
@@ -88,6 +97,7 @@
struct hd_struct **part; /* [indexed by minor] */
struct block_device_operations *fops;
struct request_queue *queue;
+ struct rawio_cmd_filter filter;
void *private_data;
sector_t capacity;
On Mer, 2004-09-15 at 22:08, Marc Ballarin wrote:
> Hi,
> this is a modified version of the previous patch.
You need to check for capable(CAP_SYS_RAWIO) otherwise you elevate
anyone with access bypass capabilities to CAP_SYS_RAWIO equivalent
powers.
On Wed, 2004-09-15 at 22:38 +0100, Alan Cox wrote:
> On Mer, 2004-09-15 at 22:08, Marc Ballarin wrote:
> > Hi,
> > this is a modified version of the previous patch.
>
> You need to check for capable(CAP_SYS_RAWIO) otherwise you elevate
> anyone with access bypass capabilities to CAP_SYS_RAWIO equivalent
> powers.
Do you mean in the ->store path?
--
Peter
On Wed, 15 Sep 2004 22:38:47 +0100
Alan Cox <[email protected]> wrote:
> You need to check for capable(CAP_SYS_RAWIO) otherwise you elevate
> anyone with access bypass capabilities to CAP_SYS_RAWIO equivalent
> powers.
True. File permissions aren't enough.
Will something like this suffice?
static ssize_t rcf_store_write(struct rawio_cmd_filter *rcf, const char *page,
size_t count)
{
...
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
while(i < RCF_MAX_NR_CMDS)
clear_bit(i++, rcf->write_ok);
...
Regards
On Iau, 2004-09-16 at 00:03, Peter Jones wrote:
> > You need to check for capable(CAP_SYS_RAWIO) otherwise you elevate
> > anyone with access bypass capabilities to CAP_SYS_RAWIO equivalent
> > powers.
>
> Do you mean in the ->store path?
Thats the one - otherwise you can add commands without the ability you'd
normally have to bypass
On Thu, 2004-09-16 at 01:33 +0200, Marc Ballarin wrote:
> On Wed, 15 Sep 2004 22:38:47 +0100
> Alan Cox <[email protected]> wrote:
>
> > You need to check for capable(CAP_SYS_RAWIO) otherwise you elevate
> > anyone with access bypass capabilities to CAP_SYS_RAWIO equivalent
> > powers.
>
> True. File permissions aren't enough.
>
> Will something like this suffice?
[...]
Yes.
Here's another version of the patch. It does what yours did, as well as
that. It also combines the read and write code so there's one of each,
with a parameter to tell which it's supposed to modify.
rcf_cmds_store() might need some locking - I'm not sure, and haven't
investigated yet.
The default commands are still there; I haven't had time to make them a
config option yet, and the jury still seems out on how defaults should
work, anyway.
I haven't booted with this one, but I have compiled it...
diff -urpN linux-2.5-export/drivers/block/Makefile pjones-2.5-export/drivers/block/Makefile
--- linux-2.5-export/drivers/block/Makefile 2004-09-15 12:42:18 -04:00
+++ pjones-2.5-export/drivers/block/Makefile 2004-09-15 19:25:32 -04:00
@@ -13,7 +13,7 @@
# kblockd threads
#
-obj-y := elevator.o ll_rw_blk.o ioctl.o genhd.o scsi_ioctl.o
+obj-y := elevator.o ll_rw_blk.o ioctl.o genhd.o scsi_ioctl.o rcf.o
obj-$(CONFIG_IOSCHED_NOOP) += noop-iosched.o
obj-$(CONFIG_IOSCHED_AS) += as-iosched.o
diff -urpN linux-2.5-export/drivers/block/genhd.c pjones-2.5-export/drivers/block/genhd.c
--- linux-2.5-export/drivers/block/genhd.c 2004-09-15 12:41:22 -04:00
+++ pjones-2.5-export/drivers/block/genhd.c 2004-09-15 19:24:41 -04:00
@@ -197,6 +197,7 @@ void add_disk(struct gendisk *disk)
disk->minors, NULL, exact_match, exact_lock, disk);
register_disk(disk);
blk_register_queue(disk);
+ blk_register_filter(disk);
}
EXPORT_SYMBOL(add_disk);
@@ -204,6 +205,7 @@ EXPORT_SYMBOL(del_gendisk); /* in partit
void unlink_gendisk(struct gendisk *disk)
{
+ blk_unregister_filter(disk);
blk_unregister_queue(disk);
blk_unregister_region(MKDEV(disk->major, disk->first_minor),
disk->minors);
@@ -382,6 +384,13 @@ static ssize_t disk_stats_read(struct ge
jiffies_to_msecs(disk_stat_read(disk, io_ticks)),
jiffies_to_msecs(disk_stat_read(disk, time_in_queue)));
}
+
+static ssize_t disk_driver_read(struct gendisk * disk, char *page)
+{
+ return sprintf(page, "%s\n", disk->disk_name);
+
+}
+
static struct disk_attribute disk_attr_dev = {
.attr = {.name = "dev", .mode = S_IRUGO },
.show = disk_dev_read
@@ -402,6 +411,10 @@ static struct disk_attribute disk_attr_s
.attr = {.name = "stat", .mode = S_IRUGO },
.show = disk_stats_read
};
+static struct disk_attribute disk_attr_driver = {
+ .attr = {.name = "driver", .mode = S_IRUGO },
+ .show = disk_driver_read
+};
static struct attribute * default_attrs[] = {
&disk_attr_dev.attr,
@@ -409,6 +422,7 @@ static struct attribute * default_attrs[
&disk_attr_removable.attr,
&disk_attr_size.attr,
&disk_attr_stat.attr,
+ &disk_attr_driver.attr,
NULL,
};
diff -urpN linux-2.5-export/drivers/block/rcf.c pjones-2.5-export/drivers/block/rcf.c
--- linux-2.5-export/drivers/block/rcf.c 1969-12-31 19:00:00 -05:00
+++ pjones-2.5-export/drivers/block/rcf.c 2004-09-15 19:25:53 -04:00
@@ -0,0 +1,304 @@
+/*
+ * Copyright 2004 Peter M. Jones <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public Licens
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-
+ *
+ */
+
+#include <linux/list.h>
+#include <linux/genhd.h>
+#include <linux/spinlock.h>
+#include <linux/parser.h>
+#include <asm/bitops.h>
+
+#include <scsi/scsi.h>
+#include <linux/cdrom.h>
+
+int rcf_verify_command(struct file *file, unsigned char *cmd)
+{
+ struct rawio_cmd_filter *rcf;
+ struct inode *inode;
+
+ inode = file->f_dentry->d_inode;
+ if (!inode)
+ return -EINVAL;
+
+ rcf = &inode->i_bdev->bd_disk->filter;
+
+ /* root can do any command. */
+ if (capable(CAP_SYS_RAWIO))
+ return 0;
+
+ /* Anybody who can open the device can do a read-safe command */
+ if (test_bit(cmd[0], rcf->read_ok))
+ return 0;
+
+ /* Write-safe commands require a writable open */
+ if (test_bit(cmd[0], rcf->write_ok))
+ if (file->f_mode & FMODE_WRITE)
+ return 0;
+
+ /* Or else the user has no perms */
+ return -EPERM;
+}
+
+EXPORT_SYMBOL(rcf_verify_command);
+
+/* and now, the sysfs stuff */
+static ssize_t rcf_cmds_show(struct rawio_cmd_filter *rcf, char *page, int rw)
+{
+ char *npage = page;
+ unsigned long *okbits;
+ int x;
+
+ if (rw == READ)
+ okbits = rcf->read_ok;
+ else
+ okbits = rcf->write_ok;
+
+ for (x=0; x < RCF_MAX_NR_CMDS; x++)
+ if (test_bit(x, okbits)) {
+ sprintf(npage, "%02x", x);
+ npage += 2;
+ if (x < RCF_MAX_NR_CMDS-1)
+ sprintf(npage++, " ");
+ }
+ if (npage != page)
+ npage += sprintf(npage, "\n");
+
+ return (npage - page);
+}
+
+static ssize_t rcf_readcmds_show(struct rawio_cmd_filter *rcf, char *page)
+{
+ return rcf_cmds_show(rcf, page, READ);
+}
+
+static ssize_t rcf_writecmds_show(struct rawio_cmd_filter *rcf, char *page)
+{
+ return rcf_cmds_show(rcf, page, WRITE);
+}
+
+static ssize_t rcf_cmds_store(struct rawio_cmd_filter *rcf, const char *page,
+ size_t count, int rw)
+{
+ ssize_t ret=0;
+ unsigned long okbits[RCF_CMD_LONGS], *target_okbits;
+ int cmd, status, len;
+ substring_t ss;
+
+ memset(&okbits, 0, sizeof (okbits));
+
+ for (len = strlen(page); len > 0; len-=3) {
+ if (len < 2)
+ break;
+ ss.from = (char *)page+ret;
+ ss.to = (char *)page+ret+1;
+ ret+=3;
+ status = match_hex(&ss, &cmd);
+ /* either of these cases means invalid input, so do nothing. */
+ if (status || cmd >= RCF_MAX_NR_CMDS)
+ return -EINVAL;
+
+ set_bit(cmd, okbits);
+ }
+
+ if (rw == READ)
+ target_okbits = rcf->read_ok;
+ else
+ target_okbits = rcf->write_ok;
+ memmove(target_okbits, okbits, sizeof (okbits));
+
+ return count;
+}
+
+static ssize_t rcf_readcmds_store(struct rawio_cmd_filter *rcf,
+ const char *page, size_t count)
+{
+ return rcf_cmds_store(rcf, page, count, READ);
+}
+
+static ssize_t rcf_writecmds_store(struct rawio_cmd_filter *rcf,
+ const char *page, size_t count)
+{
+ return rcf_cmds_store(rcf, page, count, WRITE);
+}
+
+struct rcf_sysfs_entry {
+ struct attribute attr;
+ ssize_t (*show)(struct rawio_cmd_filter *, char *);
+ ssize_t (*store)(struct rawio_cmd_filter *, const char *, size_t);
+};
+
+static struct rcf_sysfs_entry rcf_readcmds_entry = {
+ .attr = {.name = "ok_read_commands", .mode = S_IRUGO | S_IWUSR },
+ .show = rcf_readcmds_show,
+ .store = rcf_readcmds_store,
+};
+
+static struct rcf_sysfs_entry rcf_writecmds_entry = {
+ .attr = {.name = "ok_write_commands", .mode = S_IRUGO | S_IWUSR },
+ .show = rcf_writecmds_show,
+ .store = rcf_writecmds_store,
+};
+
+static struct attribute *default_attrs[] = {
+ &rcf_readcmds_entry.attr,
+ &rcf_writecmds_entry.attr,
+ NULL,
+};
+
+#define to_rcf(atr) container_of((atr), struct rcf_sysfs_entry, attr)
+
+static ssize_t
+rcf_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
+{
+ struct rcf_sysfs_entry *entry = to_rcf(attr);
+ struct rawio_cmd_filter *rcf;
+
+ rcf = container_of(kobj, struct rawio_cmd_filter, kobj);
+ if (!entry->show)
+ return 0;
+
+ return entry->show(rcf, page);
+}
+
+static ssize_t
+rcf_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *page, size_t length)
+{
+ struct rcf_sysfs_entry *entry = to_rcf(attr);
+ struct rawio_cmd_filter *rcf;
+
+ rcf = container_of(kobj, struct rawio_cmd_filter, kobj);
+ if (!entry->store)
+ return -EINVAL;
+
+ if (!capable(CAP_SYS_RAWIO))
+ return -EPERM;
+ return entry->store(rcf, page, length);
+}
+
+static struct sysfs_ops rcf_sysfs_ops = {
+ .show = rcf_attr_show,
+ .store = rcf_attr_store,
+};
+
+static struct kobj_type rcf_ktype = {
+ .sysfs_ops = &rcf_sysfs_ops,
+ .default_attrs = default_attrs,
+};
+
+static void rcf_set_defaults(struct rawio_cmd_filter *rcf)
+{
+ /* Basic read-only commands */
+ set_bit(TEST_UNIT_READY, rcf->read_ok);
+ set_bit(REQUEST_SENSE, rcf->read_ok);
+ set_bit(READ_6, rcf->read_ok);
+ set_bit(READ_10, rcf->read_ok);
+ set_bit(READ_12, rcf->read_ok);
+ set_bit(READ_16, rcf->read_ok);
+ set_bit(READ_BUFFER, rcf->read_ok);
+ set_bit(READ_LONG, rcf->read_ok);
+ set_bit(INQUIRY, rcf->read_ok);
+ set_bit(MODE_SENSE, rcf->read_ok);
+ set_bit(MODE_SENSE_10, rcf->read_ok);
+ set_bit(START_STOP, rcf->read_ok);
+ set_bit(GPCMD_VERIFY_10, rcf->read_ok);
+ set_bit(VERIFY_16, rcf->read_ok);
+ set_bit(READ_BUFFER, rcf->read_ok);
+
+ /* Audio CD commands */
+ set_bit(GPCMD_PLAY_CD, rcf->read_ok);
+ set_bit(GPCMD_PLAY_AUDIO_10, rcf->read_ok);
+ set_bit(GPCMD_PLAY_AUDIO_MSF, rcf->read_ok);
+ set_bit(GPCMD_PLAY_AUDIO_TI, rcf->read_ok);
+ set_bit(GPCMD_PAUSE_RESUME, rcf->read_ok);
+
+ /* CD/DVD data reading */
+ set_bit(GPCMD_READ_CD, rcf->read_ok);
+ set_bit(GPCMD_READ_CD_MSF, rcf->read_ok);
+ set_bit(GPCMD_READ_DISC_INFO, rcf->read_ok);
+ set_bit(GPCMD_READ_CDVD_CAPACITY, rcf->read_ok);
+ set_bit(GPCMD_READ_DVD_STRUCTURE, rcf->read_ok);
+ set_bit(GPCMD_READ_HEADER, rcf->read_ok);
+ set_bit(GPCMD_READ_TRACK_RZONE_INFO, rcf->read_ok);
+ set_bit(GPCMD_READ_SUBCHANNEL, rcf->read_ok);
+ set_bit(GPCMD_READ_TOC_PMA_ATIP, rcf->read_ok);
+ set_bit(GPCMD_REPORT_KEY, rcf->read_ok);
+ set_bit(GPCMD_SCAN, rcf->read_ok);
+ set_bit(GPCMD_GET_CONFIGURATION, rcf->read_ok);
+ set_bit(GPCMD_READ_FORMAT_CAPACITIES, rcf->read_ok);
+ set_bit(GPCMD_GET_EVENT_STATUS_NOTIFICATION, rcf->read_ok);
+ set_bit(GPCMD_GET_PERFORMANCE, rcf->read_ok);
+ set_bit(GPCMD_SEEK, rcf->read_ok);
+ set_bit(GPCMD_STOP_PLAY_SCAN, rcf->read_ok);
+
+ /* Basic writing commands */
+ set_bit(WRITE_6, rcf->write_ok);
+ set_bit(WRITE_10, rcf->write_ok);
+ set_bit(WRITE_VERIFY, rcf->write_ok);
+ set_bit(WRITE_12, rcf->write_ok);
+ set_bit(WRITE_VERIFY_12, rcf->write_ok);
+ set_bit(WRITE_16, rcf->write_ok);
+ set_bit(WRITE_LONG, rcf->write_ok);
+ set_bit(ERASE, rcf->write_ok);
+ set_bit(GPCMD_MODE_SELECT_10, rcf->write_ok);
+ set_bit(MODE_SELECT, rcf->write_ok);
+ set_bit(GPCMD_BLANK, rcf->write_ok);
+ set_bit(GPCMD_CLOSE_TRACK, rcf->write_ok);
+ set_bit(GPCMD_FLUSH_CACHE, rcf->write_ok);
+ set_bit(GPCMD_FORMAT_UNIT, rcf->write_ok);
+ set_bit(GPCMD_REPAIR_RZONE_TRACK, rcf->write_ok);
+ set_bit(GPCMD_RESERVE_RZONE_TRACK, rcf->write_ok);
+ set_bit(GPCMD_SEND_DVD_STRUCTURE, rcf->write_ok);
+ set_bit(GPCMD_SEND_EVENT, rcf->write_ok);
+ set_bit(GPCMD_SEND_KEY, rcf->write_ok);
+ set_bit(GPCMD_SEND_OPC, rcf->write_ok);
+ set_bit(GPCMD_SEND_CUE_SHEET, rcf->write_ok);
+ set_bit(GPCMD_SET_SPEED, rcf->write_ok);
+ set_bit(GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL, rcf->write_ok);
+ set_bit(GPCMD_LOAD_UNLOAD, rcf->write_ok);
+ set_bit(GPCMD_SET_STREAMING, rcf->write_ok);
+}
+
+int blk_register_filter(struct gendisk *disk)
+{
+ int ret;
+ struct rawio_cmd_filter *rcf = &disk->filter;
+
+ rcf->kobj.parent = kobject_get(&disk->kobj);
+ if (!rcf->kobj.parent)
+ return -EBUSY;
+
+ snprintf(rcf->kobj.name, KOBJ_NAME_LEN, "%s", "filter");
+ rcf->kobj.ktype = &rcf_ktype;
+
+ ret = kobject_register(&rcf->kobj);
+ if (ret < 0)
+ return ret;
+
+ rcf_set_defaults(&disk->filter);
+
+ return 0;
+}
+
+void blk_unregister_filter(struct gendisk *disk)
+{
+ struct rawio_cmd_filter *rcf = &disk->filter;
+
+ kobject_unregister(&rcf->kobj);
+ kobject_put(&disk->kobj);
+}
diff -urpN linux-2.5-export/drivers/block/scsi_ioctl.c pjones-2.5-export/drivers/block/scsi_ioctl.c
--- linux-2.5-export/drivers/block/scsi_ioctl.c 2004-09-15 12:42:48 -04:00
+++ pjones-2.5-export/drivers/block/scsi_ioctl.c 2004-09-15 19:25:59 -04:00
@@ -105,105 +105,6 @@ static int sg_emulated_host(request_queu
return put_user(1, p);
}
-#define CMD_READ_SAFE 0x01
-#define CMD_WRITE_SAFE 0x02
-#define safe_for_read(cmd) [cmd] = CMD_READ_SAFE
-#define safe_for_write(cmd) [cmd] = CMD_WRITE_SAFE
-
-static int verify_command(struct file *file, unsigned char *cmd)
-{
- static const unsigned char cmd_type[256] = {
-
- /* Basic read-only commands */
- safe_for_read(TEST_UNIT_READY),
- safe_for_read(REQUEST_SENSE),
- safe_for_read(READ_6),
- safe_for_read(READ_10),
- safe_for_read(READ_12),
- safe_for_read(READ_16),
- safe_for_read(READ_BUFFER),
- safe_for_read(READ_LONG),
- safe_for_read(INQUIRY),
- safe_for_read(MODE_SENSE),
- safe_for_read(MODE_SENSE_10),
- safe_for_read(START_STOP),
- safe_for_read(GPCMD_VERIFY_10),
- safe_for_read(VERIFY_16),
- safe_for_read(READ_BUFFER),
-
- /* Audio CD commands */
- safe_for_read(GPCMD_PLAY_CD),
- safe_for_read(GPCMD_PLAY_AUDIO_10),
- safe_for_read(GPCMD_PLAY_AUDIO_MSF),
- safe_for_read(GPCMD_PLAY_AUDIO_TI),
- safe_for_read(GPCMD_PAUSE_RESUME),
-
- /* CD/DVD data reading */
- safe_for_read(GPCMD_READ_CD),
- safe_for_read(GPCMD_READ_CD_MSF),
- safe_for_read(GPCMD_READ_DISC_INFO),
- safe_for_read(GPCMD_READ_CDVD_CAPACITY),
- safe_for_read(GPCMD_READ_DVD_STRUCTURE),
- safe_for_read(GPCMD_READ_HEADER),
- safe_for_read(GPCMD_READ_TRACK_RZONE_INFO),
- safe_for_read(GPCMD_READ_SUBCHANNEL),
- safe_for_read(GPCMD_READ_TOC_PMA_ATIP),
- safe_for_read(GPCMD_REPORT_KEY),
- safe_for_read(GPCMD_SCAN),
- safe_for_read(GPCMD_GET_CONFIGURATION),
- safe_for_read(GPCMD_READ_FORMAT_CAPACITIES),
- safe_for_read(GPCMD_GET_EVENT_STATUS_NOTIFICATION),
- safe_for_read(GPCMD_GET_PERFORMANCE),
- safe_for_read(GPCMD_SEEK),
- safe_for_read(GPCMD_STOP_PLAY_SCAN),
-
- /* Basic writing commands */
- safe_for_write(WRITE_6),
- safe_for_write(WRITE_10),
- safe_for_write(WRITE_VERIFY),
- safe_for_write(WRITE_12),
- safe_for_write(WRITE_VERIFY_12),
- safe_for_write(WRITE_16),
- safe_for_write(WRITE_LONG),
- safe_for_write(ERASE),
- safe_for_write(GPCMD_MODE_SELECT_10),
- safe_for_write(MODE_SELECT),
- safe_for_write(GPCMD_BLANK),
- safe_for_write(GPCMD_CLOSE_TRACK),
- safe_for_write(GPCMD_FLUSH_CACHE),
- safe_for_write(GPCMD_FORMAT_UNIT),
- safe_for_write(GPCMD_REPAIR_RZONE_TRACK),
- safe_for_write(GPCMD_RESERVE_RZONE_TRACK),
- safe_for_write(GPCMD_SEND_DVD_STRUCTURE),
- safe_for_write(GPCMD_SEND_EVENT),
- safe_for_write(GPCMD_SEND_KEY),
- safe_for_write(GPCMD_SEND_OPC),
- safe_for_write(GPCMD_SEND_CUE_SHEET),
- safe_for_write(GPCMD_SET_SPEED),
- safe_for_write(GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL),
- safe_for_write(GPCMD_LOAD_UNLOAD),
- safe_for_write(GPCMD_SET_STREAMING),
- };
- unsigned char type = cmd_type[cmd[0]];
-
- /* Anybody who can open the device can do a read-safe command */
- if (type & CMD_READ_SAFE)
- return 0;
-
- /* Write-safe commands just require a writable open.. */
- if (type & CMD_WRITE_SAFE) {
- if (file->f_mode & FMODE_WRITE)
- return 0;
- }
-
- /* And root can do any command.. */
- if (capable(CAP_SYS_RAWIO))
- return 0;
-
- /* Otherwise fail it with an "Operation not permitted" */
- return -EPERM;
-}
-
static int sg_io(struct file *file, request_queue_t *q,
struct gendisk *bd_disk, struct sg_io_hdr *hdr)
{
@@ -220,7 +121,7 @@ static int sg_io(struct file *file, requ
return -EINVAL;
if (copy_from_user(cmd, hdr->cmdp, hdr->cmd_len))
return -EFAULT;
- if (verify_command(file, cmd))
+ if (rcf_verify_command(file, cmd))
return -EPERM;
/*
@@ -370,7 +271,7 @@ static int sg_scsi_ioctl(struct file *fi
if (copy_from_user(buffer, sic->data + cmdlen, in_len))
goto error;
- err = verify_command(file, rq->cmd);
+ err = rcf_verify_command(file, rq->cmd);
if (err)
goto error;
diff -urpN linux-2.5-export/include/linux/blkdev.h pjones-2.5-export/include/linux/blkdev.h
--- linux-2.5-export/include/linux/blkdev.h 2004-09-15 12:40:23 -04:00
+++ pjones-2.5-export/include/linux/blkdev.h 2004-09-15 19:23:54 -04:00
@@ -513,6 +513,8 @@ struct sec_size {
unsigned block_size_bits;
};
+extern int blk_register_filter(struct gendisk *disk);
+extern void blk_unregister_filter(struct gendisk *disk);
extern int blk_register_queue(struct gendisk *disk);
extern void blk_unregister_queue(struct gendisk *disk);
extern void register_disk(struct gendisk *dev);
diff -urpN linux-2.5-export/include/linux/genhd.h pjones-2.5-export/include/linux/genhd.h
--- linux-2.5-export/include/linux/genhd.h 2004-09-15 12:39:54 -04:00
+++ pjones-2.5-export/include/linux/genhd.h 2004-09-15 19:23:42 -04:00
@@ -78,6 +78,15 @@ struct disk_stats {
unsigned io_ticks;
unsigned time_in_queue;
};
+
+#define RCF_MAX_NR_CMDS 256
+#define RCF_CMD_LONGS (RCF_MAX_NR_CMDS / (sizeof (unsigned long) * 8))
+
+struct rawio_cmd_filter {
+ unsigned long read_ok[RCF_CMD_LONGS];
+ unsigned long write_ok[RCF_CMD_LONGS];
+ struct kobject kobj;
+};
struct gendisk {
int major; /* major number of driver */
@@ -88,6 +97,7 @@ struct gendisk {
struct hd_struct **part; /* [indexed by minor] */
struct block_device_operations *fops;
struct request_queue *queue;
+ struct rawio_cmd_filter filter;
void *private_data;
sector_t capacity;
--
Peter
On Wed, 2004-09-15 at 21:50 +0200, Jens Axboe wrote:
> > This could also be done in your approach. One possibility would be to
> > start with empty filter and call from CD/DVD registering a function that
> > sets the filter you currently have as default. This would be both flexible
> > and safe.
>
> I think that's a really nice idea. I've said right from the beginning
> that the command tables cannot even be per-device type, at least with
> CDROMs we have examples of commands with different meanings. But at
> least with a device-type default list we're a little closer.
It'd be pretty easy to add device-type defaults to my patch, and make
them registered by the individual drivers.
I'm just not sure I see the point in doing it. Without them, you get a
_somewhat_ reasonable set of defaults if you don't want to mess with it,
and distros can easily set their own per-device tables for each device
type, vendor specific commands, etc. It's even pretty simple to just
deny everything, if that's what distro maintainers or sysadmins want to
do.
Not doing it also means that the device driver code itself needs less
maintenance, and won't need to be updated every time some vendor comes
up with a new READ command. That makes it easier if for example distros
decide to push new command tables as updates, I think.
> A good reminder of why the whole thing is a mess :-)
It sure is.
--
Peter
On Wed, 15 Sep 2004 16:32:24 -0400
Peter Jones <[email protected]> wrote:
> It'd be pretty easy to add device-type defaults to my patch, and make
> them registered by the individual drivers.
Even this won't get us very far. Maybe we could probably find suitable
defaults for most modern hardware, but this still won't help users of
older or non-standard hardware.
For example, remember LG's abuse of CACHE_FLUSH for firmware updates.
Here I've done some grepping over cdrecord and k3b:
http://marc.theaimsgroup.com/?l=linux-kernel&m=109269018602502&w=2
>
> I'm just not sure I see the point in doing it. Without them, you get a
> _somewhat_ reasonable set of defaults if you don't want to mess with it,
> and distros can easily set their own per-device tables for each device
> type, vendor specific commands, etc. It's even pretty simple to just
> deny everything, if that's what distro maintainers or sysadmins want to
> do.
Per device-type filters are unavoidable (unless we deny everything by
default).
This is especially true for MODE_SELECT. See here:
http://marc.theaimsgroup.com/?l=linux-kernel&m=109274414601613&w=2
>
> Not doing it also means that the device driver code itself needs less
> maintenance, and won't need to be updated every time some vendor comes
> up with a new READ command. That makes it easier if for example distros
> decide to push new command tables as updates, I think.
Why not deny everything and have a "smart" user space tool that assigns
filters to devices during startup? The tool's database could be updated
regularly by distributors.
>
> > A good reminder of why the whole thing is a mess :-)
>
> It sure is.
IMHO all the more reason to keep all knowledge about individual devices
(and thus the need to provide updates) out of the kernel.
BTW: My impression was, that in the long term applications should be
adopted to work safely with increased capabilities. Is this still the
goal?
mfg
On Wed, 15 Sep 2004 19:36:18 -0400
Peter Jones <[email protected]> wrote:
> Yes.
>
> Here's another version of the patch. It does what yours did, as well as
> that. It also combines the read and write code so there's one of each,
> with a parameter to tell which it's supposed to modify.
Looks *slightly* better than mine ;-)
But
> +static ssize_t rcf_cmds_store(struct rawio_cmd_filter *rcf, const char *page,
> + size_t count, int rw)
...
> + ss.from = (char *)page+ret;
> + ss.to = (char *)page+ret+1;
needs to be
> + ss.from = (char *)page+ret;
> + ss.to = (char *)page+ret+2;
Regards
Hi,
I've created another version. I fixed the +1 -> +2 issue and added a
debbuging function.
I think this is needed because nowadays hardware hardly ships with
in-depth documentation. It greatly helps in determinig needed filter
rules.
Debugging is enabled with "echo 1 > /sys/block/hdX/filter/rcf_debug".
If a command is blocked, messages like the following are given via
printk:
RCF: hdX: command XY not allowed in write-mode
RCF: hdX: command YZ not allowed in read-mode
BTW: There is still a compiler warning about implicit declaration of
rcf_verify_command when building scsi_ioctl.c
Regards
diff -Nurp orig/linux-2.6.8.1/drivers/block/genhd.c linux-2.6.8.1/drivers/block/genhd.c
--- orig/linux-2.6.8.1/drivers/block/genhd.c 2004-08-19 13:53:46.000000000 +0200
+++ linux-2.6.8.1/drivers/block/genhd.c 2004-09-16 12:26:56.000000000 +0200
@@ -197,6 +197,7 @@ void add_disk(struct gendisk *disk)
disk->minors, NULL, exact_match, exact_lock, disk);
register_disk(disk);
blk_register_queue(disk);
+ blk_register_filter(disk);
}
EXPORT_SYMBOL(add_disk);
@@ -204,6 +205,7 @@ EXPORT_SYMBOL(del_gendisk); /* in partit
void unlink_gendisk(struct gendisk *disk)
{
+ blk_unregister_filter(disk);
blk_unregister_queue(disk);
blk_unregister_region(MKDEV(disk->major, disk->first_minor),
disk->minors);
@@ -382,6 +384,13 @@ static ssize_t disk_stats_read(struct ge
jiffies_to_msecs(disk_stat_read(disk, io_ticks)),
jiffies_to_msecs(disk_stat_read(disk, time_in_queue)));
}
+
+static ssize_t disk_driver_read(struct gendisk * disk, char *page)
+{
+ return sprintf(page, "%s\n", disk->disk_name);
+
+}
+
static struct disk_attribute disk_attr_dev = {
.attr = {.name = "dev", .mode = S_IRUGO },
.show = disk_dev_read
@@ -402,6 +411,10 @@ static struct disk_attribute disk_attr_s
.attr = {.name = "stat", .mode = S_IRUGO },
.show = disk_stats_read
};
+static struct disk_attribute disk_attr_driver = {
+ .attr = {.name = "driver", .mode = S_IRUGO },
+ .show = disk_driver_read
+};
static struct attribute * default_attrs[] = {
&disk_attr_dev.attr,
@@ -409,6 +422,7 @@ static struct attribute * default_attrs[
&disk_attr_removable.attr,
&disk_attr_size.attr,
&disk_attr_stat.attr,
+ &disk_attr_driver.attr,
NULL,
};
diff -Nurp orig/linux-2.6.8.1/drivers/block/Makefile linux-2.6.8.1/drivers/block/Makefile
--- orig/linux-2.6.8.1/drivers/block/Makefile 2004-09-16 16:10:51.000000000 +0200
+++ linux-2.6.8.1/drivers/block/Makefile 2004-09-16 12:26:56.000000000 +0200
@@ -13,7 +13,7 @@
# kblockd threads
#
-obj-y := elevator.o ll_rw_blk.o ioctl.o genhd.o scsi_ioctl.o
+obj-y := elevator.o ll_rw_blk.o ioctl.o genhd.o scsi_ioctl.o rcf.o
obj-$(CONFIG_IOSCHED_NOOP) += noop-iosched.o
obj-$(CONFIG_IOSCHED_AS) += as-iosched.o
diff -Nurp orig/linux-2.6.8.1/drivers/block/rcf.c linux-2.6.8.1/drivers/block/rcf.c
--- orig/linux-2.6.8.1/drivers/block/rcf.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.8.1/drivers/block/rcf.c 2004-09-16 16:44:22.000000000 +0200
@@ -0,0 +1,341 @@
+/*
+ * Copyright 2004 Peter M. Jones <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public Licens
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-
+ *
+ */
+
+#include <linux/list.h>
+#include <linux/genhd.h>
+#include <linux/spinlock.h>
+#include <linux/parser.h>
+#include <asm/bitops.h>
+
+#include <scsi/scsi.h>
+#include <linux/cdrom.h>
+
+int rcf_verify_command(struct file *file, unsigned char *cmd)
+{
+ struct rawio_cmd_filter *rcf;
+ struct inode *inode;
+
+ inode = file->f_dentry->d_inode;
+ if (!inode)
+ return -EINVAL;
+
+ rcf = &inode->i_bdev->bd_disk->filter;
+
+ /* root can do any command. */
+ if (capable(CAP_SYS_RAWIO))
+ return 0;
+
+ /* Anybody who can open the device can do a read-safe command */
+ if (test_bit(cmd[0], rcf->read_ok))
+ return 0;
+
+ /* Write-safe commands require a writable open */
+ if (test_bit(cmd[0], rcf->write_ok))
+ if (file->f_mode & FMODE_WRITE)
+ return 0;
+
+ /* Or else the user has no perms */
+
+ /* Debug output*/
+ if (rcf->debug_mode) {
+ if(file->f_mode & FMODE_WRITE)
+ printk(KERN_WARNING "RCF: %s: command %02x not allowed "
+ "in write-mode\n", file->f_dentry->d_name.name, cmd[0]);
+ else
+ printk(KERN_WARNING "RCF: %s: command %02x not allowed "
+ "in read-mode\n", file->f_dentry->d_name.name, cmd[0]);
+ }
+ return -EPERM;
+
+}
+
+EXPORT_SYMBOL(rcf_verify_command);
+
+/* and now, the sysfs stuff */
+static ssize_t rcf_cmds_show(struct rawio_cmd_filter *rcf, char *page, int rw)
+{
+ char *npage = page;
+ unsigned long *okbits;
+ int x;
+
+ if (rw == READ)
+ okbits = rcf->read_ok;
+ else
+ okbits = rcf->write_ok;
+
+ for (x=0; x < RCF_MAX_NR_CMDS; x++)
+ if (test_bit(x, okbits)) {
+ sprintf(npage, "%02x", x);
+ npage += 2;
+ if (x < RCF_MAX_NR_CMDS-1)
+ sprintf(npage++, " ");
+ }
+ if (npage != page)
+ npage += sprintf(npage, "\n");
+
+ return (npage - page);
+}
+
+static ssize_t rcf_readcmds_show(struct rawio_cmd_filter *rcf, char *page)
+{
+ return rcf_cmds_show(rcf, page, READ);
+}
+
+static ssize_t rcf_writecmds_show(struct rawio_cmd_filter *rcf, char *page)
+{
+ return rcf_cmds_show(rcf, page, WRITE);
+}
+
+static ssize_t rcf_cmds_store(struct rawio_cmd_filter *rcf, const char *page,
+ size_t count, int rw)
+{
+ ssize_t ret=0;
+ unsigned long okbits[RCF_CMD_LONGS], *target_okbits;
+ int cmd, status, len;
+ substring_t ss;
+
+ memset(&okbits, 0, sizeof (okbits));
+
+ for (len = strlen(page); len > 0; len-=3) {
+ if (len < 2)
+ break;
+ ss.from = (char *)page+ret;
+ ss.to = (char *)page+ret+2;
+ ret+=3;
+ status = match_hex(&ss, &cmd);
+ /* either of these cases means invalid input, so do nothing. */
+ if (status || cmd >= RCF_MAX_NR_CMDS)
+ return -EINVAL;
+
+ set_bit(cmd, okbits);
+ }
+
+ if (rw == READ)
+ target_okbits = rcf->read_ok;
+ else
+ target_okbits = rcf->write_ok;
+ memmove(target_okbits, okbits, sizeof (okbits));
+
+ return count;
+}
+
+static ssize_t rcf_readcmds_store(struct rawio_cmd_filter *rcf,
+ const char *page, size_t count)
+{
+ return rcf_cmds_store(rcf, page, count, READ);
+}
+
+static ssize_t rcf_writecmds_store(struct rawio_cmd_filter *rcf,
+ const char *page, size_t count)
+{
+ return rcf_cmds_store(rcf, page, count, WRITE);
+}
+
+static ssize_t rcf_debugmode_show(struct rawio_cmd_filter *rcf, char *page)
+{
+ return sprintf(page, "%d\n", rcf->debug_mode);
+}
+
+static ssize_t rcf_debugmode_store(struct rawio_cmd_filter *rcf,
+ const char *page, size_t count)
+{
+ u32 state;
+ state = simple_strtoul(page, NULL, 10);
+ if (state > 1)
+ return -EINVAL;
+ rcf->debug_mode = state;
+ return count;
+}
+
+struct rcf_sysfs_entry {
+ struct attribute attr;
+ ssize_t (*show)(struct rawio_cmd_filter *, char *);
+ ssize_t (*store)(struct rawio_cmd_filter *, const char *, size_t);
+};
+
+static struct rcf_sysfs_entry rcf_readcmds_entry = {
+ .attr = {.name = "ok_read_commands", .mode = S_IRUGO | S_IWUSR },
+ .show = rcf_readcmds_show,
+ .store = rcf_readcmds_store,
+};
+
+static struct rcf_sysfs_entry rcf_writecmds_entry = {
+ .attr = {.name = "ok_write_commands", .mode = S_IRUGO | S_IWUSR },
+ .show = rcf_writecmds_show,
+ .store = rcf_writecmds_store,
+};
+
+static struct rcf_sysfs_entry rcf_debugmode_entry = {
+ .attr = {.name = "rcf_debug", .mode = S_IRUGO | S_IWUSR },
+ .show = rcf_debugmode_show,
+ .store = rcf_debugmode_store,
+};
+
+static struct attribute *default_attrs[] = {
+ &rcf_readcmds_entry.attr,
+ &rcf_writecmds_entry.attr,
+ &rcf_debugmode_entry.attr,
+ NULL,
+};
+
+#define to_rcf(atr) container_of((atr), struct rcf_sysfs_entry, attr)
+
+static ssize_t
+rcf_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
+{
+ struct rcf_sysfs_entry *entry = to_rcf(attr);
+ struct rawio_cmd_filter *rcf;
+
+ rcf = container_of(kobj, struct rawio_cmd_filter, kobj);
+ if (!entry->show)
+ return 0;
+
+ return entry->show(rcf, page);
+}
+
+static ssize_t
+rcf_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *page, size_t length)
+{
+ struct rcf_sysfs_entry *entry = to_rcf(attr);
+ struct rawio_cmd_filter *rcf;
+
+ rcf = container_of(kobj, struct rawio_cmd_filter, kobj);
+ if (!entry->store)
+ return -EINVAL;
+
+ if (!capable(CAP_SYS_RAWIO))
+ return -EPERM;
+ return entry->store(rcf, page, length);
+}
+
+static struct sysfs_ops rcf_sysfs_ops = {
+ .show = rcf_attr_show,
+ .store = rcf_attr_store,
+};
+
+static struct kobj_type rcf_ktype = {
+ .sysfs_ops = &rcf_sysfs_ops,
+ .default_attrs = default_attrs,
+};
+
+static void rcf_set_defaults(struct rawio_cmd_filter *rcf)
+{
+ /* Basic read-only commands */
+ set_bit(TEST_UNIT_READY, rcf->read_ok);
+ set_bit(REQUEST_SENSE, rcf->read_ok);
+ set_bit(READ_6, rcf->read_ok);
+ set_bit(READ_10, rcf->read_ok);
+ set_bit(READ_12, rcf->read_ok);
+ set_bit(READ_16, rcf->read_ok);
+ set_bit(READ_BUFFER, rcf->read_ok);
+ set_bit(READ_LONG, rcf->read_ok);
+ set_bit(INQUIRY, rcf->read_ok);
+ set_bit(MODE_SENSE, rcf->read_ok);
+ set_bit(MODE_SENSE_10, rcf->read_ok);
+ set_bit(START_STOP, rcf->read_ok);
+ set_bit(GPCMD_VERIFY_10, rcf->read_ok);
+ set_bit(VERIFY_16, rcf->read_ok);
+ set_bit(READ_BUFFER, rcf->read_ok);
+
+ /* Audio CD commands */
+ set_bit(GPCMD_PLAY_CD, rcf->read_ok);
+ set_bit(GPCMD_PLAY_AUDIO_10, rcf->read_ok);
+ set_bit(GPCMD_PLAY_AUDIO_MSF, rcf->read_ok);
+ set_bit(GPCMD_PLAY_AUDIO_TI, rcf->read_ok);
+ set_bit(GPCMD_PAUSE_RESUME, rcf->read_ok);
+
+ /* CD/DVD data reading */
+ set_bit(GPCMD_READ_CD, rcf->read_ok);
+ set_bit(GPCMD_READ_CD_MSF, rcf->read_ok);
+ set_bit(GPCMD_READ_DISC_INFO, rcf->read_ok);
+ set_bit(GPCMD_READ_CDVD_CAPACITY, rcf->read_ok);
+ set_bit(GPCMD_READ_DVD_STRUCTURE, rcf->read_ok);
+ set_bit(GPCMD_READ_HEADER, rcf->read_ok);
+ set_bit(GPCMD_READ_TRACK_RZONE_INFO, rcf->read_ok);
+ set_bit(GPCMD_READ_SUBCHANNEL, rcf->read_ok);
+ set_bit(GPCMD_READ_TOC_PMA_ATIP, rcf->read_ok);
+ set_bit(GPCMD_REPORT_KEY, rcf->read_ok);
+ set_bit(GPCMD_SCAN, rcf->read_ok);
+ set_bit(GPCMD_GET_CONFIGURATION, rcf->read_ok);
+ set_bit(GPCMD_READ_FORMAT_CAPACITIES, rcf->read_ok);
+ set_bit(GPCMD_GET_EVENT_STATUS_NOTIFICATION, rcf->read_ok);
+ set_bit(GPCMD_GET_PERFORMANCE, rcf->read_ok);
+ set_bit(GPCMD_SEEK, rcf->read_ok);
+ set_bit(GPCMD_STOP_PLAY_SCAN, rcf->read_ok);
+
+ /* Basic writing commands */
+ set_bit(WRITE_6, rcf->write_ok);
+ set_bit(WRITE_10, rcf->write_ok);
+ set_bit(WRITE_VERIFY, rcf->write_ok);
+ set_bit(WRITE_12, rcf->write_ok);
+ set_bit(WRITE_VERIFY_12, rcf->write_ok);
+ set_bit(WRITE_16, rcf->write_ok);
+ set_bit(WRITE_LONG, rcf->write_ok);
+ set_bit(ERASE, rcf->write_ok);
+ set_bit(GPCMD_MODE_SELECT_10, rcf->write_ok);
+ set_bit(MODE_SELECT, rcf->write_ok);
+ set_bit(GPCMD_BLANK, rcf->write_ok);
+ set_bit(GPCMD_CLOSE_TRACK, rcf->write_ok);
+ set_bit(GPCMD_FLUSH_CACHE, rcf->write_ok);
+ set_bit(GPCMD_FORMAT_UNIT, rcf->write_ok);
+ set_bit(GPCMD_REPAIR_RZONE_TRACK, rcf->write_ok);
+ set_bit(GPCMD_RESERVE_RZONE_TRACK, rcf->write_ok);
+ set_bit(GPCMD_SEND_DVD_STRUCTURE, rcf->write_ok);
+ set_bit(GPCMD_SEND_EVENT, rcf->write_ok);
+ set_bit(GPCMD_SEND_KEY, rcf->write_ok);
+ set_bit(GPCMD_SEND_OPC, rcf->write_ok);
+ set_bit(GPCMD_SEND_CUE_SHEET, rcf->write_ok);
+ set_bit(GPCMD_SET_SPEED, rcf->write_ok);
+ set_bit(GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL, rcf->write_ok);
+ set_bit(GPCMD_LOAD_UNLOAD, rcf->write_ok);
+ set_bit(GPCMD_SET_STREAMING, rcf->write_ok);
+
+ /* Disable debug mode by default */
+ rcf->debug_mode = 0;
+}
+
+int blk_register_filter(struct gendisk *disk)
+{
+ int ret;
+ struct rawio_cmd_filter *rcf = &disk->filter;
+
+ rcf->kobj.parent = kobject_get(&disk->kobj);
+ if (!rcf->kobj.parent)
+ return -EBUSY;
+
+ snprintf(rcf->kobj.name, KOBJ_NAME_LEN, "%s", "filter");
+ rcf->kobj.ktype = &rcf_ktype;
+
+ ret = kobject_register(&rcf->kobj);
+ if (ret < 0)
+ return ret;
+
+ rcf_set_defaults(&disk->filter);
+
+ return 0;
+}
+
+void blk_unregister_filter(struct gendisk *disk)
+{
+ struct rawio_cmd_filter *rcf = &disk->filter;
+
+ kobject_unregister(&rcf->kobj);
+ kobject_put(&disk->kobj);
+}
diff -Nurp orig/linux-2.6.8.1/drivers/block/scsi_ioctl.c linux-2.6.8.1/drivers/block/scsi_ioctl.c
--- orig/linux-2.6.8.1/drivers/block/scsi_ioctl.c 2004-09-16 16:10:51.000000000 +0200
+++ linux-2.6.8.1/drivers/block/scsi_ioctl.c 2004-09-16 12:26:56.000000000 +0200
@@ -105,105 +105,6 @@ static int sg_emulated_host(request_queu
return put_user(1, p);
}
-#define CMD_READ_SAFE 0x01
-#define CMD_WRITE_SAFE 0x02
-#define safe_for_read(cmd) [cmd] = CMD_READ_SAFE
-#define safe_for_write(cmd) [cmd] = CMD_WRITE_SAFE
-
-static int verify_command(struct file *file, unsigned char *cmd)
-{
- static const unsigned char cmd_type[256] = {
-
- /* Basic read-only commands */
- safe_for_read(TEST_UNIT_READY),
- safe_for_read(REQUEST_SENSE),
- safe_for_read(READ_6),
- safe_for_read(READ_10),
- safe_for_read(READ_12),
- safe_for_read(READ_16),
- safe_for_read(READ_BUFFER),
- safe_for_read(READ_LONG),
- safe_for_read(INQUIRY),
- safe_for_read(MODE_SENSE),
- safe_for_read(MODE_SENSE_10),
- safe_for_read(START_STOP),
- safe_for_read(GPCMD_VERIFY_10),
- safe_for_read(VERIFY_16),
- safe_for_read(READ_BUFFER),
-
- /* Audio CD commands */
- safe_for_read(GPCMD_PLAY_CD),
- safe_for_read(GPCMD_PLAY_AUDIO_10),
- safe_for_read(GPCMD_PLAY_AUDIO_MSF),
- safe_for_read(GPCMD_PLAY_AUDIO_TI),
- safe_for_read(GPCMD_PAUSE_RESUME),
-
- /* CD/DVD data reading */
- safe_for_read(GPCMD_READ_CD),
- safe_for_read(GPCMD_READ_CD_MSF),
- safe_for_read(GPCMD_READ_DISC_INFO),
- safe_for_read(GPCMD_READ_CDVD_CAPACITY),
- safe_for_read(GPCMD_READ_DVD_STRUCTURE),
- safe_for_read(GPCMD_READ_HEADER),
- safe_for_read(GPCMD_READ_TRACK_RZONE_INFO),
- safe_for_read(GPCMD_READ_SUBCHANNEL),
- safe_for_read(GPCMD_READ_TOC_PMA_ATIP),
- safe_for_read(GPCMD_REPORT_KEY),
- safe_for_read(GPCMD_SCAN),
- safe_for_read(GPCMD_GET_CONFIGURATION),
- safe_for_read(GPCMD_READ_FORMAT_CAPACITIES),
- safe_for_read(GPCMD_GET_EVENT_STATUS_NOTIFICATION),
- safe_for_read(GPCMD_GET_PERFORMANCE),
- safe_for_read(GPCMD_SEEK),
- safe_for_read(GPCMD_STOP_PLAY_SCAN),
-
- /* Basic writing commands */
- safe_for_write(WRITE_6),
- safe_for_write(WRITE_10),
- safe_for_write(WRITE_VERIFY),
- safe_for_write(WRITE_12),
- safe_for_write(WRITE_VERIFY_12),
- safe_for_write(WRITE_16),
- safe_for_write(WRITE_LONG),
- safe_for_write(ERASE),
- safe_for_write(GPCMD_MODE_SELECT_10),
- safe_for_write(MODE_SELECT),
- safe_for_write(GPCMD_BLANK),
- safe_for_write(GPCMD_CLOSE_TRACK),
- safe_for_write(GPCMD_FLUSH_CACHE),
- safe_for_write(GPCMD_FORMAT_UNIT),
- safe_for_write(GPCMD_REPAIR_RZONE_TRACK),
- safe_for_write(GPCMD_RESERVE_RZONE_TRACK),
- safe_for_write(GPCMD_SEND_DVD_STRUCTURE),
- safe_for_write(GPCMD_SEND_EVENT),
- safe_for_write(GPCMD_SEND_KEY),
- safe_for_write(GPCMD_SEND_OPC),
- safe_for_write(GPCMD_SEND_CUE_SHEET),
- safe_for_write(GPCMD_SET_SPEED),
- safe_for_write(GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL),
- safe_for_write(GPCMD_LOAD_UNLOAD),
- safe_for_write(GPCMD_SET_STREAMING),
- };
- unsigned char type = cmd_type[cmd[0]];
-
- /* Anybody who can open the device can do a read-safe command */
- if (type & CMD_READ_SAFE)
- return 0;
-
- /* Write-safe commands just require a writable open.. */
- if (type & CMD_WRITE_SAFE) {
- if (file->f_mode & FMODE_WRITE)
- return 0;
- }
-
- /* And root can do any command.. */
- if (capable(CAP_SYS_RAWIO))
- return 0;
-
- /* Otherwise fail it with an "Operation not permitted" */
- return -EPERM;
-}
-
static int sg_io(struct file *file, request_queue_t *q,
struct gendisk *bd_disk, struct sg_io_hdr *hdr)
{
@@ -220,7 +121,7 @@ static int sg_io(struct file *file, requ
return -EINVAL;
if (copy_from_user(cmd, hdr->cmdp, hdr->cmd_len))
return -EFAULT;
- if (verify_command(file, cmd))
+ if (rcf_verify_command(file, cmd))
return -EPERM;
/*
@@ -370,7 +271,7 @@ static int sg_scsi_ioctl(struct file *fi
if (copy_from_user(buffer, sic->data + cmdlen, in_len))
goto error;
- err = verify_command(file, rq->cmd);
+ err = rcf_verify_command(file, rq->cmd);
if (err)
goto error;
diff -Nurp orig/linux-2.6.8.1/include/linux/blkdev.h linux-2.6.8.1/include/linux/blkdev.h
--- orig/linux-2.6.8.1/include/linux/blkdev.h 2004-09-16 16:10:53.000000000 +0200
+++ linux-2.6.8.1/include/linux/blkdev.h 2004-09-16 12:26:56.000000000 +0200
@@ -512,6 +512,8 @@ struct sec_size {
unsigned block_size_bits;
};
+extern int blk_register_filter(struct gendisk *disk);
+extern void blk_unregister_filter(struct gendisk *disk);
extern int blk_register_queue(struct gendisk *disk);
extern void blk_unregister_queue(struct gendisk *disk);
extern void register_disk(struct gendisk *dev);
diff -Nurp orig/linux-2.6.8.1/include/linux/genhd.h linux-2.6.8.1/include/linux/genhd.h
--- orig/linux-2.6.8.1/include/linux/genhd.h 2004-09-16 16:10:53.000000000 +0200
+++ linux-2.6.8.1/include/linux/genhd.h 2004-09-16 15:29:39.000000000 +0200
@@ -78,6 +78,16 @@ struct disk_stats {
unsigned io_ticks;
unsigned time_in_queue;
};
+
+#define RCF_MAX_NR_CMDS 256
+#define RCF_CMD_LONGS (RCF_MAX_NR_CMDS / (sizeof (unsigned long) * 8))
+
+struct rawio_cmd_filter {
+ unsigned long read_ok[RCF_CMD_LONGS];
+ unsigned long write_ok[RCF_CMD_LONGS];
+ int debug_mode;
+ struct kobject kobj;
+};
struct gendisk {
int major; /* major number of driver */
@@ -88,6 +98,7 @@ struct gendisk {
struct hd_struct **part; /* [indexed by minor] */
struct block_device_operations *fops;
struct request_queue *queue;
+ struct rawio_cmd_filter filter;
void *private_data;
sector_t capacity;
On Thu, 2004-09-16 at 17:10 +0200, Marc Ballarin wrote:
> Hi,
> I've created another version. I fixed the +1 -> +2 issue and added a
> debbuging function.
> I think this is needed because nowadays hardware hardly ships with
> in-depth documentation. It greatly helps in determinig needed filter
> rules.
>
> Debugging is enabled with "echo 1 > /sys/block/hdX/filter/rcf_debug".
> If a command is blocked, messages like the following are given via
> printk:
> RCF: hdX: command XY not allowed in write-mode
> RCF: hdX: command YZ not allowed in read-mode
>
> BTW: There is still a compiler warning about implicit declaration of
> rcf_verify_command when building scsi_ioctl.c
Oh, hrm. Yeah, that needs to be declared in the header. Thanks for
pointing it out.
Here's a new diff. This has changes based on your debug changes. It
also removes the (useless) "driver" sysfs entry, as it doesn't show what
I thought. It'd still be nice to list what type of device this is
somewhere in /sys/block/*/ .
diff -urpN linux-2.5-export/drivers/block/Makefile pjones-2.5-export/drivers/block/Makefile
--- linux-2.5-export/drivers/block/Makefile 2004-09-15 12:42:18 -04:00
+++ pjones-2.5-export/drivers/block/Makefile 2004-09-16 13:20:40 -04:00
@@ -13,7 +13,7 @@
# kblockd threads
#
-obj-y := elevator.o ll_rw_blk.o ioctl.o genhd.o scsi_ioctl.o
+obj-y := elevator.o ll_rw_blk.o ioctl.o genhd.o scsi_ioctl.o rcf.o
obj-$(CONFIG_IOSCHED_NOOP) += noop-iosched.o
obj-$(CONFIG_IOSCHED_AS) += as-iosched.o
diff -urpN linux-2.5-export/drivers/block/genhd.c pjones-2.5-export/drivers/block/genhd.c
--- linux-2.5-export/drivers/block/genhd.c 2004-09-15 12:41:22 -04:00
+++ pjones-2.5-export/drivers/block/genhd.c 2004-09-16 13:20:33 -04:00
@@ -197,6 +197,7 @@ void add_disk(struct gendisk *disk)
disk->minors, NULL, exact_match, exact_lock, disk);
register_disk(disk);
blk_register_queue(disk);
+ blk_register_filter(disk);
}
EXPORT_SYMBOL(add_disk);
@@ -204,6 +205,7 @@ EXPORT_SYMBOL(del_gendisk); /* in partit
void unlink_gendisk(struct gendisk *disk)
{
+ blk_unregister_filter(disk);
blk_unregister_queue(disk);
blk_unregister_region(MKDEV(disk->major, disk->first_minor),
disk->minors);
@@ -382,6 +384,7 @@ static ssize_t disk_stats_read(struct ge
jiffies_to_msecs(disk_stat_read(disk, io_ticks)),
jiffies_to_msecs(disk_stat_read(disk, time_in_queue)));
}
+
static struct disk_attribute disk_attr_dev = {
.attr = {.name = "dev", .mode = S_IRUGO },
.show = disk_dev_read
diff -urpN linux-2.5-export/drivers/block/rcf.c pjones-2.5-export/drivers/block/rcf.c
--- linux-2.5-export/drivers/block/rcf.c 1969-12-31 19:00:00 -05:00
+++ pjones-2.5-export/drivers/block/rcf.c 2004-09-16 13:20:44 -04:00
@@ -0,0 +1,338 @@
+/*
+ * Copyright 2004 Peter M. Jones <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public Licens
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-
+ *
+ */
+
+#include <linux/list.h>
+#include <linux/genhd.h>
+#include <linux/spinlock.h>
+#include <linux/parser.h>
+#include <asm/bitops.h>
+
+#include <scsi/scsi.h>
+#include <linux/cdrom.h>
+
+int rcf_verify_command(struct file *file, unsigned char *cmd)
+{
+ struct rawio_cmd_filter *rcf;
+ struct gendisk *disk;
+ struct inode *inode;
+
+ inode = file->f_dentry->d_inode;
+ if (!inode)
+ return -EINVAL;
+
+ disk = inode->i_bdev->bd_disk;
+ rcf = &disk->filter;
+
+ /* root can do any command. */
+ if (capable(CAP_SYS_RAWIO))
+ return 0;
+
+ /* Anybody who can open the device can do a read-safe command */
+ if (test_bit(cmd[0], rcf->read_ok))
+ return 0;
+
+ /* Write-safe commands require a writable open */
+ if (test_bit(cmd[0], rcf->write_ok) && (file->f_mode & FMODE_WRITE))
+ return 0;
+
+ /* Or else the user has no perms */
+ if (rcf->debug) {
+ if(file->f_mode & FMODE_WRITE)
+ printk(KERN_WARNING
+ "RCF: %s: command %02x not allowed for write\n",
+ disk->disk_name, cmd[0]);
+ else
+ printk(KERN_WARNING
+ "RCF: %s: command %02x not allowed for read\n",
+ disk->disk_name, cmd[0]);
+ }
+ return -EPERM;
+}
+
+EXPORT_SYMBOL(rcf_verify_command);
+
+/* and now, the sysfs stuff */
+static ssize_t rcf_cmds_show(struct rawio_cmd_filter *rcf, char *page, int rw)
+{
+ char *npage = page;
+ unsigned long *okbits;
+ int x;
+
+ if (rw == READ)
+ okbits = rcf->read_ok;
+ else
+ okbits = rcf->write_ok;
+
+ for (x=0; x < RCF_MAX_NR_CMDS; x++)
+ if (test_bit(x, okbits)) {
+ sprintf(npage, "%02x", x);
+ npage += 2;
+ if (x < RCF_MAX_NR_CMDS-1)
+ sprintf(npage++, " ");
+ }
+ if (npage != page)
+ npage += sprintf(npage, "\n");
+
+ return (npage - page);
+}
+
+static ssize_t rcf_readcmds_show(struct rawio_cmd_filter *rcf, char *page)
+{
+ return rcf_cmds_show(rcf, page, READ);
+}
+
+static ssize_t rcf_writecmds_show(struct rawio_cmd_filter *rcf, char *page)
+{
+ return rcf_cmds_show(rcf, page, WRITE);
+}
+
+static ssize_t rcf_debug_show(struct rawio_cmd_filter *rcf, char *page)
+{
+
+ return sprintf(page, "%d\n", rcf->debug);
+}
+
+static ssize_t rcf_cmds_store(struct rawio_cmd_filter *rcf, const char *page,
+ size_t count, int rw)
+{
+ ssize_t ret=0;
+ unsigned long okbits[RCF_CMD_LONGS], *target_okbits;
+ int cmd, status, len;
+ substring_t ss;
+
+ memset(&okbits, 0, sizeof (okbits));
+
+ for (len = strlen(page); len > 0; len-=3) {
+ if (len < 2)
+ break;
+ ss.from = (char *)page+ret;
+ ss.to = (char *)page+ret+2;
+ ret+=3;
+ status = match_hex(&ss, &cmd);
+ /* either of these cases means invalid input, so do nothing. */
+ if (status || cmd >= RCF_MAX_NR_CMDS)
+ return -EINVAL;
+
+ set_bit(cmd, okbits);
+ }
+
+ if (rw == READ)
+ target_okbits = rcf->read_ok;
+ else
+ target_okbits = rcf->write_ok;
+ memmove(target_okbits, okbits, sizeof (okbits));
+
+ return count;
+}
+
+static ssize_t rcf_readcmds_store(struct rawio_cmd_filter *rcf,
+ const char *page, size_t count)
+{
+ return rcf_cmds_store(rcf, page, count, READ);
+}
+
+static ssize_t rcf_writecmds_store(struct rawio_cmd_filter *rcf,
+ const char *page, size_t count)
+{
+ return rcf_cmds_store(rcf, page, count, WRITE);
+}
+
+static ssize_t rcf_debug_store(struct rawio_cmd_filter *rcf,
+ const char *page, size_t count)
+{
+ if (page[0] == '0')
+ rcf->debug = 0;
+ else
+ rcf->debug = 1;
+ return count;
+}
+
+struct rcf_sysfs_entry {
+ struct attribute attr;
+ ssize_t (*show)(struct rawio_cmd_filter *, char *);
+ ssize_t (*store)(struct rawio_cmd_filter *, const char *, size_t);
+};
+
+static struct rcf_sysfs_entry rcf_readcmds_entry = {
+ .attr = {.name = "ok_read_commands", .mode = S_IRUGO | S_IWUSR },
+ .show = rcf_readcmds_show,
+ .store = rcf_readcmds_store,
+};
+
+static struct rcf_sysfs_entry rcf_writecmds_entry = {
+ .attr = {.name = "ok_write_commands", .mode = S_IRUGO | S_IWUSR },
+ .show = rcf_writecmds_show,
+ .store = rcf_writecmds_store,
+};
+
+static struct rcf_sysfs_entry rcf_debug_entry = {
+ .attr = {.name = "debug", .mode = S_IRUGO | S_IWUSR },
+ .show = rcf_debug_show,
+ .store = rcf_debug_store,
+};
+
+static struct attribute *default_attrs[] = {
+ &rcf_readcmds_entry.attr,
+ &rcf_writecmds_entry.attr,
+ &rcf_debug_entry.attr,
+ NULL,
+};
+
+#define to_rcf(atr) container_of((atr), struct rcf_sysfs_entry, attr)
+
+static ssize_t
+rcf_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
+{
+ struct rcf_sysfs_entry *entry = to_rcf(attr);
+ struct rawio_cmd_filter *rcf;
+
+ rcf = container_of(kobj, struct rawio_cmd_filter, kobj);
+ if (!entry->show)
+ return 0;
+
+ return entry->show(rcf, page);
+}
+
+static ssize_t
+rcf_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *page, size_t length)
+{
+ struct rcf_sysfs_entry *entry = to_rcf(attr);
+ struct rawio_cmd_filter *rcf;
+
+ if (!capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ rcf = container_of(kobj, struct rawio_cmd_filter, kobj);
+ if (!entry->store)
+ return -EINVAL;
+ return entry->store(rcf, page, length);
+}
+
+static struct sysfs_ops rcf_sysfs_ops = {
+ .show = rcf_attr_show,
+ .store = rcf_attr_store,
+};
+
+static struct kobj_type rcf_ktype = {
+ .sysfs_ops = &rcf_sysfs_ops,
+ .default_attrs = default_attrs,
+};
+
+static void rcf_set_defaults(struct rawio_cmd_filter *rcf)
+{
+ /* Basic read-only commands */
+ set_bit(TEST_UNIT_READY, rcf->read_ok);
+ set_bit(REQUEST_SENSE, rcf->read_ok);
+ set_bit(READ_6, rcf->read_ok);
+ set_bit(READ_10, rcf->read_ok);
+ set_bit(READ_12, rcf->read_ok);
+ set_bit(READ_16, rcf->read_ok);
+ set_bit(READ_BUFFER, rcf->read_ok);
+ set_bit(READ_LONG, rcf->read_ok);
+ set_bit(INQUIRY, rcf->read_ok);
+ set_bit(MODE_SENSE, rcf->read_ok);
+ set_bit(MODE_SENSE_10, rcf->read_ok);
+ set_bit(START_STOP, rcf->read_ok);
+ set_bit(GPCMD_VERIFY_10, rcf->read_ok);
+ set_bit(VERIFY_16, rcf->read_ok);
+ set_bit(READ_BUFFER, rcf->read_ok);
+
+ /* Audio CD commands */
+ set_bit(GPCMD_PLAY_CD, rcf->read_ok);
+ set_bit(GPCMD_PLAY_AUDIO_10, rcf->read_ok);
+ set_bit(GPCMD_PLAY_AUDIO_MSF, rcf->read_ok);
+ set_bit(GPCMD_PLAY_AUDIO_TI, rcf->read_ok);
+ set_bit(GPCMD_PAUSE_RESUME, rcf->read_ok);
+
+ /* CD/DVD data reading */
+ set_bit(GPCMD_READ_CD, rcf->read_ok);
+ set_bit(GPCMD_READ_CD_MSF, rcf->read_ok);
+ set_bit(GPCMD_READ_DISC_INFO, rcf->read_ok);
+ set_bit(GPCMD_READ_CDVD_CAPACITY, rcf->read_ok);
+ set_bit(GPCMD_READ_DVD_STRUCTURE, rcf->read_ok);
+ set_bit(GPCMD_READ_HEADER, rcf->read_ok);
+ set_bit(GPCMD_READ_TRACK_RZONE_INFO, rcf->read_ok);
+ set_bit(GPCMD_READ_SUBCHANNEL, rcf->read_ok);
+ set_bit(GPCMD_READ_TOC_PMA_ATIP, rcf->read_ok);
+ set_bit(GPCMD_REPORT_KEY, rcf->read_ok);
+ set_bit(GPCMD_SCAN, rcf->read_ok);
+ set_bit(GPCMD_GET_CONFIGURATION, rcf->read_ok);
+ set_bit(GPCMD_READ_FORMAT_CAPACITIES, rcf->read_ok);
+ set_bit(GPCMD_GET_EVENT_STATUS_NOTIFICATION, rcf->read_ok);
+ set_bit(GPCMD_GET_PERFORMANCE, rcf->read_ok);
+ set_bit(GPCMD_SEEK, rcf->read_ok);
+ set_bit(GPCMD_STOP_PLAY_SCAN, rcf->read_ok);
+
+ /* Basic writing commands */
+ set_bit(WRITE_6, rcf->write_ok);
+ set_bit(WRITE_10, rcf->write_ok);
+ set_bit(WRITE_VERIFY, rcf->write_ok);
+ set_bit(WRITE_12, rcf->write_ok);
+ set_bit(WRITE_VERIFY_12, rcf->write_ok);
+ set_bit(WRITE_16, rcf->write_ok);
+ set_bit(WRITE_LONG, rcf->write_ok);
+ set_bit(ERASE, rcf->write_ok);
+ set_bit(GPCMD_MODE_SELECT_10, rcf->write_ok);
+ set_bit(MODE_SELECT, rcf->write_ok);
+ set_bit(GPCMD_BLANK, rcf->write_ok);
+ set_bit(GPCMD_CLOSE_TRACK, rcf->write_ok);
+ set_bit(GPCMD_FLUSH_CACHE, rcf->write_ok);
+ set_bit(GPCMD_FORMAT_UNIT, rcf->write_ok);
+ set_bit(GPCMD_REPAIR_RZONE_TRACK, rcf->write_ok);
+ set_bit(GPCMD_RESERVE_RZONE_TRACK, rcf->write_ok);
+ set_bit(GPCMD_SEND_DVD_STRUCTURE, rcf->write_ok);
+ set_bit(GPCMD_SEND_EVENT, rcf->write_ok);
+ set_bit(GPCMD_SEND_KEY, rcf->write_ok);
+ set_bit(GPCMD_SEND_OPC, rcf->write_ok);
+ set_bit(GPCMD_SEND_CUE_SHEET, rcf->write_ok);
+ set_bit(GPCMD_SET_SPEED, rcf->write_ok);
+ set_bit(GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL, rcf->write_ok);
+ set_bit(GPCMD_LOAD_UNLOAD, rcf->write_ok);
+ set_bit(GPCMD_SET_STREAMING, rcf->write_ok);
+}
+
+int blk_register_filter(struct gendisk *disk)
+{
+ int ret;
+ struct rawio_cmd_filter *rcf = &disk->filter;
+
+ rcf->kobj.parent = kobject_get(&disk->kobj);
+ if (!rcf->kobj.parent)
+ return -EBUSY;
+
+ snprintf(rcf->kobj.name, KOBJ_NAME_LEN, "%s", "filter");
+ rcf->kobj.ktype = &rcf_ktype;
+
+ ret = kobject_register(&rcf->kobj);
+ if (ret < 0)
+ return ret;
+
+ rcf_set_defaults(&disk->filter);
+
+ return 0;
+}
+
+void blk_unregister_filter(struct gendisk *disk)
+{
+ struct rawio_cmd_filter *rcf = &disk->filter;
+
+ kobject_unregister(&rcf->kobj);
+ kobject_put(&disk->kobj);
+}
diff -urpN linux-2.5-export/drivers/block/scsi_ioctl.c pjones-2.5-export/drivers/block/scsi_ioctl.c
--- linux-2.5-export/drivers/block/scsi_ioctl.c 2004-09-15 12:42:48 -04:00
+++ pjones-2.5-export/drivers/block/scsi_ioctl.c 2004-09-16 13:20:46 -04:00
@@ -105,105 +105,6 @@ static int sg_emulated_host(request_queu
return put_user(1, p);
}
-#define CMD_READ_SAFE 0x01
-#define CMD_WRITE_SAFE 0x02
-#define safe_for_read(cmd) [cmd] = CMD_READ_SAFE
-#define safe_for_write(cmd) [cmd] = CMD_WRITE_SAFE
-
-static int verify_command(struct file *file, unsigned char *cmd)
-{
- static const unsigned char cmd_type[256] = {
-
- /* Basic read-only commands */
- safe_for_read(TEST_UNIT_READY),
- safe_for_read(REQUEST_SENSE),
- safe_for_read(READ_6),
- safe_for_read(READ_10),
- safe_for_read(READ_12),
- safe_for_read(READ_16),
- safe_for_read(READ_BUFFER),
- safe_for_read(READ_LONG),
- safe_for_read(INQUIRY),
- safe_for_read(MODE_SENSE),
- safe_for_read(MODE_SENSE_10),
- safe_for_read(START_STOP),
- safe_for_read(GPCMD_VERIFY_10),
- safe_for_read(VERIFY_16),
- safe_for_read(READ_BUFFER),
-
- /* Audio CD commands */
- safe_for_read(GPCMD_PLAY_CD),
- safe_for_read(GPCMD_PLAY_AUDIO_10),
- safe_for_read(GPCMD_PLAY_AUDIO_MSF),
- safe_for_read(GPCMD_PLAY_AUDIO_TI),
- safe_for_read(GPCMD_PAUSE_RESUME),
-
- /* CD/DVD data reading */
- safe_for_read(GPCMD_READ_CD),
- safe_for_read(GPCMD_READ_CD_MSF),
- safe_for_read(GPCMD_READ_DISC_INFO),
- safe_for_read(GPCMD_READ_CDVD_CAPACITY),
- safe_for_read(GPCMD_READ_DVD_STRUCTURE),
- safe_for_read(GPCMD_READ_HEADER),
- safe_for_read(GPCMD_READ_TRACK_RZONE_INFO),
- safe_for_read(GPCMD_READ_SUBCHANNEL),
- safe_for_read(GPCMD_READ_TOC_PMA_ATIP),
- safe_for_read(GPCMD_REPORT_KEY),
- safe_for_read(GPCMD_SCAN),
- safe_for_read(GPCMD_GET_CONFIGURATION),
- safe_for_read(GPCMD_READ_FORMAT_CAPACITIES),
- safe_for_read(GPCMD_GET_EVENT_STATUS_NOTIFICATION),
- safe_for_read(GPCMD_GET_PERFORMANCE),
- safe_for_read(GPCMD_SEEK),
- safe_for_read(GPCMD_STOP_PLAY_SCAN),
-
- /* Basic writing commands */
- safe_for_write(WRITE_6),
- safe_for_write(WRITE_10),
- safe_for_write(WRITE_VERIFY),
- safe_for_write(WRITE_12),
- safe_for_write(WRITE_VERIFY_12),
- safe_for_write(WRITE_16),
- safe_for_write(WRITE_LONG),
- safe_for_write(ERASE),
- safe_for_write(GPCMD_MODE_SELECT_10),
- safe_for_write(MODE_SELECT),
- safe_for_write(GPCMD_BLANK),
- safe_for_write(GPCMD_CLOSE_TRACK),
- safe_for_write(GPCMD_FLUSH_CACHE),
- safe_for_write(GPCMD_FORMAT_UNIT),
- safe_for_write(GPCMD_REPAIR_RZONE_TRACK),
- safe_for_write(GPCMD_RESERVE_RZONE_TRACK),
- safe_for_write(GPCMD_SEND_DVD_STRUCTURE),
- safe_for_write(GPCMD_SEND_EVENT),
- safe_for_write(GPCMD_SEND_KEY),
- safe_for_write(GPCMD_SEND_OPC),
- safe_for_write(GPCMD_SEND_CUE_SHEET),
- safe_for_write(GPCMD_SET_SPEED),
- safe_for_write(GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL),
- safe_for_write(GPCMD_LOAD_UNLOAD),
- safe_for_write(GPCMD_SET_STREAMING),
- };
- unsigned char type = cmd_type[cmd[0]];
-
- /* Anybody who can open the device can do a read-safe command */
- if (type & CMD_READ_SAFE)
- return 0;
-
- /* Write-safe commands just require a writable open.. */
- if (type & CMD_WRITE_SAFE) {
- if (file->f_mode & FMODE_WRITE)
- return 0;
- }
-
- /* And root can do any command.. */
- if (capable(CAP_SYS_RAWIO))
- return 0;
-
- /* Otherwise fail it with an "Operation not permitted" */
- return -EPERM;
-}
-
static int sg_io(struct file *file, request_queue_t *q,
struct gendisk *bd_disk, struct sg_io_hdr *hdr)
{
@@ -220,7 +121,7 @@ static int sg_io(struct file *file, requ
return -EINVAL;
if (copy_from_user(cmd, hdr->cmdp, hdr->cmd_len))
return -EFAULT;
- if (verify_command(file, cmd))
+ if (rcf_verify_command(file, cmd))
return -EPERM;
/*
@@ -370,7 +271,7 @@ static int sg_scsi_ioctl(struct file *fi
if (copy_from_user(buffer, sic->data + cmdlen, in_len))
goto error;
- err = verify_command(file, rq->cmd);
+ err = rcf_verify_command(file, rq->cmd);
if (err)
goto error;
diff -urpN linux-2.5-export/include/linux/blkdev.h pjones-2.5-export/include/linux/blkdev.h
--- linux-2.5-export/include/linux/blkdev.h 2004-09-15 12:40:23 -04:00
+++ pjones-2.5-export/include/linux/blkdev.h 2004-09-16 13:20:31 -04:00
@@ -513,6 +513,9 @@ struct sec_size {
unsigned block_size_bits;
};
+extern int rcf_verify_command(struct file *file, unsigned char *cmd);
+extern int blk_register_filter(struct gendisk *disk);
+extern void blk_unregister_filter(struct gendisk *disk);
extern int blk_register_queue(struct gendisk *disk);
extern void blk_unregister_queue(struct gendisk *disk);
extern void register_disk(struct gendisk *dev);
diff -urpN linux-2.5-export/include/linux/genhd.h pjones-2.5-export/include/linux/genhd.h
--- linux-2.5-export/include/linux/genhd.h 2004-09-15 12:39:54 -04:00
+++ pjones-2.5-export/include/linux/genhd.h 2004-09-16 13:20:27 -04:00
@@ -78,6 +78,16 @@ struct disk_stats {
unsigned io_ticks;
unsigned time_in_queue;
};
+
+#define RCF_MAX_NR_CMDS 256
+#define RCF_CMD_LONGS (RCF_MAX_NR_CMDS / (sizeof (unsigned long) * 8))
+
+struct rawio_cmd_filter {
+ unsigned long read_ok[RCF_CMD_LONGS];
+ unsigned long write_ok[RCF_CMD_LONGS];
+ int debug;
+ struct kobject kobj;
+};
struct gendisk {
int major; /* major number of driver */
@@ -88,6 +98,7 @@ struct gendisk {
struct hd_struct **part; /* [indexed by minor] */
struct block_device_operations *fops;
struct request_queue *queue;
+ struct rawio_cmd_filter filter;
void *private_data;
sector_t capacity;
--
Peter
"If the facts don't fit the theory, change the facts."
-- Einstein