snapshot_conf.c 32.9 KB
Newer Older
1 2 3
/*
 * snapshot_conf.c: domain snapshot XML processing
 *
4
 * Copyright (C) 2006-2019 Red Hat, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17
 * Copyright (C) 2006-2008 Daniel P. Berrange
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with this library.  If not, see
19 20 21 22 23 24 25 26 27
 * <http://www.gnu.org/licenses/>.
 */

#include <config.h>

#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>

28
#include "configmake.h"
29
#include "internal.h"
30
#include "virbitmap.h"
31
#include "virbuffer.h"
32 33 34
#include "count-one-bits.h"
#include "datatypes.h"
#include "domain_conf.h"
35
#include "virlog.h"
36
#include "viralloc.h"
37 38 39 40 41
#include "netdev_bandwidth_conf.h"
#include "netdev_vport_profile_conf.h"
#include "nwfilter_conf.h"
#include "secret_conf.h"
#include "snapshot_conf.h"
42
#include "virstoragefile.h"
43
#include "viruuid.h"
44
#include "virfile.h"
45
#include "virerror.h"
46
#include "virxml.h"
47
#include "virstring.h"
48
#include "virdomainsnapshotobjlist.h"
49

50 51 52
#define LIBVIRT_SNAPSHOT_CONF_PRIV_H_ALLOW
#include "snapshot_conf_priv.h"

53 54
#define VIR_FROM_THIS VIR_FROM_DOMAIN_SNAPSHOT

55 56
VIR_LOG_INIT("conf.snapshot_conf");

57 58 59 60 61 62 63 64 65 66 67 68 69 70
static virClassPtr virDomainSnapshotDefClass;
static void virDomainSnapshotDefDispose(void *obj);

static int
virDomainSnapshotOnceInit(void)
{
    if (!VIR_CLASS_NEW(virDomainSnapshotDef, virClassForDomainMomentDef()))
        return -1;

    return 0;
}

VIR_ONCE_GLOBAL_INIT(virDomainSnapshot);

71 72
VIR_ENUM_IMPL(virDomainSnapshotLocation,
              VIR_DOMAIN_SNAPSHOT_LOCATION_LAST,
73 74 75
              "default",
              "no",
              "internal",
76 77
              "external",
);
78 79

/* virDomainSnapshotState is really virDomainState plus one extra state */
80 81
VIR_ENUM_IMPL(virDomainSnapshotState,
              VIR_DOMAIN_SNAPSHOT_LAST,
82 83 84 85 86 87 88 89
              "nostate",
              "running",
              "blocked",
              "paused",
              "shutdown",
              "shutoff",
              "crashed",
              "pmsuspended",
90 91
              "disk-snapshot",
);
92 93 94 95 96 97

/* Snapshot Def functions */
static void
virDomainSnapshotDiskDefClear(virDomainSnapshotDiskDefPtr disk)
{
    VIR_FREE(disk->name);
98
    virObjectUnref(disk->src);
99
    disk->src = NULL;
100 101
}

102
/* Allocate a new virDomainSnapshotDef; free with virObjectUnref() */
103 104 105 106 107
virDomainSnapshotDefPtr
virDomainSnapshotDefNew(void)
{
    virDomainSnapshotDefPtr def;

108 109 110 111
    if (virDomainSnapshotInitialize() < 0)
        return NULL;

    def = virObjectNew(virDomainSnapshotDefClass);
112 113 114
    return def;
}

115 116
static void
virDomainSnapshotDefDispose(void *obj)
117
{
118
    virDomainSnapshotDefPtr def = obj;
119
    size_t i;
120

121
    VIR_FREE(def->file);
122 123 124
    for (i = 0; i < def->ndisks; i++)
        virDomainSnapshotDiskDefClear(&def->disks[i]);
    VIR_FREE(def->disks);
125
    virObjectUnref(def->cookie);
126 127
}

128
int
129
virDomainSnapshotDiskDefParseXML(xmlNodePtr node,
130
                                 xmlXPathContextPtr ctxt,
131
                                 virDomainSnapshotDiskDefPtr def,
132 133
                                 unsigned int flags,
                                 virDomainXMLOptionPtr xmlopt)
134 135 136
{
    int ret = -1;
    char *snapshot = NULL;
137
    char *type = NULL;
138
    char *driver = NULL;
139
    xmlNodePtr cur;
140 141 142
    xmlNodePtr saved = ctxt->node;

    ctxt->node = node;
143

144
    if (!(def->src = virStorageSourceNew()))
145 146
        goto cleanup;

147 148 149 150 151 152 153 154 155
    def->name = virXMLPropString(node, "name");
    if (!def->name) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("missing name from disk snapshot element"));
        goto cleanup;
    }

    snapshot = virXMLPropString(node, "snapshot");
    if (snapshot) {
E
Eric Blake 已提交
156
        def->snapshot = virDomainSnapshotLocationTypeFromString(snapshot);
157
        if (def->snapshot <= 0) {
158
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
159 160 161 162 163 164
                           _("unknown disk snapshot setting '%s'"),
                           snapshot);
            goto cleanup;
        }
    }

