diff --git a/block.c b/block.c
index e27d528..8869583 100644
--- a/block.c
+++ b/block.c
@@ -3199,10 +3199,8 @@ static BlockDriverAIOCB *bdrv_aio_rw_vector(BlockDriverState *bs,
 
     if (is_write) {
         qemu_iovec_to_buffer(acb->qiov, acb->bounce);
-        acb->ret = bs->drv->bdrv_write(bs, sector_num, acb->bounce, nb_sectors);
-    } else {
-        acb->ret = bs->drv->bdrv_read(bs, sector_num, acb->bounce, nb_sectors);
     }
+    acb->ret = bs->drv->bdrv_rw(bs, sector_num, acb->bounce, nb_sectors, is_write);
 
     qemu_bh_schedule(acb->bh);
 
diff --git a/block/bochs.c b/block/bochs.c
index ab7944d..bdeb167 100644
--- a/block/bochs.c
+++ b/block/bochs.c
@@ -209,11 +209,14 @@ static int bochs_read(BlockDriverState *bs, int64_t sector_num,
     return 0;
 }
 
-static coroutine_fn int bochs_co_read(BlockDriverState *bs, int64_t sector_num,
-                                      uint8_t *buf, int nb_sectors)
+static coroutine_fn int bochs_co_rw(BlockDriverState *bs, int64_t sector_num,
+                                    uint8_t *buf, int nb_sectors, bool is_write)
 {
     int ret;
     BDRVBochsState *s = bs->opaque;
+    if (is_write) {
+        return -EROFS;
+    }
     qemu_co_mutex_lock(&s->lock);
     ret = bochs_read(bs, sector_num, buf, nb_sectors);
     qemu_co_mutex_unlock(&s->lock);
@@ -231,7 +234,7 @@ static BlockDriver bdrv_bochs = {
     .instance_size	= sizeof(BDRVBochsState),
     .bdrv_probe		= bochs_probe,
     .bdrv_open		= bochs_open,
-    .bdrv_read          = bochs_co_read,
+    .bdrv_rw            = bochs_co_rw,
     .bdrv_close		= bochs_close,
 };
 
diff --git a/block/cloop.c b/block/cloop.c
index 7570eb8..71e0007 100644
--- a/block/cloop.c
+++ b/block/cloop.c
@@ -156,11 +156,14 @@ static int cloop_read(BlockDriverState *bs, int64_t sector_num,
     return 0;
 }
 
-static coroutine_fn int cloop_co_read(BlockDriverState *bs, int64_t sector_num,
-                                      uint8_t *buf, int nb_sectors)
+static coroutine_fn int cloop_co_rw(BlockDriverState *bs, int64_t sector_num,
+                                    uint8_t *buf, int nb_sectors, bool is_write)
 {
     int ret;
     BDRVCloopState *s = bs->opaque;
+    if (is_write) {
+        return -EROFS;
+    }
     qemu_co_mutex_lock(&s->lock);
     ret = cloop_read(bs, sector_num, buf, nb_sectors);
     qemu_co_mutex_unlock(&s->lock);
@@ -183,7 +186,7 @@ static BlockDriver bdrv_cloop = {
     .instance_size  = sizeof(BDRVCloopState),
     .bdrv_probe     = cloop_probe,
     .bdrv_open      = cloop_open,
-    .bdrv_read      = cloop_co_read,
+    .bdrv_rw        = cloop_co_rw,
     .bdrv_close     = cloop_close,
 };
 
diff --git a/block/cow.c b/block/cow.c
index bb5927c..7cdfe87 100644
--- a/block/cow.c
+++ b/block/cow.c
@@ -214,17 +214,6 @@ static int coroutine_fn cow_read(BlockDriverState *bs, int64_t sector_num,
     return 0;
 }
 
-static coroutine_fn int cow_co_read(BlockDriverState *bs, int64_t sector_num,
-                                    uint8_t *buf, int nb_sectors)
-{
-    int ret;
-    BDRVCowState *s = bs->opaque;
-    qemu_co_mutex_lock(&s->lock);
-    ret = cow_read(bs, sector_num, buf, nb_sectors);
-    qemu_co_mutex_unlock(&s->lock);
-    return ret;
-}
-
 static int cow_write(BlockDriverState *bs, int64_t sector_num,
                      const uint8_t *buf, int nb_sectors)
 {
@@ -240,13 +229,14 @@ static int cow_write(BlockDriverState *bs, int64_t sector_num,
     return cow_update_bitmap(bs, sector_num, nb_sectors);
 }
 
-static coroutine_fn int cow_co_write(BlockDriverState *bs, int64_t sector_num,
-                                     const uint8_t *buf, int nb_sectors)
+static coroutine_fn int cow_co_rw(BlockDriverState *bs, int64_t sector_num,
+                                  uint8_t *buf, int nb_sectors, bool is_write)
 {
     int ret;
     BDRVCowState *s = bs->opaque;
     qemu_co_mutex_lock(&s->lock);
-    ret = cow_write(bs, sector_num, buf, nb_sectors);
+    ret = is_write ? cow_write(bs, sector_num, buf, nb_sectors) :
+                     cow_read(bs, sector_num, buf, nb_sectors);
     qemu_co_mutex_unlock(&s->lock);
     return ret;
 }
@@ -346,8 +336,7 @@ static BlockDriver bdrv_cow = {
     .bdrv_close     = cow_close,
     .bdrv_create    = cow_create,
 
-    .bdrv_read              = cow_co_read,
-    .bdrv_write             = cow_co_write,
+    .bdrv_rw                = cow_co_rw,
     .bdrv_co_flush_to_disk  = cow_co_flush,
     .bdrv_co_is_allocated   = cow_co_is_allocated,
 
diff --git a/block/dmg.c b/block/dmg.c
index 37902a4..df7c8e4 100644
--- a/block/dmg.c
+++ b/block/dmg.c
@@ -282,11 +282,14 @@ static int dmg_read(BlockDriverState *bs, int64_t sector_num,
     return 0;
 }
 
-static coroutine_fn int dmg_co_read(BlockDriverState *bs, int64_t sector_num,
-                                    uint8_t *buf, int nb_sectors)
+static coroutine_fn int dmg_co_rw(BlockDriverState *bs, int64_t sector_num,
+                                  uint8_t *buf, int nb_sectors, bool is_write)
 {
     int ret;
     BDRVDMGState *s = bs->opaque;
+    if (is_write) {
+        return EROFS;
+    }
     qemu_co_mutex_lock(&s->lock);
     ret = dmg_read(bs, sector_num, buf, nb_sectors);
     qemu_co_mutex_unlock(&s->lock);
@@ -313,7 +316,7 @@ static BlockDriver bdrv_dmg = {
     .instance_size	= sizeof(BDRVDMGState),
     .bdrv_probe		= dmg_probe,
     .bdrv_open		= dmg_open,
-    .bdrv_read          = dmg_co_read,
+    .bdrv_rw            = dmg_co_rw,
     .bdrv_close		= dmg_close,
 };
 
diff --git a/block/parallels.c b/block/parallels.c
index d30f0ec..0081780 100644
--- a/block/parallels.c
+++ b/block/parallels.c
@@ -136,11 +136,14 @@ static int parallels_read(BlockDriverState *bs, int64_t sector_num,
     return 0;
 }
 
-static coroutine_fn int parallels_co_read(BlockDriverState *bs, int64_t sector_num,
-                                          uint8_t *buf, int nb_sectors)
+static coroutine_fn int parallels_co_rw(BlockDriverState *bs, int64_t sector_num,
+                                        uint8_t *buf, int nb_sectors, bool is_write)
 {
     int ret;
     BDRVParallelsState *s = bs->opaque;
+    if (is_write) {
+        return -EROFS;
+    }
     qemu_co_mutex_lock(&s->lock);
     ret = parallels_read(bs, sector_num, buf, nb_sectors);
     qemu_co_mutex_unlock(&s->lock);
@@ -158,7 +161,7 @@ static BlockDriver bdrv_parallels = {
     .instance_size	= sizeof(BDRVParallelsState),
     .bdrv_probe		= parallels_probe,
     .bdrv_open		= parallels_open,
-    .bdrv_read          = parallels_co_read,
+    .bdrv_rw            = parallels_co_rw,
     .bdrv_close		= parallels_close,
 };
 
diff --git a/block/vmdk.c b/block/vmdk.c
index 5623ac1..1759a42 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -1046,17 +1046,6 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
     return 0;
 }
 
-static coroutine_fn int vmdk_co_read(BlockDriverState *bs, int64_t sector_num,
-                                     uint8_t *buf, int nb_sectors)
-{
-    int ret;
-    BDRVVmdkState *s = bs->opaque;
-    qemu_co_mutex_lock(&s->lock);
-    ret = vmdk_read(bs, sector_num, buf, nb_sectors);
-    qemu_co_mutex_unlock(&s->lock);
-    return ret;
-}
-
 static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
                      const uint8_t *buf, int nb_sectors)
 {
@@ -1141,13 +1130,14 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
     return 0;
 }
 
-static coroutine_fn int vmdk_co_write(BlockDriverState *bs, int64_t sector_num,
-                                      const uint8_t *buf, int nb_sectors)
+static coroutine_fn int vmdk_co_rw(BlockDriverState *bs, int64_t sector_num,
+                                   uint8_t *buf, int nb_sectors, bool is_write)
 {
     int ret;
     BDRVVmdkState *s = bs->opaque;
     qemu_co_mutex_lock(&s->lock);
-    ret = vmdk_write(bs, sector_num, buf, nb_sectors);
+    ret = is_write ? vmdk_write(bs, sector_num, buf, nb_sectors)
+                   : vmdk_read(bs, sector_num, buf, nb_sectors);
     qemu_co_mutex_unlock(&s->lock);
     return ret;
 }
@@ -1593,8 +1583,7 @@ static BlockDriver bdrv_vmdk = {
     .instance_size  = sizeof(BDRVVmdkState),
     .bdrv_probe     = vmdk_probe,
     .bdrv_open      = vmdk_open,
-    .bdrv_read      = vmdk_co_read,
-    .bdrv_write     = vmdk_co_write,
+    .bdrv_rw        = vmdk_co_rw,
     .bdrv_close     = vmdk_close,
     .bdrv_create    = vmdk_create,
     .bdrv_co_flush_to_disk  = vmdk_co_flush,
diff --git a/block/vpc.c b/block/vpc.c
index 6b4816f..837439a 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -445,17 +445,6 @@ static int vpc_read(BlockDriverState *bs, int64_t sector_num,
     return 0;
 }
 
-static coroutine_fn int vpc_co_read(BlockDriverState *bs, int64_t sector_num,
-                                    uint8_t *buf, int nb_sectors)
-{
-    int ret;
-    BDRVVPCState *s = bs->opaque;
-    qemu_co_mutex_lock(&s->lock);
-    ret = vpc_read(bs, sector_num, buf, nb_sectors);
-    qemu_co_mutex_unlock(&s->lock);
-    return ret;
-}
-
 static int vpc_write(BlockDriverState *bs, int64_t sector_num,
     const uint8_t *buf, int nb_sectors)
 {
@@ -496,13 +485,14 @@ static int vpc_write(BlockDriverState *bs, int64_t sector_num,
     return 0;
 }
 
-static coroutine_fn int vpc_co_write(BlockDriverState *bs, int64_t sector_num,
-                                     const uint8_t *buf, int nb_sectors)
+static coroutine_fn int vpc_co_rw(BlockDriverState *bs, int64_t sector_num,
+                                  uint8_t *buf, int nb_sectors, bool is_write)
 {
     int ret;
     BDRVVPCState *s = bs->opaque;
     qemu_co_mutex_lock(&s->lock);
-    ret = vpc_write(bs, sector_num, buf, nb_sectors);
+    ret = is_write ? vpc_write(bs, sector_num, buf, nb_sectors)
+                   : vpc_read(bs, sector_num, buf, nb_sectors);
     qemu_co_mutex_unlock(&s->lock);
     return ret;
 }
@@ -787,8 +777,7 @@ static BlockDriver bdrv_vpc = {
     .bdrv_close     = vpc_close,
     .bdrv_create    = vpc_create,
 
-    .bdrv_read              = vpc_co_read,
-    .bdrv_write             = vpc_co_write,
+    .bdrv_rw                = vpc_co_rw,
     .bdrv_co_flush_to_disk  = vpc_co_flush,
 
     .create_options = vpc_create_options,
diff --git a/block/vvfat.c b/block/vvfat.c
index 9ef21dd..f582250 100644
--- a/block/vvfat.c
+++ b/block/vvfat.c
@@ -1299,17 +1299,6 @@ DLOG(fprintf(stderr, "sector %d not allocated\n", (int)sector_num));
     return 0;
 }
 
-static coroutine_fn int vvfat_co_read(BlockDriverState *bs, int64_t sector_num,
-                                      uint8_t *buf, int nb_sectors)
-{
-    int ret;
-    BDRVVVFATState *s = bs->opaque;
-    qemu_co_mutex_lock(&s->lock);
-    ret = vvfat_read(bs, sector_num, buf, nb_sectors);
-    qemu_co_mutex_unlock(&s->lock);
-    return ret;
-}
-
 /* LATER TODO: statify all functions */
 
 /*
@@ -2750,13 +2739,14 @@ DLOG(checkpoint());
     return 0;
 }
 
-static coroutine_fn int vvfat_co_write(BlockDriverState *bs, int64_t sector_num,
-                                       const uint8_t *buf, int nb_sectors)
+static coroutine_fn int vvfat_co_rw(BlockDriverState *bs, int64_t sector_num,
+                                    uint8_t *buf, int nb_sectors, bool is_write)
 {
     int ret;
     BDRVVVFATState *s = bs->opaque;
     qemu_co_mutex_lock(&s->lock);
-    ret = vvfat_write(bs, sector_num, buf, nb_sectors);
+    ret = is_write ? vvfat_write(bs, sector_num, buf, nb_sectors)
+                   : vvfat_read(bs, sector_num, buf, nb_sectors);
     qemu_co_mutex_unlock(&s->lock);
     return ret;
 }
@@ -2773,9 +2763,12 @@ static int coroutine_fn vvfat_co_is_allocated(BlockDriverState *bs,
     return 1;
 }
 
-static int write_target_commit(BlockDriverState *bs, int64_t sector_num,
-	const uint8_t* buffer, int nb_sectors) {
+static int write_target_commit_rw(BlockDriverState *bs, int64_t sector_num,
+				  uint8_t* buffer, int nb_sectors, bool is_write) {
     BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque);
+    if (!is_write) {
+       return -ENOSYS;
+    }
     return try_commit(s);
 }
 
@@ -2787,7 +2780,7 @@ static void write_target_close(BlockDriverState *bs) {
 
 static BlockDriver vvfat_write_target = {
     .format_name        = "vvfat_write_target",
-    .bdrv_write         = write_target_commit,
+    .bdrv_rw            = write_target_commit_rw,
     .bdrv_close         = write_target_close,
 };
 
@@ -2855,8 +2848,7 @@ static BlockDriver bdrv_vvfat = {
     .format_name	= "vvfat",
     .instance_size	= sizeof(BDRVVVFATState),
     .bdrv_file_open	= vvfat_open,
-    .bdrv_read          = vvfat_co_read,
-    .bdrv_write         = vvfat_co_write,
+    .bdrv_rw            = vvfat_co_rw,
     .bdrv_close		= vvfat_close,
     .bdrv_co_is_allocated = vvfat_co_is_allocated,
     .protocol_name	= "fat",
diff --git a/block_int.h b/block_int.h
index 04f4b83..f3930df 100644
--- a/block_int.h
+++ b/block_int.h
@@ -106,10 +106,8 @@ struct BlockDriver {
     int (*bdrv_probe_device)(const char *filename);
     int (*bdrv_open)(BlockDriverState *bs, int flags);
     int (*bdrv_file_open)(BlockDriverState *bs, const char *filename, int flags);
-    int (*bdrv_read)(BlockDriverState *bs, int64_t sector_num,
-                     uint8_t *buf, int nb_sectors);
-    int (*bdrv_write)(BlockDriverState *bs, int64_t sector_num,
-                      const uint8_t *buf, int nb_sectors);
+    int (*bdrv_rw)(BlockDriverState *bs, int64_t sector_num,
+                   uint8_t *buf, int nb_sectors, bool is_write);
     void (*bdrv_close)(BlockDriverState *bs);
     int (*bdrv_create)(const char *filename, QEMUOptionParameter *options);
     int (*bdrv_set_key)(BlockDriverState *bs, const char *key);
