Add support for copy-on-write storage volumes

This commit is contained in:
Daniel P. Berrange 2009-01-27 18:30:03 +00:00
parent 765bca14d1
commit 489fde7680
11 changed files with 620 additions and 151 deletions

View File

@ -1,3 +1,22 @@
Tue Jan 27 18:17:07 GMT 2009 Daniel P. Berrange <berrange@redhat.com>
Support Copy-on-Write storage volumes
* docs/formatstorage.html.in: Add notes about backingStore
XML for storage volumes wanting copy-on-write (eg qcow,
LVM snapshots).
* src/libvirt_private.syms: Add virStorageVolFormatFileSystemTypeFromString
* src/storage_backend.c, src/storage_backend.h: Refactor the
virStorageBackendUpdateVolInfo* methods to allow re-use for
backingStore files
* src/storage_backend_fs.c: Extract backing store data out of
Cow, QCow, QCow2, and VMDK file formats. Allow creation of volumes
with a backing store
* src/storage_backend_logical.c: Extract information about master
volume for snapshots, and allow creation of snapshots.
* src/storage_backend_iscsi.c: Adapt to storage_backend.h changes
* src/storage_conf.h, src/storage_conf.c: Support new backingStore
XML element for COW file data
Tue Jan 27 16:27:07 +0100 2009 Jim Meyering <meyering@redhat.com> Tue Jan 27 16:27:07 +0100 2009 Jim Meyering <meyering@redhat.com>
* POTFILES.in: update: remove src/lxc_conf.c; Add src/bridge.c. * POTFILES.in: update: remove src/lxc_conf.c; Add src/bridge.c.

View File

@ -131,6 +131,8 @@
<a href="#StorageVolFirst">General metadata</a> <a href="#StorageVolFirst">General metadata</a>
</li><li> </li><li>
<a href="#StorageVolTarget">Target elements</a> <a href="#StorageVolTarget">Target elements</a>
</li><li>
<a href="#StorageVolBacking">Backing store elements</a>
</li></ul> </li></ul>
</li><li> </li><li>
<a href="#examples">Example configuration</a> <a href="#examples">Example configuration</a>
@ -328,14 +330,14 @@
... ...
&lt;target&gt; &lt;target&gt;
&lt;path&gt;/var/lib/virt/images/sparse.img&lt;/path&gt; &lt;path&gt;/var/lib/virt/images/sparse.img&lt;/path&gt;
&lt;format&gt;qcow2&lt;/format&gt;
&lt;permissions&gt; &lt;permissions&gt;
&lt;owner&gt;0744&lt;/owner&gt; &lt;owner&gt;0744&lt;/owner&gt;
&lt;group&gt;0744&lt;/group&gt; &lt;group&gt;0744&lt;/group&gt;
&lt;mode&gt;0744&lt;/mode&gt; &lt;mode&gt;0744&lt;/mode&gt;
&lt;label&gt;virt_image_t&lt;/label&gt; &lt;label&gt;virt_image_t&lt;/label&gt;
&lt;/permissions&gt; &lt;/permissions&gt;
&lt;/target&gt; &lt;/target&gt;</pre>
&lt;/volume&gt;</pre>
<dl><dt><code>path</code></dt><dd>Provides the location at which the volume can be accessed on <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 the local filesystem, as an absolute path. This is a readonly
attribute, so shouldn't be specified when creating a volume. attribute, so shouldn't be specified when creating a volume.
@ -355,6 +357,45 @@
element contains the numeric group ID. The <code>label</code> element element contains the numeric group ID. The <code>label</code> element
contains the MAC (eg SELinux) label string. contains the MAC (eg SELinux) label string.
<span class="since">Since 0.4.1</span> <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>
...
&lt;backingStore&gt;
&lt;path&gt;/var/lib/virt/images/master.img&lt;/path&gt;
&lt;format&gt;raw&lt;/format&gt;
&lt;permissions&gt;
&lt;owner&gt;0744&lt;/owner&gt;
&lt;group&gt;0744&lt;/group&gt;
&lt;mode&gt;0744&lt;/mode&gt;
&lt;label&gt;virt_image_t&lt;/label&gt;
&lt;/permissions&gt;
&lt;/backingStore&gt;
&lt;/volume&gt;</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> </dd></dl>
<h2> <h2>
<a name="examples" id="examples">Example configuration</a> <a name="examples" id="examples">Example configuration</a>

View File