165
    if ((type = virXMLPropString(node, "type"))) {
166 167 168
        if ((def->src->type = virStorageTypeFromString(type)) <= 0 ||
            def->src->type == VIR_STORAGE_TYPE_VOLUME ||
            def->src->type == VIR_STORAGE_TYPE_DIR) {
169 170 171 172 173
            virReportError(VIR_ERR_XML_ERROR,
                           _("unknown disk snapshot type '%s'"), type);
            goto cleanup;
        }
    } else {
174
        def->src->type = VIR_STORAGE_TYPE_FILE;
175
    }
176

177
    if ((cur = virXPathNode("./source", ctxt)) &&
178
        virDomainStorageSourceParse(cur, ctxt, def->src, flags, xmlopt) < 0)
179 180
        goto cleanup;

181 182
    if ((driver = virXPathString("string(./driver/@type)", ctxt)) &&
        (def->src->format = virStorageFileFormatTypeFromString(driver)) <= 0) {
183
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
184
                           _("unknown disk snapshot driver '%s'"), driver);
185
            goto cleanup;
186 187
    }

188
    /* validate that the passed path is absolute */
189
    if (virStorageSourceIsRelative(def->src)) {
190 191 192 193 194 195
        virReportError(VIR_ERR_XML_ERROR,
                       _("disk snapshot image path '%s' must be absolute"),
                       def->src->path);
        goto cleanup;
    }

196
    if (!def->snapshot && (def->src->path || def->src->format))
E
Eric Blake 已提交
197
        def->snapshot = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL;
198 199

    ret = 0;
200
 cleanup:
201 202 203
    ctxt->node = saved;

    VIR_FREE(driver);
204
    VIR_FREE(snapshot);
205
    VIR_FREE(type);
206 207 208 209 210 211 212
    if (ret < 0)
        virDomainSnapshotDiskDefClear(def);
    return ret;
}

/* flags is bitwise-or of virDomainSnapshotParseFlags.
 * If flags does not include VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE, then
213 214
 * caps are ignored. If flags does not include
 * VIR_DOMAIN_SNAPSHOT_PARSE_INTERNAL, then current is ignored.
215
 */
216 217 218 219
static virDomainSnapshotDefPtr
virDomainSnapshotDefParse(xmlXPathContextPtr ctxt,
                          virCapsPtr caps,
                          virDomainXMLOptionPtr xmlopt,
220
                          bool *current,
221
                          unsigned int flags)
222 223 224 225
{
    virDomainSnapshotDefPtr def = NULL;
    virDomainSnapshotDefPtr ret = NULL;
    xmlNodePtr *nodes = NULL;
226 227
    size_t i;
    int n;
228 229 230
    char *creation = NULL, *state = NULL;
    int active;
    char *tmp;
231 232 233
    char *memorySnapshot = NULL;
    char *memoryFile = NULL;
    bool offline = !!(flags & VIR_DOMAIN_SNAPSHOT_PARSE_OFFLINE);
234
    virSaveCookieCallbacksPtr saveCookie = virDomainXMLOptionGetSaveCookie(xmlopt);
235

236 237
    if (!(def = virDomainSnapshotDefNew()))
        return NULL;
238

239 240
    def->parent.name = virXPathString("string(./name)", ctxt);
    if (def->parent.name == NULL) {
241 242 243 244 245 246 247
        if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE) {
            virReportError(VIR_ERR_XML_ERROR, "%s",
                           _("a redefined snapshot must have a name"));
            goto cleanup;
        }
    }

248
    def->parent.description = virXPathString("string(./description)", ctxt);
249 250 251

    if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE) {
        if (virXPathLongLong("string(./creationTime)", ctxt,
252
                             &def->parent.creationTime) < 0) {
253 254 255 256 257
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("missing creationTime from existing snapshot"));
            goto cleanup;
        }

258
        def->parent.parent_name = virXPathString("string(./parent/name)", ctxt);
259 260 261 262 263 264 265 266 267 268 269

        state = virXPathString("string(./state)", ctxt);
        if (state == NULL) {
            /* there was no state in an existing snapshot; this
             * should never happen
             */
            virReportError(VIR_ERR_XML_ERROR, "%s",
                           _("missing state from existing snapshot"));
            goto cleanup;
        }
        def->state = virDomainSnapshotStateTypeFromString(state);
270
        if (def->state <= 0) {
271
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
272 273 274 275
                           _("Invalid state '%s' in domain snapshot XML"),
                           state);
            goto cleanup;
        }
