qemu-block
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-block] [PATCH 06/10] qemu-img: Prepare for locked images


From: Kevin Wolf
Subject: [Qemu-block] [PATCH 06/10] qemu-img: Prepare for locked images
Date: Tue, 22 Dec 2015 17:46:22 +0100

This patch extends qemu-img for working with locked images. It prints a
helpful error message when trying to access a locked image read-write,
and adds a 'qemu-img force-unlock' command as well as a 'qemu-img check
-r all --force' option in order to override a lock left behind after a
qemu crash.

Signed-off-by: Kevin Wolf <address@hidden>
---
 include/block/block.h |  1 +
 include/qapi/error.h  |  1 +
 qapi/common.json      |  3 +-
 qemu-img-cmds.hx      | 10 ++++--
 qemu-img.c            | 96 +++++++++++++++++++++++++++++++++++++++++++--------
 qemu-img.texi         | 20 ++++++++++-
 6 files changed, 113 insertions(+), 18 deletions(-)

diff --git a/include/block/block.h b/include/block/block.h
index 0d00ac1..1ae655c 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -101,6 +101,7 @@ typedef struct HDGeometry {
 #define BDRV_OPT_CACHE_DIRECT   "cache.direct"
 #define BDRV_OPT_CACHE_NO_FLUSH "cache.no-flush"
 
+#define BDRV_OPT_OVERRIDE_LOCK  "override-lock"
 
 #define BDRV_SECTOR_BITS   9
 #define BDRV_SECTOR_SIZE   (1ULL << BDRV_SECTOR_BITS)
diff --git a/include/qapi/error.h b/include/qapi/error.h
index 6285cf5..53591bc 100644
--- a/include/qapi/error.h
+++ b/include/qapi/error.h
@@ -102,6 +102,7 @@ typedef enum ErrorClass {
     ERROR_CLASS_DEVICE_NOT_ACTIVE = QAPI_ERROR_CLASS_DEVICENOTACTIVE,
     ERROR_CLASS_DEVICE_NOT_FOUND = QAPI_ERROR_CLASS_DEVICENOTFOUND,
     ERROR_CLASS_KVM_MISSING_CAP = QAPI_ERROR_CLASS_KVMMISSINGCAP,
+    ERROR_CLASS_IMAGE_FILE_LOCKED = QAPI_ERROR_CLASS_IMAGEFILELOCKED,
 } ErrorClass;
 
 /*
diff --git a/qapi/common.json b/qapi/common.json
index 9353a7b..1bf6e46 100644
--- a/qapi/common.json
+++ b/qapi/common.json
@@ -27,7 +27,8 @@
 { 'enum': 'QapiErrorClass',
   # Keep this in sync with ErrorClass in error.h
   'data': [ 'GenericError', 'CommandNotFound', 'DeviceEncrypted',
-            'DeviceNotActive', 'DeviceNotFound', 'KVMMissingCap' ] }
+            'DeviceNotActive', 'DeviceNotFound', 'KVMMissingCap',
+            'ImageFileLocked' ] }
 
 ##
 # @VersionTriple
diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
index 9567774..dd4aebc 100644
--- a/qemu-img-cmds.hx
+++ b/qemu-img-cmds.hx
@@ -10,9 +10,9 @@ STEXI
 ETEXI
 
 DEF("check", img_check,
-    "check [-q] [-f fmt] [--output=ofmt] [-r [leaks | all]] [-T src_cache] 
filename")
+    "check [-q] [-f fmt] [--force] [--output=ofmt] [-r [leaks | all]] [-T 
src_cache] filename")
 STEXI
address@hidden check [-q] [-f @var{fmt}] address@hidden [-r [leaks | all]] [-T 
@var{src_cache}] @var{filename}
address@hidden check [-q] [-f @var{fmt}] [--force] address@hidden [-r [leaks | 
all]] [-T @var{src_cache}] @var{filename}
 ETEXI
 
 DEF("create", img_create,
@@ -39,6 +39,12 @@ STEXI
 @item convert [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T 
@var{src_cache}] [-O @var{output_fmt}] [-o @var{options}] [-s 
@var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] 
@var{filename} address@hidden [...]] @var{output_filename}
 ETEXI
 
+DEF("force-unlock", img_force_unlock,
+    "force_unlock [-f fmt] filename")
+STEXI
address@hidden force-unlock [-f @var{fmt}] @var{filename}
+ETEXI
+
 DEF("info", img_info,
     "info [-f fmt] [--output=ofmt] [--backing-chain] filename")
 STEXI
diff --git a/qemu-img.c b/qemu-img.c
index 3d48b4f..4fe15cd 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -47,6 +47,7 @@ typedef struct img_cmd_t {
 enum {
     OPTION_OUTPUT = 256,
     OPTION_BACKING_CHAIN = 257,
+    OPTION_FORCE = 258,
 };
 
 typedef enum OutputFormat {
@@ -198,7 +199,7 @@ static int print_block_option_help(const char *filename, 
const char *fmt)
 
 static BlockBackend *img_open(const char *id, const char *filename,
                               const char *fmt, int flags,
-                              bool require_io, bool quiet)
+                              bool require_io, bool quiet, bool force)
 {
     BlockBackend *blk;
     BlockDriverState *bs;
@@ -206,12 +207,34 @@ static BlockBackend *img_open(const char *id, const char 
*filename,
     Error *local_err = NULL;
     QDict *options = NULL;
 
+    options = qdict_new();
     if (fmt) {
-        options = qdict_new();
         qdict_put(options, "driver", qstring_from_str(fmt));
     }
+    QINCREF(options);
 
     blk = blk_new_open(id, filename, NULL, options, flags, &local_err);
+    if (!blk && error_get_class(local_err) == ERROR_CLASS_IMAGE_FILE_LOCKED) {
+        if (force) {
+            qdict_put(options, BDRV_OPT_OVERRIDE_LOCK, qstring_from_str("on"));
+            blk = blk_new_open(id, filename, NULL, options, flags, NULL);
+            if (blk) {
+                error_free(local_err);
+            }
+        } else {
+            error_report("The image file '%s' is locked and cannot be "
+                         "opened for write access as this may cause image "
+                         "corruption.", filename);
+            error_report("If it is locked in error (e.g. because "
+                         "of an unclean shutdown) and you are sure that no "
+                         "other processes are working on the image file, you "
+                         "can use 'qemu-img force-unlock' or the --force flag "
+                         "for 'qemu-img check' in order to override this "
+                         "check.");
+            error_free(local_err);
+            goto fail;
+        }
+    }
     if (!blk) {
         error_report("Could not open '%s': %s", filename,
                      error_get_pretty(local_err));
@@ -234,6 +257,7 @@ static BlockBackend *img_open(const char *id, const char 
*filename,
     return blk;
 fail:
     blk_unref(blk);
+    QDECREF(options);
     return NULL;
 }
 
@@ -492,6 +516,7 @@ static int img_check(int argc, char **argv)
     int flags = BDRV_O_FLAGS | BDRV_O_CHECK;
     ImageCheck *check;
     bool quiet = false;
+    bool force = false;
 
     fmt = NULL;
     output = NULL;
@@ -500,6 +525,7 @@ static int img_check(int argc, char **argv)
         int option_index = 0;
         static const struct option long_options[] = {
             {"help", no_argument, 0, 'h'},
+            {"force", no_argument, 0, OPTION_FORCE},
             {"format", required_argument, 0, 'f'},
             {"repair", required_argument, 0, 'r'},
             {"output", required_argument, 0, OPTION_OUTPUT},
@@ -515,6 +541,9 @@ static int img_check(int argc, char **argv)
         case 'h':
             help();
             break;
+        case OPTION_FORCE:
+            force = true;
+            break;
         case 'f':
             fmt = optarg;
             break;
@@ -561,7 +590,7 @@ static int img_check(int argc, char **argv)
         return 1;
     }
 
-    blk = img_open("image", filename, fmt, flags, true, quiet);
+    blk = img_open("image", filename, fmt, flags, true, quiet, force);
     if (!blk) {
         return 1;
     }
@@ -633,6 +662,44 @@ fail:
     return ret;
 }
 
+static int img_force_unlock(int argc, char **argv)
+{
+    BlockBackend *blk;
+    const char *format = NULL;
+    const char *filename;
+    char c;
+
+    for (;;) {
+        c = getopt(argc, argv, "hf:");
+        if (c == -1) {
+            break;
+        }
+        switch (c) {
+        case '?':
+        case 'h':
+            help();
+            break;
+        case 'f':
+            format = optarg;
+            break;
+        }
+    }
+
+    if (optind != argc - 1) {
+        error_exit("Expecting one image file name");
+    }
+    filename = argv[optind];
+
+    /* Just force-opening and closing the image is enough to unlock it */
+    blk = img_open("image", filename, format, BDRV_O_FLAGS | BDRV_O_RDWR,
+                   false, false, true);
+    if (blk) {
+        blk_unref(blk);
+    }
+
+    return 0;
+}
+
 typedef struct CommonBlockJobCBInfo {
     BlockDriverState *bs;
     Error **errp;
@@ -727,7 +794,7 @@ static int img_commit(int argc, char **argv)
         return 1;
     }
 
