9ae3a8
From e04d52892c7fcd0e2ec70f77f3e5b934d45918c8 Mon Sep 17 00:00:00 2001
9ae3a8
From: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
Date: Wed, 31 Jul 2013 09:56:17 +0200
9ae3a8
Subject: block: add block driver read only whitelist
9ae3a8
9ae3a8
Message-id: <6873f36f1d3c26ad7b84bf2150c0a98afd6c5e72.1375208619.git.mrezanin@redhat.com>
9ae3a8
Patchwork-id: 52827
9ae3a8
O-Subject: [RHEL7 qemu-kvm PATCH 4.5/5] block: add block driver read only whitelist
9ae3a8
Bugzilla: 836675
9ae3a8
RH-Acked-by: Fam Zheng <famz@redhat.com>
9ae3a8
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
9ae3a8
RH-Acked-by: Michal Novotny <minovotn@redhat.com>
9ae3a8
9ae3a8
From: Fam Zheng <famz@redhat.com>
9ae3a8
9ae3a8
This is missing patch required for "VMDK Backports and Spec Update" serie.
9ae3a8
9ae3a8
We may want to include a driver in the whitelist for read only tasks
9ae3a8
such as diagnosing or exporting guest data (with libguestfs as a good
9ae3a8
example). This patch introduces a readonly whitelist option, and for
9ae3a8
backward compatibility, the old configure option --block-drv-whitelist
9ae3a8
is now an alias to rw whitelist.
9ae3a8
9ae3a8
Drivers in readonly list is only permitted to open file readonly, and
9ae3a8
returns -ENOTSUP for RW opening.
9ae3a8
9ae3a8
E.g. To include vmdk readonly, and others read+write:
9ae3a8
    ./configure --target-list=x86_64-softmmu \
9ae3a8
                --block-drv-rw-whitelist=qcow2,raw,file,qed \
9ae3a8
                --block-drv-ro-whitelist=vmdk
9ae3a8
9ae3a8
Signed-off-by: Fam Zheng <famz@redhat.com>
9ae3a8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9ae3a8
(cherry picked from commit b64ec4e4ade581d662753cdeb0d7e0e27aafbf81)
9ae3a8
9ae3a8
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
9ae3a8
diff --git a/block.c b/block.c
9ae3a8
index 3f87489..65c0b60 100644
9ae3a8
--- a/block.c
9ae3a8
+++ b/block.c
9ae3a8
@@ -328,28 +328,40 @@ BlockDriver *bdrv_find_format(const char *format_name)
9ae3a8
     return NULL;
9ae3a8
 }
9ae3a8
 
9ae3a8
-static int bdrv_is_whitelisted(BlockDriver *drv)
9ae3a8
+static int bdrv_is_whitelisted(BlockDriver *drv, bool read_only)
9ae3a8
 {
9ae3a8
-    static const char *whitelist[] = {
9ae3a8
-        CONFIG_BDRV_WHITELIST
9ae3a8
+    static const char *whitelist_rw[] = {
9ae3a8
+        CONFIG_BDRV_RW_WHITELIST
9ae3a8
+    };
9ae3a8
+    static const char *whitelist_ro[] = {
9ae3a8
+        CONFIG_BDRV_RO_WHITELIST
9ae3a8
     };
9ae3a8
     const char **p;
9ae3a8
 
9ae3a8
-    if (!whitelist[0])
9ae3a8
+    if (!whitelist_rw[0] && !whitelist_ro[0]) {
9ae3a8
         return 1;               /* no whitelist, anything goes */
9ae3a8
+    }
9ae3a8
 
9ae3a8
-    for (p = whitelist; *p; p++) {
9ae3a8
+    for (p = whitelist_rw; *p; p++) {
9ae3a8
         if (!strcmp(drv->format_name, *p)) {
9ae3a8
             return 1;
9ae3a8
         }
9ae3a8
     }
9ae3a8
+    if (read_only) {
9ae3a8
+        for (p = whitelist_ro; *p; p++) {
9ae3a8
+            if (!strcmp(drv->format_name, *p)) {
9ae3a8
+                return 1;
9ae3a8
+            }
9ae3a8
+        }
9ae3a8
+    }
9ae3a8
     return 0;
9ae3a8
 }
9ae3a8
 
9ae3a8
-BlockDriver *bdrv_find_whitelisted_format(const char *format_name)
9ae3a8
+BlockDriver *bdrv_find_whitelisted_format(const char *format_name,
9ae3a8
+                                          bool read_only)
9ae3a8
 {
9ae3a8
     BlockDriver *drv = bdrv_find_format(format_name);
9ae3a8
-    return drv && bdrv_is_whitelisted(drv) ? drv : NULL;
9ae3a8
+    return drv && bdrv_is_whitelisted(drv, read_only) ? drv : NULL;
9ae3a8
 }
9ae3a8
 
9ae3a8
 typedef struct CreateCo {
9ae3a8
@@ -684,10 +696,6 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
9ae3a8
 
9ae3a8
     trace_bdrv_open_common(bs, filename ?: "", flags, drv->format_name);
9ae3a8
 
9ae3a8
-    if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv)) {
9ae3a8
-        return -ENOTSUP;
9ae3a8
-    }
9ae3a8
-
9ae3a8
     /* bdrv_open() with directly using a protocol as drv. This layer is already
9ae3a8
      * opened, so assign it to bs (while file becomes a closed BlockDriverState)
9ae3a8
      * and return immediately. */
9ae3a8
@@ -698,9 +706,15 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
9ae3a8
 
9ae3a8
     bs->open_flags = flags;
9ae3a8
     bs->buffer_alignment = 512;
9ae3a8
+    open_flags = bdrv_open_flags(bs, flags);
9ae3a8
+    bs->read_only = !(open_flags & BDRV_O_RDWR);
9ae3a8
+
9ae3a8
+    if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) {
9ae3a8
+        return -ENOTSUP;
9ae3a8
+    }
9ae3a8
 
