Block patches

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABAgAGBQJTehNaAAoJEH8JsnLIjy/Ws+QP/0C7GTkY+CAgSSoQmIKr96lf
 k0iRIypxMw68S6NEXtiqFn1yLVv41+puamrznmOSBqxZwwWLpu+J28M8i3M3lr2Q
 /bs7l22+HfakfJ6AJkYXaLPqAlRdCnM3HOLWpDuVFLeaLlHV14w4oWCIAs/lA6Tg
 n4S4nKpWUzm97NnNvQjf953kSFZ/xfH72PcICE5vyaQBnrDMMUnRyffdnVBEGMjd
 hhx/demJNXt+01XxC4VrnvpibGvMthEbQoRwemFi2snD6YXhk9XcT+jiD2VMrgCr
 fC316vdAFAiVNvI+JjCRE/1gaMRI+m0tNpymzGWnbnEc8P86KUaitASRc150NDSO
 UgpDg7oneMXC66OdZXG0XqojiAQ8sqHrvMpV+YiirJUbwIcD5ITDKt9omuIjOWjj
 ENXHOk2U87xoFfqBRRbsuO+U2QtfPDFA4jRjh5ppUy/0xuW/YL3SBCSdUHR8jalM
 H8mYcC9zKsL7D71Nh6spU4btNK2xjZT+vPoiurHNyiBSVniHagsKPGtzQCqhJEa+
 y9xCBCyqZvHBvQ2w1pE4DBOIvt3L0kKd7pRxRch9letCA6Fo/ktb7rvkDkcPVh50
 I0kphrnWqGLgC+8oMvh/gjwtzvWkTCfc8jhvzAcBGaInQr+spSaduCAnrGTpfBh1
 vfvc1o3NUVhvqipMgdzq
 =LnQj
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging

Block patches

# gpg: Signature made Mon 19 May 2014 15:21:14 BST using RSA key ID C88F2FD6
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>"

* remotes/kevin/tags/for-upstream: (22 commits)
  block: optimize zero writes with bdrv_write_zeroes
  blockdev: add a function to parse enum ids from strings
  util: add qemu_iovec_is_zero
  qcow1: Stricter backing file length check
  qcow1: Validate image size (CVE-2014-0223)
  qcow1: Validate L2 table size (CVE-2014-0222)
  qcow1: Check maximum cluster size
  qcow1: Make padding in the header explicit
  curl: Add usage documentation
  curl: Add sslverify option
  curl: Remove broken parsing of options from url
  curl: Fix build when curl_multi_socket_action isn't available
  qemu-iotests: Fix blkdebug in VM drive in 030
  qemu-iotests: Fix core dump suppression in test 039
  iotests: Add test for the JSON protocol
  block: Allow JSON filenames
  check-qdict: Add test for qdict_join()
  qdict: Add qdict_join()
  block: add test for vhdx image created by Disk2VHD
  block: vhdx - account for identical header sections
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2014-05-20 11:57:52 +01:00
commit ca8c0fab95
31 changed files with 809 additions and 96 deletions

60
block.c
View file