276 277
        offline = (def->state == VIR_DOMAIN_SNAPSHOT_SHUTOFF ||
                   def->state == VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT);
278 279 280 281 282 283

        /* Older snapshots were created with just <domain>/<uuid>, and
         * lack domain/@type.  In that case, leave dom NULL, and
         * clients will have to decide between best effort
         * initialization or outright failure.  */
        if ((tmp = virXPathString("string(./domain/@type)", ctxt))) {
284 285
            int domainflags = VIR_DOMAIN_DEF_PARSE_INACTIVE |
                              VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE;
286 287 288 289 290 291 292 293
            xmlNodePtr domainNode = virXPathNode("./domain", ctxt);

            VIR_FREE(tmp);
            if (!domainNode) {
                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                               _("missing domain in snapshot"));
                goto cleanup;
            }
294
            def->parent.dom = virDomainDefParseNode(ctxt->node->doc, domainNode,
295
                                                    caps, xmlopt, NULL, domainflags);
296
            if (!def->parent.dom)
297 298 299 300
                goto cleanup;
        } else {
            VIR_WARN("parsing older snapshot that lacks domain");
        }
301
    } else if (virDomainXMLOptionRunMomentPostParse(xmlopt, &def->parent) < 0) {
302
        goto cleanup;
303 304
    }

305 306 307 308 309
    memorySnapshot = virXPathString("string(./memory/@snapshot)", ctxt);
    memoryFile = virXPathString("string(./memory/@file)", ctxt);
    if (memorySnapshot) {
        def->memory = virDomainSnapshotLocationTypeFromString(memorySnapshot);
        if (def->memory <= 0) {
310
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
311 312 313 314 315 316 317 318 319 320 321
                           _("unknown memory snapshot setting '%s'"),
                           memorySnapshot);
            goto cleanup;
        }
        if (memoryFile &&
            def->memory != VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) {
            virReportError(VIR_ERR_XML_ERROR,
                           _("memory filename '%s' requires external snapshot"),
                           memoryFile);
            goto cleanup;
        }
322 323 324 325 326 327
        if (!memoryFile &&
            def->memory == VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) {
            virReportError(VIR_ERR_XML_ERROR, "%s",
                           _("external memory snapshots require a filename"));
            goto cleanup;
        }
328 329 330 331 332 333 334 335 336 337
    } else if (memoryFile) {
        def->memory = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL;
    } else if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE) {
        def->memory = (offline ?
                       VIR_DOMAIN_SNAPSHOT_LOCATION_NONE :
                       VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL);
    }
    if (offline && def->memory &&
        def->memory != VIR_DOMAIN_SNAPSHOT_LOCATION_NONE) {
        virReportError(VIR_ERR_XML_ERROR, "%s",
338 339
                       _("memory state cannot be saved with offline or "
                         "disk-only snapshot"));
340 341
        goto cleanup;
    }
342
    VIR_STEAL_PTR(def->file, memoryFile);
343

344 345 346 347 348 349 350 351
    /* verify that memory path is absolute */
    if (def->file && def->file[0] != '/') {
        virReportError(VIR_ERR_XML_ERROR,
                       _("memory snapshot file path (%s) must be absolute"),
                       def->file);
        goto cleanup;
    }

352
    if ((n = virXPathNodeSet("./disks/*", ctxt, &nodes)) < 0)
353
        goto cleanup;
354
    if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_DISKS) {
355
        if (n && VIR_ALLOC_N(def->disks, n) < 0)
356
            goto cleanup;
357
        def->ndisks = n;
358
        for (i = 0; i < def->ndisks; i++) {
359 360
            if (virDomainSnapshotDiskDefParseXML(nodes[i], ctxt, &def->disks[i],
                                                 flags, xmlopt) < 0)
361 362 363
                goto cleanup;
        }
        VIR_FREE(nodes);
364
    } else if (n) {
365 366 367 368 369 370
        virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
                       _("unable to handle disk requests in snapshot"));
        goto cleanup;
    }

    if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_INTERNAL) {
371 372 373 374 375
        if (!current) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("internal parse requested with NULL current"));
            goto cleanup;
        }
376 377 378 379 380
        if (virXPathInt("string(./active)", ctxt, &active) < 0) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("Could not find 'active' element"));
            goto cleanup;
        }
381
        *current = active != 0;
382 383
    }

384 385 386
    if (!offline && virSaveCookieParse(ctxt, &def->cookie, saveCookie) < 0)
        goto cleanup;

387
    VIR_STEAL_PTR(ret, def);
388

389
 cleanup:
390 391 392
    VIR_FREE(creation);
    VIR_FREE(state);
    VIR_FREE(nodes);
