diff --git a/AUTHORS b/AUTHORS index 7c3104ab88..4fbfb20e65 100644 --- a/AUTHORS +++ b/AUTHORS @@ -75,6 +75,7 @@ Patches have also been contributed by: Javier Fontan Federico Simoncelli Amy Griffis + Henrik Persson E [....send patches to get your name here....] diff --git a/ChangeLog b/ChangeLog index b45bb13358..27c8773d5f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +Fri Jun 26 18:15:08 CEST 2009 Daniel Veillard + + * src/libvirt_private.syms src/parthelper.c src/storage_backend_disk.c + src/storage_conf.c src/storage_conf.h: allow to create storage + volumes on disk backend, patches by Henrik Persson + * AUTHORS: add Henrik Persson + Fri Jun 26 17:06:18 CEST 2009 Daniel Veillard * src/Makefile.am src/libvirt.c src/libvirt_private.syms src/logging.c diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 3958d9b4f3..52c49674c2 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -307,6 +307,7 @@ virStoragePoolFormatFileSystemNetTypeToString; virStorageVolFormatFileSystemTypeToString; virStorageVolFormatFileSystemTypeFromString; virStoragePoolTypeFromString; +virStoragePartedFsTypeTypeToString; virStoragePoolObjLock; virStoragePoolObjUnlock; diff --git a/src/parthelper.c b/src/parthelper.c index c699035a9a..ff5a1b6c21 100644 --- a/src/parthelper.c +++ b/src/parthelper.c @@ -35,20 +35,31 @@ #include #include +/* we don't need to include the full internal.h just for this */ +#define STRNEQ(a,b) (strcmp((a),(b)) != 0) + /* Make the comparisons below fail if your parted headers are so old that they lack the definition. */ #ifndef PED_PARTITION_PROTECTED # define PED_PARTITION_PROTECTED 0 #endif +enum diskCommand { + DISK_LAYOUT = 0, + DISK_GEOMETRY +}; + int main(int argc, char **argv) { PedDevice *dev; PedDisk *disk; PedPartition *part; + int cmd = DISK_LAYOUT; - if (argc != 2) { - fprintf(stderr, "syntax: %s DEVICE\n", argv[0]); + if (argc == 3 && STRNEQ(argv[2], "-g")) { + cmd = DISK_GEOMETRY; + } else if (argc != 2) { + fprintf(stderr, "syntax: %s DEVICE [-g]\n", argv[0]); return 1; } @@ -57,6 +68,15 @@ int main(int argc, char **argv) return 2; } + /* return the geometry of the disk and then exit */ + if(cmd == DISK_GEOMETRY) { + printf("%d%c%d%c%d%c%", + dev->hw_geom.cylinders, '\0', + dev->hw_geom.heads, '\0', + dev->hw_geom.sectors, '\0'); + return 0; + } + if ((disk = ped_disk_new(dev)) == NULL) { fprintf(stderr, "unable to access disk %s\n", argv[1]); return 2; diff --git a/src/storage_backend_disk.c b/src/storage_backend_disk.c index 4b0da3d7d4..eb694a7d2e 100644 --- a/src/storage_backend_disk.c +++ b/src/storage_backend_disk.c @@ -36,6 +36,8 @@ #define PARTHELPER BINDIR "/libvirt_parthelper" +#define SECTOR_SIZE 512 + static int virStorageBackendDiskMakeDataVol(virConnectPtr conn, virStoragePoolObjPtr pool, @@ -128,6 +130,16 @@ virStorageBackendDiskMakeDataVol(virConnectPtr conn, if (virStorageBackendUpdateVolInfo(conn, vol, 1) < 0) return -1; + /* set partition type */ + if(STREQ(groups[1], "normal")) + vol->target.type = VIR_STORAGE_VOL_DISK_TYPE_PRIMARY; + else if(STREQ(groups[1], "logical")) + vol->target.type = VIR_STORAGE_VOL_DISK_TYPE_LOGICAL; + else if(STREQ(groups[1], "extended")) + vol->target.type = VIR_STORAGE_VOL_DISK_TYPE_EXTENDED; + else + vol->target.type = VIR_STORAGE_VOL_DISK_TYPE_NONE; + vol->type = VIR_STORAGE_VOL_BLOCK; /* The above gets allocation wrong for @@ -158,6 +170,14 @@ virStorageBackendDiskMakeFreeExtent(virConnectPtr conn ATTRIBUTE_UNUSED, dev->nfreeExtent, 0, sizeof(dev->freeExtents[0])); + /* set type of free area */ + if(STREQ(groups[1], "logical")) { + dev->freeExtents[dev->nfreeExtent].type = VIR_STORAGE_FREE_LOGICAL; + } else { + dev->freeExtents[dev->nfreeExtent].type = VIR_STORAGE_FREE_NORMAL; + } + + if (virStrToLong_ull(groups[3], NULL, 10, &dev->freeExtents[dev->nfreeExtent].start) < 0) return -1; /* Don't bother to re-alloc freeExtents - it'll be free'd shortly */ @@ -166,6 +186,11 @@ virStorageBackendDiskMakeFreeExtent(virConnectPtr conn ATTRIBUTE_UNUSED, &dev->freeExtents[dev->nfreeExtent].end) < 0) return -1; /* Don't bother to re-alloc freeExtents - it'll be free'd shortly */ + /* first block reported as free, even if it is not */ + if (dev->freeExtents[dev->nfreeExtent].start == 0) { + dev->freeExtents[dev->nfreeExtent].start = SECTOR_SIZE; + } + pool->def->available += (dev->freeExtents[dev->nfreeExtent].end - dev->freeExtents[dev->nfreeExtent].start); @@ -255,6 +280,35 @@ virStorageBackendDiskReadPartitions(virConnectPtr conn, vol); } +static int +virStorageBackendDiskMakePoolGeometry(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool, + size_t ntok ATTRIBUTE_UNUSED, + char **const groups, + void *data ATTRIBUTE_UNUSED) +{ + + pool->def->source.devices[0].geometry.cyliders = atoi(groups[0]); + pool->def->source.devices[0].geometry.heads = atoi(groups[1]); + pool->def->source.devices[0].geometry.sectors = atoi(groups[2]); + + return 0; +} + +static int +virStorageBackendDiskReadGeometry(virConnectPtr conn, virStoragePoolObjPtr pool) +{ + const char *prog[] = { + PARTHELPER, pool->def->source.devices[0].path, "-g", NULL, + }; + + return virStorageBackendRunProgNul(conn, + pool, + prog, + 3, + virStorageBackendDiskMakePoolGeometry, + NULL); +} static int virStorageBackendDiskRefreshPool(virConnectPtr conn, @@ -265,6 +319,10 @@ virStorageBackendDiskRefreshPool(virConnectPtr conn, virStorageBackendWaitForDevices(conn); + if (virStorageBackendDiskReadGeometry(conn, pool) != 0) { + return -1; + } + return virStorageBackendDiskReadPartitions(conn, pool, NULL); } @@ -294,46 +352,218 @@ virStorageBackendDiskBuildPool(virConnectPtr conn, return 0; } +/** + * Decides what kind of partition type that should be created. + * Important when the partition table is of msdos type + */ static int -virStorageBackendDiskCreateVol(virConnectPtr conn, - virStoragePoolObjPtr pool, - virStorageVolDefPtr vol) +virStorageBackendDiskPartTypeToCreate(virStoragePoolObjPtr pool) { - int i; - char start[100], end[100]; - unsigned long long startOffset, endOffset, smallestSize = 0; - int smallestExtent = -1; - virStoragePoolSourceDevicePtr dev = &pool->def->source.devices[0]; - /* XXX customizable partition types */ - const char *cmdargv[] = { - PARTED, - pool->def->source.devices[0].path, - "mkpart", - "--script", - "ext2", - start, - end, - NULL - }; - - for (i = 0 ; i < dev->nfreeExtent ; i++) { - unsigned long long size = - dev->freeExtents[i].end - - dev->freeExtents[i].start; - if (size > vol->allocation && - (smallestSize == 0 || - size < smallestSize)) { - smallestSize = size; - smallestExtent = i; + if (pool->def->source.format == VIR_STORAGE_POOL_DISK_DOS) { + /* count primary and extended paritions, + can't be more than 3 to create a new primary partition */ + int i; + int count = 0; + for (i = 0; i < pool->volumes.count; i++) { + if (pool->volumes.objs[i]->target.type == VIR_STORAGE_VOL_DISK_TYPE_PRIMARY || + pool->volumes.objs[i]->target.type == VIR_STORAGE_VOL_DISK_TYPE_EXTENDED) { + count++; + } + } + if (count >= 4) { + return VIR_STORAGE_VOL_DISK_TYPE_LOGICAL; } } + + /* for all other cases, all partitions are primary */ + return VIR_STORAGE_VOL_DISK_TYPE_PRIMARY; +} + +static int +virStorageBackendDiskPartFormat(virConnectPtr conn, virStoragePoolObjPtr pool, + virStorageVolDefPtr vol, + char* partFormat) +{ + int i; + if (pool->def->source.format == VIR_STORAGE_POOL_DISK_DOS) { + const char *partedFormat = virStoragePartedFsTypeTypeToString(vol->target.format); + if(partedFormat == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Invalid partition type")); + return -1; + } + if (vol->target.format == VIR_STORAGE_VOL_DISK_EXTENDED) { + /* make sure we don't have a extended partition already */ + for (i = 0; i < pool->volumes.count; i++) { + if (pool->volumes.objs[i]->target.format == VIR_STORAGE_VOL_DISK_EXTENDED) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("extended partition already exists")); + return -1; + } + } + sprintf(partFormat, "%s", partedFormat); + } else { + /* create primary partition as long as it is possible + and after that check if an extended partition exists + to create logical partitions. */ + /* XXX Only support one extended partition */ + switch (virStorageBackendDiskPartTypeToCreate(pool)) { + case VIR_STORAGE_VOL_DISK_TYPE_PRIMARY: + sprintf(partFormat, "primary %s", partedFormat); + break; + case VIR_STORAGE_VOL_DISK_TYPE_LOGICAL: + /* make sure we have a extended partition */ + for (i = 0; i < pool->volumes.count; i++) { + if (pool->volumes.objs[i]->target.format == VIR_STORAGE_VOL_DISK_EXTENDED) { + sprintf(partFormat, "logical %s", partedFormat); + break; + } + } + if (i == pool->volumes.count) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("no extended partition found and no primary partition available")); + return -1; + } + break; + default: + break; + } + } + } else { + sprintf(partFormat, "primary"); + } + return 0; +} + +/** + * Aligns a new partition to nearest cylinder boundry + * when haveing a msdos partition table type + * to avoid any problem with all ready existing + * partitions + */ +static int +virStorageBackendDiskPartBoundries(virConnectPtr conn, + virStoragePoolObjPtr pool, + unsigned long long *start, + unsigned long long *end, + unsigned long long allocation) +{ + int i; + int smallestExtent = -1; + unsigned long long smallestSize = 0; + unsigned long long extraBytes = 0; + unsigned long long alignedAllocation = allocation; + virStoragePoolSourceDevicePtr dev = &pool->def->source.devices[0]; + unsigned long long cylinderSize = dev->geometry.heads * + dev->geometry.sectors * SECTOR_SIZE; + + DEBUG("find free area: allocation %llu, cyl size %llu\n", allocation, cylinderSize); + int partType = virStorageBackendDiskPartTypeToCreate(pool); + + /* how many extra bytes we have since we allocate + aligned to the cylinder boundry */ + extraBytes = cylinderSize - (allocation % cylinderSize); + + for (i = 0 ; i < dev->nfreeExtent ; i++) { + unsigned long long size = + dev->freeExtents[i].end - + dev->freeExtents[i].start; + unsigned long long neededSize = allocation; + + if (pool->def->source.format == VIR_STORAGE_POOL_DISK_DOS) { + /* align to cylinder boundry */ + neededSize += extraBytes; + if ((*start % cylinderSize) > extraBytes) { + /* add an extra cylinder if the offset can't fit within + the extra bytes we have */ + neededSize += cylinderSize; + } + /* if we are creating a logical patition, we need one extra + block between partitions (or actually move start one block) */ + if (partType == VIR_STORAGE_VOL_DISK_TYPE_LOGICAL) { + size -= SECTOR_SIZE; + } + } + if (size > neededSize && + (smallestSize == 0 || + size < smallestSize)) { + /* for logical partition, the free extent + must be within a logical free area */ + if (partType == VIR_STORAGE_VOL_DISK_TYPE_LOGICAL && + dev->freeExtents[i].type != VIR_STORAGE_FREE_LOGICAL) { + continue; + /* for primary partition, the free extent + must not be within a logical free area */ + } else if(partType == VIR_STORAGE_VOL_DISK_TYPE_PRIMARY && + dev->freeExtents[i].type != VIR_STORAGE_FREE_NORMAL) { + continue; + } + smallestSize = size; + smallestExtent = i; + alignedAllocation = neededSize; + } + } + if (smallestExtent == -1) { virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s", _("no large enough free extent")); return -1; } - startOffset = dev->freeExtents[smallestExtent].start; - endOffset = startOffset + vol->allocation; + + DEBUG("aligned alloc %llu\n", alignedAllocation); + *start = dev->freeExtents[smallestExtent].start; + + if (partType == VIR_STORAGE_VOL_DISK_TYPE_LOGICAL) { + /* for logical partition, skip one block */ + *start += SECTOR_SIZE; + } + + *end = *start + alignedAllocation; + if (pool->def->source.format == VIR_STORAGE_POOL_DISK_DOS) { + /* adjust our allocation if start is not at a cylinder boundry */ + *end -= (*start % cylinderSize); + } + + /* counting in byte, we want the last byte of the current sector */ + *end -= 1; + DEBUG("final aligned start %llu, end %llu\n", *start, *end); + return 0; +} + + +static int +virStorageBackendDiskCreateVol(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol) +{ + char start[100], end[100], partFormat[100]; + unsigned long long startOffset = 0, endOffset = 0; + const char *cmdargv[] = { + PARTED, + pool->def->source.devices[0].path, + "mkpart", + "--script", + partFormat, + start, + end, + NULL + }; + + if (virStorageBackendDiskPartFormat(conn, pool, vol, partFormat) != 0) { + return -1; + } + + if (vol->key == NULL) { + /* XXX base off a unique key of the underlying disk */ + if ((vol->key = strdup(vol->target.path)) == NULL) { + virReportOOMError(conn); + return -1; + } + } + + if(virStorageBackendDiskPartBoundries(conn, pool, &startOffset, &endOffset, vol->allocation) != 0) { + return -1; + } snprintf(start, sizeof(start)-1, "%lluB", startOffset); start[sizeof(start)-1] = '\0'; @@ -343,6 +573,9 @@ virStorageBackendDiskCreateVol(virConnectPtr conn, if (virRun(conn, cmdargv, NULL) < 0) return -1; + /* wait for device node to show up */ + virStorageBackendWaitForDevices(conn); + /* Blow away free extent info, as we're about to re-populate it */ VIR_FREE(pool->def->source.devices[0].freeExtents); pool->def->source.devices[0].nfreeExtent = 0; diff --git a/src/storage_conf.c b/src/storage_conf.c index 493eaa7492..f5d9b6ce72 100644 --- a/src/storage_conf.c +++ b/src/storage_conf.c @@ -93,6 +93,12 @@ VIR_ENUM_IMPL(virStorageVolFormatFileSystem, "cloop", "cow", "dmg", "iso", "qcow", "qcow2", "vmdk", "vpc") +VIR_ENUM_IMPL(virStoragePartedFsType, + VIR_STORAGE_PARTED_FS_TYPE_LAST, + "none", "ext2", "fat16", + "fat32", "linux-swap", + "ext2", "ext2", + "extended") typedef const char *(*virStorageVolFormatToString)(int format); typedef int (*virStorageVolFormatFromString)(const char *format); diff --git a/src/storage_conf.h b/src/storage_conf.h index a2a164e1a3..61d3841007 100644 --- a/src/storage_conf.h +++ b/src/storage_conf.h @@ -76,6 +76,7 @@ struct _virStorageVolTarget { char *path; int format; virStoragePerms perms; + int type; /* only used by disk backend for partition type */ }; @@ -154,6 +155,18 @@ struct _virStoragePoolSourceHost { }; +/* + * For MSDOS partitions, the free area + * is important when creating + * logical partitions + */ +enum virStorageFreeType { + VIR_STORAGE_FREE_NONE = 0, + VIR_STORAGE_FREE_NORMAL, + VIR_STORAGE_FREE_LOGICAL, + VIR_STORAGE_FREE_LAST +}; + /* * Available extents on the underlying storage */ @@ -162,6 +175,7 @@ typedef virStoragePoolSourceDeviceExtent *virStoragePoolSourceDeviceExtentPtr; struct _virStoragePoolSourceDeviceExtent { unsigned long long start; unsigned long long end; + int type; /* free space type */ }; @@ -176,6 +190,13 @@ struct _virStoragePoolSourceDevice { virStoragePoolSourceDeviceExtentPtr freeExtents; char *path; int format; /* Pool specific source format */ + /* When the source device is a physical disk, + the geometry data is needed */ + struct _geometry { + int cyliders; + int heads; + int sectors; + } geometry; }; @@ -443,6 +464,30 @@ enum virStorageVolFormatDisk { }; VIR_ENUM_DECL(virStorageVolFormatDisk) +enum virStorageVolTypeDisk { + VIR_STORAGE_VOL_DISK_TYPE_NONE = 0, + VIR_STORAGE_VOL_DISK_TYPE_PRIMARY, + VIR_STORAGE_VOL_DISK_TYPE_LOGICAL, + VIR_STORAGE_VOL_DISK_TYPE_EXTENDED, + VIR_STORAGE_VOL_DISK_TYPE_LAST, +}; +/* + * Mapping of Parted fs-types + * MUST be kept in the same order + * as virStorageVolFormatDisk + */ +enum virStoragePartedFsType { + VIR_STORAGE_PARTED_FS_TYPE_NONE = 0, + VIR_STORAGE_PARTED_FS_TYPE_LINUX, + VIR_STORAGE_PARTED_FS_TYPE_FAT16, + VIR_STORAGE_PARTED_FS_TYPE_FAT32, + VIR_STORAGE_PARTED_FS_TYPE_LINUX_SWAP, + VIR_STORAGE_PARTED_FS_TYPE_LINUX_LVM, + VIR_STORAGE_PARTED_FS_TYPE_LINUX_RAID, + VIR_STORAGE_PARTED_FS_TYPE_EXTENDED, + VIR_STORAGE_PARTED_FS_TYPE_LAST, +}; +VIR_ENUM_DECL(virStoragePartedFsType) #endif /* __VIR_STORAGE_CONF_H__ */