Regular block device writes go through blkdev_write_iter(), which does
bdev_read_only(), while zeroout/discard/etc requests are never checked,
both userspace- and kernel-triggered.  Add a generic catch-all check to
generic_make_request_checks() to actually enforce ioctl(BLKROSET) and
set_disk_ro(), which is used by quite a few drivers for things like
snapshots, read-only backing files/images, etc.

Signed-off-by: Ilya Dryomov <idryo...@gmail.com>
---
 block/blk-core.c | 23 ++++++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/block/blk-core.c b/block/blk-core.c
index b8d1aa2d1008..139ff47caf4a 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -2022,6 +2022,20 @@ static inline int bio_check_eod(struct bio *bio, 
unsigned int nr_sectors)
        return 0;
 }
 
+static inline bool bio_check_ro(struct bio *bio)
+{
+       struct hd_struct *p;
+       int ret = false;
+
+       rcu_read_lock();
+       p = __disk_get_part(bio->bi_disk, bio->bi_partno);
+       if (!p || (p->policy && op_is_write(bio_op(bio))))
+               ret = true;
+       rcu_read_unlock();
+
+       return ret;
+}
+
 static noinline_for_stack bool
 generic_make_request_checks(struct bio *bio)
 {
@@ -2044,11 +2058,18 @@ generic_make_request_checks(struct bio *bio)
                goto end_io;
        }
 
+       if (bio_check_ro(bio)) {
+               printk(KERN_ERR
+                      "generic_make_request: Trying to write "
+                       "to read-only block-device %s (partno %d)\n",
+                       bio_devname(bio, b), bio->bi_partno);
+               goto end_io;
+       }
+
        /*
         * For a REQ_NOWAIT based request, return -EOPNOTSUPP
         * if queue is not a request based queue.
         */
-
        if ((bio->bi_opf & REQ_NOWAIT) && !queue_is_rq_based(q))
                goto not_supported;
 
-- 
2.4.3

Reply via email to