diff options
Diffstat (limited to 'block.c')
-rw-r--r-- | block.c | 343 |
1 files changed, 210 insertions, 133 deletions
@@ -332,10 +332,21 @@ void bdrv_register(BlockDriver *bdrv) } /* create a new block device (by default it is empty) */ -BlockDriverState *bdrv_new(const char *device_name) +BlockDriverState *bdrv_new(const char *device_name, Error **errp) { BlockDriverState *bs; + if (bdrv_find(device_name)) { + error_setg(errp, "Device with id '%s' already exists", + device_name); + return NULL; + } + if (bdrv_find_node(device_name)) { + error_setg(errp, "Device with node-name '%s' already exists", + device_name); + return NULL; + } + bs = g_malloc0(sizeof(BlockDriverState)); QLIST_INIT(&bs->dirty_bitmaps); pstrcpy(bs->device_name, sizeof(bs->device_name), device_name); @@ -767,6 +778,11 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags) { int open_flags = flags | BDRV_O_CACHE_WB; + /* The backing file of a temporary snapshot is read-only */ + if (flags & BDRV_O_SNAPSHOT) { + open_flags &= ~BDRV_O_RDWR; + } + /* * Clear flags that are internal to the block layer before opening the * image. @@ -783,38 +799,36 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags) return open_flags; } -static int bdrv_assign_node_name(BlockDriverState *bs, - const char *node_name, - Error **errp) +static void bdrv_assign_node_name(BlockDriverState *bs, + const char *node_name, + Error **errp) { if (!node_name) { - return 0; + return; } /* empty string node name is invalid */ if (node_name[0] == '\0') { error_setg(errp, "Empty node name"); - return -EINVAL; + return; } /* takes care of avoiding namespaces collisions */ if (bdrv_find(node_name)) { error_setg(errp, "node-name=%s is conflicting with a device id", node_name); - return -EINVAL; + return; } /* takes care of avoiding duplicates node names */ if (bdrv_find_node(node_name)) { error_setg(errp, "Duplicate node name"); - return -EINVAL; + return; } /* copy node name into the bs and insert it into the graph list */ pstrcpy(bs->node_name, sizeof(bs->node_name), node_name); QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs, node_list); - - return 0; } /* @@ -849,9 +863,10 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, trace_bdrv_open_common(bs, filename ?: "", flags, drv->format_name); node_name = qdict_get_try_str(options, "node-name"); - ret = bdrv_assign_node_name(bs, node_name, errp); - if (ret < 0) { - return ret; + bdrv_assign_node_name(bs, node_name, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return -EINVAL; } qdict_del(options, "node-name"); @@ -968,7 +983,7 @@ static int bdrv_file_open(BlockDriverState *bs, const char *filename, { BlockDriver *drv; const char *drvname; - bool allow_protocol_prefix = false; + bool parse_filename = false; Error *local_err = NULL; int ret; @@ -977,7 +992,7 @@ static int bdrv_file_open(BlockDriverState *bs, const char *filename, filename = qdict_get_try_str(*options, "filename"); } else if (filename && !qdict_haskey(*options, "filename")) { qdict_put(*options, "filename", qstring_from_str(filename)); - allow_protocol_prefix = true; + parse_filename = true; } else { error_setg(errp, "Can't specify 'file' and 'filename' options at the " "same time"); @@ -994,7 +1009,7 @@ static int bdrv_file_open(BlockDriverState *bs, const char *filename, } qdict_del(*options, "driver"); } else if (filename) { - drv = bdrv_find_protocol(filename, allow_protocol_prefix); + drv = bdrv_find_protocol(filename, parse_filename); if (!drv) { error_setg(errp, "Unknown protocol"); } @@ -1010,7 +1025,7 @@ static int bdrv_file_open(BlockDriverState *bs, const char *filename, } /* Parse the filename and open it */ - if (drv->bdrv_parse_filename && filename) { + if (drv->bdrv_parse_filename && parse_filename) { drv->bdrv_parse_filename(filename, *options, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -1053,14 +1068,14 @@ fail: */ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp) { - char backing_filename[PATH_MAX]; - int back_flags, ret; + char *backing_filename = g_malloc0(PATH_MAX); + int back_flags, ret = 0; BlockDriver *back_drv = NULL; Error *local_err = NULL; if (bs->backing_hd != NULL) { QDECREF(options); - return 0; + goto free_exit; } /* NULL means an empty set of options */ @@ -1073,10 +1088,9 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp) backing_filename[0] = '\0'; } else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) { QDECREF(options); - return 0; + goto free_exit; } else { - bdrv_get_full_backing_filename(bs, backing_filename, - sizeof(backing_filename)); + bdrv_get_full_backing_filename(bs, backing_filename, PATH_MAX); } if (bs->backing_format[0] != '\0') { @@ -1097,7 +1111,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp) error_setg(errp, "Could not open backing file: %s", error_get_pretty(local_err)); error_free(local_err); - return ret; + goto free_exit; } if (bs->backing_hd->file) { @@ -1108,7 +1122,9 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp) /* Recalculate the BlockLimits with the backing file */ bdrv_refresh_limits(bs); - return 0; +free_exit: + g_free(backing_filename); + return ret; } /* @@ -1162,6 +1178,75 @@ done: return ret; } +void bdrv_append_temp_snapshot(BlockDriverState *bs, Error **errp) +{ + /* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */ + char *tmp_filename = g_malloc0(PATH_MAX + 1); + int64_t total_size; + BlockDriver *bdrv_qcow2; + QEMUOptionParameter *create_options; + QDict *snapshot_options; + BlockDriverState *bs_snapshot; + Error *local_err; + int ret; + + /* if snapshot, we create a temporary backing file and open it + instead of opening 'filename' directly */ + + /* Get the required size from the image */ + total_size = bdrv_getlength(bs); + if (total_size < 0) { + error_setg_errno(errp, -total_size, "Could not get image size"); + goto out; + } + total_size &= BDRV_SECTOR_MASK; + + /* Create the temporary image */ + ret = get_tmp_filename(tmp_filename, PATH_MAX + 1); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not get temporary filename"); + goto out; + } + + bdrv_qcow2 = bdrv_find_format("qcow2"); + create_options = parse_option_parameters("", bdrv_qcow2->create_options, + NULL); + + set_option_parameter_int(create_options, BLOCK_OPT_SIZE, total_size); + + ret = bdrv_create(bdrv_qcow2, tmp_filename, create_options, &local_err); + free_option_parameters(create_options); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not create temporary overlay " + "'%s': %s", tmp_filename, + error_get_pretty(local_err)); + error_free(local_err); + goto out; + } + + /* Prepare a new options QDict for the temporary file */ + snapshot_options = qdict_new(); + qdict_put(snapshot_options, "file.driver", + qstring_from_str("file")); + qdict_put(snapshot_options, "file.filename", + qstring_from_str(tmp_filename)); + + bs_snapshot = bdrv_new("", &error_abort); + bs_snapshot->is_temporary = 1; + + ret = bdrv_open(&bs_snapshot, NULL, NULL, snapshot_options, + bs->open_flags & ~BDRV_O_SNAPSHOT, bdrv_qcow2, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + goto out; + } + + bdrv_append(bs_snapshot, bs); + +out: + g_free(tmp_filename); +} + /* * Opens a disk image (raw, qcow2, vmdk, ...) * @@ -1182,8 +1267,6 @@ int bdrv_open(BlockDriverState **pbs, const char *filename, BlockDriver *drv, Error **errp) { int ret; - /* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */ - char tmp_filename[PATH_MAX + 1]; BlockDriverState *file = NULL, *bs; const char *drvname; Error *local_err = NULL; @@ -1218,7 +1301,7 @@ int bdrv_open(BlockDriverState **pbs, const char *filename, if (*pbs) { bs = *pbs; } else { - bs = bdrv_new(""); + bs = bdrv_new("", &error_abort); } /* NULL means an empty set of options */ @@ -1243,74 +1326,6 @@ int bdrv_open(BlockDriverState **pbs, const char *filename, } } - /* For snapshot=on, create a temporary qcow2 overlay */ - if (flags & BDRV_O_SNAPSHOT) { - BlockDriverState *bs1; - int64_t total_size; - BlockDriver *bdrv_qcow2; - QEMUOptionParameter *create_options; - QDict *snapshot_options; - - /* if snapshot, we create a temporary backing file and open it - instead of opening 'filename' directly */ - - /* Get the required size from the image */ - QINCREF(options); - bs1 = NULL; - ret = bdrv_open(&bs1, filename, NULL, options, BDRV_O_NO_BACKING, - drv, &local_err); - if (ret < 0) { - goto fail; - } - total_size = bdrv_getlength(bs1) & BDRV_SECTOR_MASK; - - bdrv_unref(bs1); - - /* Create the temporary image */ - ret = get_tmp_filename(tmp_filename, sizeof(tmp_filename)); - if (ret < 0) { - error_setg_errno(errp, -ret, "Could not get temporary filename"); - goto fail; - } - - bdrv_qcow2 = bdrv_find_format("qcow2"); - create_options = parse_option_parameters("", bdrv_qcow2->create_options, - NULL); - - set_option_parameter_int(create_options, BLOCK_OPT_SIZE, total_size); - - ret = bdrv_create(bdrv_qcow2, tmp_filename, create_options, &local_err); - free_option_parameters(create_options); - if (ret < 0) { - error_setg_errno(errp, -ret, "Could not create temporary overlay " - "'%s': %s", tmp_filename, - error_get_pretty(local_err)); - error_free(local_err); - local_err = NULL; - goto fail; - } - - /* Prepare a new options QDict for the temporary file, where user - * options refer to the backing file */ - if (filename) { - qdict_put(options, "file.filename", qstring_from_str(filename)); - } - if (drv) { - qdict_put(options, "driver", qstring_from_str(drv->format_name)); - } - - snapshot_options = qdict_new(); - qdict_put(snapshot_options, "backing", options); - qdict_flatten(snapshot_options); - - bs->options = snapshot_options; - options = qdict_clone_shallow(bs->options); - - filename = tmp_filename; - drv = bdrv_qcow2; - bs->is_temporary = 1; - } - /* Open image file without format layer */ if (flags & BDRV_O_RDWR) { flags |= BDRV_O_ALLOW_RDWR; @@ -1321,7 +1336,7 @@ int bdrv_open(BlockDriverState **pbs, const char *filename, bdrv_open_flags(bs, flags | BDRV_O_UNMAP) | BDRV_O_PROTOCOL, true, &local_err); if (ret < 0) { - goto fail; + goto unlink_and_fail; } /* Find the right image format driver */ @@ -1372,6 +1387,17 @@ int bdrv_open(BlockDriverState **pbs, const char *filename, } } + /* For snapshot=on, create a temporary qcow2 overlay. bs points to the + * temporary snapshot afterwards. */ + if (flags & BDRV_O_SNAPSHOT) { + bdrv_append_temp_snapshot(bs, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto close_and_fail; + } + } + + done: /* Check if any unknown options were used */ if (options && (qdict_size(options) != 0)) { @@ -1388,12 +1414,19 @@ done: ret = -EINVAL; goto close_and_fail; } - QDECREF(options); if (!bdrv_key_required(bs)) { bdrv_dev_change_media_cb(bs, true); + } else if (!runstate_check(RUN_STATE_PRELAUNCH) + && !runstate_check(RUN_STATE_INMIGRATE) + && !runstate_check(RUN_STATE_PAUSED)) { /* HACK */ + error_setg(errp, + "Guest must be stopped for opening of encrypted image"); + ret = -EBUSY; + goto close_and_fail; } + QDECREF(options); *pbs = bs; return 0; @@ -2561,6 +2594,10 @@ static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset, { int64_t len; + if (size > INT_MAX) { + return -EIO; + } + if (!bdrv_is_inserted(bs)) return -ENOMEDIUM; @@ -2581,6 +2618,10 @@ static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset, static int bdrv_check_request(BlockDriverState *bs, int64_t sector_num, int nb_sectors) { + if (nb_sectors < 0 || nb_sectors > INT_MAX / BDRV_SECTOR_SIZE) { + return -EIO; + } + return bdrv_check_byte_request(bs, sector_num * BDRV_SECTOR_SIZE, nb_sectors * BDRV_SECTOR_SIZE); } @@ -2662,6 +2703,10 @@ static int bdrv_rw_co(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, .iov_len = nb_sectors * BDRV_SECTOR_SIZE, }; + if (nb_sectors < 0 || nb_sectors > INT_MAX / BDRV_SECTOR_SIZE) { + return -EINVAL; + } + qemu_iovec_init_external(&qiov, &iov, 1); return bdrv_prwv_co(bs, sector_num << BDRV_SECTOR_BITS, &qiov, is_write, flags); @@ -2717,10 +2762,16 @@ int bdrv_write_zeroes(BlockDriverState *bs, int64_t sector_num, */ int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags) { - int64_t target_size = bdrv_getlength(bs) / BDRV_SECTOR_SIZE; + int64_t target_size; int64_t ret, nb_sectors, sector_num = 0; int n; + target_size = bdrv_getlength(bs); + if (target_size < 0) { + return target_size; + } + target_size /= BDRV_SECTOR_SIZE; + for (;;) { nb_sectors = target_size - sector_num; if (nb_sectors <= 0) { @@ -4055,7 +4106,7 @@ int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag) int bdrv_debug_resume(BlockDriverState *bs, const char *tag) { - while (bs && bs->drv && !bs->drv->bdrv_debug_resume) { + while (bs && (!bs->drv || !bs->drv->bdrv_debug_resume)) { bs = bs->file; } @@ -4774,19 +4825,43 @@ flush_parent: return bdrv_co_flush(bs->file); } -void bdrv_invalidate_cache(BlockDriverState *bs) +void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp) { - if (bs->drv && bs->drv->bdrv_invalidate_cache) { - bs->drv->bdrv_invalidate_cache(bs); + Error *local_err = NULL; + int ret; + + if (!bs->drv) { + return; + } + + if (bs->drv->bdrv_invalidate_cache) { + bs->drv->bdrv_invalidate_cache(bs, &local_err); + } else if (bs->file) { + bdrv_invalidate_cache(bs->file, &local_err); + } + if (local_err) { + error_propagate(errp, local_err); + return; + } + + ret = refresh_total_sectors(bs, bs->total_sectors); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not refresh total sector count"); + return; } } -void bdrv_invalidate_cache_all(void) +void bdrv_invalidate_cache_all(Error **errp) { BlockDriverState *bs; + Error *local_err = NULL; QTAILQ_FOREACH(bs, &bdrv_states, device_list) { - bdrv_invalidate_cache(bs); + bdrv_invalidate_cache(bs, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } } } @@ -5048,7 +5123,8 @@ bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov) return true; } -BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, int granularity) +BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, int granularity, + Error **errp) { int64_t bitmap_size; BdrvDirtyBitmap *bitmap; @@ -5057,7 +5133,13 @@ BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, int granularity) granularity >>= BDRV_SECTOR_BITS; assert(granularity); - bitmap_size = (bdrv_getlength(bs) >> BDRV_SECTOR_BITS); + bitmap_size = bdrv_getlength(bs); + if (bitmap_size < 0) { + error_setg_errno(errp, -bitmap_size, "could not get length of device"); + errno = -bitmap_size; + return NULL; + } + bitmap_size >>= BDRV_SECTOR_BITS; bitmap = g_malloc0(sizeof(BdrvDirtyBitmap)); bitmap->bitmap = hbitmap_alloc(bitmap_size, ffs(granularity) - 1); QLIST_INSERT_HEAD(&bs->dirty_bitmaps, bitmap, list); @@ -5390,43 +5472,37 @@ int bdrv_amend_options(BlockDriverState *bs, QEMUOptionParameter *options) return bs->drv->bdrv_amend_options(bs, options); } -/* Used to recurse on single child block filters. - * Single child block filter will store their child in bs->file. +/* This function will be called by the bdrv_recurse_is_first_non_filter method + * of block filter and by bdrv_is_first_non_filter. + * It is used to test if the given bs is the candidate or recurse more in the + * node graph. */ -bool bdrv_generic_is_first_non_filter(BlockDriverState *bs, +bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs, BlockDriverState *candidate) { - if (!bs->drv) { - return false; - } - - if (!bs->drv->authorizations[BS_IS_A_FILTER]) { - if (bs == candidate) { - return true; - } else { - return false; - } - } - - if (!bs->drv->authorizations[BS_FILTER_PASS_DOWN]) { + /* return false if basic checks fails */ + if (!bs || !bs->drv) { return false; } - if (!bs->file) { - return false; + /* the code reached a non block filter driver -> check if the bs is + * the same as the candidate. It's the recursion termination condition. + */ + if (!bs->drv->is_filter) { + return bs == candidate; } + /* Down this path the driver is a block filter driver */ - return bdrv_recurse_is_first_non_filter(bs->file, candidate); -} - -bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs, - BlockDriverState *candidate) -{ - if (bs->drv && bs->drv->bdrv_recurse_is_first_non_filter) { + /* If the block filter recursion method is defined use it to recurse down + * the node graph. + */ + if (bs->drv->bdrv_recurse_is_first_non_filter) { return bs->drv->bdrv_recurse_is_first_non_filter(bs, candidate); } - return bdrv_generic_is_first_non_filter(bs, candidate); + /* the driver is a block filter but don't allow to recurse -> return false + */ + return false; } /* This function checks if the candidate is the first non filter bs down it's @@ -5441,6 +5517,7 @@ bool bdrv_is_first_non_filter(BlockDriverState *candidate) QTAILQ_FOREACH(bs, &bdrv_states, device_list) { bool perm; + /* try to recurse in this top level bs */ perm = bdrv_recurse_is_first_non_filter(bs, candidate); /* candidate is the first non filter */ |