[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
- [Qemu-block] [PATCH 03/10] block: Assert no write requests under BDRV_O_INCOMING, (continued)
- [Qemu-block] [PATCH 03/10] block: Assert no write requests under BDRV_O_INCOMING, Kevin Wolf, 2015/12/22
- [Qemu-block] [PATCH 04/10] block: Fix error path in bdrv_invalidate_cache(), Kevin Wolf, 2015/12/22
- [Qemu-block] [PATCH 05/10] block: Inactivate BDS when migration completes, Kevin Wolf, 2015/12/22
- [Qemu-block] [PATCH 07/10] qcow2: Implement .bdrv_inactivate, Kevin Wolf, 2015/12/22
- [Qemu-block] [PATCH 08/10] qcow2: Fix BDRV_O_INCOMING handling in qcow2_invalidate_cache(), Kevin Wolf, 2015/12/22
- [Qemu-block] [PATCH 06/10] qemu-img: Prepare for locked images,
Kevin Wolf <=
- [Qemu-block] [PATCH 09/10] qcow2: Make image inaccessible after failed qcow2_invalidate_cache(), Kevin Wolf, 2015/12/22
- [Qemu-block] [PATCH 10/10] qcow2: Add image locking, Kevin Wolf, 2015/12/22
- Re: [Qemu-block] [Qemu-devel] [PATCH 00/10] qcow2: Implement image locking, Fam Zheng, 2015/12/22