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>
* POTFILES.in: update: remove src/lxc_conf.c; Add src/bridge.c.

View File

@ -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 @@
...
&lt;target&gt;
&lt;path&gt;/var/lib/virt/images/sparse.img&lt;/path&gt;
&lt;format&gt;qcow2&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;/target&gt;
&lt;/volume&gt;</pre>
&lt;/target&gt;</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.
@ -355,6 +357,45 @@
element contains the numeric group ID. The <code>label</code> element
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>
...
&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" id="examples">Example configuration</a>

View File

@ -234,14 +234,14 @@
...
&lt;target&gt;
&lt;path&gt;/var/lib/virt/images/sparse.img&lt;/path&gt;
&lt;format&gt;qcow2&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;/target&gt;
&lt;/volume&gt;</pre>
&lt;/target&gt;</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>
...
&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>
<p>

View File

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

View File

@ -99,29 +99,53 @@ virStorageBackendForType(int type) {
int
virStorageBackendUpdateVolInfo(virConnectPtr conn,
virStorageVolDefPtr vol,
int withCapacity)
virStorageBackendUpdateVolTargetInfo(virConnectPtr conn,
virStorageVolTargetPtr target,
unsigned long long *allocation,
unsigned long long *capacity)
{
int ret, fd;
if ((fd = open(vol->target.path, O_RDONLY)) < 0) {
if ((fd = open(target->path, O_RDONLY)) < 0) {
virReportSystemError(conn, errno,
_("cannot open volume '%s'"),
vol->target.path);
target->path);
return -1;
}
ret = virStorageBackendUpdateVolInfoFD(conn,
vol,
fd,
withCapacity);
ret = virStorageBackendUpdateVolTargetInfoFD(conn,
target,
fd,
allocation,
capacity);
close(fd);
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 {
int part_table_type;
unsigned short offset;
@ -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(virConnectPtr conn,
if (fstat(fd, &sb) < 0) {
virReportSystemError(conn, errno,
_("cannot stat file '%s'"),
vol->target.path);
target->path);
return -1;
}
@ -176,38 +201,41 @@ virStorageBackendUpdateVolInfoFD(virConnectPtr conn,
!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(virConnectPtr conn,
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(virConnectPtr conn,
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 & S_IRWXUGO;
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;

View File

@ -64,10 +64,15 @@ int virStorageBackendUpdateVolInfo(virConnectPtr conn,
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);

View File

@ -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,228 @@ 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)
{
#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
* 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 +307,7 @@ static int virStorageBackendProbeFile(virConnectPtr conn,
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 +347,9 @@ static int virStorageBackendProbeFile(virConnectPtr conn,
}
/* 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 +359,7 @@ static int virStorageBackendProbeFile(virConnectPtr conn,
((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 +370,37 @@ static int virStorageBackendProbeFile(virConnectPtr conn,
((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 +409,15 @@ static int virStorageBackendProbeFile(virConnectPtr conn,
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 +816,7 @@ virStorageBackendFileSystemRefresh(virConnectPtr conn,
while ((ent = readdir(dir)) != NULL) {
int ret;
char *backingStore;
if (VIR_ALLOC(vol) < 0)
goto no_memory;
@ -655,7 +836,11 @@ virStorageBackendFileSystemRefresh(virConnectPtr conn,
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 +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,
pool->volumes.count+1) < 0)
goto no_memory;
@ -836,28 +1063,48 @@ virStorageBackendFileSystemVolCreate(virConnectPtr conn,
}
} else {
#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];
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,
_("unknown storage vol type %d"),
vol->target.format);
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 */
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) {
unlink(vol->target.path);
return -1;
@ -884,6 +1131,12 @@ virStorageBackendFileSystemVolCreate(virConnectPtr conn,
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 +1187,9 @@ virStorageBackendFileSystemVolCreate(virConnectPtr conn,
}
/* 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;

View File

@ -228,7 +228,11 @@ virStorageBackendISCSINewLun(virConnectPtr conn, virStoragePoolObjPtr pool,
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 */

View File

@ -106,18 +106,27 @@ virStorageBackendLogicalMakeVol(virConnectPtr conn,
}
if (vol->target.path == NULL) {
if (VIR_ALLOC_N(vol->target.path, strlen(pool->def->target.path) +
1 + strlen(vol->name) + 1) < 0) {
virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("volume"));
if (virAsprintf(&vol->target.path, "%s/%s",
pool->def->target.path, vol->name) < 0) {
virReportOOMError(conn);
virStorageVolDefFree(vol);
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 &&
(vol->key = strdup(groups[1])) == NULL) {
(vol->key = strdup(groups[2])) == NULL) {
virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("volume"));
return -1;
}
@ -134,22 +143,22 @@ virStorageBackendLogicalMakeVol(virConnectPtr conn,
}
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 +177,14 @@ virStorageBackendLogicalFindLVs(virConnectPtr conn,
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 +194,15 @@ virStorageBackendLogicalFindLVs(virConnectPtr conn,
* 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 +574,25 @@ virStorageBackendLogicalCreateVol(virConnectPtr conn,
{
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';

View File

@ -249,6 +249,8 @@ virStorageVolDefFree(virStorageVolDefPtr def) {
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 conn,
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 conn,
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))

View File

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