-    blk = img_open("image", filename, fmt, flags, true, quiet);
+    blk = img_open("image", filename, fmt, flags, true, quiet, false);
     if (!blk) {
         return 1;
     }
@@ -1032,14 +1099,14 @@ static int img_compare(int argc, char **argv)
         goto out3;
     }
 
-    blk1 = img_open("image_1", filename1, fmt1, flags, true, quiet);
+    blk1 = img_open("image_1", filename1, fmt1, flags, true, quiet, false);
     if (!blk1) {
         ret = 2;
         goto out3;
     }
     bs1 = blk_bs(blk1);
 
-    blk2 = img_open("image_2", filename2, fmt2, flags, true, quiet);
+    blk2 = img_open("image_2", filename2, fmt2, flags, true, quiet, false);
     if (!blk2) {
         ret = 2;
         goto out2;
@@ -1679,7 +1746,7 @@ static int img_convert(int argc, char **argv)
         char *id = bs_n > 1 ? g_strdup_printf("source_%d", bs_i)
                             : g_strdup("source");
         blk[bs_i] = img_open(id, argv[optind + bs_i], fmt, src_flags,
-                             true, quiet);
+                             true, quiet, false);
         g_free(id);
         if (!blk[bs_i]) {
             ret = -1;
@@ -1823,7 +1890,8 @@ static int img_convert(int argc, char **argv)
         goto out;
     }
 
