virstoragetest.c 35.1 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
#include "dirname.h"
33

34 35
#include "storage/storage_driver.h"

36 37
#define VIR_FROM_THIS VIR_FROM_NONE

38 39
VIR_LOG_INIT("tests.storagetest");

40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
#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 已提交
62
static char *canonwrap;
63
static char *absqed;
E
Eric Blake 已提交
64
static char *canonqed;
E
Eric Blake 已提交
65
static char *absdir;
E
Eric Blake 已提交
66
static char *canondir;
67 68 69 70 71 72 73 74 75 76 77
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 已提交
78
    VIR_FREE(canonwrap);
79
    VIR_FREE(absqed);
E
Eric Blake 已提交
80
    VIR_FREE(canonqed);
E
Eric Blake 已提交
81
    VIR_FREE(absdir);
E
Eric Blake 已提交
82
    VIR_FREE(canondir);
83 84 85 86 87 88 89 90
    VIR_FREE(abslink2);

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

91
    virFileDeleteTree(datadir);
92 93
}

94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131

static virStorageSourcePtr
testStorageFileGetMetadata(const char *path,
                           int format,
                           uid_t uid, gid_t gid,
                           bool allow_probe)
{
    virStorageSourcePtr ret = NULL;

    if (VIR_ALLOC(ret) < 0)
        return NULL;

    ret->type = VIR_STORAGE_TYPE_FILE;
    ret->format = format;

    if (VIR_STRDUP(ret->relPath, path) < 0)
        goto error;

    if (!(ret->relDir = mdir_name(path))) {
        virReportOOMError();
        goto error;
    }

    if (!(ret->path = canonicalize_file_name(path))) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "failed to resolve '%s'", path);
        goto error;
    }

    if (virStorageFileGetMetadata(ret, uid, gid, allow_probe) < 0)
        goto error;

    return ret;

 error:
    virStorageSourceFree(ret);
    return NULL;
}

132 133 134 135 136
static int
testPrepImages(void)
{
    int ret = EXIT_FAILURE;
    virCommandPtr cmd = NULL;
137 138
    char *buf = NULL;
    bool compat = false;
139 140 141 142

    qemuimg = virFindFileInPath("kvm-img");
    if (!qemuimg)
        qemuimg = virFindFileInPath("qemu-img");
143 144
    if (!qemuimg)
        goto skip;
145

146 147 148
    /* Clean up from any earlier failed tests */
    virFileDeleteTree(datadir);

149 150 151 152 153 154 155 156 157 158 159 160
    /* 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);

161 162 163 164
    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 已提交
165
        virAsprintf(&absdir, "%s/dir", datadir) < 0 ||
166
        virAsprintf(&abslink2, "%s/sub/link2", datadir) < 0)
167 168 169 170 171 172
        goto cleanup;

    if (virFileMakePath(datadir "/sub") < 0) {
        fprintf(stderr, "unable to create directory %s\n", datadir "/sub");
        goto cleanup;
    }
E
Eric Blake 已提交
173 174 175 176
    if (virFileMakePath(datadir "/dir") < 0) {
        fprintf(stderr, "unable to create directory %s\n", datadir "/dir");
        goto cleanup;
    }
E
Eric Blake 已提交
177 178 179 180
    if (!(canondir = canonicalize_file_name(absdir))) {
        virReportOOMError();
        goto cleanup;
    }
181 182 183 184 185 186

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

187 188
    if (virAsprintf(&buf, "%1024d", 0) < 0 ||
        virFileWriteStr("raw", buf, 0600) < 0) {
189
        fprintf(stderr, "unable to create raw file\n");
190
        goto cleanup;
191
    }
192 193 194 195 196 197 198 199
    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);
200 201 202 203
    cmd = virCommandNewArgList(qemuimg, "create", "-f", "qcow2", NULL);
    virCommandAddArgFormat(cmd, "-obacking_file=raw,backing_fmt=raw%s",
                           compat ? ",compat=0.10" : "");
    virCommandAddArg(cmd, "qcow2");
204
    if (virCommandRun(cmd, NULL) < 0)
205
        goto skip;
206 207
    /* Make sure our later uses of 'qemu-img rebase' will work */
    virCommandFree(cmd);
208
    cmd = virCommandNewArgList(qemuimg, "rebase", "-u", "-f", "qcow2",
209
                               "-F", "raw", "-b", "raw", "qcow2", NULL);
210 211
    if (virCommandRun(cmd, NULL) < 0)
        goto skip;
212 213 214 215 216 217 218 219
    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);
