virstoragetest.c 33.6 KB
Newer Older
1
/*
2
 * Copyright (C) 2013-2014 Red Hat, Inc.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
 *
 * 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
 * License along with this library.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 * Author: Eric Blake <eblake@redhat.com>
 */

#include <config.h>

#include <stdlib.h>

#include "testutils.h"
#include "vircommand.h"
#include "virerror.h"
28
#include "virfile.h"
29 30
#include "virlog.h"
#include "virstoragefile.h"
31
#include "virstring.h"
32 33 34

#define VIR_FROM_THIS VIR_FROM_NONE

35 36
VIR_LOG_INIT("tests.storagetest");

37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
#define datadir abs_builddir "/virstoragedata"

/* This test creates the following files, all in datadir:

 * raw: 1024-byte raw file
 * qcow2: qcow2 file with 'raw' as backing
 * wrap: qcow2 file with 'qcow2' as backing
 * qed: qed file with 'raw' as backing
 * sub/link1: symlink to qcow2
 * sub/link2: symlink to wrap
 *
 * Relative names to these files are known at compile time, but absolute
 * and canonical names depend on where the test is run; for convenience,
 * we pre-populate the computation of these names for use during the test.
*/

static char *qemuimg;
static char *absraw;
static char *canonraw;
static char *absqcow2;
static char *canonqcow2;
static char *abswrap;
E
Eric Blake 已提交
59
static char *canonwrap;
60
static char *absqed;
E
Eric Blake 已提交
61
static char *canonqed;
E
Eric Blake 已提交
62
static char *absdir;
E
Eric Blake 已提交
63
static char *canondir;
64 65 66 67 68 69 70 71 72 73 74
static char *abslink2;

static void
testCleanupImages(void)
{
    VIR_FREE(qemuimg);
    VIR_FREE(absraw);
    VIR_FREE(canonraw);
    VIR_FREE(absqcow2);
    VIR_FREE(canonqcow2);
    VIR_FREE(abswrap);
E
Eric Blake 已提交
75
    VIR_FREE(canonwrap);
76
    VIR_FREE(absqed);
E
Eric Blake 已提交
77
    VIR_FREE(canonqed);
E
Eric Blake 已提交
78
    VIR_FREE(absdir);
E
Eric Blake 已提交
79
    VIR_FREE(canondir);
80 81 82 83 84 85 86 87
    VIR_FREE(abslink2);

    if (chdir(abs_builddir) < 0) {
        fprintf(stderr, "unable to return to correct directory, refusing to "
                "clean up %s\n", datadir);
        return;
    }

88
    virFileDeleteTree(datadir);
89 90 91 92 93 94 95
}