@ -1274,6 +1274,33 @@ out:
g_free(tmp_filename);
}
static QDict *parse_json_filename(const char *filename, Error **errp)
{
QObject *options_obj;
QDict *options;
int ret;
ret = strstart(filename, "json:", &filename);
assert(ret);
options_obj = qobject_from_json(filename);
if (!options_obj) {
error_setg(errp, "Could not parse the JSON options");
return NULL;
}
if (qobject_type(options_obj) != QTYPE_QDICT) {
qobject_decref(options_obj);
error_setg(errp, "Invalid JSON object given");
return NULL;
}
options = qobject_to_qdict(options_obj);
qdict_flatten(options);
return options;
}
/*
* Opens a disk image (raw, qcow2, vmdk, ...)
*
@ -1337,6 +1364,20 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
options = qdict_new();
}
if (filename && g_str_has_prefix(filename, "json:")) {
QDict *json_options = parse_json_filename(filename, &local_err);
if (local_err) {
ret = -EINVAL;
goto fail;
}
/* Options given in the filename have lower priority than options
* specified directly */
qdict_join(options, json_options, false);
QDECREF(json_options);
filename = NULL;
}
bs->options = options;
options = qdict_clone_shallow(options);
@ -3248,6 +3289,15 @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs,
ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req);
if (!ret && bs->detect_zeroes != BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF &&
!(flags & BDRV_REQ_ZERO_WRITE) && drv->bdrv_co_write_zeroes &&
qemu_iovec_is_zero(qiov)) {
flags |= BDRV_REQ_ZERO_WRITE;
if (bs->detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP) {
flags |= BDRV_REQ_MAY_UNMAP;
}
}
if (ret < 0) {
/* Do nothing, write notifier decided to fail this request */
} else if (flags & BDRV_REQ_ZERO_WRITE) {
@ -3864,7 +3914,7 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
if (!bs->drv->bdrv_co_get_block_status) {
*pnum = nb_sectors;
ret = BDRV_BLOCK_DATA;
ret = BDRV_BLOCK_DATA | BDRV_BLOCK_ALLOCATED;
if (bs->drv->protocol_name) {
ret |= BDRV_BLOCK_OFFSET_VALID | (sector_num * BDRV_SECTOR_SIZE);
}
@ -3883,6 +3933,10 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
*pnum, pnum);
}
if (ret & (BDRV_BLOCK_DATA | BDRV_BLOCK_ZERO)) {
ret |= BDRV_BLOCK_ALLOCATED;
}
if (!(ret & BDRV_BLOCK_DATA) && !(ret & BDRV_BLOCK_ZERO)) {
if (bdrv_unallocated_blocks_are_zero(bs)) {
ret |= BDRV_BLOCK_ZERO;
@ -3959,9 +4013,7 @@ int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num,
if (ret < 0) {
return ret;
}
return
(ret & BDRV_BLOCK_DATA) ||
((ret & BDRV_BLOCK_ZERO) && !bdrv_has_zero_init(bs));
return (ret & BDRV_BLOCK_ALLOCATED);
}
/*

View file

@ -23,6 +23,7 @@
*/
#include "qemu-common.h"
#include "block/block_int.h"
#include "qapi/qmp/qbool.h"
#include <curl/curl.h>
// #define DEBUG
@ -37,6 +38,21 @@
#if LIBCURL_VERSION_NUM >= 0x071000
/* The multi interface timer callback was introduced in 7.16.0 */
#define NEED_CURL_TIMER_CALLBACK
#define HAVE_SOCKET_ACTION
#endif
#ifndef HAVE_SOCKET_ACTION
/* If curl_multi_socket_action isn't available, define it statically here in
* terms of curl_multi_socket. Note that ev_bitmask will be ignored, which is
* less efficient but still safe. */
static CURLMcode __curl_multi_socket_action(CURLM *multi_handle,
curl_socket_t sockfd,
int ev_bitmask,
int *running_handles)
{
return curl_multi_socket(multi_handle, sockfd, running_handles);
}
#define curl_multi_socket_action __curl_multi_socket_action
#endif
#define PROTOCOLS (CURLPROTO_HTTP | CURLPROTO_HTTPS | \
@ -46,12 +62,16 @@
#define CURL_NUM_STATES 8
#define CURL_NUM_ACB 8
#define SECTOR_SIZE 512
#define READ_AHEAD_SIZE (256 * 1024)
#define READ_AHEAD_DEFAULT (256 * 1024)
#define FIND_RET_NONE 0
#define FIND_RET_OK 1
#define FIND_RET_WAIT 2
#define CURL_BLOCK_OPT_URL "url"
#define CURL_BLOCK_OPT_READAHEAD "readahead"
#define CURL_BLOCK_OPT_SSLVERIFY "sslverify"
struct BDRVCURLState;
typedef struct CURLAIOCB {
@ -88,6 +108,7 @@ typedef struct BDRVCURLState {
CURLState states[CURL_NUM_STATES];
char *url;
size_t readahead_size;
bool sslverify;
bool accept_range;
} BDRVCURLState;
@ -354,6 +375,8 @@ static CURLState *curl_init_state(BDRVCURLState *s)
return NULL;
}
curl_easy_setopt(state->curl, CURLOPT_URL, s->url);
curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER,
(long) s->sslverify);
curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, 5);
curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION,
(void *)curl_read_cb);
@ -396,43 +419,7 @@ static void curl_clean_state(CURLState *s)
static void curl_parse_filename(const char *filename, QDict *options,
Error **errp)
{
#define RA_OPTSTR ":readahead="
char *file;
char *ra;
const char *ra_val;
int parse_state = 0;
file = g_strdup(filename);
/* Parse a trailing ":readahead=#:" param, if present. */
ra = file + strlen(file) - 1;
while (ra >= file) {
if (parse_state == 0) {
if (*ra == ':') {
parse_state++;
} else {
break;
}
} else if (parse_state == 1) {
if (*ra > '9' || *ra < '0') {
char *opt_start = ra - strlen(RA_OPTSTR) + 1;
if (opt_start > file &&
strncmp(opt_start, RA_OPTSTR, strlen(RA_OPTSTR)) == 0) {
ra_val = ra + 1;
ra -= strlen(RA_OPTSTR) - 1;
*ra = '\0';
qdict_put(options, "readahead", qstring_from_str(ra_val));
}
break;
}
}
ra--;
}
qdict_put(options, "url", qstring_from_str(file));
g_free(file);
qdict_put(options, CURL_BLOCK_OPT_URL, qstring_from_str(filename));
}
static QemuOptsList runtime_opts = {
@ -440,15 +427,20 @@ static QemuOptsList runtime_opts = {
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
.desc = {
{
.name = "url",
.name = CURL_BLOCK_OPT_URL,
.type = QEMU_OPT_STRING,
.help = "URL to open",
},
{
.name = "readahead",
.name = CURL_BLOCK_OPT_READAHEAD,
.type = QEMU_OPT_SIZE,
.help = "Readahead size",
},
{
.name = CURL_BLOCK_OPT_SSLVERIFY,
.type = QEMU_OPT_BOOL,
.help = "Verify SSL certificate"
},
{ /* end of list */ }
},
};
@ -477,14 +469,17 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
goto out_noclean;
}
s->readahead_size = qemu_opt_get_size(opts, "readahead", READ_AHEAD_SIZE);
s->readahead_size = qemu_opt_get_size(opts, CURL_BLOCK_OPT_READAHEAD,
READ_AHEAD_DEFAULT);
if ((s->readahead_size & 0x1ff) != 0) {
error_setg(errp, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512",
s->readahead_size);
goto out_noclean;
}
file = qemu_opt_get(opts, "url");
s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY, true);
file = qemu_opt_get(opts, CURL_BLOCK_OPT_URL);
if (file == NULL) {
error_setg(errp, "curl block driver requires an 'url' option");
goto out_noclean;

View file

@ -50,6 +50,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs)
}
info->backing_file_depth = bdrv_get_backing_file_depth(bs);
info->detect_zeroes = bs->detect_zeroes;
if (bs->io_limits_enabled) {
ThrottleConfig cfg;

View file

@ -48,9 +48,10 @@ typedef struct QCowHeader {
uint64_t size; /* in bytes */
uint8_t cluster_bits;
uint8_t l2_bits;
uint16_t padding;
uint32_t crypt_method;
uint64_t l1_table_offset;
} QCowHeader;
} QEMU_PACKED QCowHeader;
#define L2_CACHE_SIZE 16
@ -60,7 +61,7 @@ typedef struct BDRVQcowState {
int cluster_sectors;
int l2_bits;
int l2_size;
int l1_size;
unsigned int l1_size;
uint64_t cluster_offset_mask;
uint64_t l1_table_offset;
uint64_t *l1_table;
@ -96,7 +97,8 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
BDRVQcowState *s = bs->opaque;
int len, i, shift, ret;
unsigned int len, i, shift;
int ret;
QCowHeader header;
ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
@ -127,11 +129,25 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
goto fail;
}
if (header.size <= 1 || header.cluster_bits < 9) {
error_setg(errp, "invalid value in qcow header");
if (header.size <= 1) {
error_setg(errp, "Image size is too small (must be at least 2 bytes)");
ret = -EINVAL;
goto fail;
}
if (header.cluster_bits < 9 || header.cluster_bits > 16) {
error_setg(errp, "Cluster size must be between 512 and 64k");
ret = -EINVAL;
goto fail;
}
/* l2_bits specifies number of entries; storing a uint64_t in each entry,
* so bytes = num_entries << 3. */
if (header.l2_bits < 9 - 3 || header.l2_bits > 16 - 3) {
error_setg(errp, "L2 table size must be between 512 and 64k");
ret = -EINVAL;
goto fail;
}
if (header.crypt_method > QCOW_CRYPT_AES) {
error_setg(errp, "invalid encryption method in qcow header");
ret = -EINVAL;
@ -151,7 +167,19 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
/* read the level 1 table */
shift = s->cluster_bits + s->l2_bits;
s->l1_size = (header.size + (1LL << shift) - 1) >> shift;
if (header.size > UINT64_MAX - (1LL << shift)) {
error_setg(errp, "Image too large");
ret = -EINVAL;
goto fail;
} else {
uint64_t l1_size = (header.size + (1LL << shift) - 1) >> shift;
if (l1_size > INT_MAX / sizeof(uint64_t)) {
error_setg(errp, "Image too large");
ret = -EINVAL;
goto fail;
}
s->l1_size = l1_size;
}
s->l1_table_offset = header.l1_table_offset;
s->l1_table = g_malloc(s->l1_size * sizeof(uint64_t));
@ -175,7 +203,9 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
if (header.backing_file_offset != 0) {
len = header.backing_file_size;
if (len > 1023) {
len = 1023;
error_setg(errp, "Backing file name too long");
ret = -EINVAL;
goto fail;
}
ret = bdrv_pread(bs->file, header.backing_file_offset,
bs->backing_file, len);

View file

@ -473,7 +473,14 @@ static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s,
} else if (h2_seq > h1_seq) {
s->curr_header = 1;
} else {
goto fail;
/* The Microsoft Disk2VHD tool will create 2 identical
* headers, with identical sequence numbers. If the headers are
* identical, don't consider the file corrupt */
if (!memcmp(header1, header2, sizeof(VHDXHeader))) {
s->curr_header = 0;
} else {
goto fail;
}
}
}

View file

@ -288,6 +288,25 @@ static int parse_block_error_action(const char *buf, bool is_read, Error **errp)
}
}
static inline int parse_enum_option(const char *lookup[], const char *buf,
int max, int def, Error **errp)
{
int i;
if (!buf) {
return def;
}
for (i = 0; i < max; i++) {
if (!strcmp(buf, lookup[i])) {
return i;
}
}
error_setg(errp, "invalid parameter value: %s", buf);
return def;
}
static bool check_throttle_config(ThrottleConfig *cfg, Error **errp)
{
if (throttle_conflicting(cfg)) {
@ -324,6 +343,7 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
QemuOpts *opts;
const char *id;
bool has_driver_specific_opts;
BlockdevDetectZeroesOptions detect_zeroes;
BlockDriver *drv = NULL;
/* Check common options by copying from bs_opts to opts, all other options
@ -452,6 +472,24 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
}
}
detect_zeroes =
parse_enum_option(BlockdevDetectZeroesOptions_lookup,
qemu_opt_get(opts, "detect-zeroes"),
BLOCKDEV_DETECT_ZEROES_OPTIONS_MAX,
BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF,
&error);
if (error) {
error_propagate(errp, error);
goto early_err;
}
if (detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP &&
!(bdrv_flags & BDRV_O_UNMAP)) {
error_setg(errp, "setting detect-zeroes to unmap is not allowed "
"without setting discard operation to unmap");
goto early_err;
}
/* init */
dinfo = g_malloc0(sizeof(*dinfo));
dinfo->id = g_strdup(qemu_opts_id(opts));
@ -462,6 +500,7 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
}
dinfo->bdrv->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0;
dinfo->bdrv->read_only = ro;
dinfo->bdrv->detect_zeroes = detect_zeroes;
dinfo->refcount = 1;
if (serial != NULL) {
dinfo->serial = g_strdup(serial);
@ -2455,6 +2494,10 @@ QemuOptsList qemu_common_drive_opts = {
.name = "copy-on-read",
.type = QEMU_OPT_BOOL,
.help = "copy read data from backing file into image file",
},{
.name = "detect-zeroes",
.type = QEMU_OPT_STRING,
.help = "try to optimize zero writes (off, on, unmap)",
},
{ /* end of list */ }
},

5
hmp.c
View file

@ -341,6 +341,11 @@ void hmp_info_block(Monitor *mon, const QDict *qdict)
info->value->inserted->backing_file_depth);
}
if (info->value->inserted->detect_zeroes != BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF) {
monitor_printf(mon, " Detect zeroes: %s\n",
BlockdevDetectZeroesOptions_lookup[info->value->inserted->detect_zeroes]);
}
if (info->value->inserted->bps
|| info->value->inserted->bps_rd
|| info->value->inserted->bps_wr

View file

@ -120,6 +120,8 @@ typedef enum {
/* BDRV_BLOCK_DATA: data is read from bs->file or another file
* BDRV_BLOCK_ZERO: sectors read as zero
* BDRV_BLOCK_OFFSET_VALID: sector stored in bs->file as raw data
* BDRV_BLOCK_ALLOCATED: the content of the block is determined by this
* layer (as opposed to the backing file)
* BDRV_BLOCK_RAW: used internally to indicate that the request
* was answered by the raw driver and that one
* should look in bs->file directly.
@ -141,10 +143,11 @@ typedef enum {
* f t f not allocated or unknown offset, read as zero
* f f f not allocated or unknown offset, read from backing_hd
*/
#define BDRV_BLOCK_DATA 1
#define BDRV_BLOCK_ZERO 2
#define BDRV_BLOCK_OFFSET_VALID 4
#define BDRV_BLOCK_RAW 8
#define BDRV_BLOCK_DATA 0x01
#define BDRV_BLOCK_ZERO 0x02
#define BDRV_BLOCK_OFFSET_VALID 0x04
#define BDRV_BLOCK_RAW 0x08
#define BDRV_BLOCK_ALLOCATED 0x10
#define BDRV_BLOCK_OFFSET_MASK BDRV_SECTOR_MASK
typedef enum {

View file

@ -364,6 +364,7 @@ struct BlockDriverState {
BlockJob *job;
QDict *options;
BlockdevDetectZeroesOptions detect_zeroes;
};
int get_tmp_filename(char *filename, int size);

View file

@ -16,6 +16,7 @@
#include "qapi/qmp/qobject.h"
#include "qapi/qmp/qlist.h"
#include "qemu/queue.h"
#include <stdbool.h>
#include <stdint.h>
#define QDICT_BUCKET_MAX 512
@ -70,4 +71,6 @@ void qdict_flatten(QDict *qdict);
void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start);
void qdict_array_split(QDict *src, QList **dst);
void qdict_join(QDict *dest, QDict *src, bool overwrite);
#endif /* QDICT_H */

View file

@ -318,6 +318,7 @@ void qemu_iovec_concat(QEMUIOVector *dst,
void qemu_iovec_concat_iov(QEMUIOVector *dst,
struct iovec *src_iov, unsigned int src_cnt,
size_t soffset, size_t sbytes);
bool qemu_iovec_is_zero(QEMUIOVector *qiov);
void qemu_iovec_destroy(QEMUIOVector *qiov);
void qemu_iovec_reset(QEMUIOVector *qiov);
size_t qemu_iovec_to_buf(QEMUIOVector *qiov, size_t offset,

View file

@ -942,6 +942,8 @@
# @encryption_key_missing: true if the backing device is encrypted but an
# valid encryption key is missing
#
# @detect_zeroes: detect and optimize zero writes (Since 2.1)
#
# @bps: total throughput limit in bytes per second is specified
#
# @bps_rd: read throughput limit in bytes per second is specified
@ -977,6 +979,7 @@
'data': { 'file': 'str', '*node-name': 'str', 'ro': 'bool', 'drv': 'str',
'*backing_file': 'str', 'backing_file_depth': 'int',
'encrypted': 'bool', 'encryption_key_missing': 'bool',
'detect_zeroes': 'BlockdevDetectZeroesOptions',
'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int',
'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int',
'image': 'ImageInfo',
@ -4254,6 +4257,22 @@
{ 'enum': 'BlockdevDiscardOptions',
'data': [ 'ignore', 'unmap' ] }
##
# @BlockdevDetectZeroesOptions
#
# Describes the operation mode for the automatic conversion of plain
# zero writes by the OS to driver specific optimized zero write commands.
#
# @off: Disabled (default)
# @on: Enabled
# @unmap: Enabled and even try to unmap blocks if possible. This requires
# also that @BlockdevDiscardOptions is set to unmap for this device.
#
# Since: 2.1
##
{ 'enum': 'BlockdevDetectZeroesOptions',
'data': [ 'off', 'on', 'unmap' ] }
##
# @BlockdevAioOptions
#
@ -4306,20 +4325,22 @@
# Options that are available for all block devices, independent of the block
# driver.
#
# @driver: block driver name
# @id: #optional id by which the new block device can be referred to.
# This is a required option on the top level of blockdev-add, and
# currently not allowed on any other level.
# @node-name: #optional the name of a block driver state node (Since 2.0)
# @discard: #optional discard-related options (default: ignore)
# @cache: #optional cache-related options
# @aio: #optional AIO backend (default: threads)
# @rerror: #optional how to handle read errors on the device
# (default: report)
# @werror: #optional how to handle write errors on the device
# (default: enospc)
# @read-only: #optional whether the block device should be read-only
# (default: false)
# @driver: block driver name
# @id: #optional id by which the new block device can be referred to.
# This is a required option on the top level of blockdev-add, and
# currently not allowed on any other level.
# @node-name: #optional the name of a block driver state node (Since 2.0)
# @discard: #optional discard-related options (default: ignore)
# @cache: #optional cache-related options
# @aio: #optional AIO backend (default: threads)
# @rerror: #optional how to handle read errors on the device
# (default: report)
# @werror: #optional how to handle write errors on the device
# (default: enospc)
# @read-only: #optional whether the block device should be read-only
# (default: false)
# @detect-zeroes: #optional detect and optimize zero writes (Since 2.1)
# (default: off)
#
# Since: 1.7
##
@ -4332,7 +4353,8 @@
'*aio': 'BlockdevAioOptions',
'*rerror': 'BlockdevOnError',
'*werror': 'BlockdevOnError',
'*read-only': 'bool' } }
'*read-only': 'bool',
'*detect-zeroes': 'BlockdevDetectZeroesOptions' } }
##
# @BlockdevOptionsFile

View file

@ -70,11 +70,8 @@ static void add_format_to_seq(void *opaque, const char *fmt_name)
{
GSequence *seq = opaque;
if (!g_sequence_lookup(seq, (gpointer)fmt_name,
compare_data, NULL)) {
g_sequence_insert_sorted(seq, (gpointer)fmt_name,
compare_data, NULL);
}
g_sequence_insert_sorted(seq, (gpointer)fmt_name,
compare_data, NULL);
}
static void QEMU_NORETURN GCC_FMT_ATTR(1, 2) error_exit(const char *fmt, ...)

View file

@ -414,6 +414,7 @@ DEF("drive", HAS_ARG, QEMU_OPTION_drive,
" [,serial=s][,addr=A][,rerror=ignore|stop|report]\n"
" [,werror=ignore|stop|report|enospc][,id=name][,aio=threads|native]\n"
" [,readonly=on|off][,copy-on-read=on|off]\n"
" [,detect-zeroes=on|off|unmap]\n"
" [[,bps=b]|[[,bps_rd=r][,bps_wr=w]]]\n"
" [[,iops=i]|[[,iops_rd=r][,iops_wr=w]]]\n"
" [[,bps_max=bm]|[[,bps_rd_max=rm][,bps_wr_max=wm]]]\n"
@ -475,6 +476,11 @@ Open drive @option{file} as read-only. Guest write attempts will fail.
@item copy-on-read=@var{copy-on-read}
@var{copy-on-read} is "on" or "off" and enables whether to copy read backing
file sectors into the image file.
@item detect-zeroes=@var{detect-zeroes}
@var{detect-zeroes} is "off", "on" or "unmap" and enables the automatic
conversion of plain zero writes by the OS to driver specific optimized
zero write commands. You may even choose "unmap" if @var{discard} is set
to "unmap" to allow a zero write to be converted to an UNMAP operation.
@end table
By default, the @option{cache=writeback} mode is used. It will report data
@ -2191,6 +2197,74 @@ qemu-system-x86_64 --drive file=gluster://192.0.2.1/testvol/a.img
@end example
See also @url{http://www.gluster.org}.
@item HTTP/HTTPS/FTP/FTPS/TFTP
QEMU supports read-only access to files accessed over http(s), ftp(s) and tftp.
Syntax using a single filename:
@example
<protocol>://[<username>[:<password>]@@]<host>/<path>
@end example
where:
@table @option
@item protocol
'http', 'https', 'ftp', 'ftps', or 'tftp'.
@item username
Optional username for authentication to the remote server.
@item password
Optional password for authentication to the remote server.
@item host
Address of the remote server.
@item path
Path on the remote server, including any query string.
@end table
The following options are also supported:
@table @option
@item url
The full URL when passing options to the driver explicitly.
@item readahead
The amount of data to read ahead with each range request to the remote server.
This value may optionally have the suffix 'T', 'G', 'M', 'K', 'k' or 'b'. If it
does not have a suffix, it will be assumed to be in bytes. The value must be a
multiple of 512 bytes. It defaults to 256k.
@item sslverify
Whether to verify the remote server's certificate when connecting over SSL. It
can have the value 'on' or 'off'. It defaults to 'on'.
@end table
Note that when passing options to qemu explicitly, @option{driver} is the value
of <protocol>.
Example: boot from a remote Fedora 20 live ISO image
@example
qemu-system-x86_64 --drive media=cdrom,file=http://dl.fedoraproject.org/pub/fedora/linux/releases/20/Live/x86_64/Fedora-Live-Desktop-x86_64-20-1.iso,readonly
qemu-system-x86_64 --drive media=cdrom,file.driver=http,file.url=http://dl.fedoraproject.org/pub/fedora/linux/releases/20/Live/x86_64/Fedora-Live-Desktop-x86_64-20-1.iso,readonly
@end example
Example: boot from a remote Fedora 20 cloud image using a local overlay for
writes, copy-on-read, and a readahead of 64k
@example
qemu-img create -f qcow2 -o backing_file='json:@{"file.driver":"http",, "file.url":"https://dl.fedoraproject.org/pub/fedora/linux/releases/20/Images/x86_64/Fedora-x86_64-20-20131211.1-sda.qcow2",, "file.readahead":"64k"@}' /tmp/Fedora-x86_64-20-20131211.1-sda.qcow2
qemu-system-x86_64 -drive file=/tmp/Fedora-x86_64-20-20131211.1-sda.qcow2,copy-on-read=on
@end example
Example: boot from an image stored on a VMware vSphere server with a self-signed
certificate using a local overlay for writes and a readahead of 64k
@example
qemu-img create -f qcow2 -o backing_file='json:@{"file.driver":"https",, "file.url":"https://user:password@@vsphere.example.com/folder/test/test-flat.vmdk?dcPath=Datacenter&dsName=datastore1",, "file.sslverify":"off",, "file.readahead":"64k"@}' /tmp/test.qcow2
qemu-system-x86_64 -drive file=/tmp/test.qcow2
@end example
ETEXI
STEXI

View file

@ -2032,6 +2032,8 @@ Each json-object contain the following:
- "iops_rd_max": read I/O operations max (json-int)
- "iops_wr_max": write I/O operations max (json-int)
- "iops_size": I/O size when limiting by iops (json-int)
- "detect_zeroes": detect and optimize zero writing (json-string)
- Possible values: "off", "on", "unmap"
- "image": the detail of the image, it is a json-object containing
the following:
- "filename": image file name (json-string)
@ -2108,6 +2110,7 @@ Example:
"iops_rd_max": 0,
"iops_wr_max": 0,
"iops_size": 0,
"detect_zeroes": "on",
"image":{
"filename":"disks/test.qcow2",
"format":"qcow2",

View file

@ -665,3 +665,35 @@ void qdict_array_split(QDict *src, QList **dst)
qlist_append_obj(*dst, subqobj ?: QOBJECT(subqdict));
}
}
/**
* qdict_join(): Absorb the src QDict into the dest QDict, that is, move all
* elements from src to dest.
*
* If an element from src has a key already present in dest, it will not be
* moved unless overwrite is true.
*
* If overwrite is true, the conflicting values in dest will be discarded and
* replaced by the corresponding values from src.
*
* Therefore, with overwrite being true, the src QDict will always be empty when
* this function returns. If overwrite is false, the src QDict will be empty
* iff there were no conflicts.
*/
void qdict_join(QDict *dest, QDict *src, bool overwrite)
{
const QDictEntry *entry, *next;
entry = qdict_first(src);
while (entry) {
next = qdict_next(src, entry);
if (overwrite || !qdict_haskey(dest, entry->key)) {
qobject_incref(entry->value);
qdict_put_obj(dest, entry->key, entry->value);
qdict_del(src, entry->key);
}
entry = next;
}
}

View file

@ -444,6 +444,92 @@ static void qdict_array_split_test(void)
QDECREF(test_dict);
}
static void qdict_join_test(void)
{
QDict *dict1, *dict2;
bool overwrite = false;
int i;
dict1 = qdict_new();
dict2 = qdict_new();
/* Test everything once without overwrite and once with */
do
{
/* Test empty dicts */
qdict_join(dict1, dict2, overwrite);
g_assert(qdict_size(dict1) == 0);
g_assert(qdict_size(dict2) == 0);
/* First iteration: Test movement */
/* Second iteration: Test empty source and non-empty destination */
qdict_put(dict2, "foo", qint_from_int(42));
for (i = 0; i < 2; i++) {
qdict_join(dict1, dict2, overwrite);
g_assert(qdict_size(dict1) == 1);
g_assert(qdict_size(dict2) == 0);
g_assert(qdict_get_int(dict1, "foo") == 42);
}
/* Test non-empty source and destination without conflict */
qdict_put(dict2, "bar", qint_from_int(23));
qdict_join(dict1, dict2, overwrite);
g_assert(qdict_size(dict1) == 2);
g_assert(qdict_size(dict2) == 0);
g_assert(qdict_get_int(dict1, "foo") == 42);
g_assert(qdict_get_int(dict1, "bar") == 23);
/* Test conflict */
qdict_put(dict2, "foo", qint_from_int(84));
qdict_join(dict1, dict2, overwrite);
g_assert(qdict_size(dict1) == 2);
g_assert(qdict_size(dict2) == !overwrite);
g_assert(qdict_get_int(dict1, "foo") == overwrite ? 84 : 42);
g_assert(qdict_get_int(dict1, "bar") == 23);
if (!overwrite) {
g_assert(qdict_get_int(dict2, "foo") == 84);
}
/* Check the references */
g_assert(qdict_get(dict1, "foo")->refcnt == 1);
g_assert(qdict_get(dict1, "bar")->refcnt == 1);
if (!overwrite) {
g_assert(qdict_get(dict2, "foo")->refcnt == 1);
}
/* Clean up */
qdict_del(dict1, "foo");
qdict_del(dict1, "bar");
if (!overwrite) {
qdict_del(dict2, "foo");
}
}
while (overwrite ^= true);
QDECREF(dict1);
QDECREF(dict2);
}
/*
* Errors test-cases
*/
@ -584,6 +670,7 @@ int main(int argc, char **argv)
g_test_add_func("/public/iterapi", qdict_iterapi_test);
g_test_add_func("/public/flatten", qdict_flatten_test);
g_test_add_func("/public/array_split", qdict_array_split_test);
g_test_add_func("/public/join", qdict_join_test);
g_test_add_func("/errors/put_exists", qdict_put_exists_test);
g_test_add_func("/errors/get_not_exists", qdict_get_not_exists_test);

View file

@ -35,7 +35,7 @@ class TestSingleDrive(iotests.QMPTestCase):
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img)
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img)
qemu_io('-c', 'write -P 0x1 0 512', backing_img)
self.vm = iotests.VM().add_drive(test_img)
self.vm = iotests.VM().add_drive("blkdebug::" + test_img)
self.vm.launch()
def tearDown(self):