9ae3a8
     assert(bs->copy_on_read == 0); /* bdrv_new() and bdrv_close() make it so */
9ae3a8
-    if ((flags & BDRV_O_RDWR) && (flags & BDRV_O_COPY_ON_READ)) {
9ae3a8
+    if (!bs->read_only && (flags & BDRV_O_COPY_ON_READ)) {
9ae3a8
         bdrv_enable_copy_on_read(bs);
9ae3a8
     }
9ae3a8
 
9ae3a8
@@ -714,9 +728,6 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
9ae3a8
     bs->opaque = g_malloc0(drv->instance_size);
9ae3a8
 
9ae3a8
     bs->enable_write_cache = !!(flags & BDRV_O_CACHE_WB);
9ae3a8
-    open_flags = bdrv_open_flags(bs, flags);
9ae3a8
-
9ae3a8
-    bs->read_only = !(open_flags & BDRV_O_RDWR);
9ae3a8
 
9ae3a8
     /* Open the image, either directly or using a protocol */
9ae3a8
     if (drv->bdrv_file_open) {
9ae3a8
@@ -801,7 +812,7 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename,
9ae3a8
     /* Find the right block driver */
9ae3a8
     drvname = qdict_get_try_str(options, "driver");
9ae3a8
     if (drvname) {
9ae3a8
-        drv = bdrv_find_whitelisted_format(drvname);
9ae3a8
+        drv = bdrv_find_whitelisted_format(drvname, !(flags & BDRV_O_RDWR));
9ae3a8
         qdict_del(options, "driver");
9ae3a8
     } else if (filename) {
9ae3a8
         drv = bdrv_find_protocol(filename);
9ae3a8
diff --git a/blockdev.c b/blockdev.c
9ae3a8
index 625d041..6500c47 100644
9ae3a8
--- a/blockdev.c
9ae3a8
+++ b/blockdev.c
9ae3a8
@@ -477,7 +477,7 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
9ae3a8
             error_printf("\n");
9ae3a8
             return NULL;
9ae3a8
         }
9ae3a8
-        drv = bdrv_find_whitelisted_format(buf);
9ae3a8
+        drv = bdrv_find_whitelisted_format(buf, ro);
9ae3a8
         if (!drv) {
9ae3a8
             error_report("'%s' invalid format", buf);
9ae3a8
             return NULL;
9ae3a8
@@ -1024,7 +1024,7 @@ void qmp_change_blockdev(const char *device, const char *filename,
9ae3a8
     }
9ae3a8
 
9ae3a8
     if (format) {
9ae3a8
-        drv = bdrv_find_whitelisted_format(format);
9ae3a8
+        drv = bdrv_find_whitelisted_format(format, bs->read_only);
9ae3a8
         if (!drv) {
9ae3a8
             error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
9ae3a8
             return;
9ae3a8
diff --git a/configure b/configure
9ae3a8
index eb74510..a71e8a1 100755
9ae3a8
--- a/configure
9ae3a8
+++ b/configure
9ae3a8
@@ -123,7 +123,8 @@ interp_prefix="/usr/gnemul/qemu-%M"
9ae3a8
 static="no"
9ae3a8
 cross_prefix=""
9ae3a8
 audio_drv_list=""
9ae3a8
-block_drv_whitelist=""
9ae3a8
+block_drv_rw_whitelist=""
9ae3a8
+block_drv_ro_whitelist=""
9ae3a8
 host_cc="cc"
9ae3a8
 libs_softmmu=""
9ae3a8
 libs_tools=""
9ae3a8
@@ -708,7 +709,9 @@ for opt do
9ae3a8
   ;;
9ae3a8
   --audio-drv-list=*) audio_drv_list="$optarg"
9ae3a8
   ;;
9ae3a8
-  --block-drv-whitelist=*) block_drv_whitelist=`echo "$optarg" | sed -e 's/,/ /g'`
9ae3a8
+  --block-drv-rw-whitelist=*|--block-drv-whitelist=*) block_drv_rw_whitelist=`echo "$optarg" | sed -e 's/,/ /g'`
9ae3a8
+  ;;
9ae3a8
+  --block-drv-ro-whitelist=*) block_drv_ro_whitelist=`echo "$optarg" | sed -e 's/,/ /g'`
9ae3a8
   ;;
9ae3a8
   --enable-debug-tcg) debug_tcg="yes"