393 394
    VIR_FREE(memorySnapshot);
    VIR_FREE(memoryFile);
395
    virObjectUnref(def);
396 397 398 399 400 401 402 403 404

    return ret;
}

virDomainSnapshotDefPtr
virDomainSnapshotDefParseNode(xmlDocPtr xml,
                              xmlNodePtr root,
                              virCapsPtr caps,
                              virDomainXMLOptionPtr xmlopt,
405
                              bool *current,
406 407 408 409 410
                              unsigned int flags)
{
    xmlXPathContextPtr ctxt = NULL;
    virDomainSnapshotDefPtr def = NULL;

411
    if (!virXMLNodeNameEqual(root, "domainsnapshot")) {
412 413 414 415
        virReportError(VIR_ERR_XML_ERROR, "%s", _("domainsnapshot"));
        goto cleanup;
    }

416 417 418 419 420 421 422 423 424 425 426 427
    if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_VALIDATE) {
        VIR_AUTOFREE(char *) schema = NULL;

        schema = virFileFindResource("domainsnapshot.rng",
                                     abs_top_srcdir "/docs/schemas",
                                     PKGDATADIR "/schemas");
        if (!schema)
            goto cleanup;
        if (virXMLValidateAgainstSchema(schema, xml) < 0)
            goto cleanup;
    }

428 429 430 431 432 433 434
    ctxt = xmlXPathNewContext(xml);
    if (ctxt == NULL) {
        virReportOOMError();
        goto cleanup;
    }

    ctxt->node = root;
435
    def = virDomainSnapshotDefParse(ctxt, caps, xmlopt, current, flags);
436
 cleanup:
437 438 439 440 441 442 443 444
    xmlXPathFreeContext(ctxt);
    return def;
}

virDomainSnapshotDefPtr
virDomainSnapshotDefParseString(const char *xmlStr,
                                virCapsPtr caps,
                                virDomainXMLOptionPtr xmlopt,
445
                                bool *current,
446 447 448 449 450 451 452 453 454
                                unsigned int flags)
{
    virDomainSnapshotDefPtr ret = NULL;
    xmlDocPtr xml;
    int keepBlanksDefault = xmlKeepBlanksDefault(0);

    if ((xml = virXMLParse(NULL, xmlStr, _("(domain_snapshot)")))) {
        xmlKeepBlanksDefault(keepBlanksDefault);
        ret = virDomainSnapshotDefParseNode(xml, xmlDocGetRootElement(xml),
455
                                            caps, xmlopt, current, flags);
456 457 458
        xmlFreeDoc(xml);
    }
    xmlKeepBlanksDefault(keepBlanksDefault);
459 460 461 462

    return ret;
}

463

464
/* Perform sanity checking on a redefined snapshot definition. If
465
 * @other is non-NULL, this may include swapping def->parent.dom from other
466
 * into def. */
467
int
468 469
virDomainSnapshotRedefineValidate(virDomainSnapshotDefPtr def,
                                  const unsigned char *domain_uuid,
470
                                  virDomainMomentObjPtr other,
471 472 473 474 475 476 477 478 479 480 481 482
                                  virDomainXMLOptionPtr xmlopt,
                                  unsigned int flags)
{
    int align_location = VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL;
    bool align_match = true;
    bool external = def->state == VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT ||
        virDomainSnapshotDefIsExternal(def);

    if ((flags & VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY) && !external) {
        virReportError(VIR_ERR_INVALID_ARG,
                       _("disk-only flag for snapshot %s requires "
                         "disk-snapshot state"),
483
                       def->parent.name);
484 485
        return -1;
    }
486
    if (def->parent.dom && memcmp(def->parent.dom->uuid, domain_uuid,
487
                                  VIR_UUID_BUFLEN)) {
488 489 490 491 492
        char uuidstr[VIR_UUID_STRING_BUFLEN];

        virUUIDFormat(domain_uuid, uuidstr);
        virReportError(VIR_ERR_INVALID_ARG,
                       _("definition for snapshot %s must use uuid %s"),
493
                       def->parent.name, uuidstr);
494 495 496 497
        return -1;
    }

    if (other) {
498 499 500 501
        virDomainSnapshotDefPtr otherdef = virDomainSnapshotObjGetDef(other);

        if ((otherdef->state == VIR_DOMAIN_SNAPSHOT_RUNNING ||
             otherdef->state == VIR_DOMAIN_SNAPSHOT_PAUSED) !=
502 503 504 505 506
            (def->state == VIR_DOMAIN_SNAPSHOT_RUNNING ||
             def->state == VIR_DOMAIN_SNAPSHOT_PAUSED)) {
            virReportError(VIR_ERR_INVALID_ARG,
                           _("cannot change between online and offline "
                             "snapshot state in snapshot %s"),
507
                           def->parent.name);
508 509 510
            return -1;
        }

511
        if ((otherdef->state == VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT) !=
512 513 514 515
            (def->state == VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT)) {
            virReportError(VIR_ERR_INVALID_ARG,
                           _("cannot change between disk only and "
                             "full system in snapshot %s"),
516
                           def->parent.name);
517 518 519
            return -1;
        }

520 521 522 523
        if (otherdef->parent.dom) {
            if (def->parent.dom) {
                if (!virDomainDefCheckABIStability(otherdef->parent.dom,
                                                   def->parent.dom, xmlopt))
524 525 526
                    return -1;
            } else {
                /* Transfer the domain def */
527
                VIR_STEAL_PTR(def->parent.dom, otherdef->parent.dom);
528 529 530 531
            }
        }
    }

532
    if (def->parent.dom) {
533 534 535 536 537 538 539 540 541 542 543 544 545 546
        if (external) {
            align_location = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL;
            align_match = false;
        }
        if (virDomainSnapshotAlignDisks(def, align_location,
                                        align_match) < 0)
            return -1;
    }


    return 0;
}