View file

@ -47,6 +47,11 @@ _supported_os Linux
_default_cache_mode "writethrough"
_supported_cache_modes "writethrough"
_no_dump_exec()
{
(ulimit -c 0; exec "$@")
}
size=128M
echo
@ -67,10 +72,7 @@ echo "== Creating a dirty image file =="
IMGOPTS="compat=1.1,lazy_refcounts=on"
_make_test_img $size
old_ulimit=$(ulimit -c)
ulimit -c 0 # do not produce a core dump on abort(3)
$QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" | _filter_qemu_io
ulimit -c "$old_ulimit"
_no_dump_exec $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" 2>&1 | _filter_qemu_io
# The dirty bit must be set
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
@ -103,10 +105,7 @@ echo "== Opening a dirty image read/write should repair it =="
IMGOPTS="compat=1.1,lazy_refcounts=on"
_make_test_img $size
old_ulimit=$(ulimit -c)
ulimit -c 0 # do not produce a core dump on abort(3)
$QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" | _filter_qemu_io
ulimit -c "$old_ulimit"
_no_dump_exec $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" 2>&1 | _filter_qemu_io
# The dirty bit must be set
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
@ -122,10 +121,7 @@ echo "== Creating an image file with lazy_refcounts=off =="
IMGOPTS="compat=1.1,lazy_refcounts=off"
_make_test_img $size
old_ulimit=$(ulimit -c)
ulimit -c 0 # do not produce a core dump on abort(3)
$QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" | _filter_qemu_io
ulimit -c "$old_ulimit"
_no_dump_exec $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" 2>&1 | _filter_qemu_io
# The dirty bit must not be set since lazy_refcounts=off
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features

