Hello, here's a version of the patch updated to apply against current CVS. Mirek
Index: ChangeLog =================================================================== RCS file: /data/cvs/libvirt/ChangeLog,v retrieving revision 1.1867 diff -u -r1.1867 ChangeLog --- ChangeLog 11 Jan 2009 11:21:29 -0000 1.1867 +++ ChangeLog 12 Jan 2009 09:17:31 -0000 @@ -1,3 +1,10 @@ +2008-12-15 Miloslav Trmač <mitr@xxxxxxxxxx> + + * src/storage_conf.h, src/storage_conf.c: New volume target option + backingstore. + * src/storage_backend_fs.c: Implement /volume/target/backingstore. + * docs/formatstorage.html.in: Document it. + Sun Jan 11 12:18:38 CET 2009 Guido Günther<agx@xxxxxxxxxxx> * src/qemu_driver.c (qemudLogFD): use snprintf instead of emulating it Index: docs/formatstorage.html.in =================================================================== RCS file: /data/cvs/libvirt/docs/formatstorage.html.in,v retrieving revision 1.5 diff -u -r1.5 formatstorage.html.in --- docs/formatstorage.html.in 4 Dec 2008 14:51:57 -0000 1.5 +++ docs/formatstorage.html.in 12 Jan 2009 09:17:31 -0000 @@ -269,6 +269,13 @@ contains the MAC (eg SELinux) label string. <span class="since">Since 0.4.1</span> </dd> + <dt><code>backingstore</code></dt> + <dd>Provides the key of a base volume. This element is optional, and + supported only in some volume types and formats. If it is present, + this volume is a copy-on-write volume that stores only changes to the + base volume. Changes to the base volume may make this volume + inconsistent. + </dd> </dl> <h2><a name="examples">Example configuration</a></h2> Index: src/storage_backend_fs.c =================================================================== RCS file: /data/cvs/libvirt/src/storage_backend_fs.c,v retrieving revision 1.24 diff -u -r1.24 storage_backend_fs.c --- src/storage_backend_fs.c 6 Jan 2009 08:43:44 -0000 1.24 +++ src/storage_backend_fs.c 12 Jan 2009 09:17:31 -0000 @@ -49,6 +49,19 @@ LV_BIG_ENDIAN /* 4321 */ }; +enum { + BACKING_STORE_OK, + BACKING_STORE_INVALID, + BACKING_STORE_ERROR, +}; + +static int cowGetBackingStore(virConnectPtr, char **, const unsigned char *, + size_t); +static int qcowXGetBackingStore(virConnectPtr, char **, const unsigned char *, + size_t); +static int vmdk4GetBackingStore(virConnectPtr, char **, const unsigned char *, + size_t); + /* Either 'magic' or 'extension' *must* be provided */ struct FileTypeInfo { int type; /* One of the constants above */ @@ -65,66 +78,220 @@ * -1 to use st_size as capacity */ int sizeBytes; /* Number of bytes for size field */ int sizeMultiplier; /* A scaling factor if size is not in bytes */ + /* Store a COW base image path (possibly relative), + * or NULL if there is no COW base image, to RES; + * return BACKING_STORE_* */ + int (*getBackingStore)(virConnectPtr conn, char **res, + const unsigned char *buf, size_t buf_size); }; const struct FileTypeInfo const fileTypeInfo[] = { /* Bochs */ /* XXX Untested { VIR_STORAGE_VOL_BOCHS, "Bochs Virtual HD Image", NULL, LV_LITTLE_ENDIAN, 64, 0x20000, - 32+16+16+4+4+4+4+4, 8, 1 },*/ + 32+16+16+4+4+4+4+4, 8, 1, NULL },*/ /* CLoop */ /* XXX Untested { VIR_STORAGE_VOL_CLOOP, "#!/bin/sh\n#V2.0 Format\nmodprobe cloop file=$0 && mount -r -t iso9660 /dev/cloop $1\n", NULL, LV_LITTLE_ENDIAN, -1, 0, - -1, 0, 0 }, */ + -1, 0, 0, NULL }, */ /* Cow */ { VIR_STORAGE_VOL_FILE_COW, "OOOM", NULL, LV_BIG_ENDIAN, 4, 2, - 4+4+1024+4, 8, 1 }, + 4+4+1024+4, 8, 1, cowGetBackingStore }, /* DMG */ /* XXX QEMU says there's no magic for dmg, but we should check... */ { VIR_STORAGE_VOL_FILE_DMG, NULL, ".dmg", 0, -1, 0, - -1, 0, 0 }, + -1, 0, 0, NULL }, /* XXX there's probably some magic for iso we can validate too... */ { VIR_STORAGE_VOL_FILE_ISO, NULL, ".iso", 0, -1, 0, - -1, 0, 0 }, + -1, 0, 0, NULL }, /* Parallels */ /* XXX Untested { VIR_STORAGE_VOL_FILE_PARALLELS, "WithoutFreeSpace", NULL, LV_LITTLE_ENDIAN, 16, 2, - 16+4+4+4+4, 4, 512 }, + 16+4+4+4+4, 4, 512, NULL }, */ /* QCow */ { VIR_STORAGE_VOL_FILE_QCOW, "QFI", NULL, LV_BIG_ENDIAN, 4, 1, - 4+4+8+4+4, 8, 1 }, + 4+4+8+4+4, 8, 1, qcowXGetBackingStore }, /* QCow 2 */ { VIR_STORAGE_VOL_FILE_QCOW2, "QFI", NULL, LV_BIG_ENDIAN, 4, 2, - 4+4+8+4+4, 8, 1 }, + 4+4+8+4+4, 8, 1, qcowXGetBackingStore }, /* VMDK 3 */ /* XXX Untested { VIR_STORAGE_VOL_FILE_VMDK, "COWD", NULL, LV_LITTLE_ENDIAN, 4, 1, - 4+4+4, 4, 512 }, + 4+4+4, 4, 512, NULL }, */ /* VMDK 4 */ { VIR_STORAGE_VOL_FILE_VMDK, "KDMV", NULL, LV_LITTLE_ENDIAN, 4, 1, - 4+4+4, 8, 512 }, + 4+4+4, 8, 512, vmdk4GetBackingStore }, /* Connectix / VirtualPC */ /* XXX Untested { VIR_STORAGE_VOL_FILE_VPC, "conectix", NULL, LV_BIG_ENDIAN, -1, 0, - -1, 0, 0}, + -1, 0, 0, NULL}, */ }; +static const struct FileTypeInfo * +virStorageBackendFileSystemVolFormatToInfo(virConnectPtr conn, int format) +{ + size_t i; + + for (i = 0; i < sizeof(fileTypeInfo) / sizeof(fileTypeInfo[0]); i++) { + if (fileTypeInfo[i].type == format) + return &fileTypeInfo[i]; + } + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unsupported volume format %d"), format); + return NULL; +} + +static int +cowGetBackingStore(virConnectPtr conn, + char **res, + const unsigned char *buf, + size_t buf_size) +{ + size_t len; + + *res = NULL; + if (buf_size < 4+4+1024) + return BACKING_STORE_INVALID; + if (buf[4+4] == '\0') /* cow_header_v2.backing_file[0] */ + return BACKING_STORE_OK; + + len = 1024; + if (VIR_ALLOC_N(*res, len + 1) < 0) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("backing store path")); + return BACKING_STORE_ERROR; + } + memcpy(*res, buf + 4+4, len); /* cow_header_v2.backing_file */ + (*res)[len] = '\0'; + if (VIR_REALLOC_N(*res, strlen(*res) + 1) < 0) { + /* Ignore failure */ + } + return BACKING_STORE_OK; +} + +static int +qcowXGetBackingStore(virConnectPtr conn, + char **res, + const unsigned char *buf, + size_t buf_size) +{ + unsigned long long offset; + unsigned long size; + + *res = NULL; + if (buf_size < 4+4+8+4) + return BACKING_STORE_INVALID; + offset = (((unsigned long long)buf[4+4] << 56) + | ((unsigned long long)buf[4+4+1] << 48) + | ((unsigned long long)buf[4+4+2] << 40) + | ((unsigned long long)buf[4+4+3] << 32) + | ((unsigned long long)buf[4+4+4] << 24) + | ((unsigned long long)buf[4+4+5] << 16) + | ((unsigned long long)buf[4+4+6] << 8) + | buf[4+4+7]); /* QCowHeader.backing_file_offset */ + if (offset > buf_size) + return BACKING_STORE_INVALID; + size = ((buf[4+4+8] << 24) + | (buf[4+4+8+1] << 16) + | (buf[4+4+8+2] << 8) + | buf[4+4+8+3]); /* QCowHeader.backing_file_size */ + if (size == 0) + return BACKING_STORE_OK; + if (offset + size > buf_size || offset + size < offset) + return BACKING_STORE_INVALID; + if (size + 1 == 0) + return BACKING_STORE_INVALID; + if (VIR_ALLOC_N(*res, size + 1) < 0) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("backing store path")); + return BACKING_STORE_ERROR; + } + memcpy(*res, buf + offset, size); + (*res)[size] = '\0'; + return BACKING_STORE_OK; +} + +static int +vmdk4GetBackingStore(virConnectPtr conn, + char **res, + const unsigned char *buf, + size_t buf_size) +{ + static const char prefix[] = "parentFileNameHint=\""; + + char desc[20*512 + 1], *start, *end; + size_t len; + + *res = NULL; + if (buf_size <= 0x200) + return BACKING_STORE_INVALID; + len = buf_size - 0x200; + if (len > sizeof(desc) - 1) + len = sizeof(desc) - 1; + memcpy(desc, buf + 0x200, len); + desc[len] = '\0'; + start = strstr(desc, prefix); + if (start == NULL) + return BACKING_STORE_OK; + start += strlen(prefix); + end = strchr(start, '"'); + if (end == NULL) + return BACKING_STORE_INVALID; + if (end == start) + return BACKING_STORE_OK; + *end = '\0'; + *res = strdup(start); + if (*res == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("backing store path")); + return BACKING_STORE_ERROR; + } + return BACKING_STORE_OK; +} + +/** + * Return an absolute path corresponding to PATH, which is absolute or relative + * to the directory containing BASE_FILE, or NULL on error + */ +static char *absolutePathFromBaseFile(const char *base_file, const char *path) +{ + size_t base_size, path_size; + char *res, *p; + + if (*path == '/') + return strdup(path); + + base_size = strlen(base_file) + 1; + path_size = strlen(path) + 1; + if (VIR_ALLOC_N(res, base_size - 1 + path_size) < 0) + return NULL; + memcpy(res, base_file, base_size); + p = strrchr(res, '/'); + if (p != NULL) + p++; + else + p = res; + memcpy(p, path, path_size); + if (VIR_REALLOC_N(res, (p + path_size) - res) < 0) { + /* Ignore failure */ + } + return res; +} + /** * Probe the header of a file to determine what type of disk image * it is, and info about its capacity if available. @@ -132,7 +299,7 @@ static int virStorageBackendProbeFile(virConnectPtr conn, virStorageVolDefPtr def) { int fd; - unsigned char head[4096]; + unsigned char head[20*512]; /* vmdk4GetBackingStore needs this much. */ int len, i, ret; if ((fd = open(def->target.path, O_RDONLY)) < 0) { @@ -220,6 +387,32 @@ /* Validation passed, we know the file format now */ def->target.format = fileTypeInfo[i].type; + if (fileTypeInfo[i].getBackingStore != NULL) { + char *base; + + switch (fileTypeInfo[i].getBackingStore(conn, &base, head, len)) { + case BACKING_STORE_OK: + break; + + case BACKING_STORE_INVALID: + continue; + + case BACKING_STORE_ERROR: + return -1; + } + if (base == NULL) + def->target.backingStore = NULL; + else { + def->target.backingStore + = absolutePathFromBaseFile(def->target.path, base); + free(base); + if (def->target.backingStore == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, + _("backing store path")); + return -1; + } + } + } return 0; } @@ -836,7 +1029,9 @@ #if HAVE_QEMU_IMG const char *type; char size[100]; - const char *imgargv[7]; + const char *imgargv[9]; + size_t i; + virStorageVolDefPtr backingStore; if ((type = virStorageVolFormatFileSystemTypeToString(vol->target.format)) == NULL) { virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, @@ -844,17 +1039,59 @@ vol->target.format); return -1; } + if (vol->target.backingStore == NULL) + backingStore = NULL; + else { + const struct FileTypeInfo *info; + + info = virStorageBackendFileSystemVolFormatToInfo(conn, + vol->target + .format); + if (info == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown storage vol type %d"), + vol->target.format); + return -1; + } + if (info->getBackingStore == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("copy-on-write not supported with " + "format %s"), type); + return -1; + } + backingStore = virStorageVolDefFindByKey(pool, + vol->target.backingStore); + if (backingStore == NULL) { + virStorageReportError(conn, VIR_ERR_NO_STORAGE_VOL, + _("unknown backing store volume key %s"), + vol->target.backingStore); + return -1; + } + if (access(backingStore->target.path, R_OK) != 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unaccessible backing store volume " + "with key %s: %s"), + vol->target.backingStore, + strerror(errno)); + return -1; + } + } /* Size in KB */ snprintf(size, sizeof(size), "%llu", vol->capacity/1024); - imgargv[0] = QEMU_IMG; - imgargv[1] = "create"; - imgargv[2] = "-f"; - imgargv[3] = type; - imgargv[4] = vol->target.path; - imgargv[5] = size; - imgargv[6] = NULL; + i = 0; + imgargv[i++] = QEMU_IMG; + imgargv[i++] = "create"; + imgargv[i++] = "-f"; + imgargv[i++] = type; + if (backingStore != NULL) { + imgargv[i++] = "-b"; + imgargv[i++] = backingStore->target.path; + } + imgargv[i++] = vol->target.path; + imgargv[i++] = size; + imgargv[i++] = NULL; if (virRun(conn, imgargv, NULL) < 0) { unlink(vol->target.path); @@ -882,6 +1119,12 @@ vol->target.format); return -1; } + if (vol->target.backingStore != NULL) { + virStorageReportError(conn, VIR_ERR_NO_SUPPORT, + _("copy-on-write image not supported with " + "qcow-create")); + return -1; + } /* Size in MB - yes different units to qemu-img :-( */ snprintf(size, sizeof(size), "%llu", vol->capacity/1024/1024); Index: src/storage_conf.c =================================================================== RCS file: /data/cvs/libvirt/src/storage_conf.c,v retrieving revision 1.34 diff -u -r1.34 storage_conf.c --- src/storage_conf.c 17 Dec 2008 21:09:44 -0000 1.34 +++ src/storage_conf.c 12 Jan 2009 09:17:31 -0000 @@ -247,6 +247,7 @@ VIR_FREE(def->target.path); VIR_FREE(def->target.perms.label); + VIR_FREE(def->target.backingStore); VIR_FREE(def); } @@ -993,6 +994,9 @@ if (virStorageVolDefParsePerms(conn, ctxt, &ret->target.perms) < 0) goto cleanup; + ret->target.backingStore + = virXPathString(conn, "string(/volume/target/backingstore)", ctxt); + return ret; cleanup: @@ -1141,6 +1145,10 @@ def->target.perms.label); virBufferAddLit(&buf," </permissions>\n"); + + if (def->target.backingStore) + virBufferVSprintf(&buf, " <backingstore>%s</backingstore>\n", + def->target.backingStore); virBufferAddLit(&buf, " </target>\n"); virBufferAddLit(&buf,"</volume>\n"); Index: src/storage_conf.h =================================================================== RCS file: /data/cvs/libvirt/src/storage_conf.h,v retrieving revision 1.14 diff -u -r1.14 storage_conf.h --- src/storage_conf.h 17 Dec 2008 21:09:44 -0000 1.14 +++ src/storage_conf.h 12 Jan 2009 09:17:31 -0000 @@ -73,6 +73,7 @@ char *path; int format; virStoragePerms perms; + char *backingStore; };
-- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list