diff --git a/block.c b/block.c index 4ad663d3fa..819eb4e009 100644 --- a/block.c +++ b/block.c @@ -667,10 +667,10 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags) * 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); @@ -698,6 +698,12 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, 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 { @@ -716,8 +722,15 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, 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); @@ -773,6 +786,18 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename, 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) { @@ -801,6 +826,7 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename, 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", @@ -809,7 +835,7 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename, 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; } @@ -838,18 +864,35 @@ 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; } @@ -864,7 +907,8 @@ int bdrv_open_backing_file(BlockDriverState *bs) /* 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); @@ -1008,7 +1052,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, } /* 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; } @@ -1020,7 +1064,10 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, /* 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; } diff --git a/block/blkdebug.c b/block/blkdebug.c index 37cfbc7fc8..71f99e4067 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -273,11 +273,6 @@ static int read_config(BDRVBlkdebugState *s, const char *filename) 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; @@ -304,44 +299,98 @@ fail: } /* 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 */ - ret = bdrv_file_open(&bs->file, filename, NULL, flags); - if (ret < 0) { - return ret; + filename = qemu_opt_get(opts, "x-image"); + if (filename == NULL) { + ret = -EINVAL; + goto fail; } - return 0; + ret = bdrv_file_open(&bs->file, filename, NULL, flags); + if (ret < 0) { + goto fail; + } + + ret = 0; +fail: + qemu_opts_del(opts); + return ret; } static void error_callback_bh(void *opaque) @@ -569,17 +618,17 @@ static int64_t blkdebug_getlength(BlockDriverState *bs) } static BlockDriver bdrv_blkdebug = { - .format_name = "blkdebug", - .protocol_name = "blkdebug", + .format_name = "blkdebug", + .protocol_name = "blkdebug", + .instance_size = sizeof(BDRVBlkdebugState), - .instance_size = sizeof(BDRVBlkdebugState), + .bdrv_parse_filename = blkdebug_parse_filename, + .bdrv_file_open = blkdebug_open, + .bdrv_close = blkdebug_close, + .bdrv_getlength = blkdebug_getlength, - .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, diff --git a/block/blkverify.c b/block/blkverify.c index 59e3b0562b..1d58cc3932 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -69,44 +69,100 @@ static void GCC_FMT_ATTR(2, 3) blkverify_err(BlkverifyAIOCB *acb, } /* 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; } - 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; - } + /* 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; + } + + ret = bdrv_file_open(&bs->file, raw, NULL, flags); + if (ret < 0) { + goto fail; + } /* 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) @@ -344,19 +400,18 @@ static BlockDriverAIOCB *blkverify_aio_flush(BlockDriverState *bs, } static BlockDriver bdrv_blkverify = { - .format_name = "blkverify", - .protocol_name = "blkverify", + .format_name = "blkverify", + .protocol_name = "blkverify", + .instance_size = sizeof(BDRVBlkverifyState), - .instance_size = sizeof(BDRVBlkverifyState), + .bdrv_parse_filename = blkverify_parse_filename, + .bdrv_file_open = blkverify_open, + .bdrv_close = blkverify_close, + .bdrv_getlength = blkverify_getlength, - .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, + .bdrv_aio_readv = blkverify_aio_readv, + .bdrv_aio_writev = blkverify_aio_writev, + .bdrv_aio_flush = blkverify_aio_flush, }; static void bdrv_blkverify_init(void) diff --git a/block/curl.c b/block/curl.c index 186e3b08ab..b8935fd991 100644 --- a/block/curl.c +++ b/block/curl.c @@ -335,12 +335,9 @@ static void curl_clean_state(CURLState *s) 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; @@ -348,19 +345,17 @@ static int curl_open(BlockDriverState *bs, const char *filename, 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; @@ -369,29 +364,77 @@ static int curl_open(BlockDriverState *bs, const char *filename, 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; @@ -423,6 +466,7 @@ static int curl_open(BlockDriverState *bs, const char *filename, curl_multi_setopt( s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb ); curl_multi_do(s); + qemu_opts_del(opts); return 0; out: @@ -430,7 +474,8 @@ out: curl_easy_cleanup(state->curl); state->curl = NULL; out_noclean: - g_free(file); + g_free(s->url); + qemu_opts_del(opts); return -EINVAL; } @@ -568,63 +613,68 @@ static int64_t curl_getlength(BlockDriverState *bs) } 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) diff --git a/block/gluster.c b/block/gluster.c index 9ccd4d443d..91acde248c 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -282,13 +282,42 @@ static int qemu_gluster_aio_flush_cb(void *opaque) 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) { @@ -322,6 +351,7 @@ static int qemu_gluster_open(BlockDriverState *bs, const char *filename, 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; diff --git a/block/iscsi.c b/block/iscsi.c index 92d6eae76f..f7199c1abb 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -1003,12 +1003,25 @@ out: 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://[%@][:]// */ -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; @@ -1016,6 +1029,9 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, 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) { @@ -1025,6 +1041,18 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, 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); @@ -1126,6 +1154,7 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, #endif out: + qemu_opts_del(opts); if (initiator_name != NULL) { g_free(initiator_name); } @@ -1190,6 +1219,7 @@ static int iscsi_create(const char *filename, QEMUOptionParameter *options) int64_t total_size = 0; BlockDriverState bs; IscsiLun *iscsilun = NULL; + QDict *bs_options; memset(&bs, 0, sizeof(BlockDriverState)); @@ -1204,7 +1234,11 @@ static int iscsi_create(const char *filename, QEMUOptionParameter *options) 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; } diff --git a/block/mirror.c b/block/mirror.c index a62ad86c28..8b07dec314 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -507,7 +507,7 @@ static void mirror_complete(BlockJob *job, Error **errp) 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, diff --git a/block/nbd.c b/block/nbd.c index d9dc454498..fab114bbb5 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -454,8 +454,7 @@ static void nbd_teardown_connection(BlockDriverState *bs) 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; diff --git a/block/qcow.c b/block/qcow.c index 3278e552bf..e2a64c79b1 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -787,8 +787,21 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num, 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); diff --git a/block/qcow2.c b/block/qcow2.c index e8934de185..2e346d8c42 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1537,8 +1537,21 @@ static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num, 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); diff --git a/block/raw-posix.c b/block/raw-posix.c index 99ac869780..c0ccf273a3 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -262,15 +262,42 @@ error: } #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; @@ -280,16 +307,18 @@ static int raw_open_common(BlockDriverState *bs, const char *filename, 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 @@ -300,16 +329,18 @@ static int raw_open_common(BlockDriverState *bs, const char *filename, } #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, @@ -1293,11 +1324,11 @@ static int check_hdev_writable(BDRVRawState *s) 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)) { @@ -1338,7 +1369,7 @@ static int hdev_open(BlockDriverState *bs, const char *filename, } #endif - ret = raw_open_common(bs, filename, flags, 0); + ret = raw_open_common(bs, options, flags, 0); if (ret < 0) { return ret; } @@ -1532,8 +1563,7 @@ static BlockDriver bdrv_host_device = { }; #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; @@ -1541,7 +1571,7 @@ static int floppy_open(BlockDriverState *bs, const char *filename, 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; @@ -1655,15 +1685,14 @@ static BlockDriver bdrv_host_floppy = { .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) @@ -1764,15 +1793,14 @@ static BlockDriver bdrv_host_cdrom = { #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; diff --git a/block/raw-win32.c b/block/raw-win32.c index ece2f1a809..7c03b6df52 100644 --- a/block/raw-win32.c +++ b/block/raw-win32.c @@ -221,21 +221,49 @@ static void raw_parse_flags(int flags, int *access_flags, DWORD *overlapped) } } -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; } } @@ -245,20 +273,27 @@ static int raw_open(BlockDriverState *bs, const char *filename, 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, @@ -495,13 +530,13 @@ static int hdev_probe_device(const char *filename) 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) diff --git a/block/rbd.c b/block/rbd.c index 141b488968..1826411492 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -441,8 +441,21 @@ static int qemu_rbd_aio_flush_cb(void *opaque) 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]; @@ -450,8 +463,23 @@ static int qemu_rbd_open(BlockDriverState *bs, const char *filename, 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), diff --git a/block/sheepdog.c b/block/sheepdog.c index 1c5b53220b..20b5d06c52 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -1126,8 +1126,21 @@ static int write_object(int fd, char *buf, uint64_t oid, int copies, 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; @@ -1135,6 +1148,20 @@ static int sd_open(BlockDriverState *bs, const char *filename, 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); @@ -1199,6 +1226,7 @@ static int sd_open(BlockDriverState *bs, const char *filename, 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: @@ -1206,6 +1234,7 @@ out: if (s->fd >= 0) { closesocket(s->fd); } + qemu_opts_del(opts); g_free(buf); return ret; } diff --git a/block/ssh.c b/block/ssh.c index 93a8b53ffd..246a70d274 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -608,8 +608,7 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options, 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; diff --git a/block/vvfat.c b/block/vvfat.c index ef74c30bfe..87b02799d0 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -1,4 +1,4 @@ -/* vim:set shiftwidth=4 ts=8: */ +/* vim:set shiftwidth=4 ts=4: */ /* * QEMU Block driver for virtual VFAT (shadows a local directory) * @@ -28,6 +28,8 @@ #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 @@ -988,11 +990,90 @@ static void vvfat_rebind(BlockDriverState *bs) 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; @@ -1003,6 +1084,65 @@ DLOG(if (stderr == NULL) { 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 */ @@ -1018,63 +1158,24 @@ DLOG(if (stderr == NULL) { 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; @@ -1094,7 +1195,10 @@ DLOG(if (stderr == NULL) { 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) @@ -2866,15 +2970,18 @@ static void vvfat_close(BlockDriverState *bs) } 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) diff --git a/include/block/block.h b/include/block/block.h index ebd95127a1..1251c5cf9f 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -137,7 +137,7 @@ int bdrv_parse_cache_flags(const char *mode, int *flags); 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, diff --git a/include/block/block_int.h b/include/block/block_int.h index 458cde3766..6078dd389f 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -87,8 +87,7 @@ struct BlockDriver { 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, diff --git a/qemu-img.c b/qemu-img.c index 31627b0da8..cd096a1361 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -1425,12 +1425,8 @@ static int img_convert(int argc, char **argv) } 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)); diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 index 8b51de3f31..8039e23ab3 100755 --- a/tests/qemu-iotests/051 +++ b/tests/qemu-iotests/051 @@ -142,6 +142,13 @@ run_qemu -drive media=cdrom,cache=writethrough 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 diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out index 48456d5b07..3d1ac7b7da 100644 --- a/tests/qemu-iotests/051.out +++ b/tests/qemu-iotests/051.out @@ -159,4 +159,14 @@ qququiquit 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 +qququiquit + +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 diff --git a/tests/qemu-iotests/053 b/tests/qemu-iotests/053 new file mode 100755 index 0000000000..bc56992582 --- /dev/null +++ b/tests/qemu-iotests/053 @@ -0,0 +1,73 @@ +#!/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 . +# + +# 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 + diff --git a/tests/qemu-iotests/053.out b/tests/qemu-iotests/053.out new file mode 100644 index 0000000000..16464e6dde --- /dev/null +++ b/tests/qemu-iotests/053.out @@ -0,0 +1,17 @@ +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 diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index bc5f250cab..dcf6391ea2 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -155,7 +155,7 @@ _filter_qemu_io() # 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 diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 324bacbcd4..68eabdaab9 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -59,3 +59,4 @@ 050 rw auto backing quick 051 rw auto 052 rw auto backing +053 rw auto