9ae3a8
   ;;
9ae3a8
@@ -1105,7 +1108,12 @@ echo "  --disable-cocoa          disable Cocoa (Mac OS X only)"
9ae3a8
 echo "  --enable-cocoa           enable Cocoa (default on Mac OS X)"
9ae3a8
 echo "  --audio-drv-list=LIST    set audio drivers list:"
9ae3a8
 echo "                           Available drivers: $audio_possible_drivers"
9ae3a8
-echo "  --block-drv-whitelist=L  set block driver whitelist"
9ae3a8
+echo "  --block-drv-whitelist=L  Same as --block-drv-rw-whitelist=L"
9ae3a8
+echo "  --block-drv-rw-whitelist=L"
9ae3a8
+echo "                           set block driver read-write whitelist"
9ae3a8
+echo "                           (affects only QEMU, not qemu-img)"
9ae3a8
+echo "  --block-drv-ro-whitelist=L"
9ae3a8
+echo "                           set block driver read-only whitelist"
9ae3a8
 echo "                           (affects only QEMU, not qemu-img)"
9ae3a8
 echo "  --enable-mixemu          enable mixer emulation"
9ae3a8
 echo "  --disable-xen            disable xen backend driver support"
9ae3a8
@@ -3525,7 +3533,8 @@ echo "curses support    $curses"
9ae3a8
 echo "curl support      $curl"
9ae3a8
 echo "mingw32 support   $mingw32"
9ae3a8
 echo "Audio drivers     $audio_drv_list"
9ae3a8
-echo "Block whitelist   $block_drv_whitelist"
9ae3a8
+echo "Block whitelist (rw) $block_drv_rw_whitelist"
9ae3a8
+echo "Block whitelist (ro) $block_drv_ro_whitelist"
9ae3a8
 echo "Mixer emulation   $mixemu"
9ae3a8
 echo "VirtFS support    $virtfs"
9ae3a8
 echo "VNC support       $vnc"
9ae3a8
@@ -3704,7 +3713,8 @@ fi
9ae3a8
 if test "$audio_win_int" = "yes" ; then
9ae3a8
   echo "CONFIG_AUDIO_WIN_INT=y" >> $config_host_mak
9ae3a8
 fi
9ae3a8
-echo "CONFIG_BDRV_WHITELIST=$block_drv_whitelist" >> $config_host_mak
9ae3a8
+echo "CONFIG_BDRV_RW_WHITELIST=$block_drv_rw_whitelist" >> $config_host_mak
9ae3a8
+echo "CONFIG_BDRV_RO_WHITELIST=$block_drv_ro_whitelist" >> $config_host_mak
9ae3a8
 if test "$mixemu" = "yes" ; then