static int
testPrepImages(void)
{
    int ret = EXIT_FAILURE;
    virCommandPtr cmd = NULL;
96 97
    char *buf = NULL;
    bool compat = false;
98 99 100 101

    qemuimg = virFindFileInPath("kvm-img");
    if (!qemuimg)
        qemuimg = virFindFileInPath("qemu-img");
102 103
    if (!qemuimg)
        goto skip;
104

105 106 107
    /* Clean up from any earlier failed tests */
    virFileDeleteTree(datadir);

108 109 110 111 112 113 114 115 116 117 118 119
    /* See if qemu-img supports '-o compat=xxx'.  If so, we force the
     * use of both v2 and v3 files; if not, it is v2 only but the test
     * still works. */
    cmd = virCommandNewArgList(qemuimg, "create", "-f", "qcow2",
                               "-o?", "/dev/null", NULL);
    virCommandSetOutputBuffer(cmd, &buf);
    if (virCommandRun(cmd, NULL) < 0)
        goto skip;
    if (strstr(buf, "compat "))
        compat = true;
    VIR_FREE(buf);

120 121 122 123
    if (virAsprintf(&absraw, "%s/raw", datadir) < 0 ||
        virAsprintf(&absqcow2, "%s/qcow2", datadir) < 0 ||
        virAsprintf(&abswrap, "%s/wrap", datadir) < 0 ||
        virAsprintf(&absqed, "%s/qed", datadir) < 0 ||
E
Eric Blake 已提交
124
        virAsprintf(&absdir, "%s/dir", datadir) < 0 ||
125
        virAsprintf(&abslink2, "%s/sub/link2", datadir) < 0)
126 127 128 129 130 131
        goto cleanup;

    if (virFileMakePath(datadir "/sub") < 0) {
        fprintf(stderr, "unable to create directory %s\n", datadir "/sub");
        goto cleanup;
    }
E
Eric Blake 已提交
132 133 134 135
    if (virFileMakePath(datadir "/dir") < 0) {
        fprintf(stderr, "unable to create directory %s\n", datadir "/dir");
        goto cleanup;
    }
E
Eric Blake 已提交
136 137 138 139
    if (!(canondir = canonicalize_file_name(absdir))) {
        virReportOOMError();
        goto cleanup;
    }
140 141 142 143 144 145

    if (chdir(datadir) < 0) {
        fprintf(stderr, "unable to test relative backing chains\n");
        goto cleanup;
    }

146 147
    if (virAsprintf(&buf, "%1024d", 0) < 0 ||
        virFileWriteStr("raw", buf, 0600) < 0) {
148
        fprintf(stderr, "unable to create raw file\n");
149
        goto cleanup;
150
    }
151 152 153 154 155 156 157 158
    if (!(canonraw = canonicalize_file_name(absraw))) {
        virReportOOMError();
        goto cleanup;
    }

    /* Create a qcow2 wrapping relative raw; later on, we modify its
     * metadata to test other configurations */
    virCommandFree(cmd);
159 160 161 162
    cmd = virCommandNewArgList(qemuimg, "create", "-f", "qcow2", NULL);
    virCommandAddArgFormat(cmd, "-obacking_file=raw,backing_fmt=raw%s",
                           compat ? ",compat=0.10" : "");
    virCommandAddArg(cmd, "qcow2");
163
    if (virCommandRun(cmd, NULL) < 0)
164
        goto skip;
165 166
    /* Make sure our later uses of 'qemu-img rebase' will work */
    virCommandFree(cmd);
167
    cmd = virCommandNewArgList(qemuimg, "rebase", "-u", "-f", "qcow2",
168
                               "-F", "raw", "-b", "raw", "qcow2", NULL);
169 170
    if (virCommandRun(cmd, NULL) < 0)
        goto skip;
171 172 173 174 175 176 177 178
    if (!(canonqcow2 = canonicalize_file_name(absqcow2))) {
        virReportOOMError();
        goto cleanup;
    }

    /* Create a second qcow2 wrapping the first, to be sure that we
     * can correctly avoid insecure probing.  */
    virCommandFree(cmd);
179
    cmd = virCommandNewArgList(qemuimg, "create", "-f", "qcow2", NULL);
180 181
    virCommandAddArgFormat(cmd, "-obacking_file=%s,backing_fmt=qcow2%s",
                           absqcow2, compat ? ",compat=1.1" : "");
182 183
    virCommandAddArg(cmd, "wrap");
    if (virCommandRun(cmd, NULL) < 0)
184
        goto skip;
E
Eric Blake 已提交
185 186 187 188
    if (!(canonwrap = canonicalize_file_name(abswrap))) {
        virReportOOMError();
        goto cleanup;
    }
189 190 191

    /* Create a qed file. */
    virCommandFree(cmd);
192
    cmd = virCommandNewArgList(qemuimg, "create", "-f", "qed", NULL);
193 194 195 196
    virCommandAddArgFormat(cmd, "-obacking_file=%s,backing_fmt=raw",
                           absraw);
    virCommandAddArg(cmd, "qed");
    if (virCommandRun(cmd, NULL) < 0)
197
        goto skip;
E
Eric Blake 已提交
198 199 200 201
    if (!(canonqed = canonicalize_file_name(absqed))) {
        virReportOOMError();
        goto cleanup;
    }
202

203
#ifdef HAVE_SYMLINK
204 205 206 207 208 209
    /* Create some symlinks in a sub-directory. */
    if (symlink("../qcow2", datadir "/sub/link1") < 0 ||
        symlink("../wrap", datadir "/sub/link2") < 0) {
        fprintf(stderr, "unable to create symlink");
        goto cleanup;
    }
210
#endif
211 212

    ret = 0;
213
 cleanup:
214
    VIR_FREE(buf);
215 216 217 218
    virCommandFree(cmd);
    if (ret)
        testCleanupImages();
    return ret;
219

220
 skip:
221 222 223
    fputs("qemu-img is too old; skipping this test\n", stderr);
    ret = EXIT_AM_SKIP;
    goto cleanup;
224 225
}

226 227 228 229 230
/* Many fields of virStorageFileMetadata have the same content whether
 * we access the file relatively or absolutely; but file names differ
 * depending on how the chain was opened.  For ease of testing, we
 * test both relative and absolute starts, and use a flag to say which
 * of the two variations to compare against.  */
231 232 233 234 235
typedef struct _testFileData testFileData;
struct _testFileData
{
    const char *expBackingStore;
    const char *expBackingStoreRaw;
E
Eric Blake 已提交
236 237
    const char *expBackingDirRel;
    const char *expBackingDirAbs;
238 239
    unsigned long long expCapacity;
    bool expEncrypted;
240 241
    const char *pathRel;
    const char *pathAbs;
E
Eric Blake 已提交
242 243 244 245 246
    const char *canonPath;
    const char *relDirRel;
    const char *relDirAbs;
    int type;
    int format;
247 248 249 250 251 252 253
};

enum {
    EXP_PASS = 0,
    EXP_FAIL = 1,
    EXP_WARN = 2,
    ALLOW_PROBE = 4,
254
    ABS_START = 8,
255 256 257 258 259 260
};

struct testChainData
{
    const char *start;
    enum virStorageFileFormat format;
261
    const testFileData *files[4];
262 263 264 265 266 267 268 269 270 271 272
    int nfiles;
    unsigned int flags;
};