220
    cmd = virCommandNewArgList(qemuimg, "create", "-f", "qcow2", NULL);
221 222
    virCommandAddArgFormat(cmd, "-obacking_file=%s,backing_fmt=qcow2%s",
                           absqcow2, compat ? ",compat=1.1" : "");
223 224
    virCommandAddArg(cmd, "wrap");
    if (virCommandRun(cmd, NULL) < 0)
225
        goto skip;
E
Eric Blake 已提交
226 227 228 229
    if (!(canonwrap = canonicalize_file_name(abswrap))) {
        virReportOOMError();
        goto cleanup;
    }
230 231 232

    /* Create a qed file. */
    virCommandFree(cmd);
233
    cmd = virCommandNewArgList(qemuimg, "create", "-f", "qed", NULL);
234 235 236 237
    virCommandAddArgFormat(cmd, "-obacking_file=%s,backing_fmt=raw",
                           absraw);
    virCommandAddArg(cmd, "qed");
    if (virCommandRun(cmd, NULL) < 0)
238
        goto skip;
E
Eric Blake 已提交
239 240 241 242
    if (!(canonqed = canonicalize_file_name(absqed))) {
        virReportOOMError();
        goto cleanup;
    }
243

244
#ifdef HAVE_SYMLINK
245 246 247 248 249 250
    /* 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;
    }
251
#endif
252 253

    ret = 0;
254
 cleanup:
255
    VIR_FREE(buf);
256 257 258 259
    virCommandFree(cmd);
    if (ret)
        testCleanupImages();
    return ret;
260

261
 skip:
262 263 264
    fputs("qemu-img is too old; skipping this test\n", stderr);
    ret = EXIT_AM_SKIP;
    goto cleanup;
265 266
}

267 268 269 270 271
/* 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.  */
272 273 274 275 276 277 278
typedef struct _testFileData testFileData;
struct _testFileData
{
    const char *expBackingStore;
    const char *expBackingStoreRaw;
    unsigned long long expCapacity;
    bool expEncrypted;
279 280
    const char *pathRel;
    const char *pathAbs;
281
    const char *path;
E
Eric Blake 已提交
282 283 284 285
    const char *relDirRel;
    const char *relDirAbs;
    int type;
    int format;
286 287 288 289 290 291 292
};

enum {
    EXP_PASS = 0,
    EXP_FAIL = 1,
    EXP_WARN = 2,
    ALLOW_PROBE = 4,
293
    ABS_START = 8,
294 295 296 297 298
};

struct testChainData
{
    const char *start;
299
    virStorageFileFormat format;
300
    const testFileData *files[4];
301 302 303 304 305 306 307 308 309
    int nfiles;
    unsigned int flags;
};

static int
testStorageChain(const void *args)
{
    const struct testChainData *data = args;
    int ret = -1;
310 311
    virStorageSourcePtr meta;
    virStorageSourcePtr elt;
312
    size_t i = 0;
E
Eric Blake 已提交
313
    char *broken = NULL;
314
    bool isAbs = !!(data->flags & ABS_START);
315

316 317
    meta = testStorageFileGetMetadata(data->start, data->format, -1, -1,
                                      (data->flags & ALLOW_PROBE) != 0);
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
    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 已提交
334 335 336 337 338 339 340 341 342 343 344 345 346
        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;
        }
