[Libvir] PATCH: 14/16: disk partition backend

Daniel P. Berrange berrange at redhat.com
Tue Feb 12 04:40:26 UTC 2008


This implements a storage pool for partitioning local disks.
It uses parted as the tool for reading & writing disk partitions.
Since parted is GPLv3, we cannot link against the code directly.
So, this driver calls out to the regular 'parted' command line
tool for creationg/deletion. For listing of partiitons we have
a trivial helper program '/usr/libexec/libvirt_parthelper'. This
outputs partition listing in a easily parseable format - each
partition, or free space extent is a record, containing 5 fields.
Records and fields are separated by NULLs. The internal libvirt
helper API virStorageBackendRunProgNul can reliably parse this
data format (thanks to Jim for the suggestion of using NULLs
instead of whitespace, since the latter is not so easy to protect
against bogus files)

Creating volumes from disk partitions is more complex than most
of the drivers. This is because partition tables have quite
strict placement constraints. There are free extent regions
and a partition must fall within one, and further more be aligned
to a suitable byte boundary. For this reason the pool XML format
will contain information about the free extents on a disk. The
volume XML will also detail the actual extents used by a partition
on disk.

All of the parted partition table types are supported. A new disk
can be initialized with the 'build' operation which will write a
new partition table. When creating volumes a partition entry type
can be specified if required. For simply assigning volumes to guests
though, it is not neccessary to use anything other than the default
of 'none'.

 .hgignore                    |    1 
 b/src/parthelper.c           |  127 ++++++++++
 b/src/storage_backend_disk.c |  508 +++++++++++++++++++++++++++++++++++++++++++
 b/src/storage_backend_disk.h |   45 +++
 configure.in                 |   48 ++++
 libvirt.spec.in              |    5 
 src/Makefile.am              |   18 +
 src/storage_backend.c        |   14 +
 8 files changed, 766 insertions(+)

diff -r 4c283aac5fd6 .hgignore
--- a/.hgignore	Thu Feb 07 14:17:57 2008 -0500
+++ b/.hgignore	Thu Feb 07 16:51:32 2008 -0500
@@ -18,6 +18,7 @@ Makefile\.in$
 ^src/virsh
 ^src/libvirt.la
 ^src/\.libs
+^src/parthelper
 ^aclocal.m4$
 ^docs/devhelp/libvirt.devhelp$
 ^libtool$
diff -r 4c283aac5fd6 configure.in
--- a/configure.in	Thu Feb 07 14:17:57 2008 -0500
+++ b/configure.in	Thu Feb 07 16:51:32 2008 -0500
@@ -27,6 +27,7 @@ GNUTLS_REQUIRED="1.0.25"
 GNUTLS_REQUIRED="1.0.25"
 AVAHI_REQUIRED="0.6.0"
 POLKIT_REQUIRED="0.6"
+PARTED_REQUIRED="1.8.0"
 
 dnl Checks for C compiler.
 AC_PROG_CC
@@ -563,6 +564,8 @@ AC_ARG_WITH(storage-lvm,
 [  --with-storage-lvm          with LVM backend for the storage driver (on)],[],[with_storage_lvm=check])
 AC_ARG_WITH(storage-iscsi,
 [  --with-storage-iscsi        with iSCSI backend for the storage driver (on)],[],[with_storage_iscsi=check])
+AC_ARG_WITH(storage-disk,
+[  --with-storage-disk         with GPartd Disk backend for the storage driver (on)],[],[with_storage_disk=check])
 
 if test "$with_storage_fs" = "yes" -o "$with_storage_fs" = "check"; then
   AC_PATH_PROG(MOUNT, [mount], [], [$PATH:/sbin:/usr/sbin])
@@ -674,6 +677,50 @@ if test "$with_storage_iscsi" = "yes" -o
   fi
 fi
 AM_CONDITIONAL(WITH_STORAGE_ISCSI, [test "$with_storage_iscsi" = "yes"])
