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@xxxxxxxxxx>: > 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@xxxxxxxxxx > https://www.redhat.com/mailman/listinfo/libvir-list > -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list