347 348 349 350 351 352
    }

    elt = meta;
    while (elt) {
        char *expect = NULL;
        char *actual = NULL;
353
        const char *expPath;
E
Eric Blake 已提交
354
        const char *expRelDir;
355 356 357 358 359 360

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

361 362
        expPath = isAbs ? data->files[i]->pathAbs
            : data->files[i]->pathRel;
E
Eric Blake 已提交
363 364
        expRelDir = isAbs ? data->files[i]->relDirAbs
            : data->files[i]->relDirRel;
365
        if (virAsprintf(&expect,
366
                        "store:%s\nraw:%s\nother:%lld %d\n"
367
                        "relPath:%s\npath:%s\nrelDir:%s\ntype:%d %d\n",
368 369 370
                        NULLSTR(data->files[i]->expBackingStore),
                        NULLSTR(data->files[i]->expBackingStoreRaw),
                        data->files[i]->expCapacity,
371
                        data->files[i]->expEncrypted,
E
Eric Blake 已提交
372
                        NULLSTR(expPath),
373
                        NULLSTR(data->files[i]->path),
E
Eric Blake 已提交
374 375 376
                        NULLSTR(expRelDir),
                        data->files[i]->type,
                        data->files[i]->format) < 0 ||
377
            virAsprintf(&actual,
378
                        "store:%s\nraw:%s\nother:%lld %d\n"
379
                        "relPath:%s\npath:%s\nrelDir:%s\ntype:%d %d\n",
380
                        NULLSTR(elt->backingStore ? elt->backingStore->path : NULL),
381
                        NULLSTR(elt->backingStoreRaw),
382
                        elt->capacity, !!elt->encryption,
383
                        NULLSTR(elt->relPath),
384
                        NULLSTR(elt->path),
E
Eric Blake 已提交
385 386
                        NULLSTR(elt->relDir),
                        elt->type, elt->format) < 0) {
387 388 389 390 391
            VIR_FREE(expect);
            VIR_FREE(actual);
            goto cleanup;
        }
        if (STRNEQ(expect, actual)) {
392
            fprintf(stderr, "chain member %zu", i);
393 394 395 396 397 398 399
            virtTestDifference(stderr, expect, actual);
            VIR_FREE(expect);
            VIR_FREE(actual);
            goto cleanup;
        }
        VIR_FREE(expect);
        VIR_FREE(actual);
400
        elt = elt->backingStore;
401 402 403 404 405 406 407 408
        i++;
    }
    if (i != data->nfiles) {
        fprintf(stderr, "probed chain was too short\n");
        goto cleanup;
    }

    ret = 0;
409
 cleanup:
E
Eric Blake 已提交
410
    VIR_FREE(broken);
411
    virStorageSourceFree(meta);
412 413 414
    return ret;
}

E
Eric Blake 已提交
415 416
struct testLookupData
{
417
    virStorageSourcePtr chain;
418
    const char *target;
E
Eric Blake 已提交
419
    const char *name;
420
    unsigned int expIndex;
E
Eric Blake 已提交
421
    const char *expResult;
422
    virStorageSourcePtr expMeta;
E
Eric Blake 已提交
423 424 425 426 427 428 429 430
    const char *expParent;
};

static int
testStorageLookup(const void *args)
{
    const struct testLookupData *data = args;
    int ret = 0;
431
    virStorageSourcePtr result;
E
Eric Blake 已提交
432
    const char *actualParent;
433 434 435 436 437 438 439 440 441 442 443
    unsigned int idx;

    if (virStorageFileParseChainIndex(data->target, data->name, &idx) < 0 &&
        data->expIndex) {
        fprintf(stderr, "call should not have failed\n");
        ret = -1;
    }
    if (idx != data->expIndex) {
        fprintf(stderr, "index: expected %u, got %u\n", data->expIndex, idx);
        ret = -1;
    }
E
Eric Blake 已提交
444

445
     /* Test twice to ensure optional parameter doesn't cause NULL deref. */
446 447 448
    result = virStorageFileChainLookup(data->chain, NULL,
                                       idx ? NULL : data->name,
                                       idx, NULL);
E
Eric Blake 已提交
449 450 451 452 453 454 455 456 457 458 459 460 461 462

    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;
        }
    }

463 464 465 466 467 468 469
    if (!result) {
        if (data->expResult) {
            fprintf(stderr, "result 1: expected %s, got NULL\n",
                    data->expResult);
            ret = -1;
        }
    } else if (STRNEQ_NULLABLE(data->expResult, result->path)) {
E
Eric Blake 已提交
470
        fprintf(stderr, "result 1: expected %s, got %s\n",
471
                NULLSTR(data->expResult), NULLSTR(result->path));
E
Eric Blake 已提交
472 473 474
        ret = -1;
    }

475 476
    result = virStorageFileChainLookup(data->chain, data->chain,
                                       data->name, idx, &actualParent);
E
Eric Blake 已提交
477 478
    if (!data->expResult)
        virResetLastError();
479 480 481 482 483 484 485 486

    if (!result) {
        if (data->expResult) {
            fprintf(stderr, "result 2: expected %s, got NULL\n",
                    data->expResult);
            ret = -1;
        }
    } else if (STRNEQ_NULLABLE(data->expResult, result->path)) {
E
Eric Blake 已提交
487
        fprintf(stderr, "result 2: expected %s, got %s\n",
488
                NULLSTR(data->expResult), NULLSTR(result->path));
E
Eric Blake 已提交
489 490
        ret = -1;
    }
491
    if (data->expMeta != result) {
E
Eric Blake 已提交
492
        fprintf(stderr, "meta: expected %p, got %p\n",
493
                data->expMeta, result);
E
Eric Blake 已提交
494 495 496 497 498 499 500 501 502 503 504
        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;
}

