static int mymain(void) { int ret; virCommandPtr cmd = NULL; struct testChainData data; virStorageSourcePtr chain = NULL; /* 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; #define TEST_ONE_CHAIN(id, start, format, flags, ...) \ do { \ size_t i; \ memset(&data, 0, sizeof(data)); \ data = (struct testChainData){ \ start, format, { __VA_ARGS__ }, 0, flags, \ }; \ for (i = 0; i < ARRAY_CARDINALITY(data.files); i++) \ if (data.files[i]) \ data.nfiles++; \ if (virtTestRun("Storage backing chain " id, \ testStorageChain, &data) < 0) \ ret = -1; \ } while (0) #define VIR_FLATTEN_2(...) __VA_ARGS__ #define VIR_FLATTEN_1(_1) VIR_FLATTEN_2 _1 #define TEST_CHAIN(id, relstart, absstart, format, chain1, flags1, \ chain2, flags2, chain3, flags3, chain4, flags4) \ do { \ TEST_ONE_CHAIN(#id "a", relstart, format, flags1, \ VIR_FLATTEN_1(chain1)); \ TEST_ONE_CHAIN(#id "b", relstart, format, flags2, \ VIR_FLATTEN_1(chain2)); \ TEST_ONE_CHAIN(#id "c", absstart, format, flags3 | ABS_START,\ VIR_FLATTEN_1(chain3)); \ TEST_ONE_CHAIN(#id "d", absstart, format, flags4 | ABS_START,\ VIR_FLATTEN_1(chain4)); \ } while (0) /* The actual tests, in several groups. */ /* Missing file */ TEST_ONE_CHAIN("0", "bogus", VIR_STORAGE_FILE_RAW, EXP_FAIL); /* Raw image, whether with right format or no specified format */ testFileData raw = { .pathRel = "raw", .pathAbs = canonraw, .path = canonraw, .relDirRel = ".", .relDirAbs = datadir, .type = VIR_STORAGE_TYPE_FILE, .format = VIR_STORAGE_FILE_RAW, }; TEST_CHAIN(1, "raw", absraw, VIR_STORAGE_FILE_RAW, (&raw), EXP_PASS, (&raw), ALLOW_PROBE | EXP_PASS, (&raw), EXP_PASS, (&raw), ALLOW_PROBE | EXP_PASS); TEST_CHAIN(2, "raw", absraw, VIR_STORAGE_FILE_AUTO, (&raw), EXP_PASS, (&raw), ALLOW_PROBE | EXP_PASS, (&raw), EXP_PASS, (&raw), ALLOW_PROBE | EXP_PASS); /* Qcow2 file with relative raw backing, format provided */ raw.pathAbs = "raw"; testFileData qcow2 = { .expBackingStore = canonraw, .expBackingStoreRaw = "raw", .expCapacity = 1024, .pathRel = "qcow2", .pathAbs = canonqcow2, .path = canonqcow2, .relDirRel = ".", .relDirAbs = datadir, .type = VIR_STORAGE_TYPE_FILE, .format = VIR_STORAGE_FILE_QCOW2, }; testFileData qcow2_as_raw = { .pathRel = "qcow2", .pathAbs = canonqcow2, .path = canonqcow2, .relDirRel = ".", .relDirAbs = datadir, .type = VIR_STORAGE_TYPE_FILE, .format = VIR_STORAGE_FILE_RAW, }; TEST_CHAIN(3, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2, (&qcow2, &raw), EXP_PASS, (&qcow2, &raw), ALLOW_PROBE | EXP_PASS, (&qcow2, &raw), EXP_PASS, (&qcow2, &raw), ALLOW_PROBE | EXP_PASS); TEST_CHAIN(4, "qcow2", absqcow2, VIR_STORAGE_FILE_AUTO, (&qcow2_as_raw), EXP_PASS, (&qcow2, &raw), ALLOW_PROBE | EXP_PASS, (&qcow2_as_raw), EXP_PASS, (&qcow2, &raw), ALLOW_PROBE | EXP_PASS); /* 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; qcow2.expBackingStoreRaw = absraw; raw.pathRel = absraw; raw.pathAbs = absraw; raw.relDirRel = datadir; /* Qcow2 file with raw as absolute backing, backing format provided */ TEST_CHAIN(5, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2, (&qcow2, &raw), EXP_PASS, (&qcow2, &raw), ALLOW_PROBE | EXP_PASS, (&qcow2, &raw), EXP_PASS, (&qcow2, &raw), ALLOW_PROBE | EXP_PASS); TEST_CHAIN(6, "qcow2", absqcow2, VIR_STORAGE_FILE_AUTO, (&qcow2_as_raw), EXP_PASS, (&qcow2, &raw), ALLOW_PROBE | EXP_PASS, (&qcow2_as_raw), EXP_PASS, (&qcow2, &raw), ALLOW_PROBE | EXP_PASS); /* Wrapped file access */ testFileData wrap = { .expBackingStore = canonqcow2, .expBackingStoreRaw = absqcow2, .expCapacity = 1024, .pathRel = "wrap", .pathAbs = abswrap, .path = canonwrap, .relDirRel = ".", .relDirAbs = datadir, .type = VIR_STORAGE_TYPE_FILE, .format = VIR_STORAGE_FILE_QCOW2, }; qcow2.pathRel = absqcow2; qcow2.relDirRel = datadir; TEST_CHAIN(7, "wrap", abswrap, VIR_STORAGE_FILE_QCOW2, (&wrap, &qcow2, &raw), EXP_PASS, (&wrap, &qcow2, &raw), ALLOW_PROBE | EXP_PASS, (&wrap, &qcow2, &raw), EXP_PASS, (&wrap, &qcow2, &raw), ALLOW_PROBE | EXP_PASS); /* 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; qcow2_as_raw.pathRel = absqcow2; qcow2_as_raw.relDirRel = datadir; /* Qcow2 file with raw as absolute backing, backing format omitted */ testFileData wrap_as_raw = { .expBackingStore = canonqcow2, .expBackingStoreRaw = absqcow2, .expCapacity = 1024, .pathRel = "wrap", .pathAbs = abswrap, .path = canonwrap, .relDirRel = ".", .relDirAbs = datadir, .type = VIR_STORAGE_TYPE_FILE, .format = VIR_STORAGE_FILE_QCOW2, }; TEST_CHAIN(8, "wrap", abswrap, VIR_STORAGE_FILE_QCOW2, (&wrap_as_raw, &qcow2_as_raw), EXP_PASS, (&wrap, &qcow2, &raw), ALLOW_PROBE | EXP_PASS, (&wrap_as_raw, &qcow2_as_raw), EXP_PASS, (&wrap, &qcow2, &raw), ALLOW_PROBE | EXP_PASS); /* 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; qcow2.expBackingStore = NULL; qcow2.expBackingStoreRaw = datadir "/bogus"; qcow2.pathRel = "qcow2"; qcow2.relDirRel = "."; /* Qcow2 file with missing backing file but specified type */ TEST_CHAIN(9, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2, (&qcow2), EXP_WARN, (&qcow2), ALLOW_PROBE | EXP_WARN, (&qcow2), EXP_WARN, (&qcow2), ALLOW_PROBE | EXP_WARN); /* 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, (&qcow2), EXP_WARN, (&qcow2), ALLOW_PROBE | EXP_WARN, (&qcow2), EXP_WARN, (&qcow2), ALLOW_PROBE | EXP_WARN); /* 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; qcow2.expBackingStore = "nbd:example.org:6000"; qcow2.expBackingStoreRaw = "nbd:example.org:6000"; /* Qcow2 file with backing protocol instead of file */ testFileData nbd = { .pathRel = "nbd:example.org:6000", .pathAbs = "nbd:example.org:6000", .path = "nbd:example.org:6000", .type = VIR_STORAGE_TYPE_NETWORK, .format = VIR_STORAGE_FILE_RAW, }; TEST_CHAIN(11, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2, (&qcow2, &nbd), EXP_PASS, (&qcow2, &nbd), ALLOW_PROBE | EXP_PASS, (&qcow2, &nbd), EXP_PASS, (&qcow2, &nbd), ALLOW_PROBE | EXP_PASS); /* qed file */ testFileData qed = { .expBackingStore = canonraw, .expBackingStoreRaw = absraw, .expCapacity = 1024, .pathRel = "qed", .pathAbs = absqed, .path = canonqed, .relDirRel = ".", .relDirAbs = datadir, .type = VIR_STORAGE_TYPE_FILE, .format = VIR_STORAGE_FILE_QED, }; testFileData qed_as_raw = { .pathRel = "qed", .pathAbs = absqed, .path = canonqed, .relDirRel = ".", .relDirAbs = datadir, .type = VIR_STORAGE_TYPE_FILE, .format = VIR_STORAGE_FILE_RAW, }; TEST_CHAIN(12, "qed", absqed, VIR_STORAGE_FILE_AUTO, (&qed_as_raw), EXP_PASS, (&qed, &raw), ALLOW_PROBE | EXP_PASS, (&qed_as_raw), EXP_PASS, (&qed, &raw), ALLOW_PROBE | EXP_PASS); /* directory */ testFileData dir = { .pathRel = "dir", .pathAbs = absdir, .path = canondir, .relDirRel = ".", .relDirAbs = datadir, .type = VIR_STORAGE_TYPE_DIR, .format = VIR_STORAGE_FILE_DIR, }; 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); #ifdef HAVE_SYMLINK /* 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 */ testFileData link1 = { .expBackingStore = canonraw, .expBackingStoreRaw = "../raw", .expCapacity = 1024, .pathRel = "../sub/link1", .pathAbs = "../sub/link1", .path = canonqcow2, .relDirRel = "sub/../sub", .relDirAbs = datadir "/sub/../sub", .type = VIR_STORAGE_TYPE_FILE, .format = VIR_STORAGE_FILE_QCOW2, }; testFileData link2 = { .expBackingStore = canonqcow2, .expBackingStoreRaw = "../sub/link1", .expCapacity = 1024, .pathRel = "sub/link2", .pathAbs = abslink2, .path = canonwrap, .relDirRel = "sub", .relDirAbs = datadir "/sub", .type = VIR_STORAGE_TYPE_FILE, .format = VIR_STORAGE_FILE_QCOW2, }; raw.pathRel = "../raw"; raw.pathAbs = "../raw"; raw.relDirRel = "sub/../sub/.."; raw.relDirAbs = datadir "/sub/../sub/.."; TEST_CHAIN(15, "sub/link2", abslink2, VIR_STORAGE_FILE_QCOW2, (&link2, &link1, &raw), EXP_PASS, (&link2, &link1, &raw), ALLOW_PROBE | EXP_PASS, (&link2, &link1, &raw), EXP_PASS, (&link2, &link1, &raw), ALLOW_PROBE | EXP_PASS); #endif /* 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; qcow2.expBackingStore = NULL; qcow2.expBackingStoreRaw = "qcow2"; /* Behavior of an infinite loop chain */ TEST_CHAIN(16, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2, (&qcow2), EXP_WARN, (&qcow2), ALLOW_PROBE | EXP_WARN, (&qcow2), EXP_WARN, (&qcow2), ALLOW_PROBE | EXP_WARN); /* 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; qcow2.expBackingStoreRaw = "wrap"; qcow2.pathRel = absqcow2; qcow2.relDirRel = datadir; /* Behavior of an infinite loop chain */ TEST_CHAIN(17, "wrap", abswrap, VIR_STORAGE_FILE_QCOW2, (&wrap, &qcow2), EXP_WARN, (&wrap, &qcow2), ALLOW_PROBE | EXP_WARN, (&wrap, &qcow2), EXP_WARN, (&wrap, &qcow2), ALLOW_PROBE | EXP_WARN); /* 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; /* Test behavior of chain lookups, absolute backing from relative start */ chain = testStorageFileGetMetadata("wrap", VIR_STORAGE_FILE_QCOW2, -1, -1, false); if (!chain) { ret = -1; goto cleanup; } #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; \ } while (0) #define TEST_LOOKUP(id, name, result, meta, parent) \ TEST_LOOKUP_TARGET(id, NULL, name, 0, result, meta, parent) TEST_LOOKUP(0, "bogus", NULL, NULL, NULL); TEST_LOOKUP(1, "wrap", chain->path, chain, NULL); TEST_LOOKUP(2, abswrap, chain->path, chain, NULL); TEST_LOOKUP(3, "qcow2", chain->backingStore->path, chain->backingStore, chain->path); TEST_LOOKUP(4, absqcow2, chain->backingStore->path, chain->backingStore, chain->path); 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); /* 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; /* Test behavior of chain lookups, relative backing from absolute start */ virStorageSourceFree(chain); chain = testStorageFileGetMetadata(abswrap, VIR_STORAGE_FILE_QCOW2, -1, -1, false); if (!chain) { ret = -1; goto cleanup; } TEST_LOOKUP(8, "bogus", NULL, NULL, NULL); TEST_LOOKUP(9, "wrap", chain->path, chain, NULL); TEST_LOOKUP(10, abswrap, chain->path, chain, NULL); TEST_LOOKUP(11, "qcow2", chain->backingStore->path, chain->backingStore, chain->path); TEST_LOOKUP(12, absqcow2, chain->backingStore->path, chain->backingStore, chain->path); 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); /* 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 */ virStorageSourceFree(chain); chain = testStorageFileGetMetadata("sub/link2", VIR_STORAGE_FILE_QCOW2, -1, -1, false); if (!chain) { ret = -1; goto cleanup; } TEST_LOOKUP(16, "bogus", NULL, NULL, NULL); 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); TEST_LOOKUP(20, "../qcow2", chain->backingStore->path, chain->backingStore, chain->path); TEST_LOOKUP(21, "qcow2", NULL, NULL, NULL); TEST_LOOKUP(22, absqcow2, chain->backingStore->path, chain->backingStore, chain->path); 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); 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); cleanup: /* Final cleanup */ virStorageSourceFree(chain); testCleanupImages(); virCommandFree(cmd); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } VIRT_TEST_MAIN(mymain)
static int mymain(void) { int ret; virCommandPtr cmd = NULL; /* 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; #define TEST_ONE_CHAIN(id, start, format, chain, flags) \ do { \ struct testChainData data = { \ start, format, chain, ARRAY_CARDINALITY(chain), flags, \ }; \ if (virtTestRun("Storage backing chain " id, 1, \ testStorageChain, &data) < 0) \ ret = -1; \ } while (0) #define TEST_CHAIN(id, relstart, absstart, format, chain1, flags1, \ chain2, flags2, chain3, flags3, chain4, flags4) \ do { \ TEST_ONE_CHAIN(#id "a", relstart, format, chain1, flags1); \ TEST_ONE_CHAIN(#id "b", relstart, format, chain2, flags2); \ TEST_ONE_CHAIN(#id "c", absstart, format, chain3, flags3); \ TEST_ONE_CHAIN(#id "d", absstart, format, chain4, flags4); \ } while (0) /* Expected details about files in chains */ const testFileData raw = { NULL, NULL, NULL, VIR_STORAGE_FILE_NONE, false, 0, false, }; const testFileData qcow2_relback_relstart = { canonraw, "raw", ".", VIR_STORAGE_FILE_RAW, true, 1024, false, }; const testFileData qcow2_relback_absstart = { canonraw, "raw", datadir, VIR_STORAGE_FILE_RAW, true, 1024, false, }; const testFileData qcow2_absback = { canonraw, absraw, datadir, VIR_STORAGE_FILE_RAW, true, 1024, false, }; const testFileData qcow2_as_probe = { canonraw, absraw, datadir, VIR_STORAGE_FILE_AUTO, true, 1024, false, }; const testFileData qcow2_bogus = { NULL, datadir "/bogus", datadir, VIR_STORAGE_FILE_NONE, false, 1024, false, }; const testFileData qcow2_protocol = { "nbd:example.org:6000", NULL, NULL, VIR_STORAGE_FILE_RAW, false, 1024, false, }; const testFileData wrap = { canonqcow2, absqcow2, datadir, VIR_STORAGE_FILE_QCOW2, true, 1024, false, }; const testFileData wrap_as_raw = { canonqcow2, absqcow2, datadir, VIR_STORAGE_FILE_RAW, true, 1024, false, }; const testFileData wrap_as_probe = { canonqcow2, absqcow2, datadir, VIR_STORAGE_FILE_AUTO, true, 1024, false, }; const testFileData qed = { canonraw, absraw, datadir, VIR_STORAGE_FILE_RAW, true, 1024, false, }; const testFileData link1_rel = { canonraw, "../raw", "sub/../sub/..", VIR_STORAGE_FILE_RAW, true, 1024, false, }; const testFileData link1_abs = { canonraw, "../raw", datadir "/sub/../sub/..", VIR_STORAGE_FILE_RAW, true, 1024, false, }; const testFileData link2_rel = { canonqcow2, "../sub/link1", "sub/../sub", VIR_STORAGE_FILE_QCOW2, true, 1024, false, }; const testFileData link2_abs = { canonqcow2, "../sub/link1", datadir "/sub/../sub", VIR_STORAGE_FILE_QCOW2, true, 1024, false, }; /* The actual tests, in several groups. */ /* Missing file */ const testFileData chain0[] = { }; TEST_ONE_CHAIN("0", "bogus", VIR_STORAGE_FILE_RAW, chain0, EXP_FAIL); /* Raw image, whether with right format or no specified format */ const testFileData chain1[] = { raw }; TEST_CHAIN(1, "raw", absraw, VIR_STORAGE_FILE_RAW, chain1, EXP_PASS, chain1, ALLOW_PROBE | EXP_PASS, chain1, EXP_PASS, chain1, ALLOW_PROBE | EXP_PASS); TEST_CHAIN(2, "raw", absraw, VIR_STORAGE_FILE_AUTO, chain1, EXP_PASS, chain1, ALLOW_PROBE | EXP_PASS, chain1, EXP_PASS, chain1, ALLOW_PROBE | EXP_PASS); /* Qcow2 file with relative raw backing, format provided */ const testFileData chain3a[] = { qcow2_relback_relstart, raw }; const testFileData chain3c[] = { qcow2_relback_absstart, raw }; const testFileData chain4a[] = { raw }; TEST_CHAIN(3, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2, chain3a, EXP_PASS, chain3a, ALLOW_PROBE | EXP_PASS, chain3c, EXP_PASS, chain3c, ALLOW_PROBE | EXP_PASS); TEST_CHAIN(4, "qcow2", absqcow2, VIR_STORAGE_FILE_AUTO, chain4a, EXP_PASS, chain3a, ALLOW_PROBE | EXP_PASS, chain4a, EXP_PASS, chain3c, ALLOW_PROBE | EXP_PASS); /* 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; /* Qcow2 file with raw as absolute backing, backing format provided */ const testFileData chain5[] = { qcow2_absback, raw }; const testFileData chain6[] = { raw }; TEST_CHAIN(5, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2, chain5, EXP_PASS, chain5, ALLOW_PROBE | EXP_PASS, chain5, EXP_PASS, chain5, ALLOW_PROBE | EXP_PASS); TEST_CHAIN(6, "qcow2", absqcow2, VIR_STORAGE_FILE_AUTO, chain6, EXP_PASS, chain5, ALLOW_PROBE | EXP_PASS, chain6, EXP_PASS, chain5, ALLOW_PROBE | EXP_PASS); /* Wrapped file access */ const testFileData chain7[] = { wrap, qcow2_absback, raw }; TEST_CHAIN(7, "wrap", abswrap, VIR_STORAGE_FILE_QCOW2, chain7, EXP_PASS, chain7, ALLOW_PROBE | EXP_PASS, chain7, EXP_PASS, chain7, ALLOW_PROBE | EXP_PASS); /* 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; /* Qcow2 file with raw as absolute backing, backing format omitted */ const testFileData chain8a[] = { wrap_as_raw, raw }; const testFileData chain8b[] = { wrap_as_probe, qcow2_as_probe, raw }; TEST_CHAIN(8, "wrap", abswrap, VIR_STORAGE_FILE_QCOW2, chain8a, EXP_PASS, chain8b, ALLOW_PROBE | EXP_PASS, chain8a, EXP_PASS, chain8b, ALLOW_PROBE | EXP_PASS); /* 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; /* Qcow2 file with missing backing file but specified type */ const testFileData chain9[] = { qcow2_bogus }; TEST_CHAIN(9, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2, chain9, EXP_WARN, chain9, ALLOW_PROBE | EXP_WARN, chain9, EXP_WARN, chain9, ALLOW_PROBE | EXP_WARN); /* 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 */ const testFileData chain10[] = { qcow2_bogus }; TEST_CHAIN(10, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2, chain10, EXP_WARN, chain10, ALLOW_PROBE | EXP_WARN, chain10, EXP_WARN, chain10, ALLOW_PROBE | EXP_WARN); /* 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; /* Qcow2 file with backing protocol instead of file */ const testFileData chain11[] = { qcow2_protocol }; TEST_CHAIN(11, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2, chain11, EXP_PASS, chain11, ALLOW_PROBE | EXP_PASS, chain11, EXP_PASS, chain11, ALLOW_PROBE | EXP_PASS); /* qed file */ const testFileData chain12a[] = { raw }; const testFileData chain12b[] = { qed, raw }; TEST_CHAIN(12, "qed", absqed, VIR_STORAGE_FILE_AUTO, chain12a, EXP_PASS, chain12b, ALLOW_PROBE | EXP_PASS, chain12a, EXP_PASS, chain12b, ALLOW_PROBE | EXP_PASS); /* 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 */ const testFileData chain13a[] = { link2_rel, link1_rel, raw }; const testFileData chain13c[] = { link2_abs, link1_abs, raw }; TEST_CHAIN(13, "sub/link2", abslink2, VIR_STORAGE_FILE_QCOW2, chain13a, EXP_PASS, chain13a, ALLOW_PROBE | EXP_PASS, chain13c, EXP_PASS, chain13c, ALLOW_PROBE | EXP_PASS); /* Final cleanup */ testCleanupImages(); virCommandFree(cmd); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; }
static int mymain(void) { int ret; virCommandPtr cmd = NULL; struct testChainData data; /* 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; #define TEST_ONE_CHAIN(id, start, format, flags, ...) \ do { \ size_t i; \ memset(&data, 0, sizeof(data)); \ data = (struct testChainData){ \ start, format, { __VA_ARGS__ }, 0, flags, \ }; \ for (i = 0; i < ARRAY_CARDINALITY(data.files); i++) \ if (data.files[i]) \ data.nfiles++; \ if (virtTestRun("Storage backing chain " id, \ testStorageChain, &data) < 0) \ ret = -1; \ } while (0) #define VIR_FLATTEN_2(...) __VA_ARGS__ #define VIR_FLATTEN_1(_1) VIR_FLATTEN_2 _1 #define TEST_CHAIN(id, relstart, absstart, format, chain1, flags1, \ chain2, flags2, chain3, flags3, chain4, flags4) \ do { \ TEST_ONE_CHAIN(#id "a", relstart, format, flags1, \ VIR_FLATTEN_1(chain1)); \ TEST_ONE_CHAIN(#id "b", relstart, format, flags2, \ VIR_FLATTEN_1(chain2)); \ TEST_ONE_CHAIN(#id "c", absstart, format, flags3, \ VIR_FLATTEN_1(chain3)); \ TEST_ONE_CHAIN(#id "d", absstart, format, flags4, \ VIR_FLATTEN_1(chain4)); \ } while (0) /* Expected details about files in chains */ const testFileData raw = { .expFormat = VIR_STORAGE_FILE_NONE, }; const testFileData qcow2_relback_relstart = { .expBackingStore = canonraw, .expBackingStoreRaw = "raw", .expDirectory = ".", .expFormat = VIR_STORAGE_FILE_RAW, .expIsFile = true, .expCapacity = 1024, }; const testFileData qcow2_relback_absstart = { .expBackingStore = canonraw, .expBackingStoreRaw = "raw", .expDirectory = datadir, .expFormat = VIR_STORAGE_FILE_RAW, .expIsFile = true, .expCapacity = 1024, }; const testFileData qcow2_absback = { .expBackingStore = canonraw, .expBackingStoreRaw = absraw, .expDirectory = datadir, .expFormat = VIR_STORAGE_FILE_RAW, .expIsFile = true, .expCapacity = 1024, }; const testFileData qcow2_as_probe = { .expBackingStore = canonraw, .expBackingStoreRaw = absraw, .expDirectory = datadir, .expFormat = VIR_STORAGE_FILE_AUTO, .expIsFile = true, .expCapacity = 1024, }; const testFileData qcow2_bogus = { .expBackingStoreRaw = datadir "/bogus", .expDirectory = datadir, .expFormat = VIR_STORAGE_FILE_NONE, .expCapacity = 1024, }; const testFileData qcow2_protocol = { .expBackingStore = "nbd:example.org:6000", .expFormat = VIR_STORAGE_FILE_RAW, .expCapacity = 1024, }; const testFileData wrap = { .expBackingStore = canonqcow2, .expBackingStoreRaw = absqcow2, .expDirectory = datadir, .expFormat = VIR_STORAGE_FILE_QCOW2, .expIsFile = true, .expCapacity = 1024, }; const testFileData wrap_as_raw = { .expBackingStore = canonqcow2, .expBackingStoreRaw = absqcow2, .expDirectory = datadir, .expFormat = VIR_STORAGE_FILE_RAW, .expIsFile = true, .expCapacity = 1024, }; const testFileData wrap_as_probe = { .expBackingStore = canonqcow2, .expBackingStoreRaw = absqcow2, .expDirectory = datadir, .expFormat = VIR_STORAGE_FILE_AUTO, .expIsFile = true, .expCapacity = 1024, }; const testFileData qed = { .expBackingStore = canonraw, .expBackingStoreRaw = absraw, .expDirectory = datadir, .expFormat = VIR_STORAGE_FILE_RAW, .expIsFile = true, .expCapacity = 1024, }; #if HAVE_SYMLINK const testFileData link1_rel = { .expBackingStore = canonraw, .expBackingStoreRaw = "../raw", .expDirectory = "sub/../sub/..", .expFormat = VIR_STORAGE_FILE_RAW, .expIsFile = true, .expCapacity = 1024, }; const testFileData link1_abs = { .expBackingStore = canonraw, .expBackingStoreRaw = "../raw", .expDirectory = datadir "/sub/../sub/..", .expFormat = VIR_STORAGE_FILE_RAW, .expIsFile = true, .expCapacity = 1024, }; const testFileData link2_rel = { .expBackingStore = canonqcow2, .expBackingStoreRaw = "../sub/link1", .expDirectory = "sub/../sub", .expFormat = VIR_STORAGE_FILE_QCOW2, .expIsFile = true, .expCapacity = 1024, }; const testFileData link2_abs = { .expBackingStore = canonqcow2, .expBackingStoreRaw = "../sub/link1", .expDirectory = datadir "/sub/../sub", .expFormat = VIR_STORAGE_FILE_QCOW2, .expIsFile = true, .expCapacity = 1024, }; #endif /* The actual tests, in several groups. */ /* Missing file */ TEST_ONE_CHAIN("0", "bogus", VIR_STORAGE_FILE_RAW, EXP_FAIL); /* Raw image, whether with right format or no specified format */ TEST_CHAIN(1, "raw", absraw, VIR_STORAGE_FILE_RAW, (&raw), EXP_PASS, (&raw), ALLOW_PROBE | EXP_PASS, (&raw), EXP_PASS, (&raw), ALLOW_PROBE | EXP_PASS); TEST_CHAIN(2, "raw", absraw, VIR_STORAGE_FILE_AUTO, (&raw), EXP_PASS, (&raw), ALLOW_PROBE | EXP_PASS, (&raw), EXP_PASS, (&raw), ALLOW_PROBE | EXP_PASS); /* Qcow2 file with relative raw backing, format provided */ TEST_CHAIN(3, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2, (&qcow2_relback_relstart, &raw), EXP_PASS, (&qcow2_relback_relstart, &raw), ALLOW_PROBE | EXP_PASS, (&qcow2_relback_absstart, &raw), EXP_PASS, (&qcow2_relback_absstart, &raw), ALLOW_PROBE | EXP_PASS); TEST_CHAIN(4, "qcow2", absqcow2, VIR_STORAGE_FILE_AUTO, (&raw), EXP_PASS, (&qcow2_relback_relstart, &raw), ALLOW_PROBE | EXP_PASS, (&raw), EXP_PASS, (&qcow2_relback_absstart, &raw), ALLOW_PROBE | EXP_PASS); /* 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; /* Qcow2 file with raw as absolute backing, backing format provided */ TEST_CHAIN(5, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2, (&qcow2_absback, &raw), EXP_PASS, (&qcow2_absback, &raw), ALLOW_PROBE | EXP_PASS, (&qcow2_absback, &raw), EXP_PASS, (&qcow2_absback, &raw), ALLOW_PROBE | EXP_PASS); TEST_CHAIN(6, "qcow2", absqcow2, VIR_STORAGE_FILE_AUTO, (&raw), EXP_PASS, (&qcow2_absback, &raw), ALLOW_PROBE | EXP_PASS, (&raw), EXP_PASS, (&qcow2_absback, &raw), ALLOW_PROBE | EXP_PASS); /* Wrapped file access */ TEST_CHAIN(7, "wrap", abswrap, VIR_STORAGE_FILE_QCOW2, (&wrap, &qcow2_absback, &raw), EXP_PASS, (&wrap, &qcow2_absback, &raw), ALLOW_PROBE | EXP_PASS, (&wrap, &qcow2_absback, &raw), EXP_PASS, (&wrap, &qcow2_absback, &raw), ALLOW_PROBE | EXP_PASS); /* 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; /* Qcow2 file with raw as absolute backing, backing format omitted */ TEST_CHAIN(8, "wrap", abswrap, VIR_STORAGE_FILE_QCOW2, (&wrap_as_raw, &raw), EXP_PASS, (&wrap_as_probe, &qcow2_as_probe, &raw), ALLOW_PROBE | EXP_PASS, (&wrap_as_raw, &raw), EXP_PASS, (&wrap_as_probe, &qcow2_as_probe, &raw), ALLOW_PROBE | EXP_PASS); /* 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; /* Qcow2 file with missing backing file but specified type */ TEST_CHAIN(9, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2, (&qcow2_bogus), EXP_WARN, (&qcow2_bogus), ALLOW_PROBE | EXP_WARN, (&qcow2_bogus), EXP_WARN, (&qcow2_bogus), ALLOW_PROBE | EXP_WARN); /* 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, (&qcow2_bogus), EXP_WARN, (&qcow2_bogus), ALLOW_PROBE | EXP_WARN, (&qcow2_bogus), EXP_WARN, (&qcow2_bogus), ALLOW_PROBE | EXP_WARN); /* 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; /* Qcow2 file with backing protocol instead of file */ TEST_CHAIN(11, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2, (&qcow2_protocol), EXP_PASS, (&qcow2_protocol), ALLOW_PROBE | EXP_PASS, (&qcow2_protocol), EXP_PASS, (&qcow2_protocol), ALLOW_PROBE | EXP_PASS); /* qed file */ TEST_CHAIN(12, "qed", absqed, VIR_STORAGE_FILE_AUTO, (&raw), EXP_PASS, (&qed, &raw), ALLOW_PROBE | EXP_PASS, (&raw), EXP_PASS, (&qed, &raw), ALLOW_PROBE | EXP_PASS); #ifdef HAVE_SYMLINK /* 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 */ TEST_CHAIN(13, "sub/link2", abslink2, VIR_STORAGE_FILE_QCOW2, (&link2_rel, &link1_rel, &raw), EXP_PASS, (&link2_rel, &link1_rel, &raw), ALLOW_PROBE | EXP_PASS, (&link2_abs, &link1_abs, &raw), EXP_PASS, (&link2_abs, &link1_abs, &raw), ALLOW_PROBE | EXP_PASS); #endif /* Final cleanup */ testCleanupImages(); virCommandFree(cmd); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } VIRT_TEST_MAIN(mymain)