@ -234,14 +234,14 @@
... ...
&lt;target&gt; &lt;target&gt;
&lt;path&gt;/var/lib/virt/images/sparse.img&lt;/path&gt; &lt;path&gt;/var/lib/virt/images/sparse.img&lt;/path&gt;
&lt;format&gt;qcow2&lt;/format&gt;
&lt;permissions&gt; &lt;permissions&gt;
&lt;owner&gt;0744&lt;/owner&gt; &lt;owner&gt;0744&lt;/owner&gt;
&lt;group&gt;0744&lt;/group&gt; &lt;group&gt;0744&lt;/group&gt;
&lt;mode&gt;0744&lt;/mode&gt; &lt;mode&gt;0744&lt;/mode&gt;
&lt;label&gt;virt_image_t&lt;/label&gt; &lt;label&gt;virt_image_t&lt;/label&gt;
&lt;/permissions&gt; &lt;/permissions&gt;
&lt;/target&gt; &lt;/target&gt;</pre>
&lt;/volume&gt;</pre>
<dl> <dl>
<dt><code>path</code></dt> <dt><code>path</code></dt>
@ -271,6 +271,54 @@
</dd> </dd>
</dl> </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>
...
&lt;backingStore&gt;
&lt;path&gt;/var/lib/virt/images/master.img&lt;/path&gt;
&lt;format&gt;raw&lt;/format&gt;
&lt;permissions&gt;
&lt;owner&gt;0744&lt;/owner&gt;
&lt;group&gt;0744&lt;/group&gt;
&lt;mode&gt;0744&lt;/mode&gt;
&lt;label&gt;virt_image_t&lt;/label&gt;
&lt;/permissions&gt;
&lt;/backingStore&gt;
&lt;/volume&gt;</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> <h2><a name="examples">Example configuration</a></h2>
<p> <p>

View File

@ -259,6 +259,7 @@ virStoragePoolFormatDiskTypeToString;
virStoragePoolFormatFileSystemTypeToString; virStoragePoolFormatFileSystemTypeToString;
virStoragePoolFormatFileSystemNetTypeToString; virStoragePoolFormatFileSystemNetTypeToString;
virStorageVolFormatFileSystemTypeToString; virStorageVolFormatFileSystemTypeToString;
virStorageVolFormatFileSystemTypeFromString;
virStoragePoolTypeFromString; virStoragePoolTypeFromString;
virStoragePoolObjLock; virStoragePoolObjLock;
virStoragePoolObjUnlock; virStoragePoolObjUnlock;

View File

