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

Daniel P. Berrange berrange at redhat.com
Wed Feb 20 04:05:40 UTC 2008


On Tue, Feb 12, 2008 at 04:40:26AM +0000, Daniel P. Berrange wrote:
> 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 |  537 +++++++++++++++++++++++++++++++++++++++++++
 b/src/storage_backend_disk.h |   45 +++
 configure.in                 |   48 +++
 libvirt.spec.in              |    5 
 po/POTFILES.in               |    1 
 src/Makefile.am              |   18 +
 src/storage_backend.c        |   14 +
 9 files changed, 796 insertions(+)

diff -r f54c0eb07193 .hgignore
--- a/.hgignore	Tue Feb 19 17:38:39 2008 -0500
+++ b/.hgignore	Tue Feb 19 17:43:53 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 f54c0eb07193 configure.in
--- a/configure.in	Tue Feb 19 17:38:39 2008 -0500
+++ b/configure.in	Tue Feb 19 17:43:53 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 f54c0eb07193 libvirt.spec.in
--- a/libvirt.spec.in	Tue Feb 19 17:38:39 2008 -0500
+++ b/libvirt.spec.in	Tue Feb 19 17:43:53 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 f54c0eb07193 po/POTFILES.in
--- a/po/POTFILES.in	Tue Feb 19 17:38:39 2008 -0500
+++ b/po/POTFILES.in	Tue Feb 19 17:43:53 2008 -0500
@@ -14,6 +14,7 @@ src/storage_backend_fs.c
 src/storage_backend_fs.c
 src/storage_backend_logical.c
 src/storage_backend_iscsi.c
+src/storage_backend_disk.c
 src/storage_conf.c
 src/storage_driver.c
 src/sexpr.c
diff -r f54c0eb07193 src/Makefile.am
--- a/src/Makefile.am	Tue Feb 19 17:38:39 2008 -0500
+++ b/src/Makefile.am	Tue Feb 19 17:43:53 2008 -0500
@@ -80,6 +80,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)
@@ -99,6 +106,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 f54c0eb07193 src/parthelper.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/parthelper.c	Tue Feb 19 17:43:53 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 f54c0eb07193 src/storage_backend.c
--- a/src/storage_backend.c	Tue Feb 19 17:38:39 2008 -0500
+++ b/src/storage_backend.c	Tue Feb 19 17:43:53 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
 };
 
@@ -110,6 +116,10 @@ virStorageBackendFromString(const char *
 #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,
@@ -135,6 +145,10 @@ virStorageBackendToString(int type) {
 #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 f54c0eb07193 src/storage_backend_disk.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/storage_backend_disk.c	Tue Feb 19 17:43:53 2008 -0500
@@ -0,0 +1,537 @@
+/*
+ * 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 "linux-swap";
+    case VIR_STORAGE_VOL_DISK_LINUX_LVM:
+        return "linux-lvm";
+    case VIR_STORAGE_VOL_DISK_LINUX_RAID:
+        return "linux-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 (virStrToLong_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 (virStrToLong_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 (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 */
+
+    if (virStrToLong_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;
+    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 (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 f54c0eb07193 src/storage_backend_disk.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/storage_backend_disk.h	Tue Feb 19 17:43:53 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