/** * Temporarily load an internal snapshot by @snapshot_id and @name. * @bs: block device used in the operation * @snapshot_id: unique snapshot ID, or NULL * @name: snapshot name, or NULL * @errp: location to store error * * If both @snapshot_id and @name are specified, load the first one with * id @snapshot_id and name @name. * If only @snapshot_id is specified, load the first one with id * @snapshot_id. * If only @name is specified, load the first one with name @name. * if none is specified, return -EINVAL. * * Returns: 0 on success, -errno on fail. If @bs is not inserted, return * -ENOMEDIUM. If @bs is not readonly, return -EINVAL. If @bs did not support * internal snapshot, return -ENOTSUP. If qemu can't find a matching @id and * @name, return -ENOENT. If @errp != NULL, it will always be filled on * failure. */ int bdrv_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_id, const char *name, Error **errp) { BlockDriver *drv = bs->drv; if (!drv) { error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs)); return -ENOMEDIUM; } if (!snapshot_id && !name) { error_setg(errp, "snapshot_id and name are both NULL"); return -EINVAL; } if (!bs->read_only) { error_setg(errp, "Device is not readonly"); return -EINVAL; } if (drv->bdrv_snapshot_load_tmp) { return drv->bdrv_snapshot_load_tmp(bs, snapshot_id, name, errp); } error_setg(errp, "Block format '%s' used by device '%s' " "does not support temporarily loading internal snapshots", drv->format_name, bdrv_get_device_name(bs)); return -ENOTSUP; }
/** * Delete an internal snapshot by @snapshot_id and @name. * @bs: block device used in the operation * @snapshot_id: unique snapshot ID, or NULL * @name: snapshot name, or NULL * @errp: location to store error * * If both @snapshot_id and @name are specified, delete the first one with * id @snapshot_id and name @name. * If only @snapshot_id is specified, delete the first one with id * @snapshot_id. * If only @name is specified, delete the first one with name @name. * if none is specified, return -EINVAL. * * Returns: 0 on success, -errno on failure. If @bs is not inserted, return * -ENOMEDIUM. If @snapshot_id and @name are both NULL, return -EINVAL. If @bs * does not support internal snapshot deletion, return -ENOTSUP. If @bs does * not support parameter @snapshot_id or @name, or one of them is not correctly * specified, return -EINVAL. If @bs can't find one matching @id and @name, * return -ENOENT. If @errp != NULL, it will always be filled with error * message on failure. */ int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id, const char *name, Error **errp) { BlockDriver *drv = bs->drv; if (!drv) { error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs)); return -ENOMEDIUM; } if (!snapshot_id && !name) { error_setg(errp, "snapshot_id and name are both NULL"); return -EINVAL; } /* drain all pending i/o before deleting snapshot */ bdrv_drain(bs); if (drv->bdrv_snapshot_delete) { return drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp); } if (bs->file) { return bdrv_snapshot_delete(bs->file->bs, snapshot_id, name, errp); } error_setg(errp, "Block format '%s' used by device '%s' " "does not support internal snapshot deletion", drv->format_name, bdrv_get_device_name(bs)); return -ENOTSUP; }
/** * Temporarily load an internal snapshot by @snapshot_id and @name. * @bs: block device used in the operation * @snapshot_id: unique snapshot ID, or NULL * @name: snapshot name, or NULL * @errp: location to store error * * If both @snapshot_id and @name are specified, load the first one with * id @snapshot_id and name @name. * If only @snapshot_id is specified, load the first one with id * @snapshot_id. * If only @name is specified, load the first one with name @name. * if none is specified, return -EINVAL. * * Returns: 0 on success, -errno on fail. If @bs is not inserted, return * -ENOMEDIUM. If @bs is not readonly, return -EINVAL. If @bs did not support * internal snapshot, return -ENOTSUP. If qemu can't find a matching @id and * @name, return -ENOENT. If @errp != NULL, it will always be filled on * failure. */ int bdrv_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_id, const char *name, Error **errp) { BlockDriver *drv = bs->drv; if (!drv) { error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs)); return -ENOMEDIUM; } if (!snapshot_id && !name) { error_setg(errp, "snapshot_id and name are both NULL"); return -EINVAL; } if (!bs->read_only) { error_setg(errp, "Device is not readonly"); return -EINVAL; } if (drv->bdrv_snapshot_load_tmp) { return drv->bdrv_snapshot_load_tmp(bs, snapshot_id, name, errp); } error_set(errp, QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, drv->format_name, bdrv_get_device_name(bs), "temporarily load internal snapshot"); return -ENOTSUP; }
int bdrv_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) { BlockDriver *drv = bs->drv; int ret, open_ret; if (!drv) { return -ENOMEDIUM; } if (drv->bdrv_snapshot_goto) { return drv->bdrv_snapshot_goto(bs, snapshot_id); } if (bs->file) { drv->bdrv_close(bs); ret = bdrv_snapshot_goto(bs->file->bs, snapshot_id); open_ret = drv->bdrv_open(bs, NULL, bs->open_flags, NULL); if (open_ret < 0) { bdrv_unref(bs->file->bs); bs->drv = NULL; return open_ret; } return ret; } return -ENOTSUP; }
/** * Delete an internal snapshot by @snapshot_id and @name. * @bs: block device used in the operation * @snapshot_id: unique snapshot ID, or NULL * @name: snapshot name, or NULL * @errp: location to store error * * If both @snapshot_id and @name are specified, delete the first one with * id @snapshot_id and name @name. * If only @snapshot_id is specified, delete the first one with id * @snapshot_id. * If only @name is specified, delete the first one with name @name. * if none is specified, return -EINVAL. * * Returns: 0 on success, -errno on failure. If @bs is not inserted, return * -ENOMEDIUM. If @snapshot_id and @name are both NULL, return -EINVAL. If @bs * does not support internal snapshot deletion, return -ENOTSUP. If @bs does * not support parameter @snapshot_id or @name, or one of them is not correctly * specified, return -EINVAL. If @bs can't find one matching @id and @name, * return -ENOENT. If @errp != NULL, it will always be filled with error * message on failure. */ int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id, const char *name, Error **errp) { BlockDriver *drv = bs->drv; if (!drv) { error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs)); return -ENOMEDIUM; } if (!snapshot_id && !name) { error_setg(errp, "snapshot_id and name are both NULL"); return -EINVAL; } /* drain all pending i/o before deleting snapshot */ bdrv_drain_all(); if (drv->bdrv_snapshot_delete) { return drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp); } if (bs->file) { return bdrv_snapshot_delete(bs->file, snapshot_id, name, errp); } error_set(errp, QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, drv->format_name, bdrv_get_device_name(bs), "internal snapshot deletion"); return -ENOTSUP; }
int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) { BlockDriver *drv = bs->drv; if (!drv) { return -ENOMEDIUM; } if (drv->bdrv_snapshot_delete) { return drv->bdrv_snapshot_delete(bs, snapshot_id); } if (bs->file) { return bdrv_snapshot_delete(bs->file, snapshot_id); } return -ENOTSUP; }
int bdrv_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_info) { BlockDriver *drv = bs->drv; if (!drv) { return -ENOMEDIUM; } if (drv->bdrv_snapshot_list) { return drv->bdrv_snapshot_list(bs, psn_info); } if (bs->file) { return bdrv_snapshot_list(bs->file->bs, psn_info); } return -ENOTSUP; }
int bdrv_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) { BlockDriver *drv = bs->drv; if (!drv) { return -ENOMEDIUM; } if (drv->bdrv_snapshot_create) { return drv->bdrv_snapshot_create(bs, sn_info); } if (bs->file) { return bdrv_snapshot_create(bs->file, sn_info); } return -ENOTSUP; }
int bdrv_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name) { BlockDriver *drv = bs->drv; if (!drv) { return -ENOMEDIUM; } if (!bs->read_only) { return -EINVAL; } if (drv->bdrv_snapshot_load_tmp) { return drv->bdrv_snapshot_load_tmp(bs, snapshot_name); } return -ENOTSUP; }
/* commit COW file into the raw image */ int bdrv_commit(BlockDriverState *bs) { BlockBackend *src, *backing; BlockDriverState *backing_file_bs = NULL; BlockDriverState *commit_top_bs = NULL; BlockDriver *drv = bs->drv; int64_t offset, length, backing_length; int ro; int64_t n; int ret = 0; uint8_t *buf = NULL; Error *local_err = NULL; if (!drv) return -ENOMEDIUM; if (!bs->backing) { return -ENOTSUP; } if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, NULL) || bdrv_op_is_blocked(bs->backing->bs, BLOCK_OP_TYPE_COMMIT_TARGET, NULL)) { return -EBUSY; } ro = bs->backing->bs->read_only; if (ro) { if (bdrv_reopen_set_read_only(bs->backing->bs, false, NULL)) { return -EACCES; } } src = blk_new(BLK_PERM_CONSISTENT_READ, BLK_PERM_ALL); backing = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); ret = blk_insert_bs(src, bs, &local_err); if (ret < 0) { error_report_err(local_err); goto ro_cleanup; } /* Insert commit_top block node above backing, so we can write to it */ backing_file_bs = backing_bs(bs); commit_top_bs = bdrv_new_open_driver(&bdrv_commit_top, NULL, BDRV_O_RDWR, &local_err); if (commit_top_bs == NULL) { error_report_err(local_err); goto ro_cleanup; } bdrv_set_aio_context(commit_top_bs, bdrv_get_aio_context(backing_file_bs)); bdrv_set_backing_hd(commit_top_bs, backing_file_bs, &error_abort); bdrv_set_backing_hd(bs, commit_top_bs, &error_abort); ret = blk_insert_bs(backing, backing_file_bs, &local_err); if (ret < 0) { error_report_err(local_err); goto ro_cleanup; } length = blk_getlength(src); if (length < 0) { ret = length; goto ro_cleanup; } backing_length = blk_getlength(backing); if (backing_length < 0) { ret = backing_length; goto ro_cleanup; } /* If our top snapshot is larger than the backing file image, * grow the backing file image if possible. If not possible, * we must return an error */ if (length > backing_length) { ret = blk_truncate(backing, length, PREALLOC_MODE_OFF, &local_err); if (ret < 0) { error_report_err(local_err); goto ro_cleanup; } } /* blk_try_blockalign() for src will choose an alignment that works for * backing as well, so no need to compare the alignment manually. */ buf = blk_try_blockalign(src, COMMIT_BUF_SIZE); if (buf == NULL) { ret = -ENOMEM; goto ro_cleanup; } for (offset = 0; offset < length; offset += n) { ret = bdrv_is_allocated(bs, offset, COMMIT_BUF_SIZE, &n); if (ret < 0) { goto ro_cleanup; } if (ret) { ret = blk_pread(src, offset, buf, n); if (ret < 0) { goto ro_cleanup; } ret = blk_pwrite(backing, offset, buf, n, 0); if (ret < 0) { goto ro_cleanup; } } } if (drv->bdrv_make_empty) { ret = drv->bdrv_make_empty(bs); if (ret < 0) { goto ro_cleanup; } blk_flush(src); } /* * Make sure all data we wrote to the backing device is actually * stable on disk. */ blk_flush(backing); ret = 0; ro_cleanup: qemu_vfree(buf); blk_unref(backing); if (backing_file_bs) { bdrv_set_backing_hd(bs, backing_file_bs, &error_abort); } bdrv_unref(commit_top_bs); blk_unref(src); if (ro) { /* ignoring error return here */ bdrv_reopen_set_read_only(bs->backing->bs, true, NULL); } return ret; }