505 506 507 508 509
static int
mymain(void)
{
    int ret;
    virCommandPtr cmd = NULL;
510
    struct testChainData data;
511
    virStorageSourcePtr chain = NULL;
512 513 514 515 516 517

    /* 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;

518
#define TEST_ONE_CHAIN(id, start, format, flags, ...)                \
519
    do {                                                             \
520 521 522 523
        size_t i;                                                    \
        memset(&data, 0, sizeof(data));                              \
        data = (struct testChainData){                               \
            start, format, { __VA_ARGS__ }, 0, flags,                \
524
        };                                                           \
525 526 527
        for (i = 0; i < ARRAY_CARDINALITY(data.files); i++)          \
            if (data.files[i])                                       \
                data.nfiles++;                                       \
528
        if (virtTestRun("Storage backing chain " id,                 \
529 530 531 532
                        testStorageChain, &data) < 0)                \
            ret = -1;                                                \
    } while (0)

533 534 535
#define VIR_FLATTEN_2(...) __VA_ARGS__
#define VIR_FLATTEN_1(_1) VIR_FLATTEN_2 _1

536 537 538
#define TEST_CHAIN(id, relstart, absstart, format, chain1, flags1,   \
                   chain2, flags2, chain3, flags3, chain4, flags4)   \
    do {                                                             \
539 540 541 542
        TEST_ONE_CHAIN(#id "a", relstart, format, flags1,            \
                       VIR_FLATTEN_1(chain1));                       \
        TEST_ONE_CHAIN(#id "b", relstart, format, flags2,            \
                       VIR_FLATTEN_1(chain2));                       \
543
        TEST_ONE_CHAIN(#id "c", absstart, format, flags3 | ABS_START,\
544
                       VIR_FLATTEN_1(chain3));                       \
545
        TEST_ONE_CHAIN(#id "d", absstart, format, flags4 | ABS_START,\
546
                       VIR_FLATTEN_1(chain4));                       \
547 548 549 550 551
    } while (0)

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

    /* Missing file */
552
    TEST_ONE_CHAIN("0", "bogus", VIR_STORAGE_FILE_RAW, EXP_FAIL);
553 554

    /* Raw image, whether with right format or no specified format */
555
    testFileData raw = {
556 557
        .pathRel = "raw",
        .pathAbs = canonraw,
558
        .path = canonraw,
E
Eric Blake 已提交
559 560 561 562
        .relDirRel = ".",
        .relDirAbs = datadir,
        .type = VIR_STORAGE_TYPE_FILE,
        .format = VIR_STORAGE_FILE_RAW,
563
    };
564
    TEST_CHAIN(1, "raw", absraw, VIR_STORAGE_FILE_RAW,
565 566 567 568
               (&raw), EXP_PASS,
               (&raw), ALLOW_PROBE | EXP_PASS,
               (&raw), EXP_PASS,
               (&raw), ALLOW_PROBE | EXP_PASS);
569
    TEST_CHAIN(2, "raw", absraw, VIR_STORAGE_FILE_AUTO,
570 571 572 573
               (&raw), EXP_PASS,
               (&raw), ALLOW_PROBE | EXP_PASS,
               (&raw), EXP_PASS,
               (&raw), ALLOW_PROBE | EXP_PASS);
574 575

    /* Qcow2 file with relative raw backing, format provided */
576
    raw.pathAbs = "raw";
577
    testFileData qcow2 = {
578 579 580
        .expBackingStore = canonraw,
        .expBackingStoreRaw = "raw",
        .expCapacity = 1024,
581 582
        .pathRel = "qcow2",
        .pathAbs = canonqcow2,
583
        .path = canonqcow2,
E
Eric Blake 已提交
584 585 586 587
        .relDirRel = ".",
        .relDirAbs = datadir,
        .type = VIR_STORAGE_TYPE_FILE,
        .format = VIR_STORAGE_FILE_QCOW2,
588 589 590 591
    };
    testFileData qcow2_as_raw = {
        .pathRel = "qcow2",
        .pathAbs = canonqcow2,
592
        .path = canonqcow2,
E
Eric Blake 已提交
593 594 595 596
        .relDirRel = ".",
        .relDirAbs = datadir,
        .type = VIR_STORAGE_TYPE_FILE,
        .format = VIR_STORAGE_FILE_RAW,
597
    };
598
    TEST_CHAIN(3, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2,
599 600 601 602
               (&qcow2, &raw), EXP_PASS,
               (&qcow2, &raw), ALLOW_PROBE | EXP_PASS,
               (&qcow2, &raw), EXP_PASS,
               (&qcow2, &raw), ALLOW_PROBE | EXP_PASS);
603
    TEST_CHAIN(4, "qcow2", absqcow2, VIR_STORAGE_FILE_AUTO,
604
               (&qcow2_as_raw), EXP_PASS,
605
               (&qcow2, &raw), ALLOW_PROBE | EXP_PASS,
606
               (&qcow2_as_raw), EXP_PASS,
607
               (&qcow2, &raw), ALLOW_PROBE | EXP_PASS);
608 609 610 611 612 613 614

    /* 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;
615
    qcow2.expBackingStoreRaw = absraw;
616 617
    raw.pathRel = absraw;
    raw.pathAbs = absraw;
E
Eric Blake 已提交
618
    raw.relDirRel = datadir;
619 620 621

    /* Qcow2 file with raw as absolute backing, backing format provided */
    TEST_CHAIN(5, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2,
622 623 624 625
               (&qcow2, &raw), EXP_PASS,
               (&qcow2, &raw), ALLOW_PROBE | EXP_PASS,
               (&qcow2, &raw), EXP_PASS,
               (&qcow2, &raw), ALLOW_PROBE | EXP_PASS);
626
    TEST_CHAIN(6, "qcow2", absqcow2, VIR_STORAGE_FILE_AUTO,
627
               (&qcow2_as_raw), EXP_PASS,
628
               (&qcow2, &raw), ALLOW_PROBE | EXP_PASS,
629
               (&qcow2_as_raw), EXP_PASS,
630
               (&qcow2, &raw), ALLOW_PROBE | EXP_PASS);
631 632

    /* Wrapped file access */
633 634 635 636
    testFileData wrap = {
        .expBackingStore = canonqcow2,
        .expBackingStoreRaw = absqcow2,
        .expCapacity = 1024,
637 638
        .pathRel = "wrap",
        .pathAbs = abswrap,
639
        .path = canonwrap,
E
Eric Blake 已提交
640 641 642 643
        .relDirRel = ".",
        .relDirAbs = datadir,
        .type = VIR_STORAGE_TYPE_FILE,
        .format = VIR_STORAGE_FILE_QCOW2,
644
    };
645
    qcow2.pathRel = absqcow2;
E
Eric Blake 已提交
646
    qcow2.relDirRel = datadir;
647
    TEST_CHAIN(7, "wrap", abswrap, VIR_STORAGE_FILE_QCOW2,
648 649 650 651
               (&wrap, &qcow2, &raw), EXP_PASS,
               (&wrap, &qcow2, &raw), ALLOW_PROBE | EXP_PASS,
               (&wrap, &qcow2, &raw), EXP_PASS,
               (&wrap, &qcow2, &raw), ALLOW_PROBE | EXP_PASS);
652 653 654 655 656 657 658 659 660 661 662 663 664

    /* 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;
665
    qcow2_as_raw.pathRel = absqcow2;
E
Eric Blake 已提交
666
    qcow2_as_raw.relDirRel = datadir;
667 668

    /* Qcow2 file with raw as absolute backing, backing format omitted */
669 670 671 672
    testFileData wrap_as_raw = {
        .expBackingStore = canonqcow2,
        .expBackingStoreRaw = absqcow2,
        .expCapacity = 1024,
673 674
        .pathRel = "wrap",
        .pathAbs = abswrap,
675
        .path = canonwrap,
E
Eric Blake 已提交
676 677 678 679
        .relDirRel = ".",
        .relDirAbs = datadir,
        .type = VIR_STORAGE_TYPE_FILE,
        .format = VIR_STORAGE_FILE_QCOW2,
680
    };
681
    TEST_CHAIN(8, "wrap", abswrap, VIR_STORAGE_FILE_QCOW2,
682
               (&wrap_as_raw, &qcow2_as_raw), EXP_PASS,
683
               (&wrap, &qcow2, &raw), ALLOW_PROBE | EXP_PASS,
684
               (&wrap_as_raw, &qcow2_as_raw), EXP_PASS,
685
               (&wrap, &qcow2, &raw), ALLOW_PROBE | EXP_PASS);
686 687 688 689 690 691 692 693

    /* 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;
694 695
    qcow2.expBackingStore = NULL;
    qcow2.expBackingStoreRaw = datadir "/bogus";
696
    qcow2.pathRel = "qcow2";
E
Eric Blake 已提交
697
    qcow2.relDirRel = ".";
698 699 700

    /* Qcow2 file with missing backing file but specified type */
    TEST_CHAIN(9, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2,
701 702 703 704
               (&qcow2), EXP_WARN,
               (&qcow2), ALLOW_PROBE | EXP_WARN,
               (&qcow2), EXP_WARN,
               (&qcow2), ALLOW_PROBE | EXP_WARN);
705 706 707 708 709 710 711 712 713 714

    /* 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,
715 716 717 718
               (&qcow2), EXP_WARN,
               (&qcow2), ALLOW_PROBE | EXP_WARN,
               (&qcow2), EXP_WARN,
               (&qcow2), ALLOW_PROBE | EXP_WARN);
719 720 721 722 723 724 725 726

    /* 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;
727
    qcow2.expBackingStore = "nbd:example.org:6000";
728
    qcow2.expBackingStoreRaw = "nbd:example.org:6000";
729 730

    /* Qcow2 file with backing protocol instead of file */
731 732 733
    testFileData nbd = {
        .pathRel = "nbd:example.org:6000",
        .pathAbs = "nbd:example.org:6000",
734
        .path = "nbd:example.org:6000",
735 736 737
        .type = VIR_STORAGE_TYPE_NETWORK,
        .format = VIR_STORAGE_FILE_RAW,
    };
738
    TEST_CHAIN(11, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2,
739 740 741 742
               (&qcow2, &nbd), EXP_PASS,
               (&qcow2, &nbd), ALLOW_PROBE | EXP_PASS,
               (&qcow2, &nbd), EXP_PASS,
               (&qcow2, &nbd), ALLOW_PROBE | EXP_PASS);
743 744

    /* qed file */
745 746 747 748
    testFileData qed = {
        .expBackingStore = canonraw,
        .expBackingStoreRaw = absraw,
        .expCapacity = 1024,
749 750
        .pathRel = "qed",
        .pathAbs = absqed,
751
        .path = canonqed,
E
Eric Blake 已提交
752 753 754 755
        .relDirRel = ".",
        .relDirAbs = datadir,
        .type = VIR_STORAGE_TYPE_FILE,
        .format = VIR_STORAGE_FILE_QED,
756 757 758 759
    };
    testFileData qed_as_raw = {
        .pathRel = "qed",
        .pathAbs = absqed,
760
        .path = canonqed,
E
Eric Blake 已提交
761 762 763 764
        .relDirRel = ".",
        .relDirAbs = datadir,
        .type = VIR_STORAGE_TYPE_FILE,
        .format = VIR_STORAGE_FILE_RAW,
765
    };
766
    TEST_CHAIN(12, "qed", absqed, VIR_STORAGE_FILE_AUTO,
767
               (&qed_as_raw), EXP_PASS,
768
               (&qed, &raw), ALLOW_PROBE | EXP_PASS,
769
               (&qed_as_raw), EXP_PASS,
770
               (&qed, &raw), ALLOW_PROBE | EXP_PASS);
771

E
Eric Blake 已提交
772
    /* directory */
773
    testFileData dir = {
774 775
        .pathRel = "dir",
        .pathAbs = absdir,
776 777 778
        .path = canondir,
        .relDirRel = ".",
        .relDirAbs = datadir,
E
Eric Blake 已提交
779 780
        .type = VIR_STORAGE_TYPE_DIR,
        .format = VIR_STORAGE_FILE_DIR,
781
    };
E
Eric Blake 已提交
782 783 784 785 786 787 788 789 790 791 792
    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);