static int
testStorageChain(const void *args)
{
    const struct testChainData *data = args;
    int ret = -1;
    virStorageFileMetadataPtr meta;
    virStorageFileMetadataPtr elt;
273
    size_t i = 0;
E
Eric Blake 已提交
274
    char *broken = NULL;
275
    bool isAbs = !!(data->flags & ABS_START);
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294

    meta = virStorageFileGetMetadata(data->start, data->format, -1, -1,
                                     (data->flags & ALLOW_PROBE) != 0);
    if (!meta) {
        if (data->flags & EXP_FAIL) {
            virResetLastError();
            ret = 0;
        }
        goto cleanup;
    } else if (data->flags & EXP_FAIL) {
        fprintf(stderr, "call should have failed\n");
        goto cleanup;
    }
    if (data->flags & EXP_WARN) {
        if (!virGetLastError()) {
            fprintf(stderr, "call should have warned\n");
            goto cleanup;
        }
        virResetLastError();
E
Eric Blake 已提交
295 296 297 298 299 300 301 302 303 304 305 306 307
        if (virStorageFileChainGetBroken(meta, &broken) || !broken) {
            fprintf(stderr, "call should identify broken part of chain\n");
            goto cleanup;
        }
    } else {
        if (virGetLastError()) {
            fprintf(stderr, "call should not have warned\n");
            goto cleanup;
        }
        if (virStorageFileChainGetBroken(meta, &broken) || broken) {
            fprintf(stderr, "chain should not be identified as broken\n");
            goto cleanup;
        }
308 309 310 311 312 313
    }

    elt = meta;
    while (elt) {
        char *expect = NULL;
        char *actual = NULL;
E
Eric Blake 已提交
314
        const char *expBackingDirectory;
315
        const char *expPath;
E
Eric Blake 已提交
316
        const char *expRelDir;
317 318 319 320 321 322

        if (i == data->nfiles) {
            fprintf(stderr, "probed chain was too long\n");
            goto cleanup;
        }

E
Eric Blake 已提交
323 324
        expBackingDirectory = isAbs ? data->files[i]->expBackingDirAbs
            : data->files[i]->expBackingDirRel;
325 326
        expPath = isAbs ? data->files[i]->pathAbs
            : data->files[i]->pathRel;
E
Eric Blake 已提交
327 328
        expRelDir = isAbs ? data->files[i]->relDirAbs
            : data->files[i]->relDirRel;
329
        if (virAsprintf(&expect,
330
                        "store:%s\nraw:%s\ndirectory:%s\nother:%lld %d\n"
E
Eric Blake 已提交
331
                        "path:%s\ncanon:%s\nrelDir:%s\ntype:%d %d\n",
332 333
                        NULLSTR(data->files[i]->expBackingStore),
                        NULLSTR(data->files[i]->expBackingStoreRaw),
E
Eric Blake 已提交
334
                        NULLSTR(expBackingDirectory),
335
                        data->files[i]->expCapacity,
336
                        data->files[i]->expEncrypted,
E
Eric Blake 已提交
337 338 339 340 341
                        NULLSTR(expPath),
                        NULLSTR(data->files[i]->canonPath),
                        NULLSTR(expRelDir),
                        data->files[i]->type,
                        data->files[i]->format) < 0 ||
342
            virAsprintf(&actual,
343
                        "store:%s\nraw:%s\ndirectory:%s\nother:%lld %d\n"
E
Eric Blake 已提交
344
                        "path:%s\ncanon:%s\nrelDir:%s\ntype:%d %d\n",
345 346 347
                        NULLSTR(elt->backingStore),
                        NULLSTR(elt->backingStoreRaw),
                        NULLSTR(elt->directory),
348
                        elt->capacity, !!elt->encryption,
E
Eric Blake 已提交
349 350 351 352
                        NULLSTR(elt->path),
                        NULLSTR(elt->canonPath),
                        NULLSTR(elt->relDir),
                        elt->type, elt->format) < 0) {
353 354 355 356 357
            VIR_FREE(expect);
            VIR_FREE(actual);
            goto cleanup;
        }
        if (STRNEQ(expect, actual)) {
358
            fprintf(stderr, "chain member %zu", i);
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
            virtTestDifference(stderr, expect, actual);
            VIR_FREE(expect);
            VIR_FREE(actual);
            goto cleanup;
        }
        VIR_FREE(expect);
        VIR_FREE(actual);
        elt = elt->backingMeta;
        i++;
    }
    if (i != data->nfiles) {
        fprintf(stderr, "probed chain was too short\n");
        goto cleanup;
    }

    ret = 0;
375
 cleanup:
E
Eric Blake 已提交
376
    VIR_FREE(broken);
377 378 379 380
    virStorageFileFreeMetadata(meta);
    return ret;
}

E
Eric Blake 已提交
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
struct testLookupData
{
    virStorageFileMetadataPtr chain;
    const char *name;
    const char *expResult;
    virStorageFileMetadataPtr expMeta;
    const char *expParent;
};

static int
testStorageLookup(const void *args)
{
    const struct testLookupData *data = args;
    int ret = 0;
    const char *actualResult;
    virStorageFileMetadataPtr actualMeta;
    const char *actualParent;

    /* This function is documented as giving results within chain, but
     * as the same string may be duplicated into more than one field,
     * we rely on STREQ rather than pointer equality.  Test twice to
     * ensure optional parameters don't cause NULL deref.  */
403 404
    actualResult = virStorageFileChainLookup(data->chain, data->name,
                                             NULL, NULL);
E
Eric Blake 已提交
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424

    if (!data->expResult) {
        if (!virGetLastError()) {
            fprintf(stderr, "call should have failed\n");
            ret = -1;
        }
        virResetLastError();
    } else {
        if (virGetLastError()) {
            fprintf(stderr, "call should not have warned\n");
            ret = -1;
        }
    }

    if (STRNEQ_NULLABLE(data->expResult, actualResult)) {
        fprintf(stderr, "result 1: expected %s, got %s\n",
                NULLSTR(data->expResult), NULLSTR(actualResult));
        ret = -1;
    }

425 426
    actualResult = virStorageFileChainLookup(data->chain, data->name,
                                             &actualMeta, &actualParent);
E
Eric Blake 已提交
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
    if (!data->expResult)
        virResetLastError();
    if (STRNEQ_NULLABLE(data->expResult, actualResult)) {
        fprintf(stderr, "result 2: expected %s, got %s\n",
                NULLSTR(data->expResult), NULLSTR(actualResult));
        ret = -1;
    }
    if (data->expMeta != actualMeta) {
        fprintf(stderr, "meta: expected %p, got %p\n",
                data->expMeta, actualMeta);
        ret = -1;
    }
    if (STRNEQ_NULLABLE(data->expParent, actualParent)) {
        fprintf(stderr, "parent: expected %s, got %s\n",
                NULLSTR(data->expParent), NULLSTR(actualParent));
        ret = -1;
    }

    return ret;
}