@ -99,29 +99,53 @@ virStorageBackendForType(int type) {
int int
virStorageBackendUpdateVolInfo(virConnectPtr conn, virStorageBackendUpdateVolTargetInfo(virConnectPtr conn,
virStorageVolDefPtr vol, virStorageVolTargetPtr target,
int withCapacity) unsigned long long *allocation,
unsigned long long *capacity)
{ {
int ret, fd; int ret, fd;
if ((fd = open(vol->target.path, O_RDONLY)) < 0) { if ((fd = open(target->path, O_RDONLY)) < 0) {
virReportSystemError(conn, errno, virReportSystemError(conn, errno,
_("cannot open volume '%s'"), _("cannot open volume '%s'"),
vol->target.path); target->path);
return -1; return -1;
} }
ret = virStorageBackendUpdateVolInfoFD(conn, ret = virStorageBackendUpdateVolTargetInfoFD(conn,
vol, target,
fd, fd,
withCapacity); allocation,
capacity);
close(fd); close(fd);
return ret; return ret;
} }
int
virStorageBackendUpdateVolInfo(virConnectPtr conn,
virStorageVolDefPtr vol,
int withCapacity)
{
int ret;
if ((ret = virStorageBackendUpdateVolTargetInfo(conn,
&vol->target,
&vol->allocation,
withCapacity ? &vol->capacity : NULL)) < 0)
return ret;
if (vol->backingStore.path &&
(ret = virStorageBackendUpdateVolTargetInfo(conn,
&vol->backingStore,
NULL, NULL)) < 0)
return ret;
return 0;
}
struct diskType { struct diskType {
int part_table_type; int part_table_type;
unsigned short offset; unsigned short offset;
@ -154,10 +178,11 @@ static struct diskType const disk_types[] = {
}; };
int int
virStorageBackendUpdateVolInfoFD(virConnectPtr conn, virStorageBackendUpdateVolTargetInfoFD(virConnectPtr conn,
virStorageVolDefPtr vol, virStorageVolTargetPtr target,
int fd, int fd,
int withCapacity) unsigned long long *allocation,
unsigned long long *capacity)
{ {
struct stat sb; struct stat sb;
#if HAVE_SELINUX #if HAVE_SELINUX
@ -167,7 +192,7 @@ virStorageBackendUpdateVolInfoFD(virConnectPtr conn,
if (fstat(fd, &sb) < 0) { if (fstat(fd, &sb) < 0) {
virReportSystemError(conn, errno, virReportSystemError(conn, errno,
_("cannot stat file '%s'"), _("cannot stat file '%s'"),
vol->target.path); target->path);
return -1; return -1;
} }
@ -176,38 +201,41 @@ virStorageBackendUpdateVolInfoFD(virConnectPtr conn,
!S_ISBLK(sb.st_mode)) !S_ISBLK(sb.st_mode))
return -2; return -2;
if (S_ISREG(sb.st_mode)) { if (allocation) {
if (S_ISREG(sb.st_mode)) {
#ifndef __MINGW32__ #ifndef __MINGW32__
vol->allocation = (unsigned long long)sb.st_blocks * *allocation = (unsigned long long)sb.st_blocks *
(unsigned long long)sb.st_blksize; (unsigned long long)sb.st_blksize;
#else #else
vol->allocation = sb.st_size; *allocation = sb.st_size;
#endif #endif
/* Regular files may be sparse, so logical size (capacity) is not same /* Regular files may be sparse, so logical size (capacity) is not same
* as actual allocation above * as actual allocation above
*/ */
if (withCapacity) if (capacity)
vol->capacity = sb.st_size; *capacity = sb.st_size;
} else { } else {
off_t end; off_t end;
/* XXX this is POSIX compliant, but doesn't work for for CHAR files, /* XXX this is POSIX compliant, but doesn't work for for CHAR files,
* only BLOCK. There is a Linux specific ioctl() for getting * only BLOCK. There is a Linux specific ioctl() for getting
* size of both CHAR / BLOCK devices we should check for in * size of both CHAR / BLOCK devices we should check for in
* configure * configure
*/ */
end = lseek(fd, 0, SEEK_END); end = lseek(fd, 0, SEEK_END);
if (end == (off_t)-1) { if (end == (off_t)-1) {
virReportSystemError(conn, errno, virReportSystemError(conn, errno,
_("cannot seek to end of file '%s'"), _("cannot seek to end of file '%s'"),
vol->target.path); target->path);
return -1; 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 */ /* 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)) { if (S_ISBLK(sb.st_mode)) {
off_t start; off_t start;
@ -219,14 +247,14 @@ virStorageBackendUpdateVolInfoFD(virConnectPtr conn,
if (start < 0) { if (start < 0) {
virReportSystemError(conn, errno, virReportSystemError(conn, errno,
_("cannot seek to beginning of file '%s'"), _("cannot seek to beginning of file '%s'"),
vol->target.path); target->path);
return -1; return -1;
} }
bytes = saferead(fd, buffer, sizeof(buffer)); bytes = saferead(fd, buffer, sizeof(buffer));
if (bytes < 0) { if (bytes < 0) {
virReportSystemError(conn, errno, virReportSystemError(conn, errno,
_("cannot read beginning of file '%s'"), _("cannot read beginning of file '%s'"),
vol->target.path); target->path);
return -1; return -1;
} }
@ -235,38 +263,38 @@ virStorageBackendUpdateVolInfoFD(virConnectPtr conn,
continue; continue;
if (memcmp(buffer+disk_types[i].offset, &disk_types[i].magic, if (memcmp(buffer+disk_types[i].offset, &disk_types[i].magic,
disk_types[i].length) == 0) { disk_types[i].length) == 0) {
vol->target.format = disk_types[i].part_table_type; target->format = disk_types[i].part_table_type;
break; break;
} }
} }
} }
vol->target.perms.mode = sb.st_mode; target->perms.mode = sb.st_mode & S_IRWXUGO;
vol->target.perms.uid = sb.st_uid; target->perms.uid = sb.st_uid;
vol->target.perms.gid = sb.st_gid; target->perms.gid = sb.st_gid;
VIR_FREE(vol->target.perms.label); VIR_FREE(target->perms.label);
#if HAVE_SELINUX #if HAVE_SELINUX
if (fgetfilecon(fd, &filecon) == -1) { if (fgetfilecon(fd, &filecon) == -1) {
if (errno != ENODATA && errno != ENOTSUP) { if (errno != ENODATA && errno != ENOTSUP) {
virReportSystemError(conn, errno, virReportSystemError(conn, errno,
_("cannot get file context of '%s'"), _("cannot get file context of '%s'"),
vol->target.path); target->path);
return -1; return -1;
} else { } else {
vol->target.perms.label = NULL; target->perms.label = NULL;
} }
} else { } else {
vol->target.perms.label = strdup(filecon); target->perms.label = strdup(filecon);
if (vol->target.perms.label == NULL) { if (target->perms.label == NULL) {
virReportOOMError(conn); virReportOOMError(conn);
return -1; return -1;
} }
freecon(filecon); freecon(filecon);
} }
#else #else
vol->target.perms.label = NULL; target->perms.label = NULL;
#endif #endif
return 0; return 0;

View File

@ -64,10 +64,15 @@ int virStorageBackendUpdateVolInfo(virConnectPtr conn,
virStorageVolDefPtr vol, virStorageVolDefPtr vol,
int withCapacity); int withCapacity);
int virStorageBackendUpdateVolInfoFD(virConnectPtr conn, int virStorageBackendUpdateVolTargetInfo(virConnectPtr conn,
virStorageVolDefPtr vol, virStorageVolTargetPtr target,
int fd, unsigned long long *allocation,
int withCapacity); unsigned long long *capacity);
int virStorageBackendUpdateVolTargetInfoFD(virConnectPtr conn,
virStorageVolTargetPtr target,
int fd,
unsigned long long *allocation,
unsigned long long *capacity);
void virStorageBackendWaitForDevices(virConnectPtr conn); void virStorageBackendWaitForDevices(virConnectPtr conn);

View File

@ -49,6 +49,19 @@ enum lv_endian {
LV_BIG_ENDIAN /* 4321 */ 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 */ /* Either 'magic' or 'extension' *must* be provided */
struct FileTypeInfo { struct FileTypeInfo {
int type; /* One of the constants above */ int type; /* One of the constants above */
@ -65,85 +78,228 @@ struct FileTypeInfo {
* -1 to use st_size as capacity */ * -1 to use st_size as capacity */
int sizeBytes; /* Number of bytes for size field */ int sizeBytes; /* Number of bytes for size field */
int sizeMultiplier; /* A scaling factor if size is not in bytes */ 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[] = { const struct FileTypeInfo const fileTypeInfo[] = {
/* Bochs */ /* Bochs */
/* XXX Untested /* XXX Untested
{ VIR_STORAGE_VOL_BOCHS, "Bochs Virtual HD Image", NULL, { VIR_STORAGE_VOL_BOCHS, "Bochs Virtual HD Image", NULL,
LV_LITTLE_ENDIAN, 64, 0x20000, 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 */ /* CLoop */
/* XXX Untested /* 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, { 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, LV_LITTLE_ENDIAN, -1, 0,
-1, 0, 0 }, */ -1, 0, 0, NULL }, */
/* Cow */ /* Cow */
{ VIR_STORAGE_VOL_FILE_COW, "OOOM", NULL, { VIR_STORAGE_VOL_FILE_COW, "OOOM", NULL,
LV_BIG_ENDIAN, 4, 2, LV_BIG_ENDIAN, 4, 2,
4+4+1024+4, 8, 1 }, 4+4+1024+4, 8, 1, cowGetBackingStore },
/* DMG */ /* DMG */
/* XXX QEMU says there's no magic for dmg, but we should check... */ /* XXX QEMU says there's no magic for dmg, but we should check... */
{ VIR_STORAGE_VOL_FILE_DMG, NULL, ".dmg", { VIR_STORAGE_VOL_FILE_DMG, NULL, ".dmg",
0, -1, 0, 0, -1, 0,
-1, 0, 0 }, -1, 0, 0, NULL },
/* XXX there's probably some magic for iso we can validate too... */ /* XXX there's probably some magic for iso we can validate too... */
{ VIR_STORAGE_VOL_FILE_ISO, NULL, ".iso", { VIR_STORAGE_VOL_FILE_ISO, NULL, ".iso",
0, -1, 0, 0, -1, 0,
-1, 0, 0 }, -1, 0, 0, NULL },
/* Parallels */ /* Parallels */
/* XXX Untested /* XXX Untested
{ VIR_STORAGE_VOL_FILE_PARALLELS, "WithoutFreeSpace", NULL, { VIR_STORAGE_VOL_FILE_PARALLELS, "WithoutFreeSpace", NULL,
LV_LITTLE_ENDIAN, 16, 2, LV_LITTLE_ENDIAN, 16, 2,
16+4+4+4+4, 4, 512 }, 16+4+4+4+4, 4, 512, NULL },
*/ */
/* QCow */ /* QCow */
{ VIR_STORAGE_VOL_FILE_QCOW, "QFI", NULL, { VIR_STORAGE_VOL_FILE_QCOW, "QFI", NULL,
LV_BIG_ENDIAN, 4, 1, LV_BIG_ENDIAN, 4, 1,
4+4+8+4+4, 8, 1 }, 4+4+8+4+4, 8, 1, qcowXGetBackingStore },
/* QCow 2 */ /* QCow 2 */
{ VIR_STORAGE_VOL_FILE_QCOW2, "QFI", NULL, { VIR_STORAGE_VOL_FILE_QCOW2, "QFI", NULL,
LV_BIG_ENDIAN, 4, 2, LV_BIG_ENDIAN, 4, 2,
4+4+8+4+4, 8, 1 }, 4+4+8+4+4, 8, 1, qcowXGetBackingStore },
/* VMDK 3 */ /* VMDK 3 */
/* XXX Untested /* XXX Untested
{ VIR_STORAGE_VOL_FILE_VMDK, "COWD", NULL, { VIR_STORAGE_VOL_FILE_VMDK, "COWD", NULL,
LV_LITTLE_ENDIAN, 4, 1, LV_LITTLE_ENDIAN, 4, 1,
4+4+4, 4, 512 }, 4+4+4, 4, 512, NULL },
*/ */
/* VMDK 4 */ /* VMDK 4 */
{ VIR_STORAGE_VOL_FILE_VMDK, "KDMV", NULL, { VIR_STORAGE_VOL_FILE_VMDK, "KDMV", NULL,
LV_LITTLE_ENDIAN, 4, 1, LV_LITTLE_ENDIAN, 4, 1,
4+4+4, 8, 512 }, 4+4+4, 8, 512, vmdk4GetBackingStore },
/* Connectix / VirtualPC */ /* Connectix / VirtualPC */
/* XXX Untested /* XXX Untested
{ VIR_STORAGE_VOL_FILE_VPC, "conectix", NULL, { VIR_STORAGE_VOL_FILE_VPC, "conectix", NULL,
LV_BIG_ENDIAN, -1, 0, LV_BIG_ENDIAN, -1, 0,
-1, 0, 0}, -1, 0, 0, NULL},
*/ */
}; };
#define VIR_FROM_THIS VIR_FROM_STORAGE #define VIR_FROM_THIS VIR_FROM_STORAGE
static int
cowGetBackingStore(virConnectPtr conn,
char **res,
const unsigned char *buf,
size_t buf_size)
{
#define COW_FILENAME_MAXLEN 1024
*res = NULL;
if (buf_size < 4+4+ COW_FILENAME_MAXLEN)
return BACKING_STORE_INVALID;
if (buf[4+4] == '\0') /* cow_header_v2.backing_file[0] */
return BACKING_STORE_OK;
*res = strndup ((const char*)buf + 4+4, COW_FILENAME_MAXLEN);
if (*res == NULL) {
virReportOOMError(conn);
return BACKING_STORE_ERROR;
}
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 * Probe the header of a file to determine what type of disk image
* it is, and info about its capacity if available. * it is, and info about its capacity if available.
*/ */
static int virStorageBackendProbeFile(virConnectPtr conn, static int virStorageBackendProbeTarget(virConnectPtr conn,
virStorageVolDefPtr def) { virStorageVolTargetPtr target,
char **backingStore,
unsigned long long *allocation,
unsigned long long *capacity) {
int fd; int fd;
unsigned char head[4096]; unsigned char head[20*512]; /* vmdk4GetBackingStore needs this much. */
int len, i, ret; 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, virReportSystemError(conn, errno,
_("cannot open volume '%s'"), _("cannot open volume '%s'"),
def->target.path); target->path);
return -1; return -1;
} }
if ((ret = virStorageBackendUpdateVolInfoFD(conn, def, fd, 1)) < 0) { if ((ret = virStorageBackendUpdateVolTargetInfoFD(conn, target, fd,
allocation,
capacity)) < 0) {
close(fd); close(fd);
return ret; /* Take care to propagate ret, it is not always -1 */ return ret; /* Take care to propagate ret, it is not always -1 */
} }
@ -151,7 +307,7 @@ static int virStorageBackendProbeFile(virConnectPtr conn,
if ((len = read(fd, head, sizeof(head))) < 0) { if ((len = read(fd, head, sizeof(head))) < 0) {
virReportSystemError(conn, errno, virReportSystemError(conn, errno,
_("cannot read header '%s'"), _("cannot read header '%s'"),
def->target.path); target->path);
close(fd); close(fd);
return -1; return -1;
} }
@ -191,9 +347,9 @@ static int virStorageBackendProbeFile(virConnectPtr conn,
} }
/* Optionally extract capacity from file */ /* Optionally extract capacity from file */
if (fileTypeInfo[i].sizeOffset != -1) { if (fileTypeInfo[i].sizeOffset != -1 && capacity) {
if (fileTypeInfo[i].endian == LV_LITTLE_ENDIAN) { 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+7] << 56) |
((unsigned long long)head[fileTypeInfo[i].sizeOffset+6] << 48) | ((unsigned long long)head[fileTypeInfo[i].sizeOffset+6] << 48) |
((unsigned long long)head[fileTypeInfo[i].sizeOffset+5] << 40) | ((unsigned long long)head[fileTypeInfo[i].sizeOffset+5] << 40) |
@ -203,7 +359,7 @@ static int virStorageBackendProbeFile(virConnectPtr conn,
((unsigned long long)head[fileTypeInfo[i].sizeOffset+1] << 8) | ((unsigned long long)head[fileTypeInfo[i].sizeOffset+1] << 8) |
((unsigned long long)head[fileTypeInfo[i].sizeOffset]); ((unsigned long long)head[fileTypeInfo[i].sizeOffset]);
} else { } else {
def->capacity = *capacity =
((unsigned long long)head[fileTypeInfo[i].sizeOffset] << 56) | ((unsigned long long)head[fileTypeInfo[i].sizeOffset] << 56) |
((unsigned long long)head[fileTypeInfo[i].sizeOffset+1] << 48) | ((unsigned long long)head[fileTypeInfo[i].sizeOffset+1] << 48) |
((unsigned long long)head[fileTypeInfo[i].sizeOffset+2] << 40) | ((unsigned long long)head[fileTypeInfo[i].sizeOffset+2] << 40) |
@ -214,13 +370,37 @@ static int virStorageBackendProbeFile(virConnectPtr conn,
((unsigned long long)head[fileTypeInfo[i].sizeOffset+7]); ((unsigned long long)head[fileTypeInfo[i].sizeOffset+7]);
} }
/* Avoid unlikely, but theoretically possible overflow */ /* Avoid unlikely, but theoretically possible overflow */
if (def->capacity > (ULLONG_MAX / fileTypeInfo[i].sizeMultiplier)) if (*capacity > (ULLONG_MAX / fileTypeInfo[i].sizeMultiplier))
continue; continue;
def->capacity *= fileTypeInfo[i].sizeMultiplier; *capacity *= fileTypeInfo[i].sizeMultiplier;
} }
/* Validation passed, we know the file format now */ /* 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; return 0;
} }
@ -229,15 +409,15 @@ static int virStorageBackendProbeFile(virConnectPtr conn,
if (fileTypeInfo[i].extension == NULL) if (fileTypeInfo[i].extension == NULL)
continue; continue;
if (!virFileHasSuffix(def->target.path, fileTypeInfo[i].extension)) if (!virFileHasSuffix(target->path, fileTypeInfo[i].extension))
continue; continue;
def->target.format = fileTypeInfo[i].type; target->format = fileTypeInfo[i].type;
return 0; return 0;
} }
/* All fails, so call it a raw file */ /* 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; return 0;
} }
@ -636,6 +816,7 @@ virStorageBackendFileSystemRefresh(virConnectPtr conn,
while ((ent = readdir(dir)) != NULL) { while ((ent = readdir(dir)) != NULL) {
int ret; int ret;
char *backingStore;
if (VIR_ALLOC(vol) < 0) if (VIR_ALLOC(vol) < 0)
goto no_memory; goto no_memory;
@ -655,7 +836,11 @@ virStorageBackendFileSystemRefresh(virConnectPtr conn,
if ((vol->key = strdup(vol->target.path)) == NULL) if ((vol->key = strdup(vol->target.path)) == NULL)
goto no_memory; goto no_memory;
if ((ret = virStorageBackendProbeFile(conn, vol) < 0)) { if ((ret = virStorageBackendProbeTarget(conn,
&vol->target,
&backingStore,
&vol->allocation,
&vol->capacity) < 0)) {
if (ret == -1) if (ret == -1)
goto no_memory; goto no_memory;
else { else {
@ -667,6 +852,48 @@ virStorageBackendFileSystemRefresh(virConnectPtr conn,
} }
} }
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, if (VIR_REALLOC_N(pool->volumes.objs,
pool->volumes.count+1) < 0) pool->volumes.count+1) < 0)
goto no_memory; goto no_memory;
@ -836,28 +1063,48 @@ virStorageBackendFileSystemVolCreate(virConnectPtr conn,
} }
} else { } else {
#if HAVE_QEMU_IMG #if HAVE_QEMU_IMG
const char *type; const char *type = virStorageVolFormatFileSystemTypeToString(vol->target.format);
const char *backingType = vol->backingStore.path ?
virStorageVolFormatFileSystemTypeToString(vol->backingStore.format) : NULL;
char size[100]; char size[100];
const char *imgargv[7]; const char **imgargv;
const char *imgargvnormal[] = {
QEMU_IMG, "create", "-f", type, vol->target.path, size, NULL,
};
/* XXX including "backingType" here too, once QEMU accepts
* the patches to specify it. It'll probably be -F backingType */
const char *imgargvbacking[] = {
QEMU_IMG, "create", "-f", type, "-b", vol->backingStore.path, vol->target.path, size, NULL,
};
if ((type = virStorageVolFormatFileSystemTypeToString(vol->target.format)) == NULL) { if (type == NULL) {
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
_("unknown storage vol type %d"), _("unknown storage vol type %d"),
vol->target.format); vol->target.format);
return -1; return -1;
} }
if (vol->backingStore.path == NULL) {
imgargv = imgargvnormal;
} else {
if (backingType == 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;
}
imgargv = imgargvbacking;
}
/* Size in KB */ /* Size in KB */
snprintf(size, sizeof(size), "%llu", vol->capacity/1024); 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;
if (virRun(conn, imgargv, NULL) < 0) { if (virRun(conn, imgargv, NULL) < 0) {
unlink(vol->target.path); unlink(vol->target.path);
return -1; return -1;
@ -884,6 +1131,12 @@ virStorageBackendFileSystemVolCreate(virConnectPtr conn,
vol->target.format); vol->target.format);
return -1; 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 :-( */ /* Size in MB - yes different units to qemu-img :-( */
snprintf(size, sizeof(size), "%llu", vol->capacity/1024/1024); snprintf(size, sizeof(size), "%llu", vol->capacity/1024/1024);
@ -934,7 +1187,9 @@ virStorageBackendFileSystemVolCreate(virConnectPtr conn,
} }
/* Refresh allocation / permissions info, but not capacity */ /* 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); unlink(vol->target.path);
close(fd); close(fd);
return -1; return -1;

View File

@ -228,7 +228,11 @@ virStorageBackendISCSINewLun(virConnectPtr conn, virStoragePoolObjPtr pool,
VIR_FREE(devpath); VIR_FREE(devpath);
if (virStorageBackendUpdateVolInfoFD(conn, vol, fd, 1) < 0) if (virStorageBackendUpdateVolTargetInfoFD(conn,
&vol->target,
fd,
&vol->allocation,
&vol->capacity) < 0)
goto cleanup; goto cleanup;
/* XXX use unique iSCSI id instead */ /* XXX use unique iSCSI id instead */

View File

@ -106,18 +106,27 @@ virStorageBackendLogicalMakeVol(virConnectPtr conn,
} }
if (vol->target.path == NULL) { if (vol->target.path == NULL) {
if (VIR_ALLOC_N(vol->target.path, strlen(pool->def->target.path) + if (virAsprintf(&vol->target.path, "%s/%s",
1 + strlen(vol->name) + 1) < 0) { pool->def->target.path, vol->name) < 0) {
virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("volume")); virReportOOMError(conn);
virStorageVolDefFree(vol);
return -1; return -1;
} }
strcpy(vol->target.path, pool->def->target.path); }
strcat(vol->target.path, "/");
strcat(vol->target.path, vol->name); if (groups[1] && !STREQ(groups[1], "")) {
if (virAsprintf(&vol->backingStore.path, "%s/%s",
pool->def->target.path, groups[1]) < 0) {
virReportOOMError(conn);
virStorageVolDefFree(vol);
return -1;
}
vol->backingStore.format = VIR_STORAGE_POOL_LOGICAL_LVM2;
} }
if (vol->key == NULL && if (vol->key == NULL &&
(vol->key = strdup(groups[1])) == NULL) { (vol->key = strdup(groups[2])) == NULL) {
virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("volume")); virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("volume"));
return -1; return -1;
} }
@ -134,22 +143,22 @@ virStorageBackendLogicalMakeVol(virConnectPtr conn,
} }
if ((vol->source.extents[vol->source.nextent].path = if ((vol->source.extents[vol->source.nextent].path =
strdup(groups[2])) == NULL) { strdup(groups[3])) == NULL) {
virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("extents")); virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("extents"));
return -1; 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, virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
"%s", _("malformed volume extent offset value")); "%s", _("malformed volume extent offset value"));
return -1; 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, virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
"%s", _("malformed volume extent length value")); "%s", _("malformed volume extent length value"));
return -1; 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, virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
"%s", _("malformed volume extent size value")); "%s", _("malformed volume extent size value"));
return -1; return -1;
@ -168,14 +177,14 @@ virStorageBackendLogicalFindLVs(virConnectPtr conn,
virStorageVolDefPtr vol) virStorageVolDefPtr vol)
{ {
/* /*
* # lvs --separator , --noheadings --units b --unbuffered --nosuffix --options "lv_name,uuid,devices,seg_size,vg_extent_size" VGNAME * # 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 * 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 * 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 * 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(251),2181038080,33554432
* Test3,UB5hFw-kmlm-LSoX-EI1t-ioVd-h7GL-M0W8Ht,/dev/hda2(187),1040187392,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 * NB can be multiple rows per volume if they have many extents
* *
@ -185,15 +194,15 @@ virStorageBackendLogicalFindLVs(virConnectPtr conn,
* not a suitable separator (rhbz 470693). * not a suitable separator (rhbz 470693).
*/ */
const char *regexes[] = { const char *regexes[] = {
"^\\s*(\\S+),(\\S+),(\\S+)\\((\\S+)\\),(\\S+),([0-9]+),?\\s*$" "^\\s*(\\S+),(\\S*),(\\S+),(\\S+)\\((\\S+)\\),(\\S+),([0-9]+),?\\s*$"
}; };
int vars[] = { int vars[] = {
6 7
}; };
const char *prog[] = { const char *prog[] = {
LVS, "--separator", ",", "--noheadings", "--units", "b", LVS, "--separator", ",", "--noheadings", "--units", "b",
"--unbuffered", "--nosuffix", "--options", "--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 pool->def->source.name, NULL
}; };
@ -565,10 +574,25 @@ virStorageBackendLogicalCreateVol(virConnectPtr conn,
{ {
int fd = -1; int fd = -1;
char size[100]; char size[100];
const char *cmdargv[] = { const char *cmdargvnew[] = {
LVCREATE, "--name", vol->name, "-L", size, LVCREATE, "--name", vol->name, "-L", size,
pool->def->target.path, NULL 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); snprintf(size, sizeof(size)-1, "%lluK", vol->capacity/1024);
size[sizeof(size)-1] = '\0'; size[sizeof(size)-1] = '\0';

View File

@ -249,6 +249,8 @@ virStorageVolDefFree(virStorageVolDefPtr def) {
VIR_FREE(def->target.path); VIR_FREE(def->target.path);
VIR_FREE(def->target.perms.label); VIR_FREE(def->target.perms.label);
VIR_FREE(def->backingStore.path);
VIR_FREE(def->backingStore.perms.label);
VIR_FREE(def); VIR_FREE(def);
} }
@ -998,6 +1000,28 @@ virStorageVolDefParseDoc(virConnectPtr conn,
if (virStorageVolDefParsePerms(conn, ctxt, &ret->target.perms) < 0) if (virStorageVolDefParsePerms(conn, ctxt, &ret->target.perms) < 0)
goto cleanup; 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; return ret;
cleanup: 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 * char *
virStorageVolDefFormat(virConnectPtr conn, virStorageVolDefFormat(virConnectPtr conn,
@ -1116,37 +1181,15 @@ virStorageVolDefFormat(virConnectPtr conn,
virBufferVSprintf(&buf," <allocation>%llu</allocation>\n", virBufferVSprintf(&buf," <allocation>%llu</allocation>\n",
def->allocation); def->allocation);
virBufferAddLit(&buf, " <target>\n"); if (virStorageVolTargetDefFormat(conn, options, &buf,
&def->target, "target") < 0)
goto cleanup;
if (def->target.path) if (def->backingStore.path &&
virBufferVSprintf(&buf," <path>%s</path>\n", def->target.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"); virBufferAddLit(&buf,"</volume>\n");
if (virBufferError(&buf)) if (virBufferError(&buf))

View File

@ -89,6 +89,7 @@ struct _virStorageVolDef {
virStorageVolSource source; virStorageVolSource source;
virStorageVolTarget target; virStorageVolTarget target;
virStorageVolTarget backingStore;
}; };
typedef struct _virStorageVolDefList virStorageVolDefList; typedef struct _virStorageVolDefList virStorageVolDefList;