+
+
+
+LIBPARTED_CFLAGS=
+LIBPARTED_LIBS=
+if test "$with_storage_disk" = "yes" -o "$with_storage_disk" = "check"; then
+  AC_PATH_PROG(PARTED, [parted], [], [$PATH:/sbin:/usr/sbin])
+  if test -z "$PARTED" ; then with_storage_disk=no ; fi
+
+  PARTED_FOUND=yes
+  if test "$with_storage_disk" != "no" -a "x$PKG_CONFIG" != "x" ; then
+    PKG_CHECK_MODULES(LIBPARTED, libparted >= $PARTED_REQUIRED, [], [PARTED_FOUND=no])
+  fi
+  if test "$PARTED_FOUND" = "no"; then
+    # RHEL-5 vintage parted is missing pkg-config files
+    save_LIBS="$LIBS"
+    save_CFLAGS="$CFLAGS"
+    PARTED_FOUND=yes
+    AC_CHECK_HEADER(parted/parted.h,,[PARTED_FOUND=no])
+    AC_CHECK_LIB(uuid, uuid_generate,,[PARTED_FOUND=no])
+    AC_CHECK_LIB(parted, ped_device_read,,[PARTED_FOUND=no])
+    LIBPARTED_LIBS="-luuid -lparted"
+    LIBS="$save_LIBS"
+    CFLAGS="$save_CFLAGS"
+  fi
+
+  if test "$PARTED_FOUND" = "no" ; then
+    if test "$with_storage_disk" = "yes" ; then
+      AC_MSG_ERROR(We need parted for disk storage driver)
+    else
+      with_storage_disk=no
+    fi
+  else
+    with_storage_disk=yes
+  fi
+
+  if test "$with_storage_disk" = "yes"; then
+    AC_DEFINE_UNQUOTED(WITH_STORAGE_DISK, 1, [whether Disk backend for storage driver is enabled])
+    AC_DEFINE_UNQUOTED([PARTED],["$PARTED"], [Location or name of the parted program])
+  fi
+fi
+AM_CONDITIONAL(WITH_STORAGE_DISK, [test "$with_storage_disk" = "yes"])
+AC_SUBST(LIBPARTED_CFLAGS)
+AC_SUBST(LIBPARTED_LIBS)
 
 
 dnl