448 449 450 451 452
static int
mymain(void)
{
    int ret;
    virCommandPtr cmd = NULL;
453
    struct testChainData data;
E
Eric Blake 已提交
454
    virStorageFileMetadataPtr chain = NULL;
455 456 457 458 459 460

    /* Prep some files with qemu-img; if that is not found on PATH, or
     * if it lacks support for qcow2 and qed, skip this test.  */
    if ((ret = testPrepImages()) != 0)
        return ret;

461
#define TEST_ONE_CHAIN(id, start, format, flags, ...)                \
462
    do {                                                             \
463 464 465 466
        size_t i;                                                    \
        memset(&data, 0, sizeof(data));                              \
        data = (struct testChainData){                               \
            start, format, { __VA_ARGS__ }, 0, flags,                \
467
        };                                                           \
468 469 470
        for (i = 0; i < ARRAY_CARDINALITY(data.files); i++)          \
            if (data.files[i])                                       \
                data.nfiles++;                                       \
471
        if (virtTestRun("Storage backing chain " id,                 \
472 473 474 475
                        testStorageChain, &data) < 0)                \
            ret = -1;                                                \
    } while (0)

476 477 478
#define VIR_FLATTEN_2(...) __VA_ARGS__
#define VIR_FLATTEN_1(_1) VIR_FLATTEN_2 _1

479 480 481
#define TEST_CHAIN(id, relstart, absstart, format, chain1, flags1,   \
                   chain2, flags2, chain3, flags3, chain4, flags4)   \
    do {                                                             \
482 483 484 485
        TEST_ONE_CHAIN(#id "a", relstart, format, flags1,            \
                       VIR_FLATTEN_1(chain1));                       \
        TEST_ONE_CHAIN(#id "b", relstart, format, flags2,            \
                       VIR_FLATTEN_1(chain2));                       \
486
        TEST_ONE_CHAIN(#id "c", absstart, format, flags3 | ABS_START,\
487
                       VIR_FLATTEN_1(chain3));                       \
488
        TEST_ONE_CHAIN(#id "d", absstart, format, flags4 | ABS_START,\
489
                       VIR_FLATTEN_1(chain4));                       \
490 491 492 493 494
    } while (0)

    /* The actual tests, in several groups. */

    /* Missing file */
495
    TEST_ONE_CHAIN("0", "bogus", VIR_STORAGE_FILE_RAW, EXP_FAIL);
496 497

    /* Raw image, whether with right format or no specified format */
498
    testFileData raw = {
499 500
        .pathRel = "raw",
        .pathAbs = canonraw,
E
Eric Blake 已提交
501 502 503 504 505
        .canonPath = canonraw,
        .relDirRel = ".",
        .relDirAbs = datadir,
        .type = VIR_STORAGE_TYPE_FILE,
        .format = VIR_STORAGE_FILE_RAW,
506
    };
507
    TEST_CHAIN(1, "raw", absraw, VIR_STORAGE_FILE_RAW,
508 509 510 511
               (&raw), EXP_PASS,
               (&raw), ALLOW_PROBE | EXP_PASS,
               (&raw), EXP_PASS,
               (&raw), ALLOW_PROBE | EXP_PASS);
512
    TEST_CHAIN(2, "raw", absraw, VIR_STORAGE_FILE_AUTO,
513 514 515 516
               (&raw), EXP_PASS,
               (&raw), ALLOW_PROBE | EXP_PASS,
               (&raw), EXP_PASS,
               (&raw), ALLOW_PROBE | EXP_PASS);
517 518

    /* Qcow2 file with relative raw backing, format provided */
519
    raw.pathAbs = "raw";
520
    testFileData qcow2 = {
521 522
        .expBackingStore = canonraw,
        .expBackingStoreRaw = "raw",
E
Eric Blake 已提交
523 524
        .expBackingDirRel = ".",
        .expBackingDirAbs = datadir,
525
        .expCapacity = 1024,
526 527
        .pathRel = "qcow2",
        .pathAbs = canonqcow2,
E
Eric Blake 已提交
528 529 530 531 532
        .canonPath = canonqcow2,
        .relDirRel = ".",
        .relDirAbs = datadir,
        .type = VIR_STORAGE_TYPE_FILE,
        .format = VIR_STORAGE_FILE_QCOW2,
533 534 535 536
    };
    testFileData qcow2_as_raw = {
        .pathRel = "qcow2",
        .pathAbs = canonqcow2,
E
Eric Blake 已提交
537 538 539 540 541
        .canonPath = canonqcow2,
        .relDirRel = ".",
        .relDirAbs = datadir,
        .type = VIR_STORAGE_TYPE_FILE,
        .format = VIR_STORAGE_FILE_RAW,
542
    };
543
    TEST_CHAIN(3, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2,
544 545 546 547
               (&qcow2, &raw), EXP_PASS,
               (&qcow2, &raw), ALLOW_PROBE | EXP_PASS,
               (&qcow2, &raw), EXP_PASS,
               (&qcow2, &raw), ALLOW_PROBE | EXP_PASS);
548
    TEST_CHAIN(4, "qcow2", absqcow2, VIR_STORAGE_FILE_AUTO,
549
               (&qcow2_as_raw), EXP_PASS,
550
               (&qcow2, &raw), ALLOW_PROBE | EXP_PASS,
551
               (&qcow2_as_raw), EXP_PASS,
552
               (&qcow2, &raw), ALLOW_PROBE | EXP_PASS);
