qemu/block/snapshot.c
<<
>>
Prefs
   1/*
   2 * Block layer snapshot related functions
   3 *
   4 * Copyright (c) 2003-2008 Fabrice Bellard
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a copy
   7 * of this software and associated documentation files (the "Software"), to deal
   8 * in the Software without restriction, including without limitation the rights
   9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 * copies of the Software, and to permit persons to whom the Software is
  11 * furnished to do so, subject to the following conditions:
  12 *
  13 * The above copyright notice and this permission notice shall be included in
  14 * all copies or substantial portions of the Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 * THE SOFTWARE.
  23 */
  24
  25#include "block/snapshot.h"
  26#include "block/block_int.h"
  27
  28QemuOptsList internal_snapshot_opts = {
  29    .name = "snapshot",
  30    .head = QTAILQ_HEAD_INITIALIZER(internal_snapshot_opts.head),
  31    .desc = {
  32        {
  33            .name = SNAPSHOT_OPT_ID,
  34            .type = QEMU_OPT_STRING,
  35            .help = "snapshot id"
  36        },{
  37            .name = SNAPSHOT_OPT_NAME,
  38            .type = QEMU_OPT_STRING,
  39            .help = "snapshot name"
  40        },{
  41            /* end of list */
  42        }
  43    },
  44};
  45
  46int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,
  47                       const char *name)
  48{
  49    QEMUSnapshotInfo *sn_tab, *sn;
  50    int nb_sns, i, ret;
  51
  52    ret = -ENOENT;
  53    nb_sns = bdrv_snapshot_list(bs, &sn_tab);
  54    if (nb_sns < 0) {
  55        return ret;
  56    }
  57    for (i = 0; i < nb_sns; i++) {
  58        sn = &sn_tab[i];
  59        if (!strcmp(sn->id_str, name) || !strcmp(sn->name, name)) {
  60            *sn_info = *sn;
  61            ret = 0;
  62            break;
  63        }
  64    }
  65    g_free(sn_tab);
  66    return ret;
  67}
  68
  69/**
  70 * Look up an internal snapshot by @id and @name.
  71 * @bs: block device to search
  72 * @id: unique snapshot ID, or NULL
  73 * @name: snapshot name, or NULL
  74 * @sn_info: location to store information on the snapshot found
  75 * @errp: location to store error, will be set only for exception
  76 *
  77 * This function will traverse snapshot list in @bs to search the matching
  78 * one, @id and @name are the matching condition:
  79 * If both @id and @name are specified, find the first one with id @id and
  80 * name @name.
  81 * If only @id is specified, find the first one with id @id.
  82 * If only @name is specified, find the first one with name @name.
  83 * if none is specified, abort().
  84 *
  85 * Returns: true when a snapshot is found and @sn_info will be filled, false
  86 * when error or not found. If all operation succeed but no matching one is
  87 * found, @errp will NOT be set.
  88 */
  89bool bdrv_snapshot_find_by_id_and_name(BlockDriverState *bs,
  90                                       const char *id,
  91                                       const char *name,
  92                                       QEMUSnapshotInfo *sn_info,
  93                                       Error **errp)
  94{
  95    QEMUSnapshotInfo *sn_tab, *sn;
  96    int nb_sns, i;
  97    bool ret = false;
  98
  99    assert(id || name);
 100
 101    nb_sns = bdrv_snapshot_list(bs, &sn_tab);
 102    if (nb_sns < 0) {
 103        error_setg_errno(errp, -nb_sns, "Failed to get a snapshot list");
 104        return false;
 105    } else if (nb_sns == 0) {
 106        return false;
 107    }
 108
 109    if (id && name) {
 110        for (i = 0; i < nb_sns; i++) {
 111            sn = &sn_tab[i];
 112            if (!strcmp(sn->id_str, id) && !strcmp(sn->name, name)) {
 113                *sn_info = *sn;
 114                ret = true;
 115                break;
 116            }
 117        }
 118    } else if (id) {
 119        for (i = 0; i < nb_sns; i++) {
 120            sn = &sn_tab[i];
 121            if (!strcmp(sn->id_str, id)) {
 122                *sn_info = *sn;
 123                ret = true;
 124                break;
 125            }
 126        }
 127    } else if (name) {
 128        for (i = 0; i < nb_sns; i++) {
 129            sn = &sn_tab[i];
 130            if (!strcmp(sn->name, name)) {
 131                *sn_info = *sn;
 132                ret = true;
 133                break;
 134            }
 135        }
 136    }
 137
 138    g_free(sn_tab);
 139    return ret;
 140}
 141
 142int bdrv_can_snapshot(BlockDriverState *bs)
 143{
 144    BlockDriver *drv = bs->drv;
 145    if (!drv || !bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) {
 146        return 0;
 147    }
 148
 149    if (!drv->bdrv_snapshot_create) {
 150        if (bs->file != NULL) {
 151            return bdrv_can_snapshot(bs->file);
 152        }
 153        return 0;
 154    }
 155
 156    return 1;
 157}
 158
 159int bdrv_snapshot_create(BlockDriverState *bs,
 160                         QEMUSnapshotInfo *sn_info)
 161{
 162    BlockDriver *drv = bs->drv;
 163    if (!drv) {
 164        return -ENOMEDIUM;
 165    }
 166    if (drv->bdrv_snapshot_create) {
 167        return drv->bdrv_snapshot_create(bs, sn_info);
 168    }
 169    if (bs->file) {
 170        return bdrv_snapshot_create(bs->file, sn_info);
 171    }
 172    return -ENOTSUP;
 173}
 174
 175int bdrv_snapshot_goto(BlockDriverState *bs,
 176                       const char *snapshot_id)
 177{
 178    BlockDriver *drv = bs->drv;
 179    int ret, open_ret;
 180
 181    if (!drv) {
 182        return -ENOMEDIUM;
 183    }
 184    if (drv->bdrv_snapshot_goto) {
 185        return drv->bdrv_snapshot_goto(bs, snapshot_id);
 186    }
 187
 188    if (bs->file) {
 189        drv->bdrv_close(bs);
 190        ret = bdrv_snapshot_goto(bs->file, snapshot_id);
 191        open_ret = drv->bdrv_open(bs, NULL, bs->open_flags, NULL);
 192        if (open_ret < 0) {
 193            bdrv_unref(bs->file);
 194            bs->drv = NULL;
 195            return open_ret;
 196        }
 197        return ret;
 198    }
 199
 200    return -ENOTSUP;
 201}
 202
 203/**
 204 * Delete an internal snapshot by @snapshot_id and @name.
 205 * @bs: block device used in the operation
 206 * @snapshot_id: unique snapshot ID, or NULL
 207 * @name: snapshot name, or NULL
 208 * @errp: location to store error
 209 *
 210 * If both @snapshot_id and @name are specified, delete the first one with
 211 * id @snapshot_id and name @name.
 212 * If only @snapshot_id is specified, delete the first one with id
 213 * @snapshot_id.
 214 * If only @name is specified, delete the first one with name @name.
 215 * if none is specified, return -EINVAL.
 216 *
 217 * Returns: 0 on success, -errno on failure. If @bs is not inserted, return
 218 * -ENOMEDIUM. If @snapshot_id and @name are both NULL, return -EINVAL. If @bs
 219 * does not support internal snapshot deletion, return -ENOTSUP. If @bs does
 220 * not support parameter @snapshot_id or @name, or one of them is not correctly
 221 * specified, return -EINVAL. If @bs can't find one matching @id and @name,
 222 * return -ENOENT. If @errp != NULL, it will always be filled with error
 223 * message on failure.
 224 */
 225int bdrv_snapshot_delete(BlockDriverState *bs,
 226                         const char *snapshot_id,
 227                         const char *name,
 228                         Error **errp)
 229{
 230    BlockDriver *drv = bs->drv;
 231    if (!drv) {
 232        error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs));
 233        return -ENOMEDIUM;
 234    }
 235    if (!snapshot_id && !name) {
 236        error_setg(errp, "snapshot_id and name are both NULL");
 237        return -EINVAL;
 238    }
 239
 240    /* drain all pending i/o before deleting snapshot */
 241    bdrv_drain_all();
 242
 243    if (drv->bdrv_snapshot_delete) {
 244        return drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp);
 245    }
 246    if (bs->file) {
 247        return bdrv_snapshot_delete(bs->file, snapshot_id, name, errp);
 248    }
 249    error_set(errp, QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
 250              drv->format_name, bdrv_get_device_name(bs),
 251              "internal snapshot deletion");
 252    return -ENOTSUP;
 253}
 254
 255void bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs,
 256                                        const char *id_or_name,
 257                                        Error **errp)
 258{
 259    int ret;
 260    Error *local_err = NULL;
 261
 262    ret = bdrv_snapshot_delete(bs, id_or_name, NULL, &local_err);
 263    if (ret == -ENOENT || ret == -EINVAL) {
 264        error_free(local_err);
 265        local_err = NULL;
 266        ret = bdrv_snapshot_delete(bs, NULL, id_or_name, &local_err);
 267    }
 268
 269    if (ret < 0) {
 270        error_propagate(errp, local_err);
 271    }
 272}
 273
 274int bdrv_snapshot_list(BlockDriverState *bs,
 275                       QEMUSnapshotInfo **psn_info)
 276{
 277    BlockDriver *drv = bs->drv;
 278    if (!drv) {
 279        return -ENOMEDIUM;
 280    }
 281    if (drv->bdrv_snapshot_list) {
 282        return drv->bdrv_snapshot_list(bs, psn_info);
 283    }
 284    if (bs->file) {
 285        return bdrv_snapshot_list(bs->file, psn_info);
 286    }
 287    return -ENOTSUP;
 288}
 289
 290/**
 291 * Temporarily load an internal snapshot by @snapshot_id and @name.
 292 * @bs: block device used in the operation
 293 * @snapshot_id: unique snapshot ID, or NULL
 294 * @name: snapshot name, or NULL
 295 * @errp: location to store error
 296 *
 297 * If both @snapshot_id and @name are specified, load the first one with
 298 * id @snapshot_id and name @name.
 299 * If only @snapshot_id is specified, load the first one with id
 300 * @snapshot_id.
 301 * If only @name is specified, load the first one with name @name.
 302 * if none is specified, return -EINVAL.
 303 *
 304 * Returns: 0 on success, -errno on fail. If @bs is not inserted, return
 305 * -ENOMEDIUM. If @bs is not readonly, return -EINVAL. If @bs did not support
 306 * internal snapshot, return -ENOTSUP. If qemu can't find a matching @id and
 307 * @name, return -ENOENT. If @errp != NULL, it will always be filled on
 308 * failure.
 309 */
 310int bdrv_snapshot_load_tmp(BlockDriverState *bs,
 311                           const char *snapshot_id,
 312                           const char *name,
 313                           Error **errp)
 314{
 315    BlockDriver *drv = bs->drv;
 316
 317    if (!drv) {
 318        error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs));
 319        return -ENOMEDIUM;
 320    }
 321    if (!snapshot_id && !name) {
 322        error_setg(errp, "snapshot_id and name are both NULL");
 323        return -EINVAL;
 324    }
 325    if (!bs->read_only) {
 326        error_setg(errp, "Device is not readonly");
 327        return -EINVAL;
 328    }
 329    if (drv->bdrv_snapshot_load_tmp) {
 330        return drv->bdrv_snapshot_load_tmp(bs, snapshot_id, name, errp);
 331    }
 332    error_set(errp, QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
 333              drv->format_name, bdrv_get_device_name(bs),
 334              "temporarily load internal snapshot");
 335    return -ENOTSUP;
 336}
 337
 338int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs,
 339                                         const char *id_or_name,
 340                                         Error **errp)
 341{
 342    int ret;
 343    Error *local_err = NULL;
 344
 345    ret = bdrv_snapshot_load_tmp(bs, id_or_name, NULL, &local_err);
 346    if (ret == -ENOENT || ret == -EINVAL) {
 347        error_free(local_err);
 348        local_err = NULL;
 349        ret = bdrv_snapshot_load_tmp(bs, NULL, id_or_name, &local_err);
 350    }
 351
 352    if (local_err) {
 353        error_propagate(errp, local_err);
 354    }
 355
 356    return ret;
 357}
 358