@@ -891,6 +938,7 @@ AC_MSG_NOTICE([   NetFS: $with_storage_f
 AC_MSG_NOTICE([   NetFS: $with_storage_fs])
 AC_MSG_NOTICE([     LVM: $with_storage_lvm])
 AC_MSG_NOTICE([   iSCSI: $with_storage_iscsi])
+AC_MSG_NOTICE([    Disk: $with_storage_disk])
 AC_MSG_NOTICE([])
 AC_MSG_NOTICE([Libraries])
 AC_MSG_NOTICE([])
diff -r 4c283aac5fd6 libvirt.spec.in
--- a/libvirt.spec.in	Thu Feb 07 14:17:57 2008 -0500
+++ b/libvirt.spec.in	Thu Feb 07 16:51:32 2008 -0500
@@ -53,6 +53,8 @@ Requires: lvm2
 Requires: lvm2
 # For ISCSI driver
 Requires: iscsi-initiator-utils
+# For disk driver
+Requires: parted
 BuildRequires: xen-devel
 BuildRequires: libxml2-devel
 BuildRequires: readline-devel
@@ -81,6 +83,8 @@ BuildRequires: lvm2
 BuildRequires: lvm2
 # For ISCSI driver
 BuildRequires: iscsi-initiator-utils
+# For disk driver
+BuildRequires: parted-devel
 Obsoletes: libvir
 ExclusiveArch: i386 x86_64 ia64
 
@@ -203,6 +207,7 @@ fi
 %if %{with_proxy} == "yes"
 %attr(4755, root, root) %{_libexecdir}/libvirt_proxy
 %endif
+%attr(0755, root, root) %{_libexecdir}/libvirt_parthelper
 %attr(0755, root, root) %{_sbindir}/libvirtd
 %doc docs/*.rng
 %doc docs/*.xml
diff -r 4c283aac5fd6 src/Makefile.am
--- a/src/Makefile.am	Thu Feb 07 14:17:57 2008 -0500
+++ b/src/Makefile.am	Thu Feb 07 16:51:32 2008 -0500
@@ -81,6 +81,13 @@ EXTRA_DIST +=  storage_backend_iscsi.h s
 EXTRA_DIST +=  storage_backend_iscsi.h storage_backend_iscsi.c
 endif
 
+if WITH_STORAGE_DISK
+CLIENT_SOURCES += storage_backend_disk.h storage_backend_disk.c
+else
+EXTRA_DIST += storage_backend_disk.h storage_backend_disk.c
+endif
+
+
 
 
 libvirt_la_SOURCES = $(CLIENT_SOURCES) $(SERVER_SOURCES)
@@ -100,6 +107,17 @@ virsh_LDADD = $(LDADDS) $(VIRSH_LIBS)
 virsh_LDADD = $(LDADDS) $(VIRSH_LIBS)
 virsh_CFLAGS = $(COVERAGE_CFLAGS) $(READLINE_CFLAGS)
 
+if WITH_STORAGE_DISK
+libexec_PROGRAMS = libvirt_parthelper
+
+libvirt_parthelper_SOURCES = parthelper.c
+libvirt_parthelper_LDFLAGS = $(WARN_CFLAGS) $(COVERAGE_LDCFLAGS)
+libvirt_parthelper_LDADD = $(LIBPARTED_LIBS)
+libvirt_parthelper_CFLAGS =  $(LIBPARTED_CFLAGS)
+else
+EXTRA_DIST += parthelper.c
+endif
+
 #
 # target to ease building test programs
 #
diff -r 4c283aac5fd6 src/parthelper.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/parthelper.c	Thu Feb 07 16:51:32 2008 -0500
@@ -0,0 +1,127 @@
+/*
+ * parthelper.c: Helper program to talk to parted with.
+ *
+ * This helper exists because parted is GPLv3+, while libvirt is LGPLv2+.
+ * Thus we can't link to parted in libvirt.so without the combined work
+ * being GPLv3+. Thus we separate via an external command. NB, this source
+ * code is still LGPLv2+, but the binary helper is effectively GPLv3+
+ *
+ * The existing 'parted' command line tool is also incredibly hard to parse
+ * in a reliable fashion if merely after a list of partitions & sizes,
+ * though it is fine for creating partitions.
+ *
+ * Copyright (C) 2007-2008 Red Hat, Inc.
+ * Copyright (C) 2007-2008 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: Daniel P. Berrange <berrange at redhat.com>
+ */
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+    PedDevice *dev;
+    PedDisk *disk;
+    PedPartition *part;
+
+    if (argc !=  2) {
+        fprintf(stderr, "syntax: %s DEVICE\n", argv[0]);
+        return 1;
+    }
+
+    if ((dev = ped_device_get(argv[1])) == NULL) {
+        fprintf(stderr, "unable to access device %s\n", argv[1]);
+        return 2;
+    }
+
+    if ((disk = ped_disk_new(dev)) == NULL) {
+        fprintf(stderr, "unable to access disk %s\n", argv[1]);
+        return 2;
+    }
+
+    /* Get the first partition, and then iterate over all */
+    part = ped_disk_get_partition(disk, 1);
+    while (part) {
+        const char *type;
+        const char *content;
+        if (part->type & PED_PARTITION_LOGICAL) {
+            type = "logical";
+            if (part->type & PED_PARTITION_FREESPACE)
+                content = "free";
+            else if (part->type & PED_PARTITION_METADATA)
+                content = "metadata";
+            else if (part->type & PED_PARTITION_PROTECTED)
+                content = "protected";
+            else
+                content = "data";
+        } else if (part->type == PED_PARTITION_EXTENDED) {
+            type = "extended";
+            content = "metadata";
+        } else {
+            type = "normal";
+            if (part->type & PED_PARTITION_FREESPACE)
+                content = "free";
+            else if (part->type & PED_PARTITION_METADATA)
+                content = "metadata";
+            else if (part->type & PED_PARTITION_PROTECTED)
+                content = "protected";
+            else
+                content = "data";
+        }
+
+        /* We do +1 on geom.end, because we want end of the last sector
+         * in bytes, not the last sector number
+         */
+        if (part->num != -1) {
+            printf("%s%d%c%s%c%s%c%llu%c%llu%c%llu%c",
+                   part->geom.dev->path,
+                   part->num, '\0',
+                   type, '\0',
+                   content, '\0',
+                   part->geom.start * 512llu, '\0',
+                   (part->geom.end + 1 ) * 512llu, '\0',
+                   part->geom.length * 512llu, '\0');
+        } else {
+            printf("%s%c%s%c%s%c%llu%c%llu%c%llu%c",
+                   "-", '\0',
+                   type, '\0',
+                   content, '\0',
+                   part->geom.start * 512llu, '\0',
+                   (part->geom.end + 1 ) * 512llu, '\0',
+                   part->geom.length * 512llu, '\0');
+        }
+        part = ped_disk_next_partition(disk, part);
+    }
+
+    return 0;
+}
+/*
+ * vim: set tabstop=4:
+ * vim: set shiftwidth=4:
+ * vim: set expandtab:
+ */
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff -r 4c283aac5fd6 src/storage_backend.c
--- a/src/storage_backend.c	Thu Feb 07 14:17:57 2008 -0500
+++ b/src/storage_backend.c	Thu Feb 07 16:51:32 2008 -0500
@@ -42,6 +42,9 @@
 #if WITH_STORAGE_ISCSI
 #include "storage_backend_iscsi.h"
 #endif
+#if WITH_STORAGE_DISK
+#include "storage_backend_disk.h"
+#endif
 
 
 #include "util.h"
@@ -60,6 +63,9 @@ static virStorageBackendPtr backends[] =
 #endif
 #if WITH_STORAGE_ISCSI
     &virStorageBackendISCSI,
+#endif
+#if WITH_STORAGE_DISK
+    &virStorageBackendDisk,
 #endif
 };
 