553 554 555 556 557 558 559

    /* Rewrite qcow2 file to use absolute backing name */
    virCommandFree(cmd);
    cmd = virCommandNewArgList(qemuimg, "rebase", "-u", "-f", "qcow2",
                               "-F", "raw", "-b", absraw, "qcow2", NULL);
    if (virCommandRun(cmd, NULL) < 0)
        ret = -1;
560
    qcow2.expBackingStoreRaw = absraw;
E
Eric Blake 已提交
561
    qcow2.expBackingDirRel = datadir;
562 563
    raw.pathRel = absraw;
    raw.pathAbs = absraw;
E
Eric Blake 已提交
564
    raw.relDirRel = datadir;
565 566 567

    /* Qcow2 file with raw as absolute backing, backing format provided */
    TEST_CHAIN(5, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2,
568 569 570 571
               (&qcow2, &raw), EXP_PASS,
               (&qcow2, &raw), ALLOW_PROBE | EXP_PASS,
               (&qcow2, &raw), EXP_PASS,
               (&qcow2, &raw), ALLOW_PROBE | EXP_PASS);
572
    TEST_CHAIN(6, "qcow2", absqcow2, VIR_STORAGE_FILE_AUTO,
573
               (&qcow2_as_raw), EXP_PASS,
574
               (&qcow2, &raw), ALLOW_PROBE | EXP_PASS,
575
               (&qcow2_as_raw), EXP_PASS,
576
               (&qcow2, &raw), ALLOW_PROBE | EXP_PASS);
577 578

    /* Wrapped file access */
579 580 581
    testFileData wrap = {
        .expBackingStore = canonqcow2,
        .expBackingStoreRaw = absqcow2,
E
Eric Blake 已提交
582 583
        .expBackingDirRel = datadir,
        .expBackingDirAbs = datadir,
584
        .expCapacity = 1024,
585 586
        .pathRel = "wrap",
        .pathAbs = abswrap,
E
Eric Blake 已提交
587 588 589 590 591
        .canonPath = canonwrap,
        .relDirRel = ".",
        .relDirAbs = datadir,
        .type = VIR_STORAGE_TYPE_FILE,
        .format = VIR_STORAGE_FILE_QCOW2,
592
    };
593
    qcow2.pathRel = absqcow2;
E
Eric Blake 已提交
594
    qcow2.relDirRel = datadir;
595
    TEST_CHAIN(7, "wrap", abswrap, VIR_STORAGE_FILE_QCOW2,
596 597 598 599
               (&wrap, &qcow2, &raw), EXP_PASS,
               (&wrap, &qcow2, &raw), ALLOW_PROBE | EXP_PASS,
               (&wrap, &qcow2, &raw), EXP_PASS,
               (&wrap, &qcow2, &raw), ALLOW_PROBE | EXP_PASS);
600 601 602 603 604 605 606 607 608 609 610 611 612

    /* Rewrite qcow2 and wrap file to omit backing file type */
    virCommandFree(cmd);
    cmd = virCommandNewArgList(qemuimg, "rebase", "-u", "-f", "qcow2",
                               "-b", absraw, "qcow2", NULL);
    if (virCommandRun(cmd, NULL) < 0)
        ret = -1;

    virCommandFree(cmd);
    cmd = virCommandNewArgList(qemuimg, "rebase", "-u", "-f", "qcow2",
                               "-b", absqcow2, "wrap", NULL);
    if (virCommandRun(cmd, NULL) < 0)
        ret = -1;
613
    qcow2_as_raw.pathRel = absqcow2;
E
Eric Blake 已提交
614
    qcow2_as_raw.relDirRel = datadir;
615 616

    /* Qcow2 file with raw as absolute backing, backing format omitted */
617 618 619
    testFileData wrap_as_raw = {
        .expBackingStore = canonqcow2,
        .expBackingStoreRaw = absqcow2,
E
Eric Blake 已提交
620 621
        .expBackingDirRel = datadir,
        .expBackingDirAbs = datadir,
622
        .expCapacity = 1024,
623 624
        .pathRel = "wrap",
        .pathAbs = abswrap,
E
Eric Blake 已提交
625 626 627 628 629
        .canonPath = canonwrap,
        .relDirRel = ".",
        .relDirAbs = datadir,
        .type = VIR_STORAGE_TYPE_FILE,
        .format = VIR_STORAGE_FILE_QCOW2,
630
    };
631
    TEST_CHAIN(8, "wrap", abswrap, VIR_STORAGE_FILE_QCOW2,
632
               (&wrap_as_raw, &qcow2_as_raw), EXP_PASS,
633
               (&wrap, &qcow2, &raw), ALLOW_PROBE | EXP_PASS,
634
               (&wrap_as_raw, &qcow2_as_raw), EXP_PASS,
635
               (&wrap, &qcow2, &raw), ALLOW_PROBE | EXP_PASS);
636 637 638 639 640 641 642 643

    /* Rewrite qcow2 to a missing backing file, with backing type */
    virCommandFree(cmd);
    cmd = virCommandNewArgList(qemuimg, "rebase", "-u", "-f", "qcow2",
                               "-F", "qcow2", "-b", datadir "/bogus",
                               "qcow2", NULL);
    if (virCommandRun(cmd, NULL) < 0)
        ret = -1;
644 645
    qcow2.expBackingStore = NULL;
    qcow2.expBackingStoreRaw = datadir "/bogus";
646
    qcow2.pathRel = "qcow2";
E
Eric Blake 已提交
647
    qcow2.relDirRel = ".";
648 649 650

    /* Qcow2 file with missing backing file but specified type */
    TEST_CHAIN(9, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2,
651 652 653 654
               (&qcow2), EXP_WARN,
               (&qcow2), ALLOW_PROBE | EXP_WARN,
               (&qcow2), EXP_WARN,
               (&qcow2), ALLOW_PROBE | EXP_WARN);
