X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/f4b5b021c847669b1c78050aea26fe9abceef6dd..9360447d34f17ce5680242e2687e2e4ba9182634:/block.c diff --git a/block.c b/block.c index 5db266be21..fa1d06d846 100644 --- a/block.c +++ b/block.c @@ -163,11 +163,16 @@ void path_combine(char *dest, int dest_size, if (path_is_absolute(filename)) { pstrcpy(dest, dest_size, filename); } else { - p = strchr(base_path, ':'); - if (p) - p++; - else - p = base_path; + const char *protocol_stripped = NULL; + + if (path_has_protocol(base_path)) { + protocol_stripped = strchr(base_path, ':'); + if (protocol_stripped) { + protocol_stripped++; + } + } + p = protocol_stripped ?: base_path; + p1 = strrchr(base_path, '/'); #ifdef _WIN32 { @@ -192,11 +197,55 @@ void path_combine(char *dest, int dest_size, } } +/* + * Helper function for bdrv_parse_filename() implementations to remove optional + * protocol prefixes (especially "file:") from a filename and for putting the + * stripped filename into the options QDict if there is such a prefix. + */ +void bdrv_parse_filename_strip_prefix(const char *filename, const char *prefix, + QDict *options) +{ + if (strstart(filename, prefix, &filename)) { + /* Stripping the explicit protocol prefix may result in a protocol + * prefix being (wrongly) detected (if the filename contains a colon) */ + if (path_has_protocol(filename)) { + QString *fat_filename; + + /* This means there is some colon before the first slash; therefore, + * this cannot be an absolute path */ + assert(!path_is_absolute(filename)); + + /* And we can thus fix the protocol detection issue by prefixing it + * by "./" */ + fat_filename = qstring_from_str("./"); + qstring_append(fat_filename, filename); + + assert(!path_has_protocol(qstring_get_str(fat_filename))); + + qdict_put(options, "filename", fat_filename); + } else { + /* If no protocol prefix was detected, we can use the shortened + * filename as-is */ + qdict_put_str(options, "filename", filename); + } + } +} + + +/* Returns whether the image file is opened as read-only. Note that this can + * return false and writing to the image file is still not possible because the + * image is inactivated. */ bool bdrv_is_read_only(BlockDriverState *bs) { return bs->read_only; } +/* Returns whether the image file can be written to right now */ +bool bdrv_is_writable(BlockDriverState *bs) +{ + return !bdrv_is_read_only(bs) && !(bs->open_flags & BDRV_O_INACTIVE); +} + int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, Error **errp) { /* Do not set read_only if copy_on_read is enabled */ @@ -762,6 +811,13 @@ static void bdrv_child_cb_drained_end(BdrvChild *child) bdrv_drained_end(bs); } +static int bdrv_child_cb_inactivate(BdrvChild *child) +{ + BlockDriverState *bs = child->opaque; + assert(bs->open_flags & BDRV_O_INACTIVE); + return 0; +} + /* * Returns the options and flags that a temporary snapshot should get, based on * the originally requested flags (the originally requested image will have @@ -800,6 +856,7 @@ static void bdrv_inherited_options(int *child_flags, QDict *child_options, * the parent. */ qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_DIRECT); qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_NO_FLUSH); + qdict_copy_default(child_options, parent_options, BDRV_OPT_FORCE_SHARE); /* Inherit the read-only option from the parent if it's not set */ qdict_copy_default(child_options, parent_options, BDRV_OPT_READ_ONLY); @@ -821,6 +878,7 @@ const BdrvChildRole child_file = { .inherit_options = bdrv_inherited_options, .drained_begin = bdrv_child_cb_drained_begin, .drained_end = bdrv_child_cb_drained_end, + .inactivate = bdrv_child_cb_inactivate, }; /* @@ -842,6 +900,7 @@ const BdrvChildRole child_format = { .inherit_options = bdrv_inherited_fmt_options, .drained_begin = bdrv_child_cb_drained_begin, .drained_end = bdrv_child_cb_drained_end, + .inactivate = bdrv_child_cb_inactivate, }; static void bdrv_backing_attach(BdrvChild *c) @@ -908,6 +967,7 @@ static void bdrv_backing_options(int *child_flags, QDict *child_options, * which is only applied on the top level (BlockBackend) */ qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_DIRECT); qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_NO_FLUSH); + qdict_copy_default(child_options, parent_options, BDRV_OPT_FORCE_SHARE); /* backing files always opened read-only */ qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "on"); @@ -926,6 +986,7 @@ const BdrvChildRole child_backing = { .inherit_options = bdrv_backing_options, .drained_begin = bdrv_child_cb_drained_begin, .drained_end = bdrv_child_cb_drained_end, + .inactivate = bdrv_child_cb_inactivate, }; static int bdrv_open_flags(BlockDriverState *bs, int flags) @@ -974,16 +1035,14 @@ static void update_flags_from_options(int *flags, QemuOpts *opts) static void update_options_from_flags(QDict *options, int flags) { if (!qdict_haskey(options, BDRV_OPT_CACHE_DIRECT)) { - qdict_put(options, BDRV_OPT_CACHE_DIRECT, - qbool_from_bool(flags & BDRV_O_NOCACHE)); + qdict_put_bool(options, BDRV_OPT_CACHE_DIRECT, flags & BDRV_O_NOCACHE); } if (!qdict_haskey(options, BDRV_OPT_CACHE_NO_FLUSH)) { - qdict_put(options, BDRV_OPT_CACHE_NO_FLUSH, - qbool_from_bool(flags & BDRV_O_NO_FLUSH)); + qdict_put_bool(options, BDRV_OPT_CACHE_NO_FLUSH, + flags & BDRV_O_NO_FLUSH); } if (!qdict_haskey(options, BDRV_OPT_READ_ONLY)) { - qdict_put(options, BDRV_OPT_READ_ONLY, - qbool_from_bool(!(flags & BDRV_O_RDWR))); + qdict_put_bool(options, BDRV_OPT_READ_ONLY, !(flags & BDRV_O_RDWR)); } } @@ -1152,6 +1211,11 @@ QemuOptsList bdrv_runtime_opts = { .type = QEMU_OPT_STRING, .help = "discard operation (ignore/off, unmap/on)", }, + { + .name = BDRV_OPT_FORCE_SHARE, + .type = QEMU_OPT_BOOL, + .help = "always accept other writers (default: off)", + }, { /* end of list */ } }, }; @@ -1191,6 +1255,16 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file, drv = bdrv_find_format(driver_name); assert(drv != NULL); + bs->force_share = qemu_opt_get_bool(opts, BDRV_OPT_FORCE_SHARE, false); + + if (bs->force_share && (bs->open_flags & BDRV_O_RDWR)) { + error_setg(errp, + BDRV_OPT_FORCE_SHARE + "=on can only be used with read-only images"); + ret = -EINVAL; + goto fail_opts; + } + if (file != NULL) { filename = blk_bs(file)->filename; } else { @@ -1204,7 +1278,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file, filename = qdict_get_try_str(options, "filename"); } - if (drv->bdrv_needs_filename && !filename) { + if (drv->bdrv_needs_filename && (!filename || !filename[0])) { error_setg(errp, "The '%s' block driver requires a file name", drv->format_name); ret = -EINVAL; @@ -1399,7 +1473,7 @@ static int bdrv_fill_options(QDict **options, const char *filename, /* Fetch the file name from the options QDict if necessary */ if (protocol && filename) { if (!qdict_haskey(*options, "filename")) { - qdict_put(*options, "filename", qstring_from_str(filename)); + qdict_put_str(*options, "filename", filename); parse_filename = true; } else { error_setg(errp, "Can't specify 'file' and 'filename' options at " @@ -1420,7 +1494,7 @@ static int bdrv_fill_options(QDict **options, const char *filename, } drvname = drv->format_name; - qdict_put(*options, "driver", qstring_from_str(drvname)); + qdict_put_str(*options, "driver", drvname); } else { error_setg(errp, "Must specify either driver or file"); return -EINVAL; @@ -1450,6 +1524,22 @@ static int bdrv_child_check_perm(BdrvChild *c, uint64_t perm, uint64_t shared, static void bdrv_child_abort_perm_update(BdrvChild *c); static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared); +static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs, + BdrvChild *c, + const BdrvChildRole *role, + uint64_t parent_perm, uint64_t parent_shared, + uint64_t *nperm, uint64_t *nshared) +{ + if (bs->drv && bs->drv->bdrv_child_perm) { + bs->drv->bdrv_child_perm(bs, c, role, + parent_perm, parent_shared, + nperm, nshared); + } + if (child_bs && child_bs->force_share) { + *nshared = BLK_PERM_ALL; + } +} + /* * Check whether permissions on this node can be changed in a way that * @cumulative_perms and @cumulative_shared_perms are the new cumulative @@ -1469,7 +1559,7 @@ static int bdrv_check_perm(BlockDriverState *bs, uint64_t cumulative_perms, /* Write permissions never work with read-only images */ if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) && - bdrv_is_read_only(bs)) + !bdrv_is_writable(bs)) { error_setg(errp, "Block node is read-only"); return -EPERM; @@ -1494,9 +1584,9 @@ static int bdrv_check_perm(BlockDriverState *bs, uint64_t cumulative_perms, /* Check all children */ QLIST_FOREACH(c, &bs->children, next) { uint64_t cur_perm, cur_shared; - drv->bdrv_child_perm(bs, c, c->role, - cumulative_perms, cumulative_shared_perms, - &cur_perm, &cur_shared); + bdrv_child_perm(bs, c->bs, c, c->role, + cumulative_perms, cumulative_shared_perms, + &cur_perm, &cur_shared); ret = bdrv_child_check_perm(c, cur_perm, cur_shared, ignore_children, errp); if (ret < 0) { @@ -1556,9 +1646,9 @@ static void bdrv_set_perm(BlockDriverState *bs, uint64_t cumulative_perms, /* Update all children */ QLIST_FOREACH(c, &bs->children, next) { uint64_t cur_perm, cur_shared; - drv->bdrv_child_perm(bs, c, c->role, - cumulative_perms, cumulative_shared_perms, - &cur_perm, &cur_shared); + bdrv_child_perm(bs, c->bs, c, c->role, + cumulative_perms, cumulative_shared_perms, + &cur_perm, &cur_shared); bdrv_child_set_perm(c, cur_perm, cur_shared); } } @@ -1588,7 +1678,7 @@ static char *bdrv_child_user_desc(BdrvChild *c) return g_strdup("another user"); } -static char *bdrv_perm_names(uint64_t perm) +char *bdrv_perm_names(uint64_t perm) { struct perm_name { uint64_t perm; @@ -1754,7 +1844,7 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c, bdrv_filter_default_perms(bs, c, role, perm, shared, &perm, &shared); /* Format drivers may touch metadata even if the guest doesn't write */ - if (!bdrv_is_read_only(bs)) { + if (bdrv_is_writable(bs)) { perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE; } @@ -1780,6 +1870,10 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c, BLK_PERM_WRITE_UNCHANGED; } + if (bs->open_flags & BDRV_O_INACTIVE) { + shared |= BLK_PERM_WRITE | BLK_PERM_RESIZE; + } + *nperm = perm; *nshared = shared; } @@ -1893,8 +1987,8 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, assert(parent_bs->drv); assert(bdrv_get_aio_context(parent_bs) == bdrv_get_aio_context(child_bs)); - parent_bs->drv->bdrv_child_perm(parent_bs, NULL, child_role, - perm, shared_perm, &perm, &shared_perm); + bdrv_child_perm(parent_bs, child_bs, NULL, child_role, + perm, shared_perm, &perm, &shared_perm); child = bdrv_root_attach_child(child_bs, child_name, child_role, perm, shared_perm, parent_bs, errp); @@ -2075,7 +2169,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, } if (bs->backing_format[0] != '\0' && !qdict_haskey(options, "driver")) { - qdict_put(options, "driver", qstring_from_str(bs->backing_format)); + qdict_put_str(options, "driver", bs->backing_format); } backing_hd = bdrv_open_inherit(*backing_filename ? backing_filename : NULL, @@ -2197,7 +2291,7 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, char *tmp_filename = g_malloc0(PATH_MAX + 1); int64_t total_size; QemuOpts *opts = NULL; - BlockDriverState *bs_snapshot; + BlockDriverState *bs_snapshot = NULL; Error *local_err = NULL; int ret; @@ -2230,38 +2324,32 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, } /* Prepare options QDict for the temporary file */ - qdict_put(snapshot_options, "file.driver", - qstring_from_str("file")); - qdict_put(snapshot_options, "file.filename", - qstring_from_str(tmp_filename)); - qdict_put(snapshot_options, "driver", - qstring_from_str("qcow2")); + qdict_put_str(snapshot_options, "file.driver", "file"); + qdict_put_str(snapshot_options, "file.filename", tmp_filename); + qdict_put_str(snapshot_options, "driver", "qcow2"); bs_snapshot = bdrv_open(NULL, NULL, snapshot_options, flags, errp); snapshot_options = NULL; if (!bs_snapshot) { - ret = -EINVAL; goto out; } - /* bdrv_append() consumes a strong reference to bs_snapshot (i.e. it will - * call bdrv_unref() on it), so in order to be able to return one, we have - * to increase bs_snapshot's refcount here */ + /* bdrv_append() consumes a strong reference to bs_snapshot + * (i.e. it will call bdrv_unref() on it) even on error, so in + * order to be able to return one, we have to increase + * bs_snapshot's refcount here */ bdrv_ref(bs_snapshot); bdrv_append(bs_snapshot, bs, &local_err); if (local_err) { error_propagate(errp, local_err); - ret = -EINVAL; + bs_snapshot = NULL; goto out; } - g_free(tmp_filename); - return bs_snapshot; - out: QDECREF(snapshot_options); g_free(tmp_filename); - return NULL; + return bs_snapshot; } /* @@ -2410,8 +2498,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, goto fail; } - qdict_put(options, "file", - qstring_from_str(bdrv_get_node_name(file_bs))); + qdict_put_str(options, "file", bdrv_get_node_name(file_bs)); } } @@ -2433,8 +2520,8 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, * sure to update both bs->options (which has the full effective * options for bs) and options (which has file.* already removed). */ - qdict_put(bs->options, "driver", qstring_from_str(drv->format_name)); - qdict_put(options, "driver", qstring_from_str(drv->format_name)); + qdict_put_str(bs->options, "driver", drv->format_name); + qdict_put_str(options, "driver", drv->format_name); } else if (!drv) { error_setg(errp, "Must specify either driver or file"); goto fail; @@ -2810,12 +2897,12 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, * that they are checked at the end of this function. */ value = qemu_opt_get(opts, "node-name"); if (value) { - qdict_put(reopen_state->options, "node-name", qstring_from_str(value)); + qdict_put_str(reopen_state->options, "node-name", value); } value = qemu_opt_get(opts, "driver"); if (value) { - qdict_put(reopen_state->options, "driver", qstring_from_str(value)); + qdict_put_str(reopen_state->options, "driver", value); } /* If we are to stay read-only, do not allow permission change @@ -3307,26 +3394,30 @@ exit: /** * Truncate file to 'offset' bytes (needed only for file protocols) */ -int bdrv_truncate(BdrvChild *child, int64_t offset) +int bdrv_truncate(BdrvChild *child, int64_t offset, Error **errp) { BlockDriverState *bs = child->bs; BlockDriver *drv = bs->drv; int ret; - /* FIXME: Some format block drivers use this function instead of implicitly - * growing their file by writing beyond its end. - * See bdrv_aligned_pwritev() for an explanation why we currently - * cannot assert this permission in that case. */ - // assert(child->perm & BLK_PERM_RESIZE); + assert(child->perm & BLK_PERM_RESIZE); - if (!drv) + if (!drv) { + error_setg(errp, "No medium inserted"); return -ENOMEDIUM; - if (!drv->bdrv_truncate) + } + if (!drv->bdrv_truncate) { + error_setg(errp, "Image format driver does not support resize"); return -ENOTSUP; - if (bs->read_only) + } + if (bs->read_only) { + error_setg(errp, "Image is read-only"); return -EACCES; + } + + assert(!(bs->open_flags & BDRV_O_INACTIVE)); - ret = drv->bdrv_truncate(bs, offset); + ret = drv->bdrv_truncate(bs, offset, errp); if (ret == 0) { ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS); bdrv_dirty_bitmap_truncate(bs); @@ -3921,7 +4012,8 @@ void bdrv_init_with_whitelist(void) void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp) { - BdrvChild *child; + BdrvChild *child, *parent; + uint64_t perm, shared_perm; Error *local_err = NULL; int ret; @@ -3957,6 +4049,26 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp) error_setg_errno(errp, -ret, "Could not refresh total sector count"); return; } + + /* Update permissions, they may differ for inactive nodes */ + bdrv_get_cumulative_perm(bs, &perm, &shared_perm); + ret = bdrv_check_perm(bs, perm, shared_perm, NULL, &local_err); + if (ret < 0) { + bs->open_flags |= BDRV_O_INACTIVE; + error_propagate(errp, local_err); + return; + } + bdrv_set_perm(bs, perm, shared_perm); + + QLIST_FOREACH(parent, &bs->parents, next_parent) { + if (parent->role->activate) { + parent->role->activate(parent, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } + } } void bdrv_invalidate_cache_all(Error **errp) @@ -3981,7 +4093,7 @@ void bdrv_invalidate_cache_all(Error **errp) static int bdrv_inactivate_recurse(BlockDriverState *bs, bool setting_flag) { - BdrvChild *child; + BdrvChild *child, *parent; int ret; if (!setting_flag && bs->drv->bdrv_inactivate) { @@ -3991,6 +4103,27 @@ static int bdrv_inactivate_recurse(BlockDriverState *bs, } } + if (setting_flag) { + uint64_t perm, shared_perm; + + bs->open_flags |= BDRV_O_INACTIVE; + + QLIST_FOREACH(parent, &bs->parents, next_parent) { + if (parent->role->inactivate) { + ret = parent->role->inactivate(parent); + if (ret < 0) { + bs->open_flags &= ~BDRV_O_INACTIVE; + return ret; + } + } + } + + /* Update permissions, they may differ for inactive nodes */ + bdrv_get_cumulative_perm(bs, &perm, &shared_perm); + bdrv_check_perm(bs, perm, shared_perm, NULL, &error_abort); + bdrv_set_perm(bs, perm, shared_perm); + } + QLIST_FOREACH(child, &bs->children, next) { ret = bdrv_inactivate_recurse(child->bs, setting_flag); if (ret < 0) { @@ -3998,9 +4131,6 @@ static int bdrv_inactivate_recurse(BlockDriverState *bs, } } - if (setting_flag) { - bs->open_flags |= BDRV_O_INACTIVE; - } return 0; } @@ -4302,8 +4432,7 @@ void bdrv_img_create(const char *filename, const char *fmt, if (backing_fmt) { backing_options = qdict_new(); - qdict_put(backing_options, "driver", - qstring_from_str(backing_fmt)); + qdict_put_str(backing_options, "driver", backing_fmt); } bs = bdrv_open(full_backing, NULL, backing_options, back_flags, @@ -4708,11 +4837,9 @@ void bdrv_refresh_filename(BlockDriverState *bs) * contain a representation of the filename, therefore the following * suffices without querying the (exact_)filename of this BDS. */ if (bs->file->bs->full_open_options) { - qdict_put_obj(opts, "driver", - QOBJECT(qstring_from_str(drv->format_name))); + qdict_put_str(opts, "driver", drv->format_name); QINCREF(bs->file->bs->full_open_options); - qdict_put_obj(opts, "file", - QOBJECT(bs->file->bs->full_open_options)); + qdict_put(opts, "file", bs->file->bs->full_open_options); bs->full_open_options = opts; } else { @@ -4728,8 +4855,7 @@ void bdrv_refresh_filename(BlockDriverState *bs) opts = qdict_new(); append_open_options(opts, bs); - qdict_put_obj(opts, "driver", - QOBJECT(qstring_from_str(drv->format_name))); + qdict_put_str(opts, "driver", drv->format_name); if (bs->exact_filename[0]) { /* This may not work for all block protocol drivers (some may @@ -4739,8 +4865,7 @@ void bdrv_refresh_filename(BlockDriverState *bs) * needs some special format of the options QDict, it needs to * implement the driver-specific bdrv_refresh_filename() function. */ - qdict_put_obj(opts, "filename", - QOBJECT(qstring_from_str(bs->exact_filename))); + qdict_put_str(opts, "filename", bs->exact_filename); } bs->full_open_options = opts;