Hi,
I'm happy to use this new feature in my code ;)
BTW,
<owner>0744</owner>
<group>0744</group>
this example code makes
me confused because it looks like
a mode octal permission set not a numeric ID. I think it's
better to use decimal digits something like '500' (ID of
first user in fedora) to be friendly.
Thanks
ozaki-r
2009/1/21 Daniel P. Berrange <berrange(a)redhat.com>:
> This is a follow up on Miloslav's proposal to add copy on write
> support to the storage APIs, changing the XML to that described
> here:
>
>
http://www.redhat.com/archives/libvir-list/2009-January/msg00231.html
>
> In addition to the original QCOW/VMDK support, I have done an impl which
> can extract the backing store for LVM volumes
>
> docs/formatstorage.html | 45 +++++
> docs/formatstorage.html.in | 52 ++++++
> src/libvirt_private.syms | 1
> src/storage_backend.c | 140 ++++++++++-------
> src/storage_backend.h | 13 +
> src/storage_backend_fs.c | 336 +++++++++++++++++++++++++++++++++++++-----
> src/storage_backend_iscsi.c | 6
> src/storage_backend_logical.c | 61 +++++--
> src/storage_conf.c | 101 +++++++++---
> src/storage_conf.h | 1
> 10 files changed, 609 insertions(+), 147 deletions(-)
>
> Daniel
>
> diff --git a/docs/formatstorage.html b/docs/formatstorage.html
> --- a/docs/formatstorage.html
> +++ b/docs/formatstorage.html
> @@ -131,6 +131,8 @@
> <a href="#StorageVolFirst">General
metadata</a>
> </li><li>
> <a href="#StorageVolTarget">Target
elements</a>
> + </li><li>
> + <a href="#StorageVolBacking">Backing store
elements</a>
> </li></ul>
> </li><li>
> <a href="#examples">Example configuration</a>
> @@ -328,14 +330,14 @@
> ...
> <target>
> <path>/var/lib/virt/images/sparse.img</path>
> + <format>qcow2</format>
> <permissions>
<owner>0744</owner>
<group>0744</group>
>
<mode>0744</mode>
> <label>virt_image_t</label>
> </permissions>
> - </target>
> - </volume></pre>
> + </target></pre>
>
<dl><dt><code>path</code></dt><dd>Provides the
location at which the volume can be accessed on
> the local filesystem, as an absolute path. This is a readonly
> attribute, so shouldn't be specified when creating a volume.
> @@ -356,6 +358,45 @@
> contains the MAC (eg SELinux) label string.
> <span class="since">Since 0.4.1</span>
> </dd></dl>
> + <h3>
> + <a name="StorageVolBacking"
id="StorageVolBacking">Backing store elements</a>
> + </h3>
> + <p>
> + A single <code>backingStore</code> element is contained within the
top level
> + <code>volume</code> element. This tag is used to describe the
optional copy
> + on write, backing store for the storage volume. It can contain the following
> + child elements:
> + </p>
> + <pre>
> + ...
> + <backingStore>
> + <path>/var/lib/virt/images/master.img</path>
> + <format>raw</format>
> + <permissions>
> + <owner>0744</owner>
> + <group>0744</group>
> + <mode>0744</mode>
> + <label>virt_image_t</label>
> + </permissions>
> + </backingStore>
> + </volume></pre>
> +
<dl><dt><code>path</code></dt><dd>Provides the
location at which the backing store can be accessed on
> + the local filesystem, as an absolute path. If omitted, there is no
> + backing store for this volume.
> + <span class="since">Since
0.6.0</span></dd><dt><code>format</code></dt><dd>Provides
information about the pool specific backing store format.
> + For disk pools it will provide the partition type. For filesystem
> + or directory pools it will provide the file format type, eg cow,
> + qcow, vmdk, raw. Consult the pool-specific docs for the list of valid
> + values. Most file formats require a backing store of the same format,
> + however, the qcow2 format allows a different backing store format.
> + <span class="since">Since
0.6.0</span></dd><dt><code>permissions</code></dt><dd>Provides
information about the permissions of the backing file.
> + It contains 4 child elements. The
> + <code>mode</code> element contains the octal permission set. The
> + <code>owner</code> element contains the numeric user ID. The
<code>group</code>
> + element contains the numeric group ID. The <code>label</code>
element
> + contains the MAC (eg SELinux) label string.
> + <span class="since">Since 0.6.0</span>
> + </dd></dl>
> <h2>
> <a name="examples" id="examples">Example
configuration</a>
> </h2>
> diff --git a/docs/formatstorage.html.in b/docs/formatstorage.html.in
> --- a/docs/formatstorage.html.in
> +++ b/docs/formatstorage.html.in
> @@ -234,14 +234,14 @@
> ...
> <target>
> <path>/var/lib/virt/images/sparse.img</path>
> + <format>qcow2</format>
> <permissions>
<owner>0744</owner>
<group>0744</group>
>
<mode>0744</mode>
> <label>virt_image_t</label>
> </permissions>
> - </target>
> - </volume></pre>
> + </target></pre>
>
> <dl>
> <dt><code>path</code></dt>
> @@ -271,6 +271,54 @@
> </dd>
> </dl>
>
> + <h3><a name="StorageVolBacking">Backing store
elements</a></h3>
> +
> + <p>
> + A single <code>backingStore</code> element is contained within the
top level
> + <code>volume</code> element. This tag is used to describe the
optional copy
> + on write, backing store for the storage volume. It can contain the following
> + child elements:
> + </p>
> +
> + <pre>
> + ...
> + <backingStore>
> + <path>/var/lib/virt/images/master.img</path>
> + <format>raw</format>
> + <permissions>
> + <owner>0744</owner>
> + <group>0744</group>
> + <mode>0744</mode>
> + <label>virt_image_t</label>
> + </permissions>
> + </backingStore>
> + </volume></pre>
> +
> + <dl>
> + <dt><code>path</code></dt>
> + <dd>Provides the location at which the backing store can be accessed on
> + the local filesystem, as an absolute path. If omitted, there is no
> + backing store for this volume.
> + <span class="since">Since 0.6.0</span></dd>
> + <dt><code>format</code></dt>
> + <dd>Provides information about the pool specific backing store format.
> + For disk pools it will provide the partition type. For filesystem
> + or directory pools it will provide the file format type, eg cow,
> + qcow, vmdk, raw. Consult the pool-specific docs for the list of valid
> + values. Most file formats require a backing store of the same format,
> + however, the qcow2 format allows a different backing store format.
> + <span class="since">Since 0.6.0</span></dd>
> + <dt><code>permissions</code></dt>
> + <dd>Provides information about the permissions of the backing file.
> + It contains 4 child elements. The
> + <code>mode</code> element contains the octal permission set. The
> + <code>owner</code> element contains the numeric user ID. The
<code>group</code>
> + element contains the numeric group ID. The <code>label</code>
element
> + contains the MAC (eg SELinux) label string.
> + <span class="since">Since 0.6.0</span>
> + </dd>
> + </dl>
> +
> <h2><a name="examples">Example
configuration</a></h2>
>
> <p>
> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
> --- a/src/libvirt_private.syms
> +++ b/src/libvirt_private.syms
> @@ -258,6 +258,7 @@ virStoragePoolFormatDiskTypeToString;
> virStoragePoolFormatFileSystemTypeToString;
> virStoragePoolFormatFileSystemNetTypeToString;
> virStorageVolFormatFileSystemTypeToString;
> +virStorageVolFormatFileSystemTypeFromString;
> virStoragePoolTypeFromString;
> virStoragePoolObjLock;
> virStoragePoolObjUnlock;
> diff --git a/src/storage_backend.c b/src/storage_backend.c
> --- a/src/storage_backend.c
> +++ b/src/storage_backend.c
> @@ -99,27 +99,51 @@ virStorageBackendForType(int type) {
>
>
> int
> +virStorageBackendUpdateVolTargetInfo(virConnectPtr conn,
> + virStorageVolTargetPtr target,
> + unsigned long long *allocation,
> + unsigned long long *capacity)
> +{
> + int ret, fd;
> +
> + if ((fd = open(target->path, O_RDONLY)) < 0) {
> + virReportSystemError(conn, errno,
> + _("cannot open volume '%s'"),
> + target->path);
> + return -1;
> + }
> +
> + ret = virStorageBackendUpdateVolTargetInfoFD(conn,
> + target,
> + fd,
> + allocation,
> + capacity);
> +
> + close(fd);
> +
> + return ret;
> +}
> +
> +int
> virStorageBackendUpdateVolInfo(virConnectPtr conn,
> virStorageVolDefPtr vol,
> int withCapacity)
> {
> - int ret, fd;
> + int ret;
>
> - if ((fd = open(vol->target.path, O_RDONLY)) < 0) {
> - virReportSystemError(conn, errno,
> - _("cannot open volume '%s'"),
> - vol->target.path);
> - return -1;
> - }
> + if ((ret = virStorageBackendUpdateVolTargetInfo(conn,
> + &vol->target,
> + &vol->allocation,
> + withCapacity ?
&vol->capacity : NULL)) < 0)
> + return ret;
>
> - ret = virStorageBackendUpdateVolInfoFD(conn,
> - vol,
> - fd,
> - withCapacity);
> + if (vol->backingStore.path &&
> + (ret = virStorageBackendUpdateVolTargetInfo(conn,
> + &vol->backingStore,
> + NULL, NULL)) < 0)
> + return ret;
>
> - close(fd);
> -
> - return ret;
> + return 0;
> }
>
> struct diskType {
> @@ -154,10 +178,11 @@ static struct diskType const disk_types[
> };
>
> int
> -virStorageBackendUpdateVolInfoFD(virConnectPtr conn,
> - virStorageVolDefPtr vol,
> - int fd,
> - int withCapacity)
> +virStorageBackendUpdateVolTargetInfoFD(virConnectPtr conn,
> + virStorageVolTargetPtr target,
> + int fd,
> + unsigned long long *allocation,
> + unsigned long long *capacity)
> {
> struct stat sb;
> #if HAVE_SELINUX
> @@ -167,7 +192,7 @@ virStorageBackendUpdateVolInfoFD(virConn
> if (fstat(fd, &sb) < 0) {
> virReportSystemError(conn, errno,
> _("cannot stat file '%s'"),
> - vol->target.path);
> + target->path);
> return -1;
> }
>
> @@ -176,38 +201,41 @@ virStorageBackendUpdateVolInfoFD(virConn
> !S_ISBLK(sb.st_mode))
> return -2;
>
> - if (S_ISREG(sb.st_mode)) {
> + if (allocation) {
> + if (S_ISREG(sb.st_mode)) {
> #ifndef __MINGW32__
> - vol->allocation = (unsigned long long)sb.st_blocks *
> - (unsigned long long)sb.st_blksize;
> + *allocation = (unsigned long long)sb.st_blocks *
> + (unsigned long long)sb.st_blksize;
> #else
> - vol->allocation = sb.st_size;
> + *allocation = sb.st_size;
> #endif
> - /* Regular files may be sparse, so logical size (capacity) is not same
> - * as actual allocation above
> - */
> - if (withCapacity)
> - vol->capacity = sb.st_size;
> - } else {
> - off_t end;
> - /* XXX this is POSIX compliant, but doesn't work for for CHAR files,
> - * only BLOCK. There is a Linux specific ioctl() for getting
> - * size of both CHAR / BLOCK devices we should check for in
> - * configure
> - */
> - end = lseek(fd, 0, SEEK_END);
> - if (end == (off_t)-1) {
> - virReportSystemError(conn, errno,
> - _("cannot seek to end of file
'%s'"),
> - vol->target.path);
> - return -1;
> + /* Regular files may be sparse, so logical size (capacity) is not same
> + * as actual allocation above
> + */
> + if (capacity)
> + *capacity = sb.st_size;
> + } else {
> + off_t end;
> + /* XXX this is POSIX compliant, but doesn't work for for CHAR
files,
> + * only BLOCK. There is a Linux specific ioctl() for getting
> + * size of both CHAR / BLOCK devices we should check for in
> + * configure
> + */
> + end = lseek(fd, 0, SEEK_END);
> + if (end == (off_t)-1) {
> + virReportSystemError(conn, errno,
> + _("cannot seek to end of file
'%s'"),
> + target->path);
> + return -1;
> + }
> + *allocation = end;
> + if (capacity)
> + *capacity = end;
> }
> - vol->allocation = end;
> - if (withCapacity) vol->capacity = end;
> }
>
> /* make sure to set the target format "unknown" to begin with */
> - vol->target.format = VIR_STORAGE_POOL_DISK_UNKNOWN;
> + target->format = VIR_STORAGE_POOL_DISK_UNKNOWN;
>
> if (S_ISBLK(sb.st_mode)) {
> off_t start;
> @@ -219,14 +247,14 @@ virStorageBackendUpdateVolInfoFD(virConn
> if (start < 0) {
> virReportSystemError(conn, errno,
> _("cannot seek to beginning of file
'%s'"),
> - vol->target.path);
> + target->path);
> return -1;
> }
> bytes = saferead(fd, buffer, sizeof(buffer));
> if (bytes < 0) {
> virReportSystemError(conn, errno,
> _("cannot read beginning of file
'%s'"),
> - vol->target.path);
> + target->path);
> return -1;
> }
>
> @@ -235,38 +263,38 @@ virStorageBackendUpdateVolInfoFD(virConn
> continue;
> if (memcmp(buffer+disk_types[i].offset, &disk_types[i].magic,
> disk_types[i].length) == 0) {
> - vol->target.format = disk_types[i].part_table_type;
> + target->format = disk_types[i].part_table_type;
> break;
> }
> }
> }
>
> - vol->target.perms.mode = sb.st_mode;
> - vol->target.perms.uid = sb.st_uid;
> - vol->target.perms.gid = sb.st_gid;
> + target->perms.mode = sb.st_mode & (0x200-1);
> + target->perms.uid = sb.st_uid;
> + target->perms.gid = sb.st_gid;
>
> - VIR_FREE(vol->target.perms.label);
> + VIR_FREE(target->perms.label);
>
> #if HAVE_SELINUX
> if (fgetfilecon(fd, &filecon) == -1) {
> if (errno != ENODATA && errno != ENOTSUP) {
> virReportSystemError(conn, errno,
> _("cannot get file context of
'%s'"),
> - vol->target.path);
> + target->path);
> return -1;
> } else {
> - vol->target.perms.label = NULL;
> + target->perms.label = NULL;
> }
> } else {
> - vol->target.perms.label = strdup(filecon);
> - if (vol->target.perms.label == NULL) {
> + target->perms.label = strdup(filecon);
> + if (target->perms.label == NULL) {
> virReportOOMError(conn);
> return -1;
> }
> freecon(filecon);
> }
> #else
> - vol->target.perms.label = NULL;
> + target->perms.label = NULL;
> #endif
>
> return 0;
> diff --git a/src/storage_backend.h b/src/storage_backend.h
> --- a/src/storage_backend.h
> +++ b/src/storage_backend.h
> @@ -64,10 +64,15 @@ int virStorageBackendUpdateVolInfo(virCo
> virStorageVolDefPtr vol,
> int withCapacity);
>
> -int virStorageBackendUpdateVolInfoFD(virConnectPtr conn,
> - virStorageVolDefPtr vol,
> - int fd,
> - int withCapacity);
> +int virStorageBackendUpdateVolTargetInfo(virConnectPtr conn,
> + virStorageVolTargetPtr target,
> + unsigned long long *allocation,
> + unsigned long long *capacity);
> +int virStorageBackendUpdateVolTargetInfoFD(virConnectPtr conn,
> + virStorageVolTargetPtr target,
> + int fd,
> + unsigned long long *allocation,
> + unsigned long long *capacity);
>
> void virStorageBackendWaitForDevices(virConnectPtr conn);
>
> diff --git a/src/storage_backend_fs.c b/src/storage_backend_fs.c
> --- a/src/storage_backend_fs.c
> +++ b/src/storage_backend_fs.c
> @@ -49,6 +49,19 @@ enum lv_endian {
> 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,85 +78,234 @@ struct FileTypeInfo {
> * -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},
> */
> };
>
> #define VIR_FROM_THIS VIR_FROM_STORAGE
>
> +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.
> */
> -static int virStorageBackendProbeFile(virConnectPtr conn,
> - virStorageVolDefPtr def) {
> +static int virStorageBackendProbeTarget(virConnectPtr conn,
> + virStorageVolTargetPtr target,
> + char **backingStore,
> + unsigned long long *allocation,
> + unsigned long long *capacity) {
> 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) {
> + if (backingStore)
> + *backingStore = NULL;
> +
> + if ((fd = open(target->path, O_RDONLY)) < 0) {
> virReportSystemError(conn, errno,
> _("cannot open volume '%s'"),
> - def->target.path);
> + target->path);
> return -1;
> }
>
> - if ((ret = virStorageBackendUpdateVolInfoFD(conn, def, fd, 1)) < 0) {
> + if ((ret = virStorageBackendUpdateVolTargetInfoFD(conn, target, fd,
> + allocation,
> + capacity)) < 0) {
> close(fd);
> return ret; /* Take care to propagate ret, it is not always -1 */
> }
> @@ -151,7 +313,7 @@ static int virStorageBackendProbeFile(vi
> if ((len = read(fd, head, sizeof(head))) < 0) {
> virReportSystemError(conn, errno,
> _("cannot read header '%s'"),
> - def->target.path);
> + target->path);
> close(fd);
> return -1;
> }
> @@ -191,9 +353,9 @@ static int virStorageBackendProbeFile(vi
> }
>
> /* Optionally extract capacity from file */
> - if (fileTypeInfo[i].sizeOffset != -1) {
> + if (fileTypeInfo[i].sizeOffset != -1 && capacity) {
> if (fileTypeInfo[i].endian == LV_LITTLE_ENDIAN) {
> - def->capacity =
> + *capacity =
> ((unsigned long long)head[fileTypeInfo[i].sizeOffset+7] <<
56) |
> ((unsigned long long)head[fileTypeInfo[i].sizeOffset+6] <<
48) |
> ((unsigned long long)head[fileTypeInfo[i].sizeOffset+5] <<
40) |
> @@ -203,7 +365,7 @@ static int virStorageBackendProbeFile(vi
> ((unsigned long long)head[fileTypeInfo[i].sizeOffset+1] <<
8) |
> ((unsigned long long)head[fileTypeInfo[i].sizeOffset]);
> } else {
> - def->capacity =
> + *capacity =
> ((unsigned long long)head[fileTypeInfo[i].sizeOffset] <<
56) |
> ((unsigned long long)head[fileTypeInfo[i].sizeOffset+1] <<
48) |
> ((unsigned long long)head[fileTypeInfo[i].sizeOffset+2] <<
40) |
> @@ -214,13 +376,37 @@ static int virStorageBackendProbeFile(vi
> ((unsigned long long)head[fileTypeInfo[i].sizeOffset+7]);
> }
> /* Avoid unlikely, but theoretically possible overflow */
> - if (def->capacity > (ULLONG_MAX /
fileTypeInfo[i].sizeMultiplier))
> + if (*capacity > (ULLONG_MAX / fileTypeInfo[i].sizeMultiplier))
> continue;
> - def->capacity *= fileTypeInfo[i].sizeMultiplier;
> + *capacity *= fileTypeInfo[i].sizeMultiplier;
> }
>
> /* Validation passed, we know the file format now */
> - def->target.format = fileTypeInfo[i].type;
> + target->format = fileTypeInfo[i].type;
> + if (fileTypeInfo[i].getBackingStore != NULL && backingStore) {
> + 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) {
> + *backingStore
> + = absolutePathFromBaseFile(target->path, base);
> + VIR_FREE(base);
> + if (*backingStore == NULL) {
> + virStorageReportError(conn, VIR_ERR_NO_MEMORY,
> + _("backing store path"));
> + return -1;
> + }
> + }
> + }
> return 0;
> }
>
> @@ -229,15 +415,15 @@ static int virStorageBackendProbeFile(vi
> if (fileTypeInfo[i].extension == NULL)
> continue;
>
> - if (!virFileHasSuffix(def->target.path, fileTypeInfo[i].extension))
> + if (!virFileHasSuffix(target->path, fileTypeInfo[i].extension))
> continue;
>
> - def->target.format = fileTypeInfo[i].type;
> + target->format = fileTypeInfo[i].type;
> return 0;
> }
>
> /* All fails, so call it a raw file */
> - def->target.format = VIR_STORAGE_VOL_FILE_RAW;
> + target->format = VIR_STORAGE_VOL_FILE_RAW;
> return 0;
> }
>
> @@ -636,6 +822,7 @@ virStorageBackendFileSystemRefresh(virCo
>
> while ((ent = readdir(dir)) != NULL) {
> int ret;
> + char *backingStore;
>
> if (VIR_ALLOC(vol) < 0)
> goto no_memory;
> @@ -655,7 +842,11 @@ virStorageBackendFileSystemRefresh(virCo
> if ((vol->key = strdup(vol->target.path)) == NULL)
> goto no_memory;
>
> - if ((ret = virStorageBackendProbeFile(conn, vol) < 0)) {
> + if ((ret = virStorageBackendProbeTarget(conn,
> + &vol->target,
> + &backingStore,
> + &vol->allocation,
> + &vol->capacity) < 0)) {
> if (ret == -1)
> goto no_memory;
> else {
> @@ -667,6 +858,48 @@ virStorageBackendFileSystemRefresh(virCo
> }
> }
>
> + if (backingStore != NULL) {
> + if (vol->target.format == VIR_STORAGE_VOL_FILE_QCOW2 &&
> + STRPREFIX("fmt:", backingStore)) {
> + char *fmtstr = backingStore + 4;
> + char *path = strchr(fmtstr, ':');
> + if (!path) {
> + VIR_FREE(backingStore);
> + } else {
> + *path = '\0';
> + if ((vol->backingStore.format =
> + virStorageVolFormatFileSystemTypeFromString(fmtstr)) <
0) {
> + VIR_FREE(backingStore);
> + } else {
> + memmove(backingStore, path, strlen(path) + 1);
> + vol->backingStore.path = backingStore;
> +
> + if (virStorageBackendUpdateVolTargetInfo(conn,
> +
&vol->backingStore,
> + NULL,
> + NULL) < 0)
> + VIR_FREE(vol->backingStore);
> + }
> + }
> + } else {
> + vol->backingStore.path = backingStore;
> +
> + if ((ret = virStorageBackendProbeTarget(conn,
> + &vol->backingStore,
> + NULL, NULL, NULL)) < 0)
{
> + if (ret == -1)
> + goto no_memory;
> + else {
> + /* Silently ignore non-regular files,
> + * eg '.' '..', 'lost+found' */
> + VIR_FREE(vol->backingStore);
> + }
> + }
> + }
> + }
> +
> +
> +
> if (VIR_REALLOC_N(pool->volumes.objs,
> pool->volumes.count+1) < 0)
> goto no_memory;
> @@ -837,8 +1070,10 @@ virStorageBackendFileSystemVolCreate(vir
> } else {
> #if HAVE_QEMU_IMG
> const char *type;
> + const char *backingType = NULL;
> char size[100];
> - const char *imgargv[7];
> + const char *imgargv[9];
> + size_t i;
>
> if ((type = virStorageVolFormatFileSystemTypeToString(vol->target.format))
== NULL) {
> virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
> @@ -846,17 +1081,36 @@ virStorageBackendFileSystemVolCreate(vir
> vol->target.format);
> return -1;
> }
> + if (vol->backingStore.path) {
> + if ((backingType =
virStorageVolFormatFileSystemTypeToString(vol->backingStore.format)) == NULL) {
> + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
> + _("unknown storage vol backing store type
%d"),
> + vol->backingStore.format);
> + return -1;
> + }
> + if (access(vol->backingStore.path, R_OK) != 0) {
> + virReportSystemError(conn, errno,
> + _("inaccessible backing store volume
%s"),
> + vol->backingStore.path);
> + 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 (vol->backingStore.path != NULL) {
> + imgargv[i++] = "-b";
> + imgargv[i++] = vol->backingStore.path;
> + }
> + imgargv[i++] = vol->target.path;
> + imgargv[i++] = size;
> + imgargv[i++] = NULL;
>
> if (virRun(conn, imgargv, NULL) < 0) {
> unlink(vol->target.path);
> @@ -884,6 +1138,12 @@ virStorageBackendFileSystemVolCreate(vir
> 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);
> @@ -934,7 +1194,9 @@ virStorageBackendFileSystemVolCreate(vir
> }
>
> /* Refresh allocation / permissions info, but not capacity */
> - if (virStorageBackendUpdateVolInfoFD(conn, vol, fd, 0) < 0) {
> + if (virStorageBackendUpdateVolTargetInfoFD(conn, &vol->target, fd,
> + &vol->allocation,
> + NULL) < 0) {
> unlink(vol->target.path);
> close(fd);
> return -1;
> diff --git a/src/storage_backend_iscsi.c b/src/storage_backend_iscsi.c
> --- a/src/storage_backend_iscsi.c
> +++ b/src/storage_backend_iscsi.c
> @@ -226,7 +226,11 @@ virStorageBackendISCSINewLun(virConnectP
>
> VIR_FREE(devpath);
>
> - if (virStorageBackendUpdateVolInfoFD(conn, vol, fd, 1) < 0)
> + if (virStorageBackendUpdateVolTargetInfoFD(conn,
> + &vol->target,
> + fd,
> + &vol->allocation,
> + &vol->capacity) < 0)
> goto cleanup;
>
> /* XXX use unique iSCSI id instead */
> diff --git a/src/storage_backend_logical.c b/src/storage_backend_logical.c
> --- a/src/storage_backend_logical.c
> +++ b/src/storage_backend_logical.c
> @@ -116,8 +116,22 @@ virStorageBackendLogicalMakeVol(virConne
> strcat(vol->target.path, vol->name);
> }
>
> + if (groups[1] && !STREQ(groups[1], "")) {
> + if (VIR_ALLOC_N(vol->backingStore.path,
strlen(pool->def->target.path) +
> + 1 + strlen(groups[1]) + 1) < 0) {
> + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s",
_("volume"));
> + return -1;
> + }
> + strcpy(vol->backingStore.path, pool->def->target.path);
> + strcat(vol->backingStore.path, "/");
> + strcat(vol->backingStore.path, groups[1]);
> +
> + vol->backingStore.format = VIR_STORAGE_POOL_LOGICAL_LVM2;
> + }
> +
> +
> if (vol->key == NULL &&
> - (vol->key = strdup(groups[1])) == NULL) {
> + (vol->key = strdup(groups[2])) == NULL) {
> virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s",
_("volume"));
> return -1;
> }
> @@ -134,22 +148,22 @@ virStorageBackendLogicalMakeVol(virConne
> }
>
> if ((vol->source.extents[vol->source.nextent].path =
> - strdup(groups[2])) == NULL) {
> + strdup(groups[3])) == NULL) {
> virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s",
_("extents"));
> return -1;
> }
>
> - if (virStrToLong_ull(groups[3], NULL, 10, &offset) < 0) {
> + if (virStrToLong_ull(groups[4], NULL, 10, &offset) < 0) {
> virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
> "%s", _("malformed volume extent offset
value"));
> return -1;
> }
> - if (virStrToLong_ull(groups[4], NULL, 10, &length) < 0) {
> + if (virStrToLong_ull(groups[5], NULL, 10, &length) < 0) {
> virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
> "%s", _("malformed volume extent length
value"));
> return -1;
> }
> - if (virStrToLong_ull(groups[5], NULL, 10, &size) < 0) {
> + if (virStrToLong_ull(groups[6], NULL, 10, &size) < 0) {
> virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
> "%s", _("malformed volume extent size
value"));
> return -1;
> @@ -168,14 +182,14 @@ virStorageBackendLogicalFindLVs(virConne
> virStorageVolDefPtr vol)
> {
> /*
> - * # lvs --separator , --noheadings --units b --unbuffered --nosuffix --options
"lv_name,uuid,devices,seg_size,vg_extent_size" VGNAME
> - *
RootLV,06UgP5-2rhb-w3Bo-3mdR-WeoL-pytO-SAa2ky,/dev/hda2(0),5234491392,33554432
> - *
SwapLV,oHviCK-8Ik0-paqS-V20c-nkhY-Bm1e-zgzU0M,/dev/hda2(156),1040187392,33554432
> - *
Test2,3pg3he-mQsA-5Sui-h0i6-HNmc-Cz7W-QSndcR,/dev/hda2(219),1073741824,33554432
> - *
Test3,UB5hFw-kmlm-LSoX-EI1t-ioVd-h7GL-M0W8Ht,/dev/hda2(251),2181038080,33554432
> - *
Test3,UB5hFw-kmlm-LSoX-EI1t-ioVd-h7GL-M0W8Ht,/dev/hda2(187),1040187392,33554432
> + * # lvs --separator , --noheadings --units b --unbuffered --nosuffix --options
"lv_name,origin,uuid,devices,seg_size,vg_extent_size" VGNAME
> + *
RootLV,,06UgP5-2rhb-w3Bo-3mdR-WeoL-pytO-SAa2ky,/dev/hda2(0),5234491392,33554432
> + *
SwapLV,,oHviCK-8Ik0-paqS-V20c-nkhY-Bm1e-zgzU0M,/dev/hda2(156),1040187392,33554432
> + *
Test2,,3pg3he-mQsA-5Sui-h0i6-HNmc-Cz7W-QSndcR,/dev/hda2(219),1073741824,33554432
> + *
Test3,,UB5hFw-kmlm-LSoX-EI1t-ioVd-h7GL-M0W8Ht,/dev/hda2(251),2181038080,33554432
> + *
Test3,Test2,UB5hFw-kmlm-LSoX-EI1t-ioVd-h7GL-M0W8Ht,/dev/hda2(187),1040187392,33554432
> *
> - * Pull out name & uuid, device, device extent start #, segment size, extent
size.
> + * Pull out name, origin, & uuid, device, device extent start #, segment
size, extent size.
> *
> * NB can be multiple rows per volume if they have many extents
> *
> @@ -185,15 +199,15 @@ virStorageBackendLogicalFindLVs(virConne
> * not a suitable separator (rhbz 470693).
> */
> const char *regexes[] = {
> - "^\\s*(\\S+),(\\S+),(\\S+)\\((\\S+)\\),(\\S+),([0-9]+),?\\s*$"
> +
"^\\s*(\\S+),(\\S*),(\\S+),(\\S+)\\((\\S+)\\),(\\S+),([0-9]+),?\\s*$"
> };
> int vars[] = {
> - 6
> + 7
> };
> const char *prog[] = {
> LVS, "--separator", ",", "--noheadings",
"--units", "b",
> "--unbuffered", "--nosuffix", "--options",
> - "lv_name,uuid,devices,seg_size,vg_extent_size",
> + "lv_name,origin,uuid,devices,seg_size,vg_extent_size",
> pool->def->source.name, NULL
> };
>
> @@ -565,10 +579,25 @@ virStorageBackendLogicalCreateVol(virCon
> {
> int fd = -1;
> char size[100];
> - const char *cmdargv[] = {
> + const char *cmdargvnew[] = {
> LVCREATE, "--name", vol->name, "-L", size,
> pool->def->target.path, NULL
> };
> + const char *cmdargvsnap[] = {
> + LVCREATE, "--name", vol->name, "-L", size,
> + "-s", vol->backingStore.path, NULL
> + };
> + const char **cmdargv = cmdargvnew;
> +
> + if (vol->backingStore.path) {
> + if (vol->backingStore.format !=
> + VIR_STORAGE_POOL_LOGICAL_LVM2) {
> + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s",
> + _("LVM snapshots must be backed by another
LVM volume"));
> + return -1;
> + }
> + cmdargv = cmdargvsnap;
> + }
>
> snprintf(size, sizeof(size)-1, "%lluK", vol->capacity/1024);
> size[sizeof(size)-1] = '\0';
> diff --git a/src/storage_conf.c b/src/storage_conf.c
> --- a/src/storage_conf.c
> +++ b/src/storage_conf.c
> @@ -249,6 +249,8 @@ virStorageVolDefFree(virStorageVolDefPtr
>
> VIR_FREE(def->target.path);
> VIR_FREE(def->target.perms.label);
> + VIR_FREE(def->backingStore.path);
> + VIR_FREE(def->backingStore.perms.label);
> VIR_FREE(def);
> }
>
> @@ -998,6 +1000,28 @@ virStorageVolDefParseDoc(virConnectPtr c
> if (virStorageVolDefParsePerms(conn, ctxt, &ret->target.perms) < 0)
> goto cleanup;
>
> +
> +
> + ret->backingStore.path = virXPathString(conn,
"string(/volume/backingStore/path)", ctxt);
> + if (options->formatFromString) {
> + char *format = virXPathString(conn,
"string(/volume/backingStore/format/@type)", ctxt);
> + if (format == NULL)
> + ret->backingStore.format = options->defaultFormat;
> + else
> + ret->backingStore.format = (options->formatFromString)(format);
> +
> + if (ret->backingStore.format < 0) {
> + virStorageReportError(conn, VIR_ERR_XML_ERROR,
> + _("unknown volume format type %s"),
format);
> + VIR_FREE(format);
> + goto cleanup;
> + }
> + VIR_FREE(format);
> + }
> +
> + if (virStorageVolDefParsePerms(conn, ctxt, &ret->backingStore.perms) <
0)
> + goto cleanup;
> +
> return ret;
>
> cleanup:
> @@ -1069,6 +1093,47 @@ virStorageVolDefParse(virConnectPtr conn
> }
>
>
> +static int
> +virStorageVolTargetDefFormat(virConnectPtr conn,
> + virStorageVolOptionsPtr options,
> + virBufferPtr buf,
> + virStorageVolTargetPtr def,
> + const char *type) {
> + virBufferVSprintf(buf, " <%s>\n", type);
> +
> + if (def->path)
> + virBufferVSprintf(buf," <path>%s</path>\n",
def->path);
> +
> + if (options->formatToString) {
> + const char *format = (options->formatToString)(def->format);
> + if (!format) {
> + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
> + _("unknown volume format number %d"),
> + def->format);
> + return -1;
> + }
> + virBufferVSprintf(buf," <format type='%s'/>\n",
format);
> + }
> +
> + virBufferAddLit(buf," <permissions>\n");
> + virBufferVSprintf(buf," <mode>0%o</mode>\n",
> + def->perms.mode);
> + virBufferVSprintf(buf," <owner>%d</owner>\n",
> + def->perms.uid);
> + virBufferVSprintf(buf," <group>%d</group>\n",
> + def->perms.gid);
> +
> +
> + if (def->perms.label)
> + virBufferVSprintf(buf," <label>%s</label>\n",
> + def->perms.label);
> +
> + virBufferAddLit(buf," </permissions>\n");
> +
> + virBufferVSprintf(buf, " </%s>\n", type);
> +
> + return 0;
> +}
>
> char *
> virStorageVolDefFormat(virConnectPtr conn,
> @@ -1116,37 +1181,15 @@ virStorageVolDefFormat(virConnectPtr con
> virBufferVSprintf(&buf,"
<allocation>%llu</allocation>\n",
> def->allocation);
>
> - virBufferAddLit(&buf, " <target>\n");
> + if (virStorageVolTargetDefFormat(conn, options, &buf,
> + &def->target, "target") <
0)
> + goto cleanup;
>
> - if (def->target.path)
> - virBufferVSprintf(&buf," <path>%s</path>\n",
def->target.path);
> + if (def->backingStore.path &&
> + virStorageVolTargetDefFormat(conn, options, &buf,
> + &def->backingStore,
"backingStore") < 0)
> + goto cleanup;
>
> - if (options->formatToString) {
> - const char *format = (options->formatToString)(def->target.format);
> - if (!format) {
> - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
> - _("unknown volume format number %d"),
> - def->target.format);
> - goto cleanup;
> - }
> - virBufferVSprintf(&buf," <format
type='%s'/>\n", format);
> - }
> -
> - virBufferAddLit(&buf," <permissions>\n");
> - virBufferVSprintf(&buf," <mode>0%o</mode>\n",
> - def->target.perms.mode);
> - virBufferVSprintf(&buf," <owner>%d</owner>\n",
> - def->target.perms.uid);
> - virBufferVSprintf(&buf," <group>%d</group>\n",
> - def->target.perms.gid);
> -
> -
> - if (def->target.perms.label)
> - virBufferVSprintf(&buf,"
<label>%s</label>\n",
> - def->target.perms.label);
> -
> - virBufferAddLit(&buf," </permissions>\n");
> - virBufferAddLit(&buf, " </target>\n");
> virBufferAddLit(&buf,"</volume>\n");
>
> if (virBufferError(&buf))
> diff --git a/src/storage_conf.h b/src/storage_conf.h
> --- a/src/storage_conf.h
> +++ b/src/storage_conf.h
> @@ -89,6 +89,7 @@ struct _virStorageVolDef {
>
> virStorageVolSource source;
> virStorageVolTarget target;
> + virStorageVolTarget backingStore;
> };
>
> typedef struct _virStorageVolDefList virStorageVolDefList;
>
>
> --
> |: Red Hat, Engineering, London -o-
http://people.redhat.com/berrange/ :|
> |:
http://libvirt.org -o-
http://virt-manager.org -o-
http://ovirt.org :|
> |:
http://autobuild.org -o-
http://search.cpan.org/~danberr/ :|
> |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|
>
> --
> Libvir-list mailing list
> Libvir-list(a)redhat.com
>
https://www.redhat.com/mailman/listinfo/libvir-list
>