655 656 657 658 659 660 661 662 663 664

    /* Rewrite qcow2 to a missing backing file, without backing type */
    virCommandFree(cmd);
    cmd = virCommandNewArgList(qemuimg, "rebase", "-u", "-f", "qcow2",
                               "-b", datadir "/bogus", "qcow2", NULL);
    if (virCommandRun(cmd, NULL) < 0)
        ret = -1;

    /* Qcow2 file with missing backing file and no specified type */
    TEST_CHAIN(10, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2,
665 666 667 668
               (&qcow2), EXP_WARN,
               (&qcow2), ALLOW_PROBE | EXP_WARN,
               (&qcow2), EXP_WARN,
               (&qcow2), ALLOW_PROBE | EXP_WARN);
669 670 671 672 673 674 675 676

    /* Rewrite qcow2 to use an nbd: protocol as backend */
    virCommandFree(cmd);
    cmd = virCommandNewArgList(qemuimg, "rebase", "-u", "-f", "qcow2",
                               "-F", "raw", "-b", "nbd:example.org:6000",
                               "qcow2", NULL);
    if (virCommandRun(cmd, NULL) < 0)
        ret = -1;
677
    qcow2.expBackingStore = "nbd:example.org:6000";
678
    qcow2.expBackingStoreRaw = "nbd:example.org:6000";
E
Eric Blake 已提交
679 680
    qcow2.expBackingDirRel = NULL;
    qcow2.expBackingDirAbs = NULL;
681 682

    /* Qcow2 file with backing protocol instead of file */
683 684 685 686 687 688 689
    testFileData nbd = {
        .pathRel = "nbd:example.org:6000",
        .pathAbs = "nbd:example.org:6000",
        .canonPath = "nbd:example.org:6000",
        .type = VIR_STORAGE_TYPE_NETWORK,
        .format = VIR_STORAGE_FILE_RAW,
    };
690
    TEST_CHAIN(11, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2,
691 692 693 694
               (&qcow2, &nbd), EXP_PASS,
               (&qcow2, &nbd), ALLOW_PROBE | EXP_PASS,
               (&qcow2, &nbd), EXP_PASS,
               (&qcow2, &nbd), ALLOW_PROBE | EXP_PASS);
695 696

    /* qed file */
697 698 699
    testFileData qed = {
        .expBackingStore = canonraw,
        .expBackingStoreRaw = absraw,
E
Eric Blake 已提交
700 701
        .expBackingDirRel = datadir,
        .expBackingDirAbs = datadir,
702
        .expCapacity = 1024,
703 704
        .pathRel = "qed",
        .pathAbs = absqed,
E
Eric Blake 已提交
705 706 707 708 709
        .canonPath = canonqed,
        .relDirRel = ".",
        .relDirAbs = datadir,
        .type = VIR_STORAGE_TYPE_FILE,
        .format = VIR_STORAGE_FILE_QED,
710 711 712 713
    };
    testFileData qed_as_raw = {
        .pathRel = "qed",
        .pathAbs = absqed,
E
Eric Blake 已提交
714 715 716 717 718
        .canonPath = canonqed,
        .relDirRel = ".",
        .relDirAbs = datadir,
        .type = VIR_STORAGE_TYPE_FILE,
        .format = VIR_STORAGE_FILE_RAW,
719
    };
720
    TEST_CHAIN(12, "qed", absqed, VIR_STORAGE_FILE_AUTO,
721
               (&qed_as_raw), EXP_PASS,
722
               (&qed, &raw), ALLOW_PROBE | EXP_PASS,
723
               (&qed_as_raw), EXP_PASS,
724
               (&qed, &raw), ALLOW_PROBE | EXP_PASS);
725

E
Eric Blake 已提交
726
    /* directory */
727
    testFileData dir = {
728 729
        .pathRel = "dir",
        .pathAbs = absdir,
E
Eric Blake 已提交
730 731
        .type = VIR_STORAGE_TYPE_DIR,
        .format = VIR_STORAGE_FILE_DIR,
732
    };
E
Eric Blake 已提交
733 734 735 736 737 738 739 740 741 742 743
    TEST_CHAIN(13, "dir", absdir, VIR_STORAGE_FILE_AUTO,
               (&dir), EXP_PASS,
               (&dir), ALLOW_PROBE | EXP_PASS,
               (&dir), EXP_PASS,
               (&dir), ALLOW_PROBE | EXP_PASS);
    TEST_CHAIN(14, "dir", absdir, VIR_STORAGE_FILE_DIR,
               (&dir), EXP_PASS,
               (&dir), ALLOW_PROBE | EXP_PASS,
               (&dir), EXP_PASS,
               (&dir), ALLOW_PROBE | EXP_PASS);

744
#ifdef HAVE_SYMLINK
745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760
    /* Rewrite qcow2 and wrap file to use backing names relative to a
     * symlink from a different directory */
    virCommandFree(cmd);
    cmd = virCommandNewArgList(qemuimg, "rebase", "-u", "-f", "qcow2",
                               "-F", "raw", "-b", "../raw", "qcow2", NULL);
    if (virCommandRun(cmd, NULL) < 0)
        ret = -1;

    virCommandFree(cmd);
    cmd = virCommandNewArgList(qemuimg, "rebase", "-u", "-f", "qcow2",
                               "-F", "qcow2", "-b", "../sub/link1", "wrap",
                               NULL);
    if (virCommandRun(cmd, NULL) < 0)
        ret = -1;

    /* Behavior of symlinks to qcow2 with relative backing files */
