qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Kevin Wolf <kwolf@redhat.com>
To: qemu-block@nongnu.org
Cc: kwolf@redhat.com, qemu-devel@nongnu.org, mreitz@redhat.com
Subject: [Qemu-devel] [PATCH 06/10] qemu-img: Prepare for locked images
Date: Tue, 22 Dec 2015 17:46:22 +0100	[thread overview]
Message-ID: <1450802786-20893-7-git-send-email-kwolf@redhat.com> (raw)
In-Reply-To: <1450802786-20893-1-git-send-email-kwolf@redhat.com>

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 <kwolf@redhat.com>
---
 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
-@item check [-q] [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] [-T @var{src_cache}] @var{filename}
+@item check [-q] [-f @var{fmt}] [--force] [--output=@var{ofmt}] [-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} [@var{filename2} [...]] @var{output_filename}
 ETEXI
 
+DEF("force-unlock", img_force_unlock,
+    "force_unlock [-f fmt] filename")
+STEXI
+@item 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
-@item check [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] [-T @var{src_cache}] @var{filename}
+@item check [-q] [-f @var{fmt}] [--force] [--output=@var{ofmt}] [-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
+@code{--force} overrides this check.
+
 @item create [-f @var{fmt}] [-o @var{options}] @var{filename} [@var{size}]
 
 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.
 
+@item 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
+@code{force-unlock} command to manually remove the in-use flag then.
+
 @item info [-f @var{fmt}] [--output=@var{ofmt}] [--backing-chain] @var{filename}
 
 Give information about the disk image @var{filename}. Use it in
-- 
1.8.3.1

  parent reply	other threads:[~2015-12-22 16:46 UTC|newest]

Thread overview: 99+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-12-22 16:46 [Qemu-devel] [PATCH 00/10] qcow2: Implement image locking Kevin Wolf
2015-12-22 16:46 ` [Qemu-devel] [PATCH 01/10] qcow2: Write feature table only for v3 images Kevin Wolf
2015-12-22 20:20   ` Eric Blake
2016-01-11 15:20     ` Kevin Wolf
2015-12-22 16:46 ` [Qemu-devel] [PATCH 02/10] qcow2: Write full header on image creation Kevin Wolf
2015-12-22 20:25   ` Eric Blake
2015-12-22 16:46 ` [Qemu-devel] [PATCH 03/10] block: Assert no write requests under BDRV_O_INCOMING Kevin Wolf
2015-12-22 20:27   ` Eric Blake
2015-12-22 16:46 ` [Qemu-devel] [PATCH 04/10] block: Fix error path in bdrv_invalidate_cache() Kevin Wolf
2015-12-22 20:31   ` Eric Blake
2015-12-22 16:46 ` [Qemu-devel] [PATCH 05/10] block: Inactivate BDS when migration completes Kevin Wolf
2015-12-22 20:43   ` Eric Blake
2016-01-05 20:21     ` [Qemu-devel] [Qemu-block] " John Snow
2016-01-13 14:25       ` Kevin Wolf
2016-01-13 16:35         ` Eric Blake
2015-12-22 16:46 ` Kevin Wolf [this message]
2015-12-22 16:57   ` [Qemu-devel] [PATCH 06/10] qemu-img: Prepare for locked images Daniel P. Berrange
2015-12-22 17:00     ` Kevin Wolf
2015-12-22 21:06   ` Eric Blake
2016-01-11 15:49     ` Markus Armbruster
2016-01-11 16:05       ` Kevin Wolf
2016-01-12 15:20         ` Markus Armbruster
2016-01-12 17:36           ` Kevin Wolf
2016-01-13  8:44             ` Markus Armbruster
2016-01-13 14:19               ` Kevin Wolf
2016-01-14 13:07                 ` Markus Armbruster
2016-01-14 14:19                   ` Kevin Wolf
2016-01-11 16:22     ` Kevin Wolf
2015-12-22 21:41   ` Eric Blake
2015-12-22 16:46 ` [Qemu-devel] [PATCH 07/10] qcow2: Implement .bdrv_inactivate Kevin Wolf
2015-12-22 21:17   ` Eric Blake
2016-01-11 15:34     ` Kevin Wolf
2015-12-22 16:46 ` [Qemu-devel] [PATCH 08/10] qcow2: Fix BDRV_O_INCOMING handling in qcow2_invalidate_cache() Kevin Wolf
2015-12-22 21:22   ` Eric Blake
2015-12-22 16:46 ` [Qemu-devel] [PATCH 09/10] qcow2: Make image inaccessible after failed qcow2_invalidate_cache() Kevin Wolf
2015-12-22 21:24   ` Eric Blake
2015-12-22 16:46 ` [Qemu-devel] [PATCH 10/10] qcow2: Add image locking Kevin Wolf
2015-12-22 22:04   ` Eric Blake
2015-12-23  3:14 ` [Qemu-devel] [PATCH 00/10] qcow2: Implement " Fam Zheng
2015-12-23  7:35   ` [Qemu-devel] [Qemu-block] " Denis V. Lunev
2015-12-23  7:46     ` [Qemu-devel] [PATCH RFC 0/5] generic image locking and crash recovery Denis V. Lunev
2015-12-23  7:46       ` [Qemu-devel] [PATCH 1/5] block: added lock image option and callback Denis V. Lunev
2015-12-23 23:48         ` Eric Blake
2016-01-11 17:31         ` Kevin Wolf
2016-01-11 17:58           ` Daniel P. Berrange
2016-01-11 18:35             ` Kevin Wolf
2016-01-13  8:52               ` Markus Armbruster
2016-01-13  9:12                 ` Denis V. Lunev
2016-01-13  9:50                   ` Daniel P. Berrange
2016-01-13  9:51               ` Daniel P. Berrange
2016-01-12  5:38           ` Denis V. Lunev
2016-01-12 10:10             ` Kevin Wolf
2016-01-12 11:33               ` Fam Zheng
2016-01-12 12:24                 ` Denis V. Lunev
2016-01-12 12:28                 ` Kevin Wolf
2016-01-12 13:17                   ` Fam Zheng
2016-01-12 13:24                     ` Daniel P. Berrange
2016-01-13  0:08                       ` Fam Zheng
2016-01-12 15:59                 ` Denis V. Lunev
2016-01-13  0:10                   ` Fam Zheng
2016-01-13 16:44                     ` Eric Blake
2016-01-14  7:23                       ` Denis V. Lunev
2015-12-23  7:46       ` [Qemu-devel] [PATCH 2/5] block: implemented bdrv_lock_image for raw file Denis V. Lunev
2015-12-23 12:40         ` Daniel P. Berrange
2015-12-23  7:46       ` [Qemu-devel] [PATCH 3/5] block: added check image option and callback bdrv_is_opened_unclean Denis V. Lunev
2015-12-23  9:09         ` Fam Zheng
2015-12-23  9:14           ` Denis V. Lunev
2015-12-23  7:46       ` [Qemu-devel] [PATCH 4/5] qcow2: implemented bdrv_is_opened_unclean Denis V. Lunev
2016-01-11 17:37         ` Kevin Wolf
2015-12-23  7:46       ` [Qemu-devel] [PATCH 5/5] block/paralels: added paralles implementation for bdrv_is_opened_unclean Denis V. Lunev
2015-12-23  8:09       ` [Qemu-devel] [PATCH RFC 0/5] generic image locking and crash recovery Fam Zheng
2015-12-23  8:36         ` Denis V. Lunev
2015-12-23 10:47   ` [Qemu-devel] [PATCH 00/10] qcow2: Implement image locking Daniel P. Berrange
2015-12-23 12:15     ` [Qemu-devel] [Qemu-block] " Roman Kagan
2015-12-23 12:29       ` Daniel P. Berrange
2015-12-23 12:41         ` Denis V. Lunev
2015-12-23 12:46           ` Daniel P. Berrange
2015-12-23 12:34       ` Daniel P. Berrange
2015-12-23 12:47         ` Denis V. Lunev
2015-12-23 12:56           ` Daniel P. Berrange
2016-01-11 17:14     ` [Qemu-devel] " Kevin Wolf
2016-01-11 17:54       ` Daniel P. Berrange
2016-01-13  8:56       ` Markus Armbruster
2016-01-13  9:11         ` [Qemu-devel] [Qemu-block] " Denis V. Lunev
2015-12-23 23:19   ` [Qemu-devel] " Max Reitz
2015-12-24  5:41     ` [Qemu-devel] [Qemu-block] " Denis V. Lunev
2015-12-24  5:42       ` Denis V. Lunev
2016-01-04 17:02       ` Max Reitz
2016-01-11 16:47       ` Kevin Wolf
2016-01-11 17:56         ` Daniel P. Berrange
2015-12-23 14:57 ` [Qemu-devel] " Vasiliy Tolstov
2015-12-23 15:08   ` [Qemu-devel] [Qemu-block] " Denis V. Lunev
2015-12-23 15:11     ` Vasiliy Tolstov
2016-01-11 16:25       ` Kevin Wolf
2015-12-23 15:09   ` Denis V. Lunev
2015-12-24  5:43 ` Denis V. Lunev
2016-01-11 16:33   ` Kevin Wolf
2016-01-11 16:38     ` Denis V. Lunev
2016-01-14 14:01 ` Max Reitz

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1450802786-20893-7-git-send-email-kwolf@redhat.com \
    --to=kwolf@redhat.com \
    --cc=mreitz@redhat.com \
    --cc=qemu-block@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).