9ae3a8
   echo "CONFIG_MIXEMU=y" >> $config_host_mak
9ae3a8
 fi
9ae3a8
diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c
9ae3a8
index 0ac65d4..247f32f 100644
9ae3a8
--- a/hw/block/xen_disk.c
9ae3a8
+++ b/hw/block/xen_disk.c
9ae3a8
@@ -780,11 +780,13 @@ static int blk_connect(struct XenDevice *xendev)
9ae3a8
 {
9ae3a8
     struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
9ae3a8
     int pers, index, qflags;
9ae3a8
+    bool readonly = true;
9ae3a8
 
9ae3a8
     /* read-only ? */
9ae3a8
     qflags = BDRV_O_CACHE_WB | BDRV_O_NATIVE_AIO;
9ae3a8
     if (strcmp(blkdev->mode, "w") == 0) {
9ae3a8
         qflags |= BDRV_O_RDWR;
9ae3a8
+        readonly = false;
9ae3a8
     }
9ae3a8
 
9ae3a8
     /* init qemu block driver */
9ae3a8
@@ -795,8 +797,10 @@ static int blk_connect(struct XenDevice *xendev)
9ae3a8
         xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n");
9ae3a8
         blkdev->bs = bdrv_new(blkdev->dev);
9ae3a8
         if (blkdev->bs) {
9ae3a8
-            if (bdrv_open(blkdev->bs, blkdev->filename, NULL, qflags,
9ae3a8
-                        bdrv_find_whitelisted_format(blkdev->fileproto)) != 0) {
9ae3a8
+            BlockDriver *drv = bdrv_find_whitelisted_format(blkdev->fileproto,
9ae3a8
+                                                           readonly);
9ae3a8
+            if (bdrv_open(blkdev->bs,
9ae3a8
+                          blkdev->filename, NULL, qflags, drv) != 0) {
9ae3a8
                 bdrv_delete(blkdev->bs);
9ae3a8
                 blkdev->bs = NULL;
9ae3a8
             }
9ae3a8
diff --git a/include/block/block.h b/include/block/block.h
9ae3a8
index 1251c5c..5604418 100644
9ae3a8
--- a/include/block/block.h
9ae3a8
+++ b/include/block/block.h
9ae3a8
@@ -124,7 +124,8 @@ void bdrv_init(void);
9ae3a8
 void bdrv_init_with_whitelist(void);
9ae3a8
 BlockDriver *bdrv_find_protocol(const char *filename);
9ae3a8
 BlockDriver *bdrv_find_format(const char *format_name);
9ae3a8
-BlockDriver *bdrv_find_whitelisted_format(const char *format_name);
9ae3a8
+BlockDriver *bdrv_find_whitelisted_format(const char *format_name,
9ae3a8
+                                          bool readonly);
9ae3a8
 int bdrv_create(BlockDriver *drv, const char* filename,
9ae3a8
     QEMUOptionParameter *options);
9ae3a8
 int bdrv_create_file(const char* filename, QEMUOptionParameter *options);
9ae3a8
diff --git a/scripts/create_config b/scripts/create_config
9ae3a8
index c471e8c..258513a 100755
9ae3a8
--- a/scripts/create_config
9ae3a8
+++ b/scripts/create_config
9ae3a8
@@ -34,8 +34,15 @@ case $line in
9ae3a8
     done
9ae3a8
     echo ""
9ae3a8
     ;;
9ae3a8
- CONFIG_BDRV_WHITELIST=*)
9ae3a8
-    echo "#define CONFIG_BDRV_WHITELIST \\"
9ae3a8
+ CONFIG_BDRV_RW_WHITELIST=*)
9ae3a8
+    echo "#define CONFIG_BDRV_RW_WHITELIST\\"
9ae3a8
+    for drv in ${line#*=}; do
9ae3a8
+      echo "    \"${drv}\",\\"
9ae3a8
+    done
9ae3a8
+    echo "    NULL"
9ae3a8
+    ;;
9ae3a8
+ CONFIG_BDRV_RO_WHITELIST=*)
9ae3a8
+    echo "#define CONFIG_BDRV_RO_WHITELIST\\"
9ae3a8
     for drv in ${line#*=}; do
9ae3a8
       echo "    \"${drv}\",\\"
9ae3a8
     done