* Removes all processed options from *options.
*/
static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
- const char *filename, QDict *options,
- int flags, BlockDriver *drv)
+ QDict *options, int flags, BlockDriver *drv)
{
int ret, open_flags;
+ const char *filename;
assert(drv != NULL);
assert(bs->file == NULL);
bdrv_enable_copy_on_read(bs);
}
+ if (file != NULL) {
+ filename = file->filename;
+ } else {
+ filename = qdict_get_try_str(options, "filename");
+ }
+
if (filename != NULL) {
pstrcpy(bs->filename, sizeof(bs->filename), filename);
} else {
if (drv->bdrv_file_open) {
assert(file == NULL);
assert(drv->bdrv_parse_filename || filename != NULL);
- ret = drv->bdrv_file_open(bs, filename, options, open_flags);
+ ret = drv->bdrv_file_open(bs, options, open_flags);
} else {
+ if (file == NULL) {
+ qerror_report(ERROR_CLASS_GENERIC_ERROR, "Can't use '%s' as a "
+ "block driver for the protocol level",
+ drv->format_name);
+ ret = -EINVAL;
+ goto free_and_fail;
+ }
assert(file != NULL);
bs->file = file;
ret = drv->bdrv_open(bs, options, open_flags);
bs->options = options;
options = qdict_clone_shallow(options);
+ /* Fetch the file name from the options QDict if necessary */
+ if (!filename) {
+ filename = qdict_get_try_str(options, "filename");
+ } else if (filename && !qdict_haskey(options, "filename")) {
+ qdict_put(options, "filename", qstring_from_str(filename));
+ } else {
+ qerror_report(ERROR_CLASS_GENERIC_ERROR, "Can't specify 'file' and "
+ "'filename' options at the same time");
+ ret = -EINVAL;
+ goto fail;
+ }
+
/* Find the right block driver */
drvname = qdict_get_try_str(options, "driver");
if (drvname) {
ret = -EINVAL;
goto fail;
}
+ qdict_del(options, "filename");
} else if (!drv->bdrv_parse_filename && !filename) {
qerror_report(ERROR_CLASS_GENERIC_ERROR,
"The '%s' block driver requires a file name",
goto fail;
}
- ret = bdrv_open_common(bs, NULL, filename, options, flags, drv);
+ ret = bdrv_open_common(bs, NULL, options, flags, drv);
if (ret < 0) {
goto fail;
}
return ret;
}
-int bdrv_open_backing_file(BlockDriverState *bs)
+/*
+ * Opens the backing file for a BlockDriverState if not yet open
+ *
+ * options is a QDict of options to pass to the block drivers, or NULL for an
+ * empty set of options. The reference to the QDict is transferred to this
+ * function (even on failure), so if the caller intends to reuse the dictionary,
+ * it needs to use QINCREF() before calling bdrv_file_open.
+ */
+int bdrv_open_backing_file(BlockDriverState *bs, QDict *options)
{
char backing_filename[PATH_MAX];
int back_flags, ret;
BlockDriver *back_drv = NULL;
if (bs->backing_hd != NULL) {
+ QDECREF(options);
return 0;
}
+ /* NULL means an empty set of options */
+ if (options == NULL) {
+ options = qdict_new();
+ }
+
bs->open_flags &= ~BDRV_O_NO_BACKING;
- if (bs->backing_file[0] == '\0') {
+ if (qdict_haskey(options, "file.filename")) {
+ backing_filename[0] = '\0';
+ } else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) {
+ QDECREF(options);
return 0;
}
/* backing files always opened read-only */
back_flags = bs->open_flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT);
- ret = bdrv_open(bs->backing_hd, backing_filename, NULL,
+ ret = bdrv_open(bs->backing_hd,
+ *backing_filename ? backing_filename : NULL, options,
back_flags, back_drv);
if (ret < 0) {
bdrv_delete(bs->backing_hd);
}
/* Open the image */
- ret = bdrv_open_common(bs, file, filename, options, flags, drv);
+ ret = bdrv_open_common(bs, file, options, flags, drv);
if (ret < 0) {
goto unlink_and_fail;
}
/* If there is a backing file, use it */
if ((flags & BDRV_O_NO_BACKING) == 0) {
- ret = bdrv_open_backing_file(bs);
+ QDict *backing_options;
+
+ extract_subqdict(options, &backing_options, "backing.");
+ ret = bdrv_open_backing_file(bs, backing_options);
if (ret < 0) {
goto close_and_fail;
}
int ret;
struct add_rule_data d;
- /* Allow usage without config file */
- if (!*filename) {
- return 0;
- }
-
f = fopen(filename, "r");
if (f == NULL) {
return -errno;
}
/* Valid blkdebug filenames look like blkdebug:path/to/config:path/to/image */
-static int blkdebug_open(BlockDriverState *bs, const char *filename,
- QDict *options, int flags)
+static void blkdebug_parse_filename(const char *filename, QDict *options,
+ Error **errp)
{
- BDRVBlkdebugState *s = bs->opaque;
- int ret;
- char *config, *c;
+ const char *c;
/* Parse the blkdebug: prefix */
- if (strncmp(filename, "blkdebug:", strlen("blkdebug:"))) {
- return -EINVAL;
+ if (!strstart(filename, "blkdebug:", &filename)) {
+ error_setg(errp, "File name string must start with 'blkdebug:'");
+ return;
}
- filename += strlen("blkdebug:");
- /* Read rules from config file */
+ /* Parse config file path */
c = strchr(filename, ':');
if (c == NULL) {
- return -EINVAL;
+ error_setg(errp, "blkdebug requires both config file and image path");
+ return;
}
- config = g_strdup(filename);
- config[c - filename] = '\0';
- ret = read_config(s, config);
- g_free(config);
- if (ret < 0) {
- return ret;
+ if (c != filename) {
+ QString *config_path;
+ config_path = qstring_from_substr(filename, 0, c - filename - 1);
+ qdict_put(options, "config", config_path);
}
+
+ /* TODO Allow multi-level nesting and set file.filename here */
filename = c + 1;
+ qdict_put(options, "x-image", qstring_from_str(filename));
+}
+
+static QemuOptsList runtime_opts = {
+ .name = "blkdebug",
+ .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
+ .desc = {
+ {
+ .name = "config",
+ .type = QEMU_OPT_STRING,
+ .help = "Path to the configuration file",
+ },
+ {
+ .name = "x-image",
+ .type = QEMU_OPT_STRING,
+ .help = "[internal use only, will be removed]",
+ },
+ { /* end of list */ }
+ },
+};
+
+static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags)
+{
+ BDRVBlkdebugState *s = bs->opaque;
+ QemuOpts *opts;
+ Error *local_err = NULL;
+ const char *filename, *config;
+ int ret;
+
+ opts = qemu_opts_create_nofail(&runtime_opts);
+ qemu_opts_absorb_qdict(opts, options, &local_err);
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ /* Read rules from config file */
+ config = qemu_opt_get(opts, "config");
+ if (config) {
+ ret = read_config(s, config);
+ if (ret < 0) {
+ goto fail;
+ }
+ }
/* Set initial state */
s->state = 1;
/* Open the backing file */
+ filename = qemu_opt_get(opts, "x-image");
+ if (filename == NULL) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
ret = bdrv_file_open(&bs->file, filename, NULL, flags);
if (ret < 0) {
- return ret;
+ goto fail;
}
- return 0;
+ ret = 0;
+fail:
+ qemu_opts_del(opts);
+ return ret;
}
static void error_callback_bh(void *opaque)
}
static BlockDriver bdrv_blkdebug = {
- .format_name = "blkdebug",
- .protocol_name = "blkdebug",
-
- .instance_size = sizeof(BDRVBlkdebugState),
+ .format_name = "blkdebug",
+ .protocol_name = "blkdebug",
+ .instance_size = sizeof(BDRVBlkdebugState),
- .bdrv_file_open = blkdebug_open,
- .bdrv_close = blkdebug_close,
- .bdrv_getlength = blkdebug_getlength,
+ .bdrv_parse_filename = blkdebug_parse_filename,
+ .bdrv_file_open = blkdebug_open,
+ .bdrv_close = blkdebug_close,
+ .bdrv_getlength = blkdebug_getlength,
- .bdrv_aio_readv = blkdebug_aio_readv,
- .bdrv_aio_writev = blkdebug_aio_writev,
+ .bdrv_aio_readv = blkdebug_aio_readv,
+ .bdrv_aio_writev = blkdebug_aio_writev,
.bdrv_debug_event = blkdebug_debug_event,
.bdrv_debug_breakpoint = blkdebug_debug_breakpoint,
}
/* Valid blkverify filenames look like blkverify:path/to/raw_image:path/to/image */
-static int blkverify_open(BlockDriverState *bs, const char *filename,
- QDict *options, int flags)
+static void blkverify_parse_filename(const char *filename, QDict *options,
+ Error **errp)
{
- BDRVBlkverifyState *s = bs->opaque;
- int ret;
- char *raw, *c;
+ const char *c;
+ QString *raw_path;
+
/* Parse the blkverify: prefix */
- if (strncmp(filename, "blkverify:", strlen("blkverify:"))) {
- return -EINVAL;
+ if (!strstart(filename, "blkverify:", &filename)) {
+ error_setg(errp, "File name string must start with 'blkverify:'");
+ return;
}
- filename += strlen("blkverify:");
/* Parse the raw image filename */
c = strchr(filename, ':');
if (c == NULL) {
- return -EINVAL;
+ error_setg(errp, "blkverify requires raw copy and original image path");
+ return;
+ }
+
+ /* TODO Implement option pass-through and set raw.filename here */
+ raw_path = qstring_from_substr(filename, 0, c - filename - 1);
+ qdict_put(options, "x-raw", raw_path);
+
+ /* TODO Allow multi-level nesting and set file.filename here */
+ filename = c + 1;
+ qdict_put(options, "x-image", qstring_from_str(filename));
+}
+
+static QemuOptsList runtime_opts = {
+ .name = "blkverify",
+ .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
+ .desc = {
+ {
+ .name = "x-raw",
+ .type = QEMU_OPT_STRING,
+ .help = "[internal use only, will be removed]",
+ },
+ {
+ .name = "x-image",
+ .type = QEMU_OPT_STRING,
+ .help = "[internal use only, will be removed]",
+ },
+ { /* end of list */ }
+ },
+};
+
+static int blkverify_open(BlockDriverState *bs, QDict *options, int flags)
+{
+ BDRVBlkverifyState *s = bs->opaque;
+ QemuOpts *opts;
+ Error *local_err = NULL;
+ const char *filename, *raw;
+ int ret;
+
+ opts = qemu_opts_create_nofail(&runtime_opts);
+ qemu_opts_absorb_qdict(opts, options, &local_err);
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ /* Parse the raw image filename */
+ raw = qemu_opt_get(opts, "x-raw");
+ if (raw == NULL) {
+ ret = -EINVAL;
+ goto fail;
}
- raw = g_strdup(filename);
- raw[c - filename] = '\0';
ret = bdrv_file_open(&bs->file, raw, NULL, flags);
- g_free(raw);
if (ret < 0) {
- return ret;
+ goto fail;
}
- filename = c + 1;
/* Open the test file */
+ filename = qemu_opt_get(opts, "x-image");
+ if (filename == NULL) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
s->test_file = bdrv_new("");
ret = bdrv_open(s->test_file, filename, NULL, flags, NULL);
if (ret < 0) {
bdrv_delete(s->test_file);
s->test_file = NULL;
- return ret;
+ goto fail;
}
- return 0;
+ ret = 0;
+fail:
+ return ret;
}
static void blkverify_close(BlockDriverState *bs)
}
static BlockDriver bdrv_blkverify = {
- .format_name = "blkverify",
- .protocol_name = "blkverify",
-
- .instance_size = sizeof(BDRVBlkverifyState),
-
- .bdrv_getlength = blkverify_getlength,
-
- .bdrv_file_open = blkverify_open,
- .bdrv_close = blkverify_close,
-
- .bdrv_aio_readv = blkverify_aio_readv,
- .bdrv_aio_writev = blkverify_aio_writev,
- .bdrv_aio_flush = blkverify_aio_flush,
+ .format_name = "blkverify",
+ .protocol_name = "blkverify",
+ .instance_size = sizeof(BDRVBlkverifyState),
+
+ .bdrv_parse_filename = blkverify_parse_filename,
+ .bdrv_file_open = blkverify_open,
+ .bdrv_close = blkverify_close,
+ .bdrv_getlength = blkverify_getlength,
+
+ .bdrv_aio_readv = blkverify_aio_readv,
+ .bdrv_aio_writev = blkverify_aio_writev,
+ .bdrv_aio_flush = blkverify_aio_flush,
};
static void bdrv_blkverify_init(void)
s->in_use = 0;
}
-static int curl_open(BlockDriverState *bs, const char *filename,
- QDict *options, int flags)
+static void curl_parse_filename(const char *filename, QDict *options,
+ Error **errp)
{
- BDRVCURLState *s = bs->opaque;
- CURLState *state = NULL;
- double d;
#define RA_OPTSTR ":readahead="
char *file;
const char *ra_val;
int parse_state = 0;
- static int inited = 0;
-
file = g_strdup(filename);
- s->readahead_size = READ_AHEAD_SIZE;
/* Parse a trailing ":readahead=#:" param, if present. */
ra = file + strlen(file) - 1;
while (ra >= file) {
if (parse_state == 0) {
- if (*ra == ':')
+ if (*ra == ':') {
parse_state++;
- else
+ } else {
break;
+ }
} else if (parse_state == 1) {
if (*ra > '9' || *ra < '0') {
char *opt_start = ra - strlen(RA_OPTSTR) + 1;
ra_val = ra + 1;
ra -= strlen(RA_OPTSTR) - 1;
*ra = '\0';
- s->readahead_size = atoi(ra_val);
- break;
- } else {
- break;
+ qdict_put(options, "readahead", qstring_from_str(ra_val));
}
+ break;
}
}
ra--;
}
+ qdict_put(options, "url", qstring_from_str(file));
+
+ g_free(file);
+}
+
+static QemuOptsList runtime_opts = {
+ .name = "curl",
+ .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
+ .desc = {
+ {
+ .name = "url",
+ .type = QEMU_OPT_STRING,
+ .help = "URL to open",
+ },
+ {
+ .name = "readahead",
+ .type = QEMU_OPT_SIZE,
+ .help = "Readahead size",
+ },
+ { /* end of list */ }
+ },
+};
+
+static int curl_open(BlockDriverState *bs, QDict *options, int flags)
+{
+ BDRVCURLState *s = bs->opaque;
+ CURLState *state = NULL;
+ QemuOpts *opts;
+ Error *local_err = NULL;
+ const char *file;
+ double d;
+
+ static int inited = 0;
+
+ opts = qemu_opts_create_nofail(&runtime_opts);
+ qemu_opts_absorb_qdict(opts, options, &local_err);
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ goto out_noclean;
+ }
+
+ s->readahead_size = qemu_opt_get_size(opts, "readahead", READ_AHEAD_SIZE);
if ((s->readahead_size & 0x1ff) != 0) {
fprintf(stderr, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512\n",
s->readahead_size);
goto out_noclean;
}
+ file = qemu_opt_get(opts, "url");
+ if (file == NULL) {
+ qerror_report(ERROR_CLASS_GENERIC_ERROR, "curl block driver requires "
+ "an 'url' option");
+ goto out_noclean;
+ }
+
if (!inited) {
curl_global_init(CURL_GLOBAL_ALL);
inited = 1;
}
DPRINTF("CURL: Opening %s\n", file);
- s->url = file;
+ s->url = g_strdup(file);
state = curl_init_state(s);
if (!state)
goto out_noclean;
curl_multi_setopt( s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb );
curl_multi_do(s);
+ qemu_opts_del(opts);
return 0;
out:
curl_easy_cleanup(state->curl);
state->curl = NULL;
out_noclean:
- g_free(file);
+ g_free(s->url);
+ qemu_opts_del(opts);
return -EINVAL;
}
}
static BlockDriver bdrv_http = {
- .format_name = "http",
- .protocol_name = "http",
+ .format_name = "http",
+ .protocol_name = "http",
- .instance_size = sizeof(BDRVCURLState),
- .bdrv_file_open = curl_open,
- .bdrv_close = curl_close,
- .bdrv_getlength = curl_getlength,
+ .instance_size = sizeof(BDRVCURLState),
+ .bdrv_parse_filename = curl_parse_filename,
+ .bdrv_file_open = curl_open,
+ .bdrv_close = curl_close,
+ .bdrv_getlength = curl_getlength,
- .bdrv_aio_readv = curl_aio_readv,
+ .bdrv_aio_readv = curl_aio_readv,
};
static BlockDriver bdrv_https = {
- .format_name = "https",
- .protocol_name = "https",
+ .format_name = "https",
+ .protocol_name = "https",
- .instance_size = sizeof(BDRVCURLState),
- .bdrv_file_open = curl_open,
- .bdrv_close = curl_close,
- .bdrv_getlength = curl_getlength,
+ .instance_size = sizeof(BDRVCURLState),
+ .bdrv_parse_filename = curl_parse_filename,
+ .bdrv_file_open = curl_open,
+ .bdrv_close = curl_close,
+ .bdrv_getlength = curl_getlength,
- .bdrv_aio_readv = curl_aio_readv,
+ .bdrv_aio_readv = curl_aio_readv,
};
static BlockDriver bdrv_ftp = {
- .format_name = "ftp",
- .protocol_name = "ftp",
+ .format_name = "ftp",
+ .protocol_name = "ftp",
- .instance_size = sizeof(BDRVCURLState),
- .bdrv_file_open = curl_open,
- .bdrv_close = curl_close,
- .bdrv_getlength = curl_getlength,
+ .instance_size = sizeof(BDRVCURLState),
+ .bdrv_parse_filename = curl_parse_filename,
+ .bdrv_file_open = curl_open,
+ .bdrv_close = curl_close,
+ .bdrv_getlength = curl_getlength,
- .bdrv_aio_readv = curl_aio_readv,
+ .bdrv_aio_readv = curl_aio_readv,
};
static BlockDriver bdrv_ftps = {
- .format_name = "ftps",
- .protocol_name = "ftps",
+ .format_name = "ftps",
+ .protocol_name = "ftps",
- .instance_size = sizeof(BDRVCURLState),
- .bdrv_file_open = curl_open,
- .bdrv_close = curl_close,
- .bdrv_getlength = curl_getlength,
+ .instance_size = sizeof(BDRVCURLState),
+ .bdrv_parse_filename = curl_parse_filename,
+ .bdrv_file_open = curl_open,
+ .bdrv_close = curl_close,
+ .bdrv_getlength = curl_getlength,
- .bdrv_aio_readv = curl_aio_readv,
+ .bdrv_aio_readv = curl_aio_readv,
};
static BlockDriver bdrv_tftp = {
- .format_name = "tftp",
- .protocol_name = "tftp",
+ .format_name = "tftp",
+ .protocol_name = "tftp",
- .instance_size = sizeof(BDRVCURLState),
- .bdrv_file_open = curl_open,
- .bdrv_close = curl_close,
- .bdrv_getlength = curl_getlength,
+ .instance_size = sizeof(BDRVCURLState),
+ .bdrv_parse_filename = curl_parse_filename,
+ .bdrv_file_open = curl_open,
+ .bdrv_close = curl_close,
+ .bdrv_getlength = curl_getlength,
- .bdrv_aio_readv = curl_aio_readv,
+ .bdrv_aio_readv = curl_aio_readv,
};
static void curl_block_init(void)
return (s->qemu_aio_count > 0);
}
-static int qemu_gluster_open(BlockDriverState *bs, const char *filename,
- QDict *options, int bdrv_flags)
+/* TODO Convert to fine grained options */
+static QemuOptsList runtime_opts = {
+ .name = "gluster",
+ .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
+ .desc = {
+ {
+ .name = "filename",
+ .type = QEMU_OPT_STRING,
+ .help = "URL to the gluster image",
+ },
+ { /* end of list */ }
+ },
+};
+
+static int qemu_gluster_open(BlockDriverState *bs, QDict *options,
+ int bdrv_flags)
{
BDRVGlusterState *s = bs->opaque;
int open_flags = O_BINARY;
int ret = 0;
GlusterConf *gconf = g_malloc0(sizeof(GlusterConf));
+ QemuOpts *opts;
+ Error *local_err = NULL;
+ const char *filename;
+
+ opts = qemu_opts_create_nofail(&runtime_opts);
+ qemu_opts_absorb_qdict(opts, options, &local_err);
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ filename = qemu_opt_get(opts, "filename");
+
s->glfs = qemu_gluster_init(gconf, filename);
if (!s->glfs) {
qemu_gluster_aio_event_reader, NULL, qemu_gluster_aio_flush_cb, s);
out:
+ qemu_opts_del(opts);
qemu_gluster_gconf_free(gconf);
if (!ret) {
return ret;
return ret;
}
+/* TODO Convert to fine grained options */
+static QemuOptsList runtime_opts = {
+ .name = "iscsi",
+ .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
+ .desc = {
+ {
+ .name = "filename",
+ .type = QEMU_OPT_STRING,
+ .help = "URL to the iscsi image",
+ },
+ { /* end of list */ }
+ },
+};
+
/*
* We support iscsi url's on the form
* iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun>
*/
-static int iscsi_open(BlockDriverState *bs, const char *filename,
- QDict *options, int flags)
+static int iscsi_open(BlockDriverState *bs, QDict *options, int flags)
{
IscsiLun *iscsilun = bs->opaque;
struct iscsi_context *iscsi = NULL;
struct scsi_task *task = NULL;
struct scsi_inquiry_standard *inq = NULL;
char *initiator_name = NULL;
+ QemuOpts *opts;
+ Error *local_err = NULL;
+ const char *filename;
int ret;
if ((BDRV_SECTOR_SIZE % 512) != 0) {
return -EINVAL;
}
+ opts = qemu_opts_create_nofail(&runtime_opts);
+ qemu_opts_absorb_qdict(opts, options, &local_err);
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ filename = qemu_opt_get(opts, "filename");
+
+
iscsi_url = iscsi_parse_full_url(iscsi, filename);
if (iscsi_url == NULL) {
error_report("Failed to parse URL : %s", filename);
#endif
out:
+ qemu_opts_del(opts);
if (initiator_name != NULL) {
g_free(initiator_name);
}
int64_t total_size = 0;
BlockDriverState bs;
IscsiLun *iscsilun = NULL;
+ QDict *bs_options;
memset(&bs, 0, sizeof(BlockDriverState));
bs.opaque = g_malloc0(sizeof(struct IscsiLun));
iscsilun = bs.opaque;
- ret = iscsi_open(&bs, filename, NULL, 0);
+ bs_options = qdict_new();
+ qdict_put(bs_options, "filename", qstring_from_str(filename));
+ ret = iscsi_open(&bs, bs_options, 0);
+ QDECREF(bs_options);
+
if (ret != 0) {
goto out;
}
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
int ret;
- ret = bdrv_open_backing_file(s->target);
+ ret = bdrv_open_backing_file(s->target, NULL);
if (ret < 0) {
char backing_filename[PATH_MAX];
bdrv_get_full_backing_filename(s->target, backing_filename,
closesocket(s->sock);
}
-static int nbd_open(BlockDriverState *bs, const char* filename,
- QDict *options, int flags)
+static int nbd_open(BlockDriverState *bs, QDict *options, int flags)
{
BDRVNBDState *s = bs->opaque;
int result;
uint8_t *out_buf;
uint64_t cluster_offset;
- if (nb_sectors != s->cluster_sectors)
- return -EINVAL;
+ if (nb_sectors != s->cluster_sectors) {
+ ret = -EINVAL;
+
+ /* Zero-pad last write if image size is not cluster aligned */
+ if (sector_num + nb_sectors == bs->total_sectors &&
+ nb_sectors < s->cluster_sectors) {
+ uint8_t *pad_buf = qemu_blockalign(bs, s->cluster_size);
+ memset(pad_buf, 0, s->cluster_size);
+ memcpy(pad_buf, buf, nb_sectors * BDRV_SECTOR_SIZE);
+ ret = qcow_write_compressed(bs, sector_num,
+ pad_buf, s->cluster_sectors);
+ qemu_vfree(pad_buf);
+ }
+ return ret;
+ }
out_buf = g_malloc(s->cluster_size + (s->cluster_size / 1000) + 128);
return 0;
}
- if (nb_sectors != s->cluster_sectors)
- return -EINVAL;
+ if (nb_sectors != s->cluster_sectors) {
+ ret = -EINVAL;
+
+ /* Zero-pad last write if image size is not cluster aligned */
+ if (sector_num + nb_sectors == bs->total_sectors &&
+ nb_sectors < s->cluster_sectors) {
+ uint8_t *pad_buf = qemu_blockalign(bs, s->cluster_size);
+ memset(pad_buf, 0, s->cluster_size);
+ memcpy(pad_buf, buf, nb_sectors * BDRV_SECTOR_SIZE);
+ ret = qcow2_write_compressed(bs, sector_num,
+ pad_buf, s->cluster_sectors);
+ qemu_vfree(pad_buf);
+ }
+ return ret;
+ }
out_buf = g_malloc(s->cluster_size + (s->cluster_size / 1000) + 128);
}
#endif
-static int raw_open_common(BlockDriverState *bs, const char *filename,
+static QemuOptsList raw_runtime_opts = {
+ .name = "raw",
+ .head = QTAILQ_HEAD_INITIALIZER(raw_runtime_opts.head),
+ .desc = {
+ {
+ .name = "filename",
+ .type = QEMU_OPT_STRING,
+ .help = "File name of the image",
+ },
+ { /* end of list */ }
+ },
+};
+
+static int raw_open_common(BlockDriverState *bs, QDict *options,
int bdrv_flags, int open_flags)
{
BDRVRawState *s = bs->opaque;
+ QemuOpts *opts;
+ Error *local_err = NULL;
+ const char *filename;
int fd, ret;
+ opts = qemu_opts_create_nofail(&raw_runtime_opts);
+ qemu_opts_absorb_qdict(opts, options, &local_err);
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ filename = qemu_opt_get(opts, "filename");
+
ret = raw_normalize_devicepath(&filename);
if (ret != 0) {
- return ret;
+ goto fail;
}
s->open_flags = open_flags;
fd = qemu_open(filename, s->open_flags, 0644);
if (fd < 0) {
ret = -errno;
- if (ret == -EROFS)
+ if (ret == -EROFS) {
ret = -EACCES;
- return ret;
+ }
+ goto fail;
}
s->fd = fd;
#ifdef CONFIG_LINUX_AIO
if (raw_set_aio(&s->aio_ctx, &s->use_aio, bdrv_flags)) {
qemu_close(fd);
- return -errno;
+ ret = -errno;
+ goto fail;
}
#endif
}
#endif
- return 0;
+ ret = 0;
+fail:
+ qemu_opts_del(opts);
+ return ret;
}
-static int raw_open(BlockDriverState *bs, const char *filename,
- QDict *options, int flags)
+static int raw_open(BlockDriverState *bs, QDict *options, int flags)
{
BDRVRawState *s = bs->opaque;
s->type = FTYPE_FILE;
- return raw_open_common(bs, filename, flags, 0);
+ return raw_open_common(bs, options, flags, 0);
}
static int raw_reopen_prepare(BDRVReopenState *state,
return 0;
}
-static int hdev_open(BlockDriverState *bs, const char *filename,
- QDict *options, int flags)
+static int hdev_open(BlockDriverState *bs, QDict *options, int flags)
{
BDRVRawState *s = bs->opaque;
int ret;
+ const char *filename = qdict_get_str(options, "filename");
#if defined(__APPLE__) && defined(__MACH__)
if (strstart(filename, "/dev/cdrom", NULL)) {
}
#endif
- ret = raw_open_common(bs, filename, flags, 0);
+ ret = raw_open_common(bs, options, flags, 0);
if (ret < 0) {
return ret;
}
};
#ifdef __linux__
-static int floppy_open(BlockDriverState *bs, const char *filename,
- QDict *options, int flags)
+static int floppy_open(BlockDriverState *bs, QDict *options, int flags)
{
BDRVRawState *s = bs->opaque;
int ret;
s->type = FTYPE_FD;
/* open will not fail even if no floppy is inserted, so add O_NONBLOCK */
- ret = raw_open_common(bs, filename, flags, O_NONBLOCK);
+ ret = raw_open_common(bs, options, flags, O_NONBLOCK);
if (ret)
return ret;
.bdrv_eject = floppy_eject,
};
-static int cdrom_open(BlockDriverState *bs, const char *filename,
- QDict *options, int flags)
+static int cdrom_open(BlockDriverState *bs, QDict *options, int flags)
{
BDRVRawState *s = bs->opaque;
s->type = FTYPE_CD;
/* open will not fail even if no CD is inserted, so add O_NONBLOCK */
- return raw_open_common(bs, filename, flags, O_NONBLOCK);
+ return raw_open_common(bs, options, flags, O_NONBLOCK);
}
static int cdrom_probe_device(const char *filename)
#endif /* __linux__ */
#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
-static int cdrom_open(BlockDriverState *bs, const char *filename,
- QDict *options, int flags)
+static int cdrom_open(BlockDriverState *bs, QDict *options, int flags)
{
BDRVRawState *s = bs->opaque;
int ret;
s->type = FTYPE_CD;
- ret = raw_open_common(bs, filename, flags, 0);
+ ret = raw_open_common(bs, options, flags, 0);
if (ret)
return ret;
}
}
-static int raw_open(BlockDriverState *bs, const char *filename,
- QDict *options, int flags)
+static QemuOptsList raw_runtime_opts = {
+ .name = "raw",
+ .head = QTAILQ_HEAD_INITIALIZER(raw_runtime_opts.head),
+ .desc = {
+ {
+ .name = "filename",
+ .type = QEMU_OPT_STRING,
+ .help = "File name of the image",
+ },
+ { /* end of list */ }
+ },
+};
+
+static int raw_open(BlockDriverState *bs, QDict *options, int flags)
{
BDRVRawState *s = bs->opaque;
int access_flags;
DWORD overlapped;
+ QemuOpts *opts;
+ Error *local_err = NULL;
+ const char *filename;
+ int ret;
s->type = FTYPE_FILE;
+ opts = qemu_opts_create_nofail(&raw_runtime_opts);
+ qemu_opts_absorb_qdict(opts, options, &local_err);
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ filename = qemu_opt_get(opts, "filename");
+
raw_parse_flags(flags, &access_flags, &overlapped);
-
+
if ((flags & BDRV_O_NATIVE_AIO) && aio == NULL) {
aio = win32_aio_init();
if (aio == NULL) {
- return -EINVAL;
+ ret = -EINVAL;
+ goto fail;
}
}
if (s->hfile == INVALID_HANDLE_VALUE) {
int err = GetLastError();
- if (err == ERROR_ACCESS_DENIED)
- return -EACCES;
- return -EINVAL;
+ if (err == ERROR_ACCESS_DENIED) {
+ ret = -EACCES;
+ } else {
+ ret = -EINVAL;
+ }
+ goto fail;
}
if (flags & BDRV_O_NATIVE_AIO) {
- int ret = win32_aio_attach(aio, s->hfile);
+ ret = win32_aio_attach(aio, s->hfile);
if (ret < 0) {
CloseHandle(s->hfile);
- return ret;
+ goto fail;
}
s->aio = aio;
}
- return 0;
+
+ ret = 0;
+fail:
+ qemu_opts_del(opts);
+ return ret;
}
static BlockDriverAIOCB *raw_aio_readv(BlockDriverState *bs,
return 0;
}
-static int hdev_open(BlockDriverState *bs, const char *filename,
- QDict *options, int flags)
+static int hdev_open(BlockDriverState *bs, QDict *options, int flags)
{
BDRVRawState *s = bs->opaque;
int access_flags, create_flags;
DWORD overlapped;
char device_name[64];
+ const char *filename = qdict_get_str(options, "filename");
if (strstart(filename, "/dev/cdrom", NULL)) {
if (find_cdrom(device_name, sizeof(device_name)) < 0)
return (s->qemu_aio_count > 0);
}
-static int qemu_rbd_open(BlockDriverState *bs, const char *filename,
- QDict *options, int flags)
+/* TODO Convert to fine grained options */
+static QemuOptsList runtime_opts = {
+ .name = "rbd",
+ .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
+ .desc = {
+ {
+ .name = "filename",
+ .type = QEMU_OPT_STRING,
+ .help = "Specification of the rbd image",
+ },
+ { /* end of list */ }
+ },
+};
+
+static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags)
{
BDRVRBDState *s = bs->opaque;
char pool[RBD_MAX_POOL_NAME_SIZE];
char conf[RBD_MAX_CONF_SIZE];
char clientname_buf[RBD_MAX_CONF_SIZE];
char *clientname;
+ QemuOpts *opts;
+ Error *local_err = NULL;
+ const char *filename;
int r;
+ opts = qemu_opts_create_nofail(&runtime_opts);
+ qemu_opts_absorb_qdict(opts, options, &local_err);
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ qemu_opts_del(opts);
+ return -EINVAL;
+ }
+
+ filename = qemu_opt_get(opts, "filename");
+ qemu_opts_del(opts);
+
if (qemu_rbd_parsename(filename, pool, sizeof(pool),
snap_buf, sizeof(snap_buf),
s->name, sizeof(s->name),
create, cache_flags);
}
-static int sd_open(BlockDriverState *bs, const char *filename,
- QDict *options, int flags)
+/* TODO Convert to fine grained options */
+static QemuOptsList runtime_opts = {
+ .name = "sheepdog",
+ .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
+ .desc = {
+ {
+ .name = "filename",
+ .type = QEMU_OPT_STRING,
+ .help = "URL to the sheepdog image",
+ },
+ { /* end of list */ }
+ },
+};
+
+static int sd_open(BlockDriverState *bs, QDict *options, int flags)
{
int ret, fd;
uint32_t vid = 0;
char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN];
uint32_t snapid;
char *buf = NULL;
+ QemuOpts *opts;
+ Error *local_err = NULL;
+ const char *filename;
+
+ opts = qemu_opts_create_nofail(&runtime_opts);
+ qemu_opts_absorb_qdict(opts, options, &local_err);
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ filename = qemu_opt_get(opts, "filename");
QLIST_INIT(&s->inflight_aio_head);
QLIST_INIT(&s->pending_aio_head);
bs->total_sectors = s->inode.vdi_size / SECTOR_SIZE;
pstrcpy(s->name, sizeof(s->name), vdi);
qemu_co_mutex_init(&s->lock);
+ qemu_opts_del(opts);
g_free(buf);
return 0;
out:
if (s->fd >= 0) {
closesocket(s->fd);
}
+ qemu_opts_del(opts);
g_free(buf);
return ret;
}
return ret;
}
-static int ssh_file_open(BlockDriverState *bs, const char *filename,
- QDict *options, int bdrv_flags)
+static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags)
{
BDRVSSHState *s = bs->opaque;
int ret;
-/* vim:set shiftwidth=4 ts=8: */
+/* vim:set shiftwidth=4 ts=4: */
/*
* QEMU Block driver for virtual VFAT (shadows a local directory)
*
#include "block/block_int.h"
#include "qemu/module.h"
#include "migration/migration.h"
+#include "qapi/qmp/qint.h"
+#include "qapi/qmp/qbool.h"
#ifndef S_IWGRP
#define S_IWGRP 0
s->bs = bs;
}
-static int vvfat_open(BlockDriverState *bs, const char* dirname,
- QDict *options, int flags)
+static QemuOptsList runtime_opts = {
+ .name = "vvfat",
+ .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
+ .desc = {
+ {
+ .name = "dir",
+ .type = QEMU_OPT_STRING,
+ .help = "Host directory to map to the vvfat device",
+ },
+ {
+ .name = "fat-type",
+ .type = QEMU_OPT_NUMBER,
+ .help = "FAT type (12, 16 or 32)",
+ },
+ {
+ .name = "floppy",
+ .type = QEMU_OPT_BOOL,
+ .help = "Create a floppy rather than a hard disk image",
+ },
+ {
+ .name = "rw",
+ .type = QEMU_OPT_BOOL,
+ .help = "Make the image writable",
+ },
+ { /* end of list */ }
+ },
+};
+
+static void vvfat_parse_filename(const char *filename, QDict *options,
+ Error **errp)
+{
+ int fat_type = 0;
+ bool floppy = false;
+ bool rw = false;
+ int i;
+
+ if (!strstart(filename, "fat:", NULL)) {
+ error_setg(errp, "File name string must start with 'fat:'");
+ return;
+ }
+
+ /* Parse options */
+ if (strstr(filename, ":32:")) {
+ fat_type = 32;
+ } else if (strstr(filename, ":16:")) {
+ fat_type = 16;
+ } else if (strstr(filename, ":12:")) {
+ fat_type = 12;
+ }
+
+ if (strstr(filename, ":floppy:")) {
+ floppy = true;
+ }
+
+ if (strstr(filename, ":rw:")) {
+ rw = true;
+ }
+
+ /* Get the directory name without options */
+ i = strrchr(filename, ':') - filename;
+ assert(i >= 3);
+ if (filename[i - 2] == ':' && qemu_isalpha(filename[i - 1])) {
+ /* workaround for DOS drive names */
+ filename += i - 1;
+ } else {
+ filename += i + 1;
+ }
+
+ /* Fill in the options QDict */
+ qdict_put(options, "dir", qstring_from_str(filename));
+ qdict_put(options, "fat-type", qint_from_int(fat_type));
+ qdict_put(options, "floppy", qbool_from_int(floppy));
+ qdict_put(options, "rw", qbool_from_int(rw));
+}
+
+static int vvfat_open(BlockDriverState *bs, QDict *options, int flags)
{
BDRVVVFATState *s = bs->opaque;
- int i, cyls, heads, secs;
+ int cyls, heads, secs;
+ bool floppy;
+ const char *dirname;
+ QemuOpts *opts;
+ Error *local_err = NULL;
+ int ret;
#ifdef DEBUG
vvv = s;
setbuf(stderr, NULL);
})
+ opts = qemu_opts_create_nofail(&runtime_opts);
+ qemu_opts_absorb_qdict(opts, options, &local_err);
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ dirname = qemu_opt_get(opts, "dir");
+ if (!dirname) {
+ qerror_report(ERROR_CLASS_GENERIC_ERROR, "vvfat block driver requires "
+ "a 'dir' option");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ s->fat_type = qemu_opt_get_number(opts, "fat-type", 0);
+ floppy = qemu_opt_get_bool(opts, "floppy", false);
+
+ if (floppy) {
+ /* 1.44MB or 2.88MB floppy. 2.88MB can be FAT12 (default) or FAT16. */
+ if (!s->fat_type) {
+ s->fat_type = 12;
+ secs = 36;
+ s->sectors_per_cluster = 2;
+ } else {
+ secs = s->fat_type == 12 ? 18 : 36;
+ s->sectors_per_cluster = 1;
+ }
+ s->first_sectors_number = 1;
+ cyls = 80;
+ heads = 2;
+ } else {
+ /* 32MB or 504MB disk*/
+ if (!s->fat_type) {
+ s->fat_type = 16;
+ }
+ cyls = s->fat_type == 12 ? 64 : 1024;
+ heads = 16;
+ secs = 63;
+ }
+
+ switch (s->fat_type) {
+ case 32:
+ fprintf(stderr, "Big fat greek warning: FAT32 has not been tested. "
+ "You are welcome to do so!\n");
+ break;
+ case 16:
+ case 12:
+ break;
+ default:
+ qerror_report(ERROR_CLASS_GENERIC_ERROR, "Valid FAT types are only "
+ "12, 16 and 32");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+
s->bs = bs;
/* LATER TODO: if FAT32, adjust */
s->fat2 = NULL;
s->downcase_short_names = 1;
- if (!strstart(dirname, "fat:", NULL))
- return -1;
-
- if (strstr(dirname, ":32:")) {
- fprintf(stderr, "Big fat greek warning: FAT32 has not been tested. You are welcome to do so!\n");
- s->fat_type = 32;
- } else if (strstr(dirname, ":16:")) {
- s->fat_type = 16;
- } else if (strstr(dirname, ":12:")) {
- s->fat_type = 12;
- }
-
- if (strstr(dirname, ":floppy:")) {
- /* 1.44MB or 2.88MB floppy. 2.88MB can be FAT12 (default) or FAT16. */
- if (!s->fat_type) {
- s->fat_type = 12;
- secs = 36;
- s->sectors_per_cluster=2;
- } else {
- secs = s->fat_type == 12 ? 18 : 36;
- s->sectors_per_cluster=1;
- }
- s->first_sectors_number = 1;
- cyls = 80;
- heads = 2;
- } else {
- /* 32MB or 504MB disk*/
- if (!s->fat_type) {
- s->fat_type = 16;
- }
- cyls = s->fat_type == 12 ? 64 : 1024;
- heads = 16;
- secs = 63;
- }
fprintf(stderr, "vvfat %s chs %d,%d,%d\n",
dirname, cyls, heads, secs);
s->sector_count = cyls * heads * secs - (s->first_sectors_number - 1);
- if (strstr(dirname, ":rw:")) {
- if (enable_write_target(s))
- return -1;
- bs->read_only = 0;
+ if (qemu_opt_get_bool(opts, "rw", false)) {
+ if (enable_write_target(s)) {
+ ret = -EIO;
+ goto fail;
+ }
+ bs->read_only = 0;
}
- i = strrchr(dirname, ':') - dirname;
- assert(i >= 3);
- if (dirname[i-2] == ':' && qemu_isalpha(dirname[i-1]))
- /* workaround for DOS drive names */
- dirname += i-1;
- else
- dirname += i+1;
-
bs->total_sectors = cyls * heads * secs;
if (init_directories(s, dirname, heads, secs)) {
- return -1;
+ ret = -EIO;
+ goto fail;
}
s->sector_count = s->faked_sectors + s->sectors_per_cluster*s->cluster_count;
migrate_add_blocker(s->migration_blocker);
}
- return 0;
+ ret = 0;
+fail:
+ qemu_opts_del(opts);
+ return ret;
}
static inline void vvfat_close_current_file(BDRVVVFATState *s)
}
static BlockDriver bdrv_vvfat = {
- .format_name = "vvfat",
- .instance_size = sizeof(BDRVVVFATState),
- .bdrv_file_open = vvfat_open,
- .bdrv_rebind = vvfat_rebind,
- .bdrv_read = vvfat_co_read,
- .bdrv_write = vvfat_co_write,
- .bdrv_close = vvfat_close,
- .bdrv_co_is_allocated = vvfat_co_is_allocated,
- .protocol_name = "fat",
+ .format_name = "vvfat",
+ .protocol_name = "fat",
+ .instance_size = sizeof(BDRVVVFATState),
+
+ .bdrv_parse_filename = vvfat_parse_filename,
+ .bdrv_file_open = vvfat_open,
+ .bdrv_close = vvfat_close,
+ .bdrv_rebind = vvfat_rebind,
+
+ .bdrv_read = vvfat_co_read,
+ .bdrv_write = vvfat_co_write,
+ .bdrv_co_is_allocated = vvfat_co_is_allocated,
};
static void bdrv_vvfat_init(void)
int bdrv_parse_discard_flags(const char *mode, int *flags);
int bdrv_file_open(BlockDriverState **pbs, const char *filename,
QDict *options, int flags);
-int bdrv_open_backing_file(BlockDriverState *bs);
+int bdrv_open_backing_file(BlockDriverState *bs, QDict *options);
int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
int flags, BlockDriver *drv);
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
void (*bdrv_reopen_abort)(BDRVReopenState *reopen_state);
int (*bdrv_open)(BlockDriverState *bs, QDict *options, int flags);
- int (*bdrv_file_open)(BlockDriverState *bs, const char *filename,
- QDict *options, int flags);
+ int (*bdrv_file_open)(BlockDriverState *bs, QDict *options, int flags);
int (*bdrv_read)(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors);
int (*bdrv_write)(BlockDriverState *bs, int64_t sector_num,
}
assert (remainder == 0);
- if (n < cluster_sectors) {
- memset(buf + n * 512, 0, cluster_size - n * 512);
- }
- if (!buffer_is_zero(buf, cluster_size)) {
- ret = bdrv_write_compressed(out_bs, sector_num, buf,
- cluster_sectors);
+ if (!buffer_is_zero(buf, n * BDRV_SECTOR_SIZE)) {
+ ret = bdrv_write_compressed(out_bs, sector_num, buf, n);
if (ret != 0) {
error_report("error while compressing sector %" PRId64
": %s", sector_num, strerror(-ret));
run_qemu -drive media=cdrom,cache=unsafe
run_qemu -drive media=cdrom,cache=invalid_value
+echo
+echo === Specifying the protocol layer ===
+echo
+
+run_qemu -drive file=$TEST_IMG,file.driver=file
+run_qemu -drive file=$TEST_IMG,file.driver=qcow2
+
# success, all done
echo "*** done"
rm -f $seq.full
Testing: -drive media=cdrom,cache=invalid_value
QEMU_PROG: -drive media=cdrom,cache=invalid_value: invalid cache option
+
+=== Specifying the protocol layer ===
+
+Testing: -drive file=TEST_DIR/t.qcow2,file.driver=file
+q\e[K\e[Dqu\e[K\e[D\e[Dqui\e[K\e[D\e[D\e[Dquit\e[K\r
+
+Testing: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: Can't use 'qcow2' as a block driver for the protocol level
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+
*** done
--- /dev/null
+#!/bin/bash
+#
+# Test qemu-img convert when image length is not a multiple of cluster size
+#
+# Copyright (C) 2013 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=stefanha@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.orig
+ _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 qcow
+_supported_proto file
+_supported_os Linux
+
+echo
+echo "== Creating single sector image =="
+
+_make_test_img 512
+$QEMU_IO -c "write -P0xa 0 512" $TEST_IMG | _filter_qemu_io
+mv $TEST_IMG $TEST_IMG.orig
+
+echo
+echo "== Converting the image, compressed =="
+
+$QEMU_IMG convert -c -O $IMGFMT $TEST_IMG.orig $TEST_IMG
+_check_test_img
+
+echo
+echo "== Checking compressed image virtual disk size =="
+
+_img_info | grep '^virtual size:'
+
+echo
+echo "== Verifying the compressed image =="
+
+$QEMU_IO -c "read -P0xa 0 512" $TEST_IMG | _filter_qemu_io
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
+
--- /dev/null
+QA output created by 053
+
+== Creating single sector image ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=512
+wrote 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Converting the image, compressed ==
+No errors were found on the image.
+
+== Checking compressed image virtual disk size ==
+virtual size: 512 (512 bytes)
+
+== Verifying the compressed image ==
+read 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+*** done
# replace occurrences of QEMU_PROG with "qemu"
_filter_qemu()
{
- sed -e "s#$(basename $QEMU_PROG)#QEMU_PROG#g"
+ sed -e "s#^$(basename $QEMU_PROG):#QEMU_PROG:#"
}
# make sure this script returns success
050 rw auto backing quick
051 rw auto
052 rw auto backing
+053 rw auto