547 548 549 550 551 552 553 554 555 556
/**
 * virDomainSnapshotDefAssignExternalNames:
 * @def: snapshot def object
 *
 * Generate default external file names for snapshot targets. Returns 0 on
 * success, -1 on error.
 */
static int
virDomainSnapshotDefAssignExternalNames(virDomainSnapshotDefPtr def)
{
557 558 559 560
    const char *origpath;
    char *tmppath;
    char *tmp;
    struct stat sb;
561
    size_t i;
562
    size_t j;
563 564 565 566

    for (i = 0; i < def->ndisks; i++) {
        virDomainSnapshotDiskDefPtr disk = &def->disks[i];

567 568 569
        if (disk->snapshot != VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL ||
            disk->src->path)
            continue;
570

571 572 573 574 575 576 577
        if (disk->src->type != VIR_STORAGE_TYPE_FILE) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("cannot generate external snapshot name "
                             "for disk '%s' on a '%s' device"),
                           disk->name, virStorageTypeToString(disk->src->type));
            return -1;
        }
578

579
        if (!(origpath = virDomainDiskGetSource(def->parent.dom->disks[i]))) {
580 581 582 583 584
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("cannot generate external snapshot name "
                             "for disk '%s' without source"),
                           disk->name);
            return -1;
585 586
        }

587 588 589 590 591 592 593 594
        if (stat(origpath, &sb) < 0 || !S_ISREG(sb.st_mode)) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("source for disk '%s' is not a regular "
                             "file; refusing to generate external "
                             "snapshot name"),
                           disk->name);
            return -1;
        }
595

596 597 598 599 600 601 602
        if (VIR_STRDUP(tmppath, origpath) < 0)
            return -1;

        /* drop suffix of the file name */
        if ((tmp = strrchr(tmppath, '.')) && !strchr(tmp, '/'))
            *tmp = '\0';

603
        if (virAsprintf(&disk->src->path, "%s.%s", tmppath, def->parent.name) < 0) {
604 605 606 607 608
            VIR_FREE(tmppath);
            return -1;
        }

        VIR_FREE(tmppath);
609 610 611 612 613 614 615 616 617 618 619

        /* verify that we didn't generate a duplicate name */
        for (j = 0; j < i; j++) {
            if (STREQ_NULLABLE(disk->src->path, def->disks[j].src->path)) {
                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                               _("cannot generate external snapshot name for "
                                 "disk '%s': collision with disk '%s'"),
                               disk->name, def->disks[j].name);
                return -1;
            }
        }
620 621 622
    }

    return 0;
623 624 625
}


626
static int
627
virDomainSnapshotCompareDiskIndex(const void *a, const void *b)
628 629 630 631 632
{
    const virDomainSnapshotDiskDef *diska = a;
    const virDomainSnapshotDiskDef *diskb = b;

    /* Integer overflow shouldn't be a problem here.  */
J
John Ferlan 已提交
633
    return diska->idx - diskb->idx;
634 635
}

636
/* Align def->disks to def->parent.dom.  Sort the list of def->disks,
637 638 639 640
 * filling in any missing disks or snapshot state defaults given by
 * the domain, with a fallback to a passed in default.  Convert paths
 * to disk targets for uniformity.  Issue an error and return -1 if
 * any def->disks[n]->name appears more than once or does not map to
E
Eric Blake 已提交
641 642
 * dom->disks.  If require_match, also ensure that there is no
 * conflicting requests for both internal and external snapshots.  */
643 644 645 646 647 648 649
int
virDomainSnapshotAlignDisks(virDomainSnapshotDefPtr def,
                            int default_snapshot,
                            bool require_match)
{
    int ret = -1;
    virBitmapPtr map = NULL;
650
    size_t i;
651 652
    int ndisks;

653
    if (!def->parent.dom) {
654 655 656 657 658
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("missing domain in snapshot"));
        goto cleanup;
    }