793
#ifdef HAVE_SYMLINK
794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809
    /* 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 */
810
    testFileData link1 = {
811 812 813
        .expBackingStore = canonraw,
        .expBackingStoreRaw = "../raw",
        .expCapacity = 1024,
814 815
        .pathRel = "../sub/link1",
        .pathAbs = "../sub/link1",
816
        .path = canonqcow2,
E
Eric Blake 已提交
817 818 819 820
        .relDirRel = "sub/../sub",
        .relDirAbs = datadir "/sub/../sub",
        .type = VIR_STORAGE_TYPE_FILE,
        .format = VIR_STORAGE_FILE_QCOW2,
821
    };
822
    testFileData link2 = {
823 824 825
        .expBackingStore = canonqcow2,
        .expBackingStoreRaw = "../sub/link1",
        .expCapacity = 1024,
826 827
        .pathRel = "sub/link2",
        .pathAbs = abslink2,
828
        .path = canonwrap,
E
Eric Blake 已提交
829 830 831 832
        .relDirRel = "sub",
        .relDirAbs = datadir "/sub",
        .type = VIR_STORAGE_TYPE_FILE,
        .format = VIR_STORAGE_FILE_QCOW2,
833
    };
834 835
    raw.pathRel = "../raw";
    raw.pathAbs = "../raw";