761
    testFileData link1 = {
762 763
        .expBackingStore = canonraw,
        .expBackingStoreRaw = "../raw",
E
Eric Blake 已提交
764 765
        .expBackingDirRel = "sub/../sub/..",
        .expBackingDirAbs = datadir "/sub/../sub/..",
766
        .expCapacity = 1024,
767 768
        .pathRel = "../sub/link1",
        .pathAbs = "../sub/link1",
E
Eric Blake 已提交
769 770 771 772 773
        .canonPath = canonqcow2,
        .relDirRel = "sub/../sub",
        .relDirAbs = datadir "/sub/../sub",
        .type = VIR_STORAGE_TYPE_FILE,
        .format = VIR_STORAGE_FILE_QCOW2,
774
    };
775
    testFileData link2 = {
776 777
        .expBackingStore = canonqcow2,
        .expBackingStoreRaw = "../sub/link1",
E
Eric Blake 已提交
778 779
        .expBackingDirRel = "sub/../sub",
        .expBackingDirAbs = datadir "/sub/../sub",
780
        .expCapacity = 1024,
781 782
        .pathRel = "sub/link2",
        .pathAbs = abslink2,
E
Eric Blake 已提交
783 784 785 786 787
        .canonPath = canonwrap,
        .relDirRel = "sub",
        .relDirAbs = datadir "/sub",
        .type = VIR_STORAGE_TYPE_FILE,
        .format = VIR_STORAGE_FILE_QCOW2,
788
    };
789 790
    raw.pathRel = "../raw";
    raw.pathAbs = "../raw";
E
Eric Blake 已提交
791 792
    raw.relDirRel = "sub/../sub/..";
    raw.relDirAbs = datadir "/sub/../sub/..";
E
Eric Blake 已提交
793
    TEST_CHAIN(15, "sub/link2", abslink2, VIR_STORAGE_FILE_QCOW2,
794 795 796 797
               (&link2, &link1, &raw), EXP_PASS,
               (&link2, &link1, &raw), ALLOW_PROBE | EXP_PASS,
               (&link2, &link1, &raw), EXP_PASS,
               (&link2, &link1, &raw), ALLOW_PROBE | EXP_PASS);
798
#endif
E
Eric Blake 已提交
799 800 801 802 803 804 805

    /* Rewrite qcow2 to be a self-referential loop */
    virCommandFree(cmd);
    cmd = virCommandNewArgList(qemuimg, "rebase", "-u", "-f", "qcow2",
                               "-F", "qcow2", "-b", "qcow2", "qcow2", NULL);
    if (virCommandRun(cmd, NULL) < 0)
        ret = -1;
806 807
    qcow2.expBackingStore = NULL;
    qcow2.expBackingStoreRaw = "qcow2";
E
Eric Blake 已提交
808 809
    qcow2.expBackingDirRel = ".";
    qcow2.expBackingDirAbs = datadir;
E
Eric Blake 已提交
810 811 812

    /* Behavior of an infinite loop chain */
    TEST_CHAIN(16, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2,
813 814 815 816
               (&qcow2), EXP_WARN,
               (&qcow2), ALLOW_PROBE | EXP_WARN,
               (&qcow2), EXP_WARN,
               (&qcow2), ALLOW_PROBE | EXP_WARN);
E
Eric Blake 已提交
817 818 819 820 821 822 823 824 825 826 827 828 829

    /* Rewrite wrap and qcow2 to be mutually-referential loop */
    virCommandFree(cmd);
    cmd = virCommandNewArgList(qemuimg, "rebase", "-u", "-f", "qcow2",
                               "-F", "qcow2", "-b", "wrap", "qcow2", NULL);
    if (virCommandRun(cmd, NULL) < 0)
        ret = -1;

    virCommandFree(cmd);
    cmd = virCommandNewArgList(qemuimg, "rebase", "-u", "-f", "qcow2",
                               "-F", "qcow2", "-b", absqcow2, "wrap", NULL);
    if (virCommandRun(cmd, NULL) < 0)
        ret = -1;
830
    qcow2.expBackingStoreRaw = "wrap";
E
Eric Blake 已提交
831
    qcow2.expBackingDirRel = datadir;
832
    qcow2.pathRel = absqcow2;
E
Eric Blake 已提交
833
    qcow2.relDirRel =  datadir;
E
Eric Blake 已提交
834 835 836

    /* Behavior of an infinite loop chain */
    TEST_CHAIN(17, "wrap", abswrap, VIR_STORAGE_FILE_QCOW2,
837 838 839 840
               (&wrap, &qcow2), EXP_WARN,
               (&wrap, &qcow2), ALLOW_PROBE | EXP_WARN,
               (&wrap, &qcow2), EXP_WARN,
               (&wrap, &qcow2), ALLOW_PROBE | EXP_WARN);
E
Eric Blake 已提交
841

E
Eric Blake 已提交
842 843 844 845 846 847 848
    /* Rewrite wrap and qcow2 back to 3-deep chain, absolute backing */
    virCommandFree(cmd);
    cmd = virCommandNewArgList(qemuimg, "rebase", "-u", "-f", "qcow2",
                               "-F", "qcow2", "-b", absraw, "qcow2", NULL);
    if (virCommandRun(cmd, NULL) < 0)
        ret = -1;

849
    /* Test behavior of chain lookups, absolute backing from relative start */
E
Eric Blake 已提交
850 851 852 853 854 855 856 857 858
    chain = virStorageFileGetMetadata("wrap", VIR_STORAGE_FILE_QCOW2,
                                      -1, -1, false);
    if (!chain) {
        ret = -1;
        goto cleanup;
    }