-    out_blk = img_open("target", out_filename, out_fmt, flags, true, quiet);
+    out_blk = img_open("target", out_filename, out_fmt, flags, true, quiet,
+                       false);
     if (!out_blk) {
         ret = -1;
         goto out;
@@ -2015,7 +2083,7 @@ static ImageInfoList *collect_image_info_list(const char 
*filename,
         g_hash_table_insert(filenames, (gpointer)filename, NULL);
 
         blk = img_open("image", filename, fmt,
-                       BDRV_O_FLAGS | BDRV_O_NO_BACKING, false, false);
+                       BDRV_O_FLAGS | BDRV_O_NO_BACKING, false, false, false);
         if (!blk) {
             goto err;
         }
@@ -2279,7 +2347,7 @@ static int img_map(int argc, char **argv)
         return 1;
     }
 
-    blk = img_open("image", filename, fmt, BDRV_O_FLAGS, true, false);
+    blk = img_open("image", filename, fmt, BDRV_O_FLAGS, true, false, false);
     if (!blk) {
         return 1;
     }
@@ -2401,7 +2469,7 @@ static int img_snapshot(int argc, char **argv)
     filename = argv[optind++];
 
     /* Open the image */
-    blk = img_open("image", filename, NULL, bdrv_oflags, true, quiet);
+    blk = img_open("image", filename, NULL, bdrv_oflags, true, quiet, false);
     if (!blk) {
         return 1;
     }
@@ -2545,7 +2613,7 @@ static int img_rebase(int argc, char **argv)
      * Ignore the old backing file for unsafe rebase in case we want to correct
      * the reference to a renamed or moved backing file.
      */
-    blk = img_open("image", filename, fmt, flags, true, quiet);
+    blk = img_open("image", filename, fmt, flags, true, quiet, false);
     if (!blk) {
         ret = -1;
         goto out;
@@ -2858,7 +2926,7 @@ static int img_resize(int argc, char **argv)
     qemu_opts_del(param);
 
     blk = img_open("image", filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR,
-                   true, quiet);
+                   true, quiet, false);
     if (!blk) {
         ret = -1;
         goto out;
@@ -2989,7 +3057,7 @@ static int img_amend(int argc, char **argv)
         goto out;
     }
 
-    blk = img_open("image", filename, fmt, flags, true, quiet);
+    blk = img_open("image", filename, fmt, flags, true, quiet, false);
     if (!blk) {
         ret = -1;
         goto out;
diff --git a/qemu-img.texi b/qemu-img.texi
index 55c6be3..70efac2 100644
--- a/qemu-img.texi
+++ b/qemu-img.texi
@@ -117,7 +117,7 @@ Skip the creation of the target volume
 Command description:
 
 @table @option
address@hidden check [-f @var{fmt}] address@hidden [-r [leaks | all]] [-T 
@var{src_cache}] @var{filename}
address@hidden check [-q] [-f @var{fmt}] [--force] address@hidden [-r [leaks | 
all]] [-T @var{src_cache}] @var{filename}
 
 Perform a consistency check on the disk image @var{filename}. The command can
 output in the format @var{ofmt} which is either @code{human} or @code{json}.
@@ -153,6 +153,12 @@ If @code{-r} is specified, exit codes representing the 
image state refer to the
 state after (the attempt at) repairing it. That is, a successful @code{-r all}
 will yield the exit code 0, independently of the image state before.
 
+The @code{-r} option requires read-write access to the image, which is
+prohibited if a qcow2 file is still marked as in use. If you know for sure that
+the information is outdated and the image is in fact not in use by another
+process any more (e.g. because the QEMU process crashed), specifying
address@hidden overrides this check.
+
 @item create [-f @var{fmt}] [-o @var{options}] @var{filename} address@hidden
 
 Create the new disk image @var{filename} of size @var{size} and format
@@ -261,6 +267,18 @@ skipped. This is useful for formats such as @code{rbd} if 
the target
 volume has already been created with site specific options that cannot
 be supplied through qemu-img.
 
address@hidden force-unlock [-f @var{fmt}] @var{filename}
+
+Read-write disk images can generally be safely opened only from a single
+process at the same time. In order to protect against corruption from
+neglecting to follow this rule, qcow2 images are automatically flagged as
+in use when they are opened and the flag is removed again on a clean
+shutdown.
+
+However, in cases of an unclean shutdown, the image might be still marked as in
+use so that any further read-write access is prohibited. You can use the
address@hidden command to manually remove the in-use flag then.
+
 @item info [-f @var{fmt}] address@hidden [--backing-chain] @var{filename}
 
 Give information about the disk image @var{filename}. Use it in
-- 
1.8.3.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]