View file

@ -11,6 +11,7 @@ No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
./039: Aborted ( ulimit -c 0; exec "$@" )
incompatible_features 0x1
ERROR cluster 5 refcount=0 reference=1
ERROR OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=0
@ -42,6 +43,7 @@ read 512/512 bytes at offset 0
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
./039: Aborted ( ulimit -c 0; exec "$@" )
incompatible_features 0x1
Repairing cluster 5 refcount=0 reference=1
wrote 512/512 bytes at offset 0
@ -52,6 +54,7 @@ incompatible_features 0x0
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
./039: Aborted ( ulimit -c 0; exec "$@" )
incompatible_features 0x0
No errors were found on the image.

View file

@ -6,7 +6,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk -device virtio-blk-pci,drive=disk,id=virtio0
QMP_VERSION
{"return": {}}
{"return": [{"io-status": "ok", "device": "disk", "locked": false, "removable": false, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]}
{"return": [{"io-status": "ok", "device": "disk", "locked": false, "removable": false, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]}
{"return": {}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}}
@ -24,7 +24,7 @@ QMP_VERSION
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk
QMP_VERSION
{"return": {}}
{"return": [{"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]}
{"return": [{"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]}
{"return": {}}
{"return": {}}
{"return": {}}
@ -44,7 +44,7 @@ Testing:
QMP_VERSION
{"return": {}}
{"return": "OK\r\n"}
{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
{"return": {}}
{"return": {}}
{"return": {}}
@ -64,14 +64,14 @@ Testing:
QMP_VERSION
{"return": {}}
{"return": {}}
{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
{"return": {}}
{"return": {}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"device": "virtio0", "path": "/machine/peripheral/virtio0"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESET"}
{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}

View file

@ -72,6 +72,13 @@ echo "=== Verify open image read-only succeeds after log replay ==="
$QEMU_IO -r -c "read -pP 0xa5 0 18M" "$TEST_IMG" 2>&1 | _filter_testdir \
| _filter_qemu_io
_cleanup_test_img
_use_sample_img test-disk2vhd.vhdx.bz2
echo
echo "=== Verify image created by Disk2VHD can be opened ==="
$QEMU_IMG info "$TEST_IMG" 2>&1 | _filter_testdir | _filter_qemu
# success, all done
echo "*** done"
rm -f $seq.full

View file

@ -18,4 +18,11 @@ No errors were found on the image.
=== Verify open image read-only succeeds after log replay ===
read 18874368/18874368 bytes at offset 0
18 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
=== Verify image created by Disk2VHD can be opened ===
image: TEST_DIR/test-disk2vhd.vhdx
file format: vhdx
virtual size: 256M (268435456 bytes)
disk size: 260M
cluster_size: 2097152
*** done

130
tests/qemu-iotests/089 Executable file
View file

@ -0,0 +1,130 @@
#!/bin/bash
#
# Test case for support of JSON filenames
#
# Copyright (C) 2014 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# creator
owner=mreitz@redhat.com
seq="$(basename $0)"
echo "QA output created by $seq"
here="$PWD"
tmp=/tmp/$$
status=1 # failure is the default!
_cleanup()
{
_cleanup_test_img
}
trap "_cleanup; exit \$status" 0 1 2 3 15
# get standard environment, filters and checks
. ./common.rc
. ./common.filter
_supported_fmt qcow2
_supported_proto file
_supported_os Linux
# Using an image filename containing quotation marks will render the JSON data
# below invalid. In that case, we have little choice but simply not to run this
# test.
case $TEST_IMG in
*'"'*)
_notrun "image filename may not contain quotation marks"
;;
esac
IMG_SIZE=64M
# Taken from test 072
echo
echo "=== Testing nested image formats ==="
echo
TEST_IMG="$TEST_IMG.base" _make_test_img $IMG_SIZE
$QEMU_IO -c 'write -P 42 0 512' -c 'write -P 23 512 512' \
-c 'write -P 66 1024 512' "$TEST_IMG.base" | _filter_qemu_io
$QEMU_IMG convert -f raw -O $IMGFMT "$TEST_IMG.base" "$TEST_IMG"
$QEMU_IO -c 'read -P 42 0 512' -c 'read -P 23 512 512' \
-c 'read -P 66 1024 512' "json:{
\"driver\": \"$IMGFMT\",
\"file\": {
\"driver\": \"$IMGFMT\",
\"file\": {
\"filename\": \"$TEST_IMG\"
}
}
}" | _filter_qemu_io
# This should fail (see test 072)
$QEMU_IO -c 'read -P 42 0 512' "$TEST_IMG" | _filter_qemu_io
# Taken from test 071
echo
echo "=== Testing blkdebug ==="
echo
_make_test_img $IMG_SIZE
$QEMU_IO -c 'write -P 42 0x38000 512' "$TEST_IMG" | _filter_qemu_io
# The "image.filename" part tests whether "a": { "b": "c" } and "a.b": "c" do
# the same (which they should).
$QEMU_IO -c 'read -P 42 0x38000 512' "json:{
\"driver\": \"$IMGFMT\",
\"file\": {
\"driver\": \"blkdebug\",
\"inject-error\": [{
\"event\": \"l2_load\"
}],
\"image.filename\": \"$TEST_IMG\"
}
}" | _filter_qemu_io
echo
echo "=== Testing qemu-img info output ==="
echo
$QEMU_IMG info "json:{\"driver\":\"qcow2\",\"file.filename\":\"$TEST_IMG\"}" \
| _filter_testdir | _filter_imgfmt
echo
echo "=== Testing option merging ==="
echo
# Both options given directly and those given in the filename should be used
$QEMU_IO -c "open -o driver=qcow2 json:{\"file.filename\":\"$TEST_IMG\"}" \
-c "info" 2>&1 | _filter_testdir | _filter_imgfmt
# Options given directly should be prioritized over those given in the filename
$QEMU_IO -c "open -o driver=qcow2 json:{\"driver\":\"raw\",\"file.filename\":\"$TEST_IMG\"}" \
-c "info" 2>&1 | _filter_testdir | _filter_imgfmt
# success, all done
echo "*** done"
rm -f $seq.full
status=0

View file

@ -0,0 +1,54 @@
QA output created by 089
=== Testing nested image formats ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 512/512 bytes at offset 512
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 512/512 bytes at offset 1024
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 512/512 bytes at offset 512
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 512/512 bytes at offset 1024
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Pattern verification failed at offset 0, 512 bytes
read 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
=== Testing blkdebug ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
wrote 512/512 bytes at offset 229376
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read failed: Input/output error
=== Testing qemu-img info output ===
image: TEST_DIR/t.IMGFMT
file format: IMGFMT
virtual size: 64M (67108864 bytes)
disk size: 324K
cluster_size: 65536
Format specific information:
compat: 1.1
lazy refcounts: false
=== Testing option merging ===
format name: IMGFMT
cluster size: 64 KiB
vm state offset: 512 MiB
Format specific information:
compat: 1.1
lazy refcounts: false
format name: IMGFMT
cluster size: 64 KiB
vm state offset: 512 MiB
Format specific information:
compat: 1.1
lazy refcounts: false
*** done

98
tests/qemu-iotests/092 Executable file
View file

@ -0,0 +1,98 @@
#!/bin/bash
#
# qcow1 format input validation tests
#
# Copyright (C) 2014 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# creator
owner=kwolf@redhat.com
seq=`basename $0`
echo "QA output created by $seq"
here=`pwd`
tmp=/tmp/$$
status=1 # failure is the default!
_cleanup()
{
rm -f $TEST_IMG.snap
_cleanup_test_img
}
trap "_cleanup; exit \$status" 0 1 2 3 15
# get standard environment, filters and checks
. ./common.rc
. ./common.filter
_supported_fmt qcow
_supported_proto generic
_supported_os Linux
offset_backing_file_offset=8
offset_backing_file_size=16
offset_size=24
offset_cluster_bits=32
offset_l2_bits=33
echo
echo "== Invalid cluster size =="
_make_test_img 64M
poke_file "$TEST_IMG" "$offset_cluster_bits" "\xff"
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
poke_file "$TEST_IMG" "$offset_cluster_bits" "\x1f"
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
poke_file "$TEST_IMG" "$offset_cluster_bits" "\x08"
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
poke_file "$TEST_IMG" "$offset_cluster_bits" "\x11"
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
echo
echo "== Invalid L2 table size =="
_make_test_img 64M
poke_file "$TEST_IMG" "$offset_l2_bits" "\xff"
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
poke_file "$TEST_IMG" "$offset_l2_bits" "\x05"
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
poke_file "$TEST_IMG" "$offset_l2_bits" "\x0e"
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
# 1 << 0x1b = 2^31 / L2_CACHE_SIZE
poke_file "$TEST_IMG" "$offset_l2_bits" "\x1b"
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
echo
echo "== Invalid size =="
_make_test_img 64M
poke_file "$TEST_IMG" "$offset_size" "\xee\xee\xee\xee\xee\xee\xee\xee"
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
poke_file "$TEST_IMG" "$offset_size" "\x7f\xff\xff\xff\xff\xff\xff\xff"
{ $QEMU_IO -c "write 0 64M" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
echo
echo "== Invalid backing file length =="
_make_test_img 64M
poke_file "$TEST_IMG" "$offset_backing_file_offset" "\x00\x00\x00\xff"
poke_file "$TEST_IMG" "$offset_backing_file_size" "\xff\xff\xff\xff"
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
poke_file "$TEST_IMG" "$offset_backing_file_size" "\x7f\xff\xff\xff"
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
# success, all done
echo "*** done"
rm -f $seq.full
status=0

View file

@ -0,0 +1,38 @@
QA output created by 092
== Invalid cluster size ==
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
qemu-io: can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k
no file open, try 'help open'
qemu-io: can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k
no file open, try 'help open'
qemu-io: can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k
no file open, try 'help open'
qemu-io: can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k
no file open, try 'help open'
== Invalid L2 table size ==
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
qemu-io: can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k
no file open, try 'help open'
qemu-io: can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k
no file open, try 'help open'
qemu-io: can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k
no file open, try 'help open'
qemu-io: can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k
no file open, try 'help open'
== Invalid size ==
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
qemu-io: can't open device TEST_DIR/t.qcow: Image too large
no file open, try 'help open'
qemu-io: can't open device TEST_DIR/t.qcow: Image too large
no file open, try 'help open'
== Invalid backing file length ==
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
qemu-io: can't open device TEST_DIR/t.qcow: Backing file name too long
no file open, try 'help open'
qemu-io: can't open device TEST_DIR/t.qcow: Backing file name too long
no file open, try 'help open'
*** done

View file

@ -150,6 +150,7 @@ _filter_win32()
_filter_qemu_io()
{
_filter_win32 | sed -e "s/[0-9]* ops\; [0-9/:. sec]* ([0-9/.inf]* [EPTGMKiBbytes]*\/sec and [0-9/.inf]* ops\/sec)/X ops\; XX:XX:XX.X (XXX YYY\/sec and XXX ops\/sec)/" \
-e "s/: line [0-9][0-9]*: *[0-9][0-9]*\( Aborted\)/:\1/" \
-e "s/qemu-io> //g"
}

View file

@ -95,5 +95,7 @@
086 rw auto quick
087 rw auto
088 rw auto
089 rw auto quick
090 rw auto quick
091 rw auto
092 rw auto quick

View file

@ -335,6 +335,27 @@ void qemu_iovec_concat(QEMUIOVector *dst,
qemu_iovec_concat_iov(dst, src->iov, src->niov, soffset, sbytes);
}
/*
* Check if the contents of the iovecs are all zero
*/
bool qemu_iovec_is_zero(QEMUIOVector *qiov)
{
int i;
for (i = 0; i < qiov->niov; i++) {
size_t offs = QEMU_ALIGN_DOWN(qiov->iov[i].iov_len, 4 * sizeof(long));
uint8_t *ptr = qiov->iov[i].iov_base;
if (offs && !buffer_is_zero(qiov->iov[i].iov_base, offs)) {
return false;
}
for (; offs < qiov->iov[i].iov_len; offs++) {
if (ptr[offs]) {
return false;
}
}
}
return true;
}
void qemu_iovec_destroy(QEMUIOVector *qiov)
{
assert(qiov->nalloc != -1);