#define TEST_LOOKUP(id, name, result, meta, parent)                  \
    do {                                                             \
859
        struct testLookupData data2 = { chain, name,                 \
E
Eric Blake 已提交
860 861 862 863 864 865 866
                                        result, meta, parent, };     \
        if (virtTestRun("Chain lookup " #id,                         \
                        testStorageLookup, &data2) < 0)              \
            ret = -1;                                                \
    } while (0)

    TEST_LOOKUP(0, "bogus", NULL, NULL, NULL);
867 868 869 870 871 872
    TEST_LOOKUP(1, "wrap", chain->canonPath, chain, NULL);
    TEST_LOOKUP(2, abswrap, chain->canonPath, chain, NULL);
    TEST_LOOKUP(3, "qcow2", chain->backingStore, chain->backingMeta,
                chain->canonPath);
    TEST_LOOKUP(4, absqcow2, chain->backingStore, chain->backingMeta,
                chain->canonPath);
E
Eric Blake 已提交
873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892
    TEST_LOOKUP(5, "raw", chain->backingMeta->backingStore,
                chain->backingMeta->backingMeta, chain->backingStore);
    TEST_LOOKUP(6, absraw, chain->backingMeta->backingStore,
                chain->backingMeta->backingMeta, chain->backingStore);
    TEST_LOOKUP(7, NULL, chain->backingMeta->backingStore,
                chain->backingMeta->backingMeta, chain->backingStore);

    /* Rewrite wrap and qcow2 back to 3-deep chain, relative backing */
    virCommandFree(cmd);
    cmd = virCommandNewArgList(qemuimg, "rebase", "-u", "-f", "qcow2",
                               "-F", "raw", "-b", "raw", "qcow2", NULL);
    if (virCommandRun(cmd, NULL) < 0)
        ret = -1;

    virCommandFree(cmd);
    cmd = virCommandNewArgList(qemuimg, "rebase", "-u", "-f", "qcow2",
                               "-F", "qcow2", "-b", "qcow2", "wrap", NULL);
    if (virCommandRun(cmd, NULL) < 0)
        ret = -1;

893
    /* Test behavior of chain lookups, relative backing from absolute start */
E
Eric Blake 已提交
894 895 896 897 898 899 900 901 902
    virStorageFileFreeMetadata(chain);
    chain = virStorageFileGetMetadata(abswrap, VIR_STORAGE_FILE_QCOW2,
                                      -1, -1, false);
    if (!chain) {
        ret = -1;
        goto cleanup;
    }

    TEST_LOOKUP(8, "bogus", NULL, NULL, NULL);
903 904 905 906 907 908
    TEST_LOOKUP(9, "wrap", chain->canonPath, chain, NULL);
    TEST_LOOKUP(10, abswrap, chain->canonPath, chain, NULL);
    TEST_LOOKUP(11, "qcow2", chain->backingStore, chain->backingMeta,
                chain->canonPath);
    TEST_LOOKUP(12, absqcow2, chain->backingStore, chain->backingMeta,
                chain->canonPath);
E
Eric Blake 已提交
909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924
    TEST_LOOKUP(13, "raw", chain->backingMeta->backingStore,
                chain->backingMeta->backingMeta, chain->backingStore);
    TEST_LOOKUP(14, absraw, chain->backingMeta->backingStore,
                chain->backingMeta->backingMeta, chain->backingStore);
    TEST_LOOKUP(15, NULL, chain->backingMeta->backingStore,
                chain->backingMeta->backingMeta, chain->backingStore);

    /* Use link to wrap with cross-directory relative backing */
    virCommandFree(cmd);
    cmd = virCommandNewArgList(qemuimg, "rebase", "-u", "-f", "qcow2",
                               "-F", "qcow2", "-b", "../qcow2", "wrap", NULL);
    if (virCommandRun(cmd, NULL) < 0)
        ret = -1;

    /* Test behavior of chain lookups, relative backing */
    virStorageFileFreeMetadata(chain);
925
    chain = virStorageFileGetMetadata("sub/link2", VIR_STORAGE_FILE_QCOW2,
E
Eric Blake 已提交
926 927 928 929 930 931 932
                                      -1, -1, false);
    if (!chain) {
        ret = -1;
        goto cleanup;
    }

    TEST_LOOKUP(16, "bogus", NULL, NULL, NULL);
933 934 935 936 937
    TEST_LOOKUP(17, "sub/link2", chain->canonPath, chain, NULL);
    TEST_LOOKUP(18, "wrap", chain->canonPath, chain, NULL);
    TEST_LOOKUP(19, abswrap, chain->canonPath, chain, NULL);
    TEST_LOOKUP(20, "../qcow2", chain->backingStore, chain->backingMeta,
                chain->canonPath);
E
Eric Blake 已提交
938
    TEST_LOOKUP(21, "qcow2", NULL, NULL, NULL);
939 940
    TEST_LOOKUP(22, absqcow2, chain->backingStore, chain->backingMeta,
                chain->canonPath);
E
Eric Blake 已提交
941 942 943 944 945 946 947 948
    TEST_LOOKUP(23, "raw", chain->backingMeta->backingStore,
                chain->backingMeta->backingMeta, chain->backingStore);
    TEST_LOOKUP(24, absraw, chain->backingMeta->backingStore,
                chain->backingMeta->backingMeta, chain->backingStore);
    TEST_LOOKUP(25, NULL, chain->backingMeta->backingStore,
                chain->backingMeta->backingMeta, chain->backingStore);

 cleanup:
949
    /* Final cleanup */
E
Eric Blake 已提交
950
    virStorageFileFreeMetadata(chain);
951 952 953 954 955 956 957
    testCleanupImages();
    virCommandFree(cmd);

    return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}

VIRT_TEST_MAIN(mymain)