E
Eric Blake 已提交
836 837
    raw.relDirRel = "sub/../sub/..";
    raw.relDirAbs = datadir "/sub/../sub/..";
E
Eric Blake 已提交
838
    TEST_CHAIN(15, "sub/link2", abslink2, VIR_STORAGE_FILE_QCOW2,
839 840 841 842
               (&link2, &link1, &raw), EXP_PASS,
               (&link2, &link1, &raw), ALLOW_PROBE | EXP_PASS,
               (&link2, &link1, &raw), EXP_PASS,
               (&link2, &link1, &raw), ALLOW_PROBE | EXP_PASS);
843
#endif
E
Eric Blake 已提交
844 845 846 847 848 849 850

    /* 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;
851 852
    qcow2.expBackingStore = NULL;
    qcow2.expBackingStoreRaw = "qcow2";
E
Eric Blake 已提交
853 854 855

    /* Behavior of an infinite loop chain */
    TEST_CHAIN(16, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2,
856 857 858 859
               (&qcow2), EXP_WARN,
               (&qcow2), ALLOW_PROBE | EXP_WARN,
               (&qcow2), EXP_WARN,
               (&qcow2), ALLOW_PROBE | EXP_WARN);
E
Eric Blake 已提交
860 861 862 863 864 865 866 867 868 869 870 871 872

    /* 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;
873
    qcow2.expBackingStoreRaw = "wrap";
874
    qcow2.pathRel = absqcow2;
E
Eric Blake 已提交
875
    qcow2.relDirRel =  datadir;
E
Eric Blake 已提交
876 877 878

    /* Behavior of an infinite loop chain */
    TEST_CHAIN(17, "wrap", abswrap, VIR_STORAGE_FILE_QCOW2,
879 880 881 882
               (&wrap, &qcow2), EXP_WARN,
               (&wrap, &qcow2), ALLOW_PROBE | EXP_WARN,
               (&wrap, &qcow2), EXP_WARN,
               (&wrap, &qcow2), ALLOW_PROBE | EXP_WARN);