659
    if (def->ndisks > def->parent.dom->ndisks) {
660 661 662 663 664 665
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("too many disk snapshot requests for domain"));
        goto cleanup;
    }

    /* Unlikely to have a guest without disks but technically possible.  */
666
    if (!def->parent.dom->ndisks) {
667 668 669 670
        ret = 0;
        goto cleanup;
    }

671
    if (!(map = virBitmapNew(def->parent.dom->ndisks)))
672 673 674 675 676
        goto cleanup;

    /* Double check requested disks.  */
    for (i = 0; i < def->ndisks; i++) {
        virDomainSnapshotDiskDefPtr disk = &def->disks[i];
677
        int idx = virDomainDiskIndexByName(def->parent.dom, disk->name, false);
678 679 680 681 682 683 684 685
        int disk_snapshot;

        if (idx < 0) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("no disk named '%s'"), disk->name);
            goto cleanup;
        }

J
Ján Tomko 已提交
686
        if (virBitmapIsBitSet(map, idx)) {
687 688 689 690 691 692
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("disk '%s' specified twice"),
                           disk->name);
            goto cleanup;
        }
        ignore_value(virBitmapSetBit(map, idx));
J
John Ferlan 已提交
693
        disk->idx = idx;
E
Eric Blake 已提交
694

695
        disk_snapshot = def->parent.dom->disks[idx]->snapshot;
696
        if (!disk->snapshot) {
E
Eric Blake 已提交
697 698 699 700 701 702 703 704 705 706
            if (disk_snapshot &&
                (!require_match ||
                 disk_snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_NONE))
                disk->snapshot = disk_snapshot;
            else
                disk->snapshot = default_snapshot;
        } else if (require_match &&
                   disk->snapshot != default_snapshot &&
                   !(disk->snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_NONE &&
                     disk_snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_NONE)) {
E
Eric Blake 已提交
707 708
            const char *tmp;

E
Eric Blake 已提交
709
            tmp = virDomainSnapshotLocationTypeToString(default_snapshot);
710 711 712 713 714
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("disk '%s' must use snapshot mode '%s'"),
                           disk->name, tmp);
            goto cleanup;
        }
715
        if (disk->src->path &&
E
Eric Blake 已提交
716
            disk->snapshot != VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) {
717 718 719
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("file '%s' for disk '%s' requires "
                             "use of external snapshot mode"),
720
                           disk->src->path, disk->name);
721 722
            goto cleanup;
        }
723
        if (STRNEQ(disk->name, def->parent.dom->disks[idx]->dst)) {
724
            VIR_FREE(disk->name);
725
            if (VIR_STRDUP(disk->name, def->parent.dom->disks[idx]->dst) < 0)
726 727 728 729 730 731 732
                goto cleanup;
        }
    }

    /* Provide defaults for all remaining disks.  */
    ndisks = def->ndisks;
    if (VIR_EXPAND_N(def->disks, def->ndisks,
733
                     def->parent.dom->ndisks - def->ndisks) < 0)
734 735
        goto cleanup;

736
    for (i = 0; i < def->parent.dom->ndisks; i++) {
737 738
        virDomainSnapshotDiskDefPtr disk;

J
Ján Tomko 已提交
739
        if (virBitmapIsBitSet(map, i))
740 741
            continue;
        disk = &def->disks[ndisks++];
742
        if (!(disk->src = virStorageSourceNew()))
743
            goto cleanup;
744
        if (VIR_STRDUP(disk->name, def->parent.dom->disks[i]->dst) < 0)
745
            goto cleanup;
J
John Ferlan 已提交
746
        disk->idx = i;
747 748

        /* Don't snapshot empty drives */
749
        if (virStorageSourceIsEmpty(def->parent.dom->disks[i]->src))
750 751
            disk->snapshot = VIR_DOMAIN_SNAPSHOT_LOCATION_NONE;
        else
752
            disk->snapshot = def->parent.dom->disks[i]->snapshot;
753

754
        disk->src->type = VIR_STORAGE_TYPE_FILE;
755 756 757 758
        if (!disk->snapshot)
            disk->snapshot = default_snapshot;
    }

759 760
    qsort(&def->disks[0], def->ndisks, sizeof(def->disks[0]),
          virDomainSnapshotCompareDiskIndex);
761

762 763 764
    /* Generate default external file names for external snapshot locations */
    if (virDomainSnapshotDefAssignExternalNames(def) < 0)
        goto cleanup;
765 766 767

    ret = 0;

768
 cleanup:
769 770 771 772
    virBitmapFree(map);
    return ret;
}

773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788