@@ -105,6 +111,10 @@ int virStorageBackendFromString(const ch
 #if WITH_STORAGE_ISCSI
     if (STREQ(type, "iscsi"))
         return VIR_STORAGE_POOL_ISCSI;
+#endif
+#if WITH_STORAGE_DISK
+    if (STREQ(type, "disk"))
+        return VIR_STORAGE_POOL_DISK;
 #endif
     virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, "unknown storage backend type %s", type);
     return -1;
@@ -127,6 +137,10 @@ const char *virStorageBackendToString(in
 #if WITH_STORAGE_ISCSI
     case VIR_STORAGE_POOL_ISCSI:
         return "iscsi";
+#endif
+#if WITH_STORAGE_DISK
+    case VIR_STORAGE_POOL_DISK:
+        return "disk";
 #endif
     }
 
diff -r 4c283aac5fd6 src/storage_backend_disk.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/storage_backend_disk.c	Thu Feb 07 16:51:32 2008 -0500
@@ -0,0 +1,508 @@
+/*
+ * storage_backend_disk.c: storage backend for disk handling
+ *
+ * Copyright (C) 2007-2008 Red Hat, Inc.
+ * Copyright (C) 2007-2008 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: Daniel P. Berrange <berrange at redhat.com>
+ */
+
+#include <config.h>
+
+#include "storage_backend_disk.h"
+#include "util.h"
+
+enum {
+    VIR_STORAGE_POOL_DISK_DOS = 0,
+    VIR_STORAGE_POOL_DISK_DVH,
+    VIR_STORAGE_POOL_DISK_GPT,
+    VIR_STORAGE_POOL_DISK_MAC,
+    VIR_STORAGE_POOL_DISK_BSD,
+    VIR_STORAGE_POOL_DISK_PC98,
+    VIR_STORAGE_POOL_DISK_SUN,
+};
+
+/*
+ * XXX these are basically partition types.
+ *
+ * fdisk has a bazillion partition ID types
+ * parted has practically none, and splits the
+ * info across 3 different attributes.
+ *
+ * So this is a semi-generic set
+ */
+enum {
+    VIR_STORAGE_VOL_DISK_NONE = 0,
+    VIR_STORAGE_VOL_DISK_LINUX,
+    VIR_STORAGE_VOL_DISK_FAT16,
+    VIR_STORAGE_VOL_DISK_FAT32,
+    VIR_STORAGE_VOL_DISK_LINUX_SWAP,
+    VIR_STORAGE_VOL_DISK_LINUX_LVM,
+    VIR_STORAGE_VOL_DISK_LINUX_RAID,
+    VIR_STORAGE_VOL_DISK_EXTENDED,
+};
+
+#define PARTHELPER BINDIR "/libvirt_parthelper"
+
+static int virStorageBackendDiskPoolFormatFromString(virConnectPtr conn,
+                                                     const char *format) {
+    if (format == NULL)
+        return VIR_STORAGE_POOL_DISK_DOS;
+
+    if (STREQ(format, "dos"))
+        return VIR_STORAGE_POOL_DISK_DOS;
+    if (STREQ(format, "dvh"))
+        return VIR_STORAGE_POOL_DISK_DVH;
+    if (STREQ(format, "gpt"))
+        return VIR_STORAGE_POOL_DISK_GPT;
+    if (STREQ(format, "mac"))
+        return VIR_STORAGE_POOL_DISK_MAC;
+    if (STREQ(format, "bsd"))
+        return VIR_STORAGE_POOL_DISK_BSD;
+    if (STREQ(format, "pc98"))
+        return VIR_STORAGE_POOL_DISK_PC98;
+    if (STREQ(format, "sun"))
+        return VIR_STORAGE_POOL_DISK_SUN;
+
+    virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unsupported pool format %s", format);
+    return -1;
+}
+
+static const char *virStorageBackendDiskPoolFormatToString(virConnectPtr conn,
+                                                           int format) {
+    switch (format) {
+    case VIR_STORAGE_POOL_DISK_DOS:
+        return "dos";
+    case VIR_STORAGE_POOL_DISK_DVH:
+        return "dvh";
+    case VIR_STORAGE_POOL_DISK_GPT:
+        return "gpt";
+    case VIR_STORAGE_POOL_DISK_MAC:
+        return "mac";
+    case VIR_STORAGE_POOL_DISK_BSD:
+        return "bsd";
+    case VIR_STORAGE_POOL_DISK_PC98:
+        return "pc98";
+    case VIR_STORAGE_POOL_DISK_SUN:
+        return "sun";
+    }
+
+    virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unsupported pool format %d", format);
+    return NULL;
+}
+
+static int virStorageBackendDiskVolFormatFromString(virConnectPtr conn,
+                                                    const char *format) {
+    if (format == NULL)
+        return VIR_STORAGE_VOL_DISK_NONE;
+
+    if (STREQ(format, "none"))
+        return VIR_STORAGE_VOL_DISK_NONE;
+    if (STREQ(format, "linux"))
+        return VIR_STORAGE_VOL_DISK_LINUX;
+    if (STREQ(format, "fat16"))
+        return VIR_STORAGE_VOL_DISK_FAT16;
+    if (STREQ(format, "fat32"))
+        return VIR_STORAGE_VOL_DISK_FAT32;
+    if (STREQ(format, "linux-swap"))
+        return VIR_STORAGE_VOL_DISK_LINUX_SWAP;
+    if (STREQ(format, "linux-lvm"))
+        return VIR_STORAGE_VOL_DISK_LINUX_LVM;
+    if (STREQ(format, "linux-raid"))
+        return VIR_STORAGE_VOL_DISK_LINUX_RAID;
+    if (STREQ(format, "extended"))
+        return VIR_STORAGE_VOL_DISK_EXTENDED;
+
+    virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unsupported volume format %s", format);
+    return -1;
+}
+
+static const char *virStorageBackendDiskVolFormatToString(virConnectPtr conn,
+                                                          int format) {
+    switch (format) {
+    case VIR_STORAGE_VOL_DISK_NONE:
+        return "none";
+    case VIR_STORAGE_VOL_DISK_LINUX:
+        return "linux";
+    case VIR_STORAGE_VOL_DISK_FAT16:
+        return "fat16";
+    case VIR_STORAGE_VOL_DISK_FAT32:
+        return "fat32";
+    case VIR_STORAGE_VOL_DISK_LINUX_SWAP:
+        return "swap";
+    case VIR_STORAGE_VOL_DISK_LINUX_LVM:
+        return "lvm";
+    case VIR_STORAGE_VOL_DISK_LINUX_RAID:
+        return "raid";
+    case VIR_STORAGE_VOL_DISK_EXTENDED:
+        return "extended";
+    }
+
+    virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unsupported volume format %d", format);
+    return NULL;
+}
+
+static int virStorageBackendDiskMakeDataVol(virConnectPtr conn,
+                                            virStoragePoolObjPtr pool,
+                                            char **const groups,
+                                            virStorageVolDefPtr vol)
+{
+    char *tmp, *devpath;
+
+    if (vol == NULL) {
+        if ((vol = calloc(1, sizeof(virStorageVolDef))) == NULL) {
+            virStorageReportError(conn, VIR_ERR_NO_MEMORY, "volume");
+            return -1;
+        }
+
+        vol->next = pool->volumes;
+        pool->volumes = vol;
+        pool->nvolumes++;
+
+        /* Prepended path will be same for all partitions, so we can
+         * strip the path to form a reasonable pool-unique name
+         */
+        tmp = strrchr(groups[0], '/');
+        if ((vol->name = strdup(tmp ? tmp + 1 : groups[0])) == NULL) {
+            virStorageReportError(conn, VIR_ERR_NO_MEMORY, "volume");
+            return -1;
+        }
+    }
+
+    if (vol->target.path == NULL) {
+        if ((devpath = strdup(groups[0])) == NULL) {
+            virStorageReportError(conn, VIR_ERR_NO_MEMORY, "volume");
+            return -1;
+        }
+
+        /* Now figure out the stable path
+         *
+         * XXX this method is O(N) because it scans the pool target
+         * dir every time its run. Should figure out a more efficient
+         * way of doing this...
+         */
+        if ((vol->target.path = virStorageBackendStablePath(conn, pool, devpath)) == NULL)
+            return -1;
+
+        if (devpath != vol->target.path)
+            free(devpath);
+        devpath = NULL;
+    }
+
+    if (vol->key == NULL) {
+        /* XXX base off a unique key of the underlying disk */
+        if ((vol->key = strdup(vol->target.path)) == NULL) {
+            virStorageReportError(conn, VIR_ERR_NO_MEMORY, "volume");
+            return -1;
+        }
+    }
+
+    if (vol->source.extents == NULL) {
+        if ((vol->source.extents = calloc(1, sizeof(*(vol->source.extents)))) == NULL) {
+            virStorageReportError(conn, VIR_ERR_NO_MEMORY, "volume extents");
+            return -1;
+        }
+        vol->source.nextent = 1;
+
+        if (xstrtol_ull(groups[3], NULL, 10,
+                        &vol->source.extents[0].start) < 0) {
+            virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot parse device start location");
+            return -1;
+        }
+
+        if (xstrtol_ull(groups[4], NULL, 10,
+                        &vol->source.extents[0].end) < 0) {
+            virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot parse device end location");
+            return -1;
+        }
+
+        if ((vol->source.extents[0].path = strdup(pool->def->source.devices[0].path)) == NULL) {
+            virStorageReportError(conn, VIR_ERR_NO_MEMORY, "extents");
+            return -1;
+        }
+    }
+
+    /* Refresh allocation/capacity/perms */
+    if (virStorageBackendUpdateVolInfo(conn, vol, 1) < 0)
+        return -1;
+
+    /* The above gets allocation wrong for extended partitions, so overwrite it */
+    vol->allocation = vol->capacity = (vol->source.extents[0].end - vol->source.extents[0].start);
+
+    if (STRNEQ(groups[2], "metadata"))
+        pool->def->allocation += vol->allocation;
+    if (vol->source.extents[0].end > pool->def->capacity)
+        pool->def->capacity = vol->source.extents[0].end;
+
+    return 0;
+}
+
+static int virStorageBackendDiskMakeFreeExtent(virConnectPtr conn ATTRIBUTE_UNUSED,
+                                               virStoragePoolObjPtr pool,
+                                               char **const groups)
+{
+    virStoragePoolSourceDeviceExtentPtr tmp;
+    virStoragePoolSourceDevicePtr dev = &pool->def->source.devices[0];
+
+    if ((tmp = realloc(dev->freeExtents,
+                       sizeof(*tmp) * (dev->nfreeExtent+1))) == NULL)
+        return -1;
+    dev->freeExtents = tmp;
+
+    memset(dev->freeExtents +
+           dev->nfreeExtent, 0, sizeof(*tmp));
+
+    if (xstrtol_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 */
+
+    if (xstrtol_ull(groups[4], NULL, 10,
+                    &dev->freeExtents[dev->nfreeExtent].end) < 0)
+        return -1; /* Don't bother to re-alloc freeExtents - it'll be free'd shortly */
+
+    pool->def->available +=
+        (dev->freeExtents[dev->nfreeExtent].end -
+         dev->freeExtents[dev->nfreeExtent].start);
+    if (dev->freeExtents[dev->nfreeExtent].end > pool->def->capacity)
+        pool->def->capacity = dev->freeExtents[dev->nfreeExtent].end;
+
+    dev->nfreeExtent++;
+
+    return 0;
+}
+
+
+static int virStorageBackendDiskMakeVol(virConnectPtr conn,
+                                        virStoragePoolObjPtr pool,
+                                        size_t ntok ATTRIBUTE_UNUSED,
+                                        char **const groups,
+                                        void *data)
+{
+    /*
+     * Ignore normal+metadata, and logical+metadata partitions
+     * since they're basically internal book-keeping regions
+     * we have no control over. Do keep extended+metadata though
+     * because that's the MS-DOS extended partition region we
+     * need to be able to view/create/delete
+     */
+    if ((STREQ(groups[1], "normal") ||
+         STREQ(groups[1], "logical")) &&
+        STREQ(groups[2], "metadata"))
+        return 0;
+
+    /* Remaining data / metdata parts get turn into volumes... */
+    if (STREQ(groups[2], "metadata") ||
+        STREQ(groups[2], "data")) {
+        virStorageVolDefPtr vol = data;
+        /* We're searching for a specific vol only, so ignore others */
+        if (vol &&
+            STRNEQ(vol->name, groups[0]))
+            return 0;
+
+        return virStorageBackendDiskMakeDataVol(conn, pool, groups, vol);
+    } else if (STREQ(groups[2], "free")) {
+        /* ....or free space extents */
+        return virStorageBackendDiskMakeFreeExtent(conn, pool, groups);
+    } else {
+        /* This codepath should never happen unless someone changed
+         * libvirt_parthelper forgot to change this code */
+        return -1;
+    }
+}
+
+
+/* To get a list of partitions we run an external helper
+ * tool which then uses parted APIs. This is because
+ * parted's API is not compatible with libvirt's license
+ * but we really really want to use parted because the
+ * other options all suck :-)
+ *
+ * All the other storage backends run an external tool for
+ * listing volumes so this really isn't too much of a pain,
+ * and we can even ensure the output is friendly.
+ */
+static int virStorageBackendDiskReadPartitions(virConnectPtr conn,
+                                               virStoragePoolObjPtr pool,
+                                               virStorageVolDefPtr vol)
+{
+
+    /*
+     *  # libvirt_parthelper DEVICE
+     * /dev/sda1      normal       data        32256    106928128    106896384
+     * /dev/sda2      normal       data    106928640 100027629568  99920701440
+     * -              normal   metadata 100027630080 100030242304      2612736
+     *
+     */
+    const char *prog[] = {
+        PARTHELPER, pool->def->source.devices[0].path, NULL,
+    };
+
+    pool->def->allocation = pool->def->capacity = pool->def->available = 0;
+
+    return virStorageBackendRunProgNul(conn,
+                                       pool,
+                                       prog,
+                                       6,
+                                       virStorageBackendDiskMakeVol,
+                                       vol);
+}
+
+
+static int virStorageBackendDiskRefreshPool(virConnectPtr conn,
+                                            virStoragePoolObjPtr pool)
+{
+    free(pool->def->source.devices[0].freeExtents);
+    pool->def->source.devices[0].nfreeExtent = 0;
+    pool->def->source.devices[0].freeExtents = NULL;
+
+    return virStorageBackendDiskReadPartitions(conn, pool, NULL);
+}
+
+
+/**
+ * Write a new partition table header
+ */
+static int virStorageBackendDiskBuildPool(virConnectPtr conn,
+                                          virStoragePoolObjPtr pool,
+                                          unsigned int flags ATTRIBUTE_UNUSED)
+{
+    /* eg parted /dev/sda mklabel msdos */
+    const char *prog[] = {
+        PARTED,
+        pool->def->source.devices[0].path,
+        "mklabel",
+        virStorageBackendDiskPoolFormatToString(conn, pool->def->source.format),
+        NULL,
+    };
+
+    if (virRun(conn, (char**)prog, NULL) < 0)
+        return -1;
+
+    return 0;
+}
+
+
+static int virStorageBackendDiskDeleteVol(virConnectPtr conn,
+                                          virStoragePoolObjPtr pool,
+                                          virStorageVolDefPtr vol,
+                                          unsigned int flags);
+
+static int virStorageBackendDiskCreateVol(virConnectPtr conn,
+                                          virStoragePoolObjPtr pool,
+                                          virStorageVolDefPtr vol)
+{
+    int i;
+    char start[100], end[100];
+    unsigned long long startOffset, endOffset, smallestSize = 0, 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 (smallestExtent == -1) {
+        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "no large enough free extent");
+        return -1;
+    }
+    startOffset = dev->freeExtents[smallestExtent].start;
+    endOffset = startOffset + vol->allocation;
+
+    snprintf(start, sizeof(start)-1, "%lluB", startOffset);
+    start[sizeof(start)-1] = '\0';
+    snprintf(end, sizeof(end)-1, "%lluB", endOffset);
+    end[sizeof(end)-1] = '\0';
+
+    if (virRun(conn, (char**)cmdargv, NULL) < 0)
+        return -1;
+
+    /* Blow away free extent info, as we're about to re-populate it */
+    free(pool->def->source.devices[0].freeExtents);
+    pool->def->source.devices[0].nfreeExtent = 0;
+    pool->def->source.devices[0].freeExtents = NULL;
+
+    /* Fetch actual extent info */
+    if (virStorageBackendDiskReadPartitions(conn, pool, vol) < 0)
+        return -1;
+
+    return 0;
+}
+
+
+static int virStorageBackendDiskDeleteVol(virConnectPtr conn,
+                                          virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
+                                          virStorageVolDefPtr vol ATTRIBUTE_UNUSED,
+                                          unsigned int flags ATTRIBUTE_UNUSED)
+{
+    /* delete a partition */
+    virStorageReportError(conn, VIR_ERR_NO_SUPPORT, "Disk pools are not yet supported");
+    return -1;
+}
+
+
+virStorageBackend virStorageBackendDisk = {
+    .type = VIR_STORAGE_POOL_DISK,
+
+    .buildPool = virStorageBackendDiskBuildPool,
+    .refreshPool = virStorageBackendDiskRefreshPool,
+
+    .createVol = virStorageBackendDiskCreateVol,
+    .deleteVol = virStorageBackendDiskDeleteVol,
+
+    .poolOptions = {
+        .flags = (VIR_STORAGE_BACKEND_POOL_SOURCE_DEVICE),
+        .formatFromString = virStorageBackendDiskPoolFormatFromString,
+        .formatToString = virStorageBackendDiskPoolFormatToString,
+    },
+    .volOptions = {
+        .formatFromString = virStorageBackendDiskVolFormatFromString,
+        .formatToString = virStorageBackendDiskVolFormatToString,
+    },
+
+    .volType = VIR_STORAGE_VOL_BLOCK,
+};
+
+/*
+ * vim: set tabstop=4:
+ * vim: set shiftwidth=4:
+ * vim: set expandtab:
+ */
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff -r 4c283aac5fd6 src/storage_backend_disk.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/storage_backend_disk.h	Thu Feb 07 16:51:32 2008 -0500
@@ -0,0 +1,45 @@
+/*
+ * storage_backend_disk.h: storage backend for disk handling
+ *
+ * Copyright (C) 2007-2008 Red Hat, Inc.
+ * Copyright (C) 2007-2008 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: Daniel P. Berrange <berrange at redhat.com>
+ */
+
+#ifndef __VIR_STORAGE_BACKEND_DISK_H__
+#define __VIR_STORAGE_BACKEND_DISK_H__
+
+#include "storage_backend.h"
+
+extern virStorageBackend virStorageBackendDisk;
+
+#endif /* __VIR_STORAGE_BACKEND_DISK_H__ */
+
+/*
+ * vim: set tabstop=4:
+ * vim: set shiftwidth=4:
+ * vim: set expandtab:
+ */
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */

-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 




More information about the libvir-list mailing list