E
Eric Blake 已提交
883

E
Eric Blake 已提交
884 885 886 887 888 889 890
    /* 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;

891
    /* Test behavior of chain lookups, absolute backing from relative start */
892 893
    chain = testStorageFileGetMetadata("wrap", VIR_STORAGE_FILE_QCOW2,
                                       -1, -1, false);
E
Eric Blake 已提交
894 895 896 897 898
    if (!chain) {
        ret = -1;
        goto cleanup;
    }

899 900 901 902 903 904 905
#define TEST_LOOKUP_TARGET(id, target, name, index, result, meta, parent)   \
    do {                                                                    \
        struct testLookupData data2 = { chain, target, name, index,         \
                                        result, meta, parent, };            \
        if (virtTestRun("Chain lookup " #id,                                \
                        testStorageLookup, &data2) < 0)                     \
            ret = -1;                                                       \
E
Eric Blake 已提交
906
    } while (0)
907 908
#define TEST_LOOKUP(id, name, result, meta, parent)                         \
    TEST_LOOKUP_TARGET(id, NULL, name, 0, result, meta, parent)
E
Eric Blake 已提交
909 910

    TEST_LOOKUP(0, "bogus", NULL, NULL, NULL);
911 912
    TEST_LOOKUP(1, "wrap", chain->path, chain, NULL);
    TEST_LOOKUP(2, abswrap, chain->path, chain, NULL);
913
    TEST_LOOKUP(3, "qcow2", chain->backingStore->path, chain->backingStore,
914
                chain->path);
915
    TEST_LOOKUP(4, absqcow2, chain->backingStore->path, chain->backingStore,
916
                chain->path);
917 918 919 920 921 922
    TEST_LOOKUP(5, "raw", chain->backingStore->backingStore->path,
                chain->backingStore->backingStore, chain->backingStore->path);
    TEST_LOOKUP(6, absraw, chain->backingStore->backingStore->path,
                chain->backingStore->backingStore, chain->backingStore->path);
    TEST_LOOKUP(7, NULL, chain->backingStore->backingStore->path,
                chain->backingStore->backingStore, chain->backingStore->path);