/* Converts public VIR_DOMAIN_SNAPSHOT_XML_* into
 * VIR_DOMAIN_SNAPSHOT_FORMAT_* flags, and silently ignores any other
 * flags. */
unsigned int
virDomainSnapshotFormatConvertXMLFlags(unsigned int flags)
{
    unsigned int formatFlags = 0;

    if (flags & VIR_DOMAIN_SNAPSHOT_XML_SECURE)
        formatFlags |= VIR_DOMAIN_SNAPSHOT_FORMAT_SECURE;

    return formatFlags;
}


789
static int
790
virDomainSnapshotDiskDefFormat(virBufferPtr buf,
791 792
                               virDomainSnapshotDiskDefPtr disk,
                               virDomainXMLOptionPtr xmlopt)
793
{
794
    int type = disk->src->type;
795

796
    if (!disk->name)
797
        return 0;
798

799
    virBufferEscapeString(buf, "<disk name='%s'", disk->name);
800 801 802
    if (disk->snapshot > 0)
        virBufferAsprintf(buf, " snapshot='%s'",
                          virDomainSnapshotLocationTypeToString(disk->snapshot));
803

804
    if (!disk->src->path && disk->src->format == 0) {
805
        virBufferAddLit(buf, "/>\n");
806
        return 0;
807 808
    }

E
Eric Blake 已提交
809
    virBufferAsprintf(buf, " type='%s'>\n", virStorageTypeToString(type));
810
    virBufferAdjustIndent(buf, 2);
811

812
    if (disk->src->format > 0)
813
        virBufferEscapeString(buf, "<driver type='%s'/>\n",
814
                              virStorageFileFormatTypeToString(disk->src->format));
815 816
    if (virDomainDiskSourceFormat(buf, disk->src, "source", 0, false, 0,
                                  xmlopt) < 0)
817
        return -1;
818

819 820
    virBufferAdjustIndent(buf, -2);
    virBufferAddLit(buf, "</disk>\n");
821
    return 0;
822 823
}

824

825 826
/* Append XML describing def into buf. Return 0 on success, or -1 on
 * failure with buf cleared. */
827
static int
828 829 830 831 832 833
virDomainSnapshotDefFormatInternal(virBufferPtr buf,
                                   const char *uuidstr,
                                   virDomainSnapshotDefPtr def,
                                   virCapsPtr caps,
                                   virDomainXMLOptionPtr xmlopt,
                                   unsigned int flags)
834
{
835
    size_t i;
836
    int domainflags = VIR_DOMAIN_DEF_FORMAT_INACTIVE;
837

838 839
    if (flags & VIR_DOMAIN_SNAPSHOT_FORMAT_SECURE)
        domainflags |= VIR_DOMAIN_DEF_FORMAT_SECURE;
840

841 842
    virBufferAddLit(buf, "<domainsnapshot>\n");
    virBufferAdjustIndent(buf, 2);
843

844 845
    virBufferEscapeString(buf, "<name>%s</name>\n", def->parent.name);
    if (def->parent.description)
846
        virBufferEscapeString(buf, "<description>%s</description>\n",
847
                              def->parent.description);
848 849 850
    if (def->state)
        virBufferAsprintf(buf, "<state>%s</state>\n",
                          virDomainSnapshotStateTypeToString(def->state));
851

852
    if (def->parent.parent_name) {
853 854
        virBufferAddLit(buf, "<parent>\n");
        virBufferAdjustIndent(buf, 2);
855
        virBufferEscapeString(buf, "<name>%s</name>\n",
856
                              def->parent.parent_name);
857 858
        virBufferAdjustIndent(buf, -2);
        virBufferAddLit(buf, "</parent>\n");
859
    }
860

861
    if (def->parent.creationTime)
862
        virBufferAsprintf(buf, "<creationTime>%lld</creationTime>\n",
863
                          def->parent.creationTime);
864

865
    if (def->memory) {
866
        virBufferAsprintf(buf, "<memory snapshot='%s'",
867
                          virDomainSnapshotLocationTypeToString(def->memory));
868 869
        virBufferEscapeString(buf, " file='%s'", def->file);
        virBufferAddLit(buf, "/>\n");
870
    }
871

872
    if (def->ndisks) {
873 874
        virBufferAddLit(buf, "<disks>\n");
        virBufferAdjustIndent(buf, 2);
875
        for (i = 0; i < def->ndisks; i++) {
876
            if (virDomainSnapshotDiskDefFormat(buf, &def->disks[i], xmlopt) < 0)
877 878
                goto error;
        }
879 880
        virBufferAdjustIndent(buf, -2);
        virBufferAddLit(buf, "</disks>\n");
881
    }
882

883 884
    if (def->parent.dom) {
        if (virDomainDefFormatInternal(def->parent.dom, caps, domainflags, buf,
885
                                       xmlopt) < 0)
886
            goto error;
E
Eric Blake 已提交
887
    } else if (uuidstr) {
888 889 890 891 892
        virBufferAddLit(buf, "<domain>\n");
        virBufferAdjustIndent(buf, 2);
        virBufferAsprintf(buf, "<uuid>%s</uuid>\n", uuidstr);
        virBufferAdjustIndent(buf, -2);
        virBufferAddLit(buf, "</domain>\n");
893
    }
894

895
    if (virSaveCookieFormatBuf(buf, def->cookie,
896 897 898
                               virDomainXMLOptionGetSaveCookie(xmlopt)) < 0)
        goto error;

899
    if (flags & VIR_DOMAIN_SNAPSHOT_FORMAT_INTERNAL)
900 901
        virBufferAsprintf(buf, "<active>%d</active>\n",
                          !!(flags & VIR_DOMAIN_SNAPSHOT_FORMAT_CURRENT));
902

903 904
    virBufferAdjustIndent(buf, -2);
    virBufferAddLit(buf, "</domainsnapshot>\n");
905

906 907
    if (virBufferCheckError(buf) < 0)
        goto error;
908

909
    return 0;
910 911

 error:
912 913 914 915 916 917 918 919 920 921 922 923 924 925 926
    virBufferFreeAndReset(buf);
    return -1;
}