E
Eric Blake 已提交
923 924 925 926 927 928 929 930 931 932 933 934 935 936

    /* 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;

937
    /* Test behavior of chain lookups, relative backing from absolute start */
938
    virStorageSourceFree(chain);
939 940
    chain = testStorageFileGetMetadata(abswrap, VIR_STORAGE_FILE_QCOW2,
                                       -1, -1, false);
E
Eric Blake 已提交
941 942 943 944 945 946
    if (!chain) {
        ret = -1;
        goto cleanup;
    }

    TEST_LOOKUP(8, "bogus", NULL, NULL, NULL);
947 948
    TEST_LOOKUP(9, "wrap", chain->path, chain, NULL);
    TEST_LOOKUP(10, abswrap, chain->path, chain, NULL);
949
    TEST_LOOKUP(11, "qcow2", chain->backingStore->path, chain->backingStore,
950
                chain->path);
951
    TEST_LOOKUP(12, absqcow2, chain->backingStore->path, chain->backingStore,
952
                chain->path);
953 954 955 956 957 958
    TEST_LOOKUP(13, "raw", chain->backingStore->backingStore->path,
                chain->backingStore->backingStore, chain->backingStore->path);
    TEST_LOOKUP(14, absraw, chain->backingStore->backingStore->path,
                chain->backingStore->backingStore, chain->backingStore->path);
    TEST_LOOKUP(15, NULL, chain->backingStore->backingStore->path,
                chain->backingStore->backingStore, chain->backingStore->path);
E
Eric Blake 已提交
959 960 961 962 963 964 965 966 967

    /* 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 */
968
    virStorageSourceFree(chain);
969 970
    chain = testStorageFileGetMetadata("sub/link2", VIR_STORAGE_FILE_QCOW2,
                                       -1, -1, false);
E
Eric Blake 已提交
971 972 973 974 975 976
    if (!chain) {
        ret = -1;
        goto cleanup;
    }

    TEST_LOOKUP(16, "bogus", NULL, NULL, NULL);
977 978 979
    TEST_LOOKUP(17, "sub/link2", chain->path, chain, NULL);
    TEST_LOOKUP(18, "wrap", chain->path, chain, NULL);
    TEST_LOOKUP(19, abswrap, chain->path, chain, NULL);
980
    TEST_LOOKUP(20, "../qcow2", chain->backingStore->path, chain->backingStore,
981
                chain->path);
E
Eric Blake 已提交
982
    TEST_LOOKUP(21, "qcow2", NULL, NULL, NULL);
983
    TEST_LOOKUP(22, absqcow2, chain->backingStore->path, chain->backingStore,
984
                chain->path);
985 986 987 988 989 990
    TEST_LOOKUP(23, "raw", chain->backingStore->backingStore->path,
                chain->backingStore->backingStore, chain->backingStore->path);
    TEST_LOOKUP(24, absraw, chain->backingStore->backingStore->path,
                chain->backingStore->backingStore, chain->backingStore->path);
    TEST_LOOKUP(25, NULL, chain->backingStore->backingStore->path,
                chain->backingStore->backingStore, chain->backingStore->path);
E
Eric Blake 已提交
991

992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006
    TEST_LOOKUP_TARGET(26, "vda", "bogus[1]", 0, NULL, NULL, NULL);
    TEST_LOOKUP_TARGET(27, "vda", "vda[-1]", 0, NULL, NULL, NULL);
    TEST_LOOKUP_TARGET(28, "vda", "vda[1][1]", 0, NULL, NULL, NULL);
    TEST_LOOKUP_TARGET(29, "vda", "wrap", 0, chain->path, chain, NULL);
    TEST_LOOKUP_TARGET(30, "vda", "vda[0]", 0, NULL, NULL, NULL);
    TEST_LOOKUP_TARGET(31, "vda", "vda[1]", 1,
                       chain->backingStore->path,
                       chain->backingStore,
                       chain->path);
    TEST_LOOKUP_TARGET(32, "vda", "vda[2]", 2,
                       chain->backingStore->backingStore->path,
                       chain->backingStore->backingStore,
                       chain->backingStore->path);
    TEST_LOOKUP_TARGET(33, "vda", "vda[3]", 3, NULL, NULL, NULL);

E
Eric Blake 已提交
1007
 cleanup:
1008
    /* Final cleanup */
1009
    virStorageSourceFree(chain);
1010 1011 1012 1013 1014 1015 1016
    testCleanupImages();
    virCommandFree(cmd);

    return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}

VIRT_TEST_MAIN(mymain)