char *
virDomainSnapshotDefFormat(const char *uuidstr,
                           virDomainSnapshotDefPtr def,
                           virCapsPtr caps,
                           virDomainXMLOptionPtr xmlopt,
                           unsigned int flags)
{
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    virCheckFlags(VIR_DOMAIN_SNAPSHOT_FORMAT_SECURE |
927 928
                  VIR_DOMAIN_SNAPSHOT_FORMAT_INTERNAL |
                  VIR_DOMAIN_SNAPSHOT_FORMAT_CURRENT, NULL);
929 930 931 932 933
    if (virDomainSnapshotDefFormatInternal(&buf, uuidstr, def, caps,
                                           xmlopt, flags) < 0)
        return NULL;

    return virBufferContentAndReset(&buf);
934 935
}

936

937
bool
938
virDomainSnapshotDefIsExternal(virDomainSnapshotDefPtr def)
939
{
940
    size_t i;
941

942
    if (def->memory == VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL)
943 944
        return true;

945 946
    for (i = 0; i < def->ndisks; i++) {
        if (def->disks[i].snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL)
947 948 949 950 951
            return true;
    }

    return false;
}
952 953

bool
954
virDomainSnapshotIsExternal(virDomainMomentObjPtr snap)
955
{
956 957 958
    virDomainSnapshotDefPtr def = virDomainSnapshotObjGetDef(snap);

    return virDomainSnapshotDefIsExternal(def);
959
}
960 961 962 963 964

int
virDomainSnapshotRedefinePrep(virDomainPtr domain,
                              virDomainObjPtr vm,
                              virDomainSnapshotDefPtr *defptr,
965
                              virDomainMomentObjPtr *snap,
966
                              virDomainXMLOptionPtr xmlopt,
967 968 969 970
                              bool *update_current,
                              unsigned int flags)
{
    virDomainSnapshotDefPtr def = *defptr;
971
    virDomainMomentObjPtr other;
972
    virDomainSnapshotDefPtr otherdef = NULL;
973
    bool check_if_stolen;
974

975 976
    if (virDomainSnapshotCheckCycles(vm->snapshots, def, vm->def->name) < 0)
        return -1;
977

978
    other = virDomainSnapshotFindByName(vm->snapshots, def->parent.name);
979 980
    if (other)
        otherdef = virDomainSnapshotObjGetDef(other);
981
    check_if_stolen = other && otherdef->parent.dom;
982 983 984
    if (virDomainSnapshotRedefineValidate(def, domain->uuid, other, xmlopt,
                                          flags) < 0) {
        /* revert any stealing of the snapshot domain definition */
985 986
        if (check_if_stolen && def->parent.dom && !otherdef->parent.dom)
            VIR_STEAL_PTR(otherdef->parent.dom, def->parent.dom);
987 988 989
        return -1;
    }
    if (other) {
990
        if (other == virDomainSnapshotGetCurrent(vm->snapshots)) {
991
            *update_current = true;
992
            virDomainSnapshotSetCurrent(vm->snapshots, NULL);
993 994 995
        }

        /* Drop and rebuild the parent relationship, but keep all
996
         * child relations by reusing snap. */
997
        virDomainMomentDropParent(other);
998
        virObjectUnref(otherdef);
999
        other->def = &(*defptr)->parent;
1000
        *defptr = NULL;
1001 1002 1003
        *snap = other;
    }

1004
    return 0;
1005
}