[gnome-disk-utility] Use abstract interface to create volumes



commit f247a3903b80a9548a51db345a09d5fe909f0e64
Author: David Zeuthen <davidz redhat com>
Date:   Thu Feb 4 16:19:11 2010 -0500

    Use abstract interface to create volumes
    
    This makes it possible to e.g. select a VG when creating a RAID array.

 src/gdu-gtk/gdu-disk-selection-widget.c            |  486 ++++++++++++++------
 src/gdu-gtk/gdu-pool-tree-model.c                  |    2 +-
 src/gdu/gdu-drive.c                                |  407 +++++++++++++++--
 src/gdu/gdu-drive.h                                |   49 ++-
 src/gdu/gdu-linux-lvm2-volume-group.c              |  172 +++++++
 src/gdu/gdu-linux-md-drive.c                       |    8 +-
 src/gdu/gdu-pool.c                                 |    1 +
 .../gdu-section-linux-lvm2-volume-group.c          |   10 +-
 src/palimpsest/gdu-section-linux-md-drive.c        |   10 +-
 src/palimpsest/gdu-shell.c                         |  130 +-----
 10 files changed, 948 insertions(+), 327 deletions(-)
---
diff --git a/src/gdu-gtk/gdu-disk-selection-widget.c b/src/gdu-gtk/gdu-disk-selection-widget.c
index 0cdeb20..287e300 100644
--- a/src/gdu-gtk/gdu-disk-selection-widget.c
+++ b/src/gdu-gtk/gdu-disk-selection-widget.c
@@ -33,6 +33,8 @@
 #define DETAILS_WIDTH 180
 #define DETAILS_MARGIN 12
 
+#define SIZE_EPSILON 1000
+
 struct GduDiskSelectionWidgetPrivate
 {
         GduPool *pool;
@@ -397,6 +399,63 @@ gdu_disk_selection_widget_get_ignored_drives (GduDiskSelectionWidget  *widget)
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+typedef enum {
+        REASON_INSUFFICIENT_SPACE,
+        REASON_MULTIPATH_COMPONENT,
+} Reason;
+
+static gboolean
+is_drive_selectable (GduDiskSelectionWidget *widget,
+                     GduDrive               *drive,
+                     Reason                 *out_reason)
+{
+        gboolean ret;
+        guint64 largest_segment;
+        gboolean whole_disk_is_uninitialized;
+        Reason reason;
+        GduDevice *d;
+
+        ret = FALSE;
+        d = NULL;
+        reason = REASON_INSUFFICIENT_SPACE;
+
+        d = gdu_presentable_get_device (GDU_PRESENTABLE (drive));
+        if (d != NULL && gdu_device_is_linux_dmmp_component (d)) {
+                ret = FALSE;
+                reason = REASON_MULTIPATH_COMPONENT;
+                goto out;
+        }
+
+        if (gdu_drive_can_create_volume (drive,
+                                         &whole_disk_is_uninitialized,
+                                         &largest_segment,
+                                         NULL, /* total_free */
+                                         NULL)) {
+                if (largest_segment >= widget->priv->component_size) {
+                        ret = TRUE;
+                }
+        }
+
+ out:
+#if 0
+        g_debug ("is_drive_selectable (%s): %d %" G_GUINT64_FORMAT " (%d %d) ...",
+                 d != NULL ? gdu_device_get_device_file (d) : "(not set)",
+                 whole_disk_is_uninitialized,
+                 largest_segment,
+                 reason,
+                 ret);
+#endif
+
+        if (d != NULL)
+                g_object_unref (d);
+
+        if (out_reason != NULL)
+                *out_reason = reason;
+        return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 static gboolean
 drive_is_selected (GduDiskSelectionWidget *widget,
                    GduPresentable         *drive)
@@ -461,7 +520,7 @@ toggle_data_func (GtkCellLayout   *cell_layout,
         is_visible = FALSE;
         is_toggled = FALSE;
         if (GDU_IS_DRIVE (p)) {
-                is_visible = TRUE;
+                is_visible = is_drive_selectable (widget, GDU_DRIVE (p), NULL);
                 is_toggled = drive_is_selected (widget, p);
         }
 
@@ -534,6 +593,41 @@ on_disk_toggled (GtkCellRendererToggle *renderer,
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
+icon_data_func (GtkCellLayout   *cell_layout,
+                GtkCellRenderer *renderer,
+                GtkTreeModel    *tree_model,
+                GtkTreeIter     *iter,
+                gpointer         user_data)
+{
+        GduDiskSelectionWidget *widget = GDU_DISK_SELECTION_WIDGET (user_data);
+        GduPresentable *p;
+        GIcon *icon;
+        gboolean sensitive;
+
+        gtk_tree_model_get (tree_model,
+                            iter,
+                            GDU_POOL_TREE_MODEL_COLUMN_PRESENTABLE, &p,
+                            GDU_POOL_TREE_MODEL_COLUMN_ICON, &icon,
+                            -1);
+
+        sensitive = TRUE;
+        if (p != NULL && GDU_IS_DRIVE (p))
+                sensitive = is_drive_selectable (widget, GDU_DRIVE (p), NULL);
+
+        g_object_set (renderer,
+                      "gicon", icon,
+                      "sensitive", sensitive,
+                      NULL);
+
+        if (icon != NULL)
+                g_object_unref (icon);
+        if (p != NULL)
+                g_object_unref (p);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
 disk_name_data_func (GtkCellLayout   *cell_layout,
                      GtkCellRenderer *renderer,
                      GtkTreeModel    *tree_model,
@@ -549,14 +643,20 @@ disk_name_data_func (GtkCellLayout   *cell_layout,
         GdkColor desc_gdk_color = {0};
         gchar *desc_color;
         GtkStateType state;
+        gboolean sensitive;
+        GduPresentable *p;
 
         tree_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget->priv->tree_view));
 
         gtk_tree_model_get (tree_model,
                             iter,
+                            GDU_POOL_TREE_MODEL_COLUMN_PRESENTABLE, &p,
                             GDU_POOL_TREE_MODEL_COLUMN_NAME, &name,
                             GDU_POOL_TREE_MODEL_COLUMN_VPD_NAME, &vpd_name,
                             -1);
+        sensitive = TRUE;
+        if (p != NULL && GDU_IS_DRIVE (p))
+                sensitive = is_drive_selectable (widget, GDU_DRIVE (p), NULL);
 
         /* This color business shouldn't be this hard... */
         style = gtk_widget_get_style (GTK_WIDGET (widget->priv->tree_view));
@@ -581,42 +681,61 @@ disk_name_data_func (GtkCellLayout   *cell_layout,
                                       (desc_gdk_color.green >> 8),
                                       (desc_gdk_color.blue >> 8));
 
-        markup = g_strdup_printf ("<small><b>%s</b>\n"
-                                  "<span fgcolor=\"%s\">%s</span></small>",
-                                  name,
-                                  desc_color,
-                                  vpd_name);
+        if (sensitive) {
+                markup = g_strdup_printf ("<small><b>%s</b>\n"
+                                          "<span fgcolor=\"%s\">%s</span></small>",
+                                          name,
+                                          desc_color,
+                                          vpd_name);
+        } else {
+                markup = g_strdup_printf ("<small><b>%s</b>\n"
+                                          "%s</small>",
+                                          name,
+                                          vpd_name);
+        }
 
         g_object_set (renderer,
                       "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
                       "ellipsize-set", TRUE,
                       "markup", markup,
+                      "sensitive", sensitive,
                       NULL);
 
         g_free (name);
         g_free (vpd_name);
         g_free (markup);
         g_free (desc_color);
+
+        if (p != NULL)
+                g_object_unref (p);
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+typedef struct {
+        GduDiskSelectionWidget *widget;
+        guint num_available_disks;
+        guint64 largest_segment_for_all;
+} CountData;
+
 static gboolean
 count_num_available_disks_func (GtkTreeModel *model,
                                 GtkTreePath  *path,
                                 GtkTreeIter  *iter,
-                                gpointer      data)
+                                gpointer      user_data)
 {
         GduPresentable *p;
-        guint *num_available_disks = data;
+        CountData *data = user_data;
 
         gtk_tree_model_get (model,
                             iter,
                             GDU_POOL_TREE_MODEL_COLUMN_PRESENTABLE, &p,
                             -1);
 
-        if (GDU_IS_DRIVE (p))
-                *num_available_disks = *num_available_disks + 1;
+        if (GDU_IS_DRIVE (p)) {
+                if (is_drive_selectable (data->widget, GDU_DRIVE (p), NULL))
+                        data->num_available_disks = data->num_available_disks + 1;
+        }
 
         g_object_unref (p);
 
@@ -627,10 +746,10 @@ static gboolean
 find_largest_segment_for_all_func (GtkTreeModel *model,
                                    GtkTreePath  *path,
                                    GtkTreeIter  *iter,
-                                   gpointer      data)
+                                   gpointer      user_data)
 {
         GduPresentable *p;
-        guint64 *largest_segment_for_all = data;
+        CountData *data = user_data;
 
         gtk_tree_model_get (model,
                             iter,
@@ -640,14 +759,14 @@ find_largest_segment_for_all_func (GtkTreeModel *model,
         if (GDU_IS_DRIVE (p)) {
                 guint64 largest_segment;
 
-                g_warn_if_fail (gdu_drive_has_unallocated_space (GDU_DRIVE (p),
-                                                                 NULL,
-                                                                 &largest_segment,
-                                                                 NULL, /* total_free */
-                                                                 NULL));
-
-                if (largest_segment > *largest_segment_for_all)
-                        *largest_segment_for_all = largest_segment;
+                if (gdu_drive_can_create_volume (GDU_DRIVE (p),
+                                                 NULL,
+                                                 &largest_segment,
+                                                 NULL, /* total_free */
+                                                 NULL)) {
+                        if (largest_segment > data->largest_segment_for_all)
+                                data->largest_segment_for_all = largest_segment;
+                }
 
         }
 
@@ -664,54 +783,54 @@ get_sizes (GduDiskSelectionWidget *widget,
            guint64                *out_largest_segment_for_all)
 {
         guint num_disks;
-        guint num_available_disks;
+        CountData data;
         guint64 largest_segment_for_selected;
-        guint64 largest_segment_for_all;
         GList *l;
 
         num_disks = 0;
-        num_available_disks = 0;
         largest_segment_for_selected = G_MAXUINT64;
-        largest_segment_for_all = 0;
+        data.widget = widget;
+        data.num_available_disks = 0;
+        data.largest_segment_for_all = 0;
 
         for (l = widget->priv->selected_drives; l != NULL; l = l->next) {
                 GduPresentable *p = GDU_PRESENTABLE (l->data);
                 guint64 largest_segment;
                 gboolean whole_disk_is_uninitialized;
 
-                g_warn_if_fail (gdu_drive_has_unallocated_space (GDU_DRIVE (p),
-                                                                 &whole_disk_is_uninitialized,
-                                                                 &largest_segment,
-                                                                 NULL, /* total_free */
-                                                                 NULL));
+                if (gdu_drive_can_create_volume (GDU_DRIVE (p),
+                                                 &whole_disk_is_uninitialized,
+                                                 &largest_segment,
+                                                 NULL, /* total_free */
+                                                 NULL)) {
+                        if (largest_segment < largest_segment_for_selected)
+                                largest_segment_for_selected = largest_segment;
 
-                if (largest_segment < largest_segment_for_selected)
-                        largest_segment_for_selected = largest_segment;
-
-                num_disks++;
+                        num_disks++;
+                }
         }
         if (largest_segment_for_selected == G_MAXUINT64)
                 largest_segment_for_selected = 0;
 
         gtk_tree_model_foreach (widget->priv->model,
                                 count_num_available_disks_func,
-                                &num_available_disks);
+                                &data);
 
         gtk_tree_model_foreach (widget->priv->model,
                                 find_largest_segment_for_all_func,
-                                &largest_segment_for_all);
+                                &data);
 
         if (out_num_disks != NULL)
                 *out_num_disks = num_disks;
 
         if (out_num_available_disks != NULL)
-                *out_num_available_disks = num_available_disks;
+                *out_num_available_disks = data.num_available_disks;
 
         if (out_largest_segment_for_selected != NULL)
                 *out_largest_segment_for_selected = largest_segment_for_selected;
 
         if (out_largest_segment_for_all != NULL)
-                *out_largest_segment_for_all = largest_segment_for_all;
+                *out_largest_segment_for_all = data.largest_segment_for_all;
 }
 
 static void
@@ -725,149 +844,220 @@ notes_data_func (GtkCellLayout   *cell_layout,
         GduPresentable *p;
         gchar *markup;
         gchar *s;
+        gboolean sensitive;
+        GduDevice *d;
+        gboolean can_create_volume;
+        guint64 largest_segment;
+        gboolean whole_disk_is_uninitialized;
+        guint num_partitions;
+        gboolean is_partitionable;
+        gboolean is_partitioned;
+        guint64 total_free;
+        guint64 remaining_size;
+        gboolean is_selectable;
+        Reason reason;
+        gchar *strsize;
+        gchar *rem_strsize;
+
+        d = NULL;
+        markup = NULL;
+        sensitive = TRUE;
 
         gtk_tree_model_get (tree_model,
                             iter,
                             GDU_POOL_TREE_MODEL_COLUMN_PRESENTABLE, &p,
                             -1);
 
-        if (GDU_IS_DRIVE (p)) {
-                GduDevice *d;
-                guint64 largest_segment;
-                gboolean whole_disk_is_uninitialized;
-                guint num_partitions;
-                gboolean is_partitioned;
-                guint64 total_free;
-                guint64 remaining_size;
-
-                d = gdu_presentable_get_device (p);
-
-                num_partitions = 0;
-                is_partitioned = FALSE;
-                remaining_size = 0;
-                if (gdu_device_is_partition_table (d)) {
-                        is_partitioned = TRUE;
-                        num_partitions = gdu_device_partition_table_get_count (d);
-                }
+        if (!GDU_IS_DRIVE (p))
+                goto out;
 
-                g_warn_if_fail (gdu_drive_has_unallocated_space (GDU_DRIVE (p),
-                                                                 &whole_disk_is_uninitialized,
-                                                                 &largest_segment,
-                                                                 &total_free,
-                                                                 NULL));
+        d = gdu_presentable_get_device (p);
+
+        num_partitions = 0;
+        is_partitioned = FALSE;
+        is_partitionable = TRUE;
+        remaining_size = 0;
+        if (d != NULL && gdu_device_is_partition_table (d)) {
+                is_partitioned = TRUE;
+                num_partitions = gdu_device_partition_table_get_count (d);
+        }
 
-                remaining_size = total_free - widget->priv->component_size;
+        /* TODO: probably want a vfunc for this */
+        if (GDU_IS_LINUX_LVM2_VOLUME_GROUP (p))
+                is_partitionable = FALSE;
 
-                if (drive_is_selected (widget, p)) {
-                        gchar *strsize;
-                        gchar *rem_strsize;
+        can_create_volume = gdu_drive_can_create_volume (GDU_DRIVE (p),
+                                                         &whole_disk_is_uninitialized,
+                                                         &largest_segment,
+                                                         &total_free,
+                                                         NULL);
+
+        is_selectable = is_drive_selectable (widget, GDU_DRIVE (p), &reason);
+        sensitive = is_selectable;
+
+        remaining_size = total_free - widget->priv->component_size;
+
+        /* handle when the drive is not selectable */
+        if (!is_selectable) {
+                switch (reason) {
+                case REASON_INSUFFICIENT_SPACE:
+                        if (largest_segment < SIZE_EPSILON) {
+                                /* Translators: Shown when the device is not selectable because
+                                 * there is no free space.
+                                 */
+                                markup = g_strdup_printf (_("No free space."));
+                        } else {
+                                strsize = gdu_util_get_size_for_display (widget->priv->component_size, FALSE, FALSE);
+                                rem_strsize = gdu_util_get_size_for_display (largest_segment, FALSE, FALSE);
+                                /* Translators: Shown when device is unselectable because not enough space is available.
+                                 * First %s (e.g. '10 GB') is how much space is needed.
+                                 * Second %s (e.g. '5 GB') is how much space is available.
+                                 */
+                                markup = g_strdup_printf (_("Insufficient space: %s is needed but largest contiguous free block is %s."),
+                                                          strsize,
+                                                          rem_strsize);
+                                g_free (strsize);
+                                g_free (rem_strsize);
+                        }
+                        break;
+                case REASON_MULTIPATH_COMPONENT:
+                        /* Translators: Shown when the device is unselectable because it is a multipath component. */
+                        markup = g_strdup_printf (_("Cannot select multipath component"));
+                        break;
+                }
+                goto out;
+        }
 
-                        strsize = gdu_util_get_size_for_display (widget->priv->component_size, FALSE, FALSE);
-                        rem_strsize = gdu_util_get_size_for_display (remaining_size, FALSE, FALSE);
+        /* drive is selectable */
+        if (drive_is_selected (widget, p)) {
+                strsize = gdu_util_get_size_for_display (widget->priv->component_size, FALSE, FALSE);
+                rem_strsize = gdu_util_get_size_for_display (remaining_size, FALSE, FALSE);
 
-                        if (whole_disk_is_uninitialized) {
-                                if (widget->priv->component_size == 0) {
+                if (whole_disk_is_uninitialized) {
+                        if (widget->priv->component_size < SIZE_EPSILON) {
+                                /* Translators: This is shown in the details column */
+                                markup = g_strdup (_("The disk will be partitioned and a partition will be created"));
+                        } else {
+                                if (remaining_size < SIZE_EPSILON) {
+                                        /* Translators: This is shown in the Details column.
+                                         * First %s is the component size e.g. '42 GB'.
+                                         */
+                                        markup = g_strdup_printf (_("The disk will be partitioned and a %s partition "
+                                                                    "will be created. "
+                                                                    "Afterwards no space will be available."),
+                                                                  strsize);
+                                } else {
+                                        /* Translators: This is shown in the Details column.
+                                         * First %s is the component size e.g. '42 GB'.
+                                         * Second %s is the remaining free space after the operation.
+                                         */
+                                        markup = g_strdup_printf (_("The disk will be partitioned and a %s partition "
+                                                                    "will be created. "
+                                                                    "Afterwards %s will be available."),
+                                                                  strsize,
+                                                                  rem_strsize);
+                                }
+                        }
+                } else {
+                        if (widget->priv->component_size < SIZE_EPSILON) {
+                                if (is_partitionable) {
                                         /* Translators: This is shown in the details column */
-                                        markup = g_strdup (_("The disk will be partitioned and a partition will be created"));
+                                        markup = g_strdup (_("A partition will be created"));
                                 } else {
-                                        if (remaining_size == 0) {
+                                        /* Translators: This is shown in the details column */
+                                        markup = g_strdup (_("A volume will be created"));
+                                }
+                        } else {
+                                if (remaining_size < SIZE_EPSILON) {
+                                        if (is_partitionable) {
                                                 /* Translators: This is shown in the Details column.
                                                  * First %s is the component size e.g. '42 GB'.
                                                  */
-                                                markup = g_strdup_printf (_("The disk will be partitioned and a %s partition "
-                                                                            "will be created. "
+                                                markup = g_strdup_printf (_("A %s partition will be created. "
                                                                             "Afterwards no space will be available."),
                                                                           strsize);
                                         } else {
                                                 /* Translators: This is shown in the Details column.
                                                  * First %s is the component size e.g. '42 GB'.
-                                                 * Second %s is the remaining free space after the operation.
                                                  */
-                                                markup = g_strdup_printf (_("The disk will be partitioned and a %s partition "
-                                                                            "will be created. "
-                                                                            "Afterwards %s will be available."),
-                                                                          strsize,
-                                                                          rem_strsize);
+                                                markup = g_strdup_printf (_("A %s volume will be created. "
+                                                                            "Afterwards no space will be available."),
+                                                                          strsize);
                                         }
-                                }
-                        } else {
-                                if (widget->priv->component_size == 0) {
-                                        /* Translators: This is shown in the details column */
-                                        markup = g_strdup (_("A partition will be created"));
                                 } else {
-                                        if (remaining_size == 0) {
+                                        if (is_partitionable) {
                                                 /* Translators: This is shown in the Details column.
                                                  * First %s is the component size e.g. '42 GB'.
+                                                 * Second %s is the remaining free space after the operation.
                                                  */
                                                 markup = g_strdup_printf (_("A %s partition will be created. "
-                                                                            "Afterwards no space will be available."),
-                                                                          strsize);
+                                                                            "Afterwards %s will be available."),
+                                                                          strsize,
+                                                                          rem_strsize);
                                         } else {
                                                 /* Translators: This is shown in the Details column.
                                                  * First %s is the component size e.g. '42 GB'.
                                                  * Second %s is the remaining free space after the operation.
                                                  */
-                                                markup = g_strdup_printf (_("A %s partition will be created. "
+                                                markup = g_strdup_printf (_("A %s volume will be created. "
                                                                             "Afterwards %s will be available."),
                                                                           strsize,
                                                                           rem_strsize);
                                         }
                                 }
                         }
-                        g_free (strsize);
-                        g_free (rem_strsize);
-
-                } else {
-                        gchar *strsize;
+                }
+                g_free (strsize);
+                g_free (rem_strsize);
+        } else {
+                gchar *strsize;
 
-                        strsize = gdu_util_get_size_for_display (largest_segment, FALSE, FALSE);
+                /* Drive is not selected */
 
-                        if (whole_disk_is_uninitialized) {
+                strsize = gdu_util_get_size_for_display (largest_segment, FALSE, FALSE);
+                if (whole_disk_is_uninitialized) {
+                        /* Translators: This is shown in the Details column.
+                         * %s is the component size e.g. '42 GB'.
+                         */
+                        markup = g_strdup_printf (_("Whole disk is uninitialized. %s available for use"),
+                                                  strsize);
+                } else {
+                        if (!is_partitioned) {
                                 /* Translators: This is shown in the Details column.
                                  * %s is the component size e.g. '42 GB'.
                                  */
-                                markup = g_strdup_printf (_("Whole disk is uninitialized. %s available for use"),
-                                                          strsize);
+                                markup = g_strdup_printf (_("%s available for use"), strsize);
                         } else {
-                                if (!is_partitioned) {
+                                if (num_partitions == 0) {
                                         /* Translators: This is shown in the Details column.
                                          * %s is the component size e.g. '42 GB'.
                                          */
-                                        markup = g_strdup_printf (_("%s available for use"), strsize);
+                                        markup = g_strdup_printf (_("The disk has no partitions. "
+                                                                    "%s available for use"),
+                                                                  strsize);
                                 } else {
-                                        if (num_partitions == 0) {
-                                                /* Translators: This is shown in the Details column.
-                                                 * %s is the component size e.g. '42 GB'.
-                                                 */
-                                                markup = g_strdup_printf (_("The disk has no partitions. "
-                                                                            "%s available for use"),
-                                                                          strsize);
-                                        } else {
-                                                s = g_strdup_printf (dngettext (GETTEXT_PACKAGE,
-                                                                                "The disk has %d partition",
-                                                                                "The disk has %d partitions",
-                                                                                num_partitions),
-                                                                     num_partitions);
-                                                /* Translators: This is shown in the Details column.
-                                                 * First %s is the dngettext() result of "The disk has %d partitions.".
-                                                 * Second %s is the component size e.g. '42 GB'.
-                                                 */
-                                                markup = g_strdup_printf (_("%s. Largest contiguous free block is %s"),
-                                                                          s,
-                                                                          strsize);
-                                                g_free (s);
-                                        }
+                                        s = g_strdup_printf (dngettext (GETTEXT_PACKAGE,
+                                                                        "The disk has %d partition",
+                                                                        "The disk has %d partitions",
+                                                                        num_partitions),
+                                                             num_partitions);
+                                        /* Translators: This is shown in the Details column.
+                                         * First %s is the dngettext() result of "The disk has %d partitions.".
+                                         * Second %s is the component size e.g. '42 GB'.
+                                         */
+                                        markup = g_strdup_printf (_("%s. Largest contiguous free block is %s"),
+                                                                  s,
+                                                                  strsize);
+                                        g_free (s);
                                 }
                         }
-
-                        g_free (strsize);
                 }
-
-                g_object_unref (d);
-        } else {
-                markup = g_strdup ("");
+                g_free (strsize);
         }
 
+ out:
+        if (markup == NULL)
+                markup = g_strdup ("");
         s = g_strconcat ("<small>",
                          markup,
                          "</small>",
@@ -875,15 +1065,20 @@ notes_data_func (GtkCellLayout   *cell_layout,
         g_object_set (renderer,
                       "markup", s,
                       "wrap-width", DETAILS_WIDTH - DETAILS_MARGIN,
+                      "sensitive", sensitive,
                       NULL);
         g_free (s);
 
         g_free (markup);
         g_object_unref (p);
+
+        if (d != NULL)
+                g_object_unref (d);
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+#if 0
 static gboolean
 model_visible_func (GtkTreeModel  *model,
                     GtkTreeIter   *iter,
@@ -903,21 +1098,9 @@ model_visible_func (GtkTreeModel  *model,
                                     iter,
                                     GDU_POOL_TREE_MODEL_COLUMN_PRESENTABLE, &p,
                                     -1);
-
                 if (p != NULL) {
                         if (GDU_IS_DRIVE (p)) {
-                                guint64 largest_segment;
-                                gboolean whole_disk_is_uninitialized;
-
-                                if (gdu_drive_has_unallocated_space (GDU_DRIVE (p),
-                                                                     &whole_disk_is_uninitialized,
-                                                                     &largest_segment,
-                                                                     NULL, /* total_free */
-                                                                     NULL)) {
-                                        if (largest_segment >= widget->priv->component_size) {
-                                                ret = TRUE;
-                                        }
-                                }
+                                ret = is_drive_selectable (widget, GDU_DRIVE (p));
                         } else {
                                 ret = TRUE;
                         }
@@ -927,7 +1110,7 @@ model_visible_func (GtkTreeModel  *model,
 
         return ret;
 }
-
+#endif
 
 /* ---------------------------------------------------------------------------------------------------- */
 
@@ -946,16 +1129,17 @@ gdu_disk_selection_widget_constructed (GObject *object)
         g_ptr_array_add (pools, widget->priv->pool);
         model = GTK_TREE_MODEL (gdu_pool_tree_model_new (pools,
                                                          NULL,
-                                                         GDU_POOL_TREE_MODEL_FLAGS_NO_VOLUMES |
-                                                         GDU_POOL_TREE_MODEL_FLAGS_NO_UNALLOCATABLE_DRIVES));
+                                                         GDU_POOL_TREE_MODEL_FLAGS_NO_VOLUMES /* | GDU_POOL_TREE_MODEL_FLAGS_NO_UNALLOCATABLE_DRIVES */));
         g_ptr_array_unref (pools);
 
         widget->priv->model = gtk_tree_model_filter_new (model, NULL);
         g_object_unref (model);
+#if 0
         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (widget->priv->model),
                                                 model_visible_func,
                                                 widget,
                                                 NULL);
+#endif
 
         tree_view = gtk_tree_view_new_with_model (widget->priv->model);
         gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (tree_view), TRUE);
@@ -984,10 +1168,11 @@ gdu_disk_selection_widget_constructed (GObject *object)
 
         renderer = gtk_cell_renderer_pixbuf_new ();
         gtk_tree_view_column_pack_start (column, renderer, FALSE);
-        gtk_tree_view_column_set_attributes (column,
-                                             renderer,
-                                             "gicon", GDU_POOL_TREE_MODEL_COLUMN_ICON,
-                                             NULL);
+        gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column),
+                                            renderer,
+                                            icon_data_func,
+                                            widget,
+                                            NULL);
         g_object_set (renderer,
                       "stock-size", GTK_ICON_SIZE_MENU,
                       NULL);
@@ -1156,10 +1341,11 @@ gdu_disk_selection_widget_get_num_available_disks (GduDiskSelectionWidget *widge
 {
         guint num_available_disks;
 
-        num_available_disks = 0;
-        gtk_tree_model_foreach (widget->priv->model,
-                                count_num_available_disks_func,
-                                &num_available_disks);
+        get_sizes (widget,
+                   NULL,
+                   &num_available_disks,
+                   NULL,
+                   NULL);
 
         return num_available_disks;
 }
diff --git a/src/gdu-gtk/gdu-pool-tree-model.c b/src/gdu-gtk/gdu-pool-tree-model.c
index ae76e84..d7453f2 100644
--- a/src/gdu-gtk/gdu-pool-tree-model.c
+++ b/src/gdu-gtk/gdu-pool-tree-model.c
@@ -495,7 +495,7 @@ should_include_presentable (GduPoolTreeModel *model,
 
         if (GDU_IS_DRIVE (presentable)) {
                 if ((model->priv->flags & GDU_POOL_TREE_MODEL_FLAGS_NO_UNALLOCATABLE_DRIVES) &&
-                    (!gdu_drive_has_unallocated_space (GDU_DRIVE (presentable), NULL, NULL, NULL, NULL)))
+                    (!gdu_drive_can_create_volume (GDU_DRIVE (presentable), NULL, NULL, NULL, NULL)))
                         goto out;
         }
 
diff --git a/src/gdu/gdu-drive.c b/src/gdu/gdu-drive.c
index a841a6c..ad56f61 100644
--- a/src/gdu/gdu-drive.c
+++ b/src/gdu/gdu-drive.c
@@ -68,11 +68,22 @@ G_DEFINE_TYPE_WITH_CODE (GduDrive, gdu_drive, G_TYPE_OBJECT,
 static void device_job_changed (GduDevice *device, gpointer user_data);
 static void device_changed (GduDevice *device, gpointer user_data);
 
-static gboolean gdu_drive_has_unallocated_space_real (GduDrive        *drive,
-                                                      gboolean        *out_whole_disk_is_unitialized,
-                                                      guint64         *out_largest_segment,
-                                                      guint64         *out_total_free,
-                                                      GduPresentable **out_presentable);
+static gboolean gdu_drive_can_create_volume_real (GduDrive        *drive,
+                                                  gboolean        *out_is_uninitialized,
+                                                  guint64         *out_largest_contiguous_free_segment,
+                                                  guint64         *out_total_free,
+                                                  GduPresentable **out_presentable);
+
+static void gdu_drive_create_volume_real (GduDrive              *drive,
+                                          guint64                size,
+                                          const gchar           *name,
+                                          GduCreateVolumeFlags   flags,
+                                          GAsyncReadyCallback    callback,
+                                          gpointer               user_data);
+
+static GduVolume *gdu_drive_create_volume_finish_real (GduDrive              *drive,
+                                                       GAsyncResult          *res,
+                                                       GError               **error);
 
 static void
 gdu_drive_finalize (GduDrive *drive)
@@ -106,7 +117,9 @@ gdu_drive_class_init (GduDriveClass *klass)
 
         obj_class->finalize = (GObjectFinalizeFunc) gdu_drive_finalize;
 
-        klass->has_unallocated_space = gdu_drive_has_unallocated_space_real;
+        klass->can_create_volume = gdu_drive_can_create_volume_real;
+        klass->create_volume = gdu_drive_create_volume_real;
+        klass->create_volume_finish = gdu_drive_create_volume_finish_real;
 
         g_type_class_add_private (klass, sizeof (GduDrivePrivate));
 }
@@ -200,26 +213,25 @@ gdu_drive_deactivate (GduDrive                *drive,
 }
 
 /**
- * gdu_drive_has_unallocated_space:
+ * gdu_drive_can_create_volume:
  * @drive: A #GduDrive.
- * @out_whole_disk_is_unitialized: Return location for whether @drive is uninitialized or %NULL.
+ * @out_is_uninitialized: Return location for whether @drive is uninitialized or %NULL.
  * @out_largest_segment: Return location for biggest contigious free block of @drive or %NULL.
  * @out_total_free: Return location for total amount of free space on @drive or %NULL.
  * @out_presentable: Return location for the presentable that represents free space or %NULL. Free
  * with g_object_unref().
  *
- * This method computes the largest contigious free block of
- * unallocated space on @drive.
+ * This method checks if a new volume can be created on @drive.
  *
  * If @drive uses removable media and there is no media inserted,
  * %FALSE is returned.
  *
  * If @drive appears to be completely uninitialized (such as a hard
- * disk full of zeros), @out_whole_disk_is_unitialized is set to
- * %TRUE, the size of the media/disk is returned in
- * @out_largest_segment and %TRUE is returned. Note that this can
- * also happen if @drive contains signatures unknown to the operating system
- * so be careful.
+ * disk full of zeros), @out_is_unitialized is set to %TRUE, the size
+ * of the media/disk is returned in @out_largest_segment and %TRUE is
+ * returned. Note that this can also happen if @drive contains
+ * signatures unknown to the operating system so be careful and always
+ * prompt the user.
  *
  * If the disk is partitioned and unallocated space exists but no more
  * partitions can be created (due to e.g. four primary partitions on a
@@ -230,22 +242,23 @@ gdu_drive_deactivate (GduDrive                *drive,
  * #GduVolumeHole (if the disk is partitioned and has free space) or
  * the #GduDrive (if the disk is uninitialized).
  *
+ * You can use gdu_drive_create_volume() to create a volume.
+ *
  * Returns: %TRUE if @drive has unallocated space, %FALSE otherwise.
  */
 gboolean
-gdu_drive_has_unallocated_space (GduDrive        *drive,
-                                 gboolean        *out_whole_disk_is_unitialized,
-                                 guint64         *out_largest_segment,
-                                 guint64         *out_total_free,
-                                 GduPresentable **out_presentable)
+gdu_drive_can_create_volume (GduDrive        *drive,
+                             gboolean        *out_is_uninitialized,
+                             guint64         *out_largest_contiguous_free_segment,
+                             guint64         *out_total_free,
+                             GduPresentable **out_presentable)
 {
         GduDriveClass *klass = GDU_DRIVE_GET_CLASS (drive);
-
-        return klass->has_unallocated_space (drive,
-                                             out_whole_disk_is_unitialized,
-                                             out_largest_segment,
-                                             out_total_free,
-                                             out_presentable);
+        return klass->can_create_volume (drive,
+                                         out_is_uninitialized,
+                                         out_largest_contiguous_free_segment,
+                                         out_total_free,
+                                         out_presentable);
 }
 
 /**
@@ -326,15 +339,15 @@ gdu_drive_count_mbr_partitions (GduDrive  *drive,
 
 
 static gboolean
-gdu_drive_has_unallocated_space_real (GduDrive        *drive,
-                                      gboolean        *out_whole_disk_is_unitialized,
-                                      guint64         *out_largest_segment,
-                                      guint64         *out_total_free,
-                                      GduPresentable **out_presentable)
+gdu_drive_can_create_volume_real (GduDrive        *drive,
+                                  gboolean        *out_is_unitialized,
+                                  guint64         *out_largest_contiguous_free_segment,
+                                  guint64         *out_total_free,
+                                  GduPresentable **out_presentable)
 {
         GduDevice *device;
         GduPool *pool;
-        guint64 largest_segment;
+        guint64 largest_contiguous_free_segment;
         guint64 total_free;
         gboolean whole_disk_uninitialized;
         GList *enclosed_presentables;
@@ -344,7 +357,7 @@ gdu_drive_has_unallocated_space_real (GduDrive        *drive,
         guint64 size;
         GduPresentable *pres;
 
-        largest_segment = 0;
+        largest_contiguous_free_segment = 0;
         total_free = 0;
         whole_disk_uninitialized = FALSE;
         ret = FALSE;
@@ -371,7 +384,7 @@ gdu_drive_has_unallocated_space_real (GduDrive        *drive,
          */
         if (!gdu_device_is_partition_table (device) && strlen (gdu_device_id_get_usage (device)) == 0) {
                 whole_disk_uninitialized = TRUE;
-                largest_segment = gdu_device_get_size (device);
+                largest_contiguous_free_segment = gdu_device_get_size (device);
                 total_free = gdu_device_get_size (device);
                 ret = TRUE;
                 pres = GDU_PRESENTABLE (drive);
@@ -389,8 +402,8 @@ gdu_drive_has_unallocated_space_real (GduDrive        *drive,
                 if (GDU_IS_VOLUME_HOLE (ep)) {
                         size = gdu_presentable_get_size (ep);
 
-                        if (size > largest_segment) {
-                                largest_segment = size;
+                        if (size > largest_contiguous_free_segment) {
+                                largest_contiguous_free_segment = size;
                                 pres = ep;
                         }
 
@@ -416,8 +429,8 @@ gdu_drive_has_unallocated_space_real (GduDrive        *drive,
 
                                         if (GDU_IS_VOLUME_HOLE (lep)) {
                                                 size = gdu_presentable_get_size (lep);
-                                                if (size > largest_segment) {
-                                                        largest_segment = size;
+                                                if (size > largest_contiguous_free_segment) {
+                                                        largest_contiguous_free_segment = size;
                                                         pres = lep;
                                                 }
 
@@ -433,10 +446,10 @@ gdu_drive_has_unallocated_space_real (GduDrive        *drive,
         g_list_foreach (enclosed_presentables, (GFunc) g_object_unref, NULL);
         g_list_free (enclosed_presentables);
 
-        ret = (largest_segment > 0);
+        ret = (largest_contiguous_free_segment > 0);
 
         /* Now igure out if the partition table is full (e.g. four primary partitions already) and
-         * return %FALSE and non-zero @out_largest_segment
+         * return %FALSE and non-zero @out_largest_contiguous_free_segment
          */
         if (g_strcmp0 (gdu_device_partition_table_get_scheme (device), "mbr") == 0 &&
             gdu_device_partition_table_get_count (device) == 4 &&
@@ -450,14 +463,14 @@ gdu_drive_has_unallocated_space_real (GduDrive        *drive,
         if (pool != NULL)
                 g_object_unref (pool);
 
-        if (out_largest_segment != NULL)
-                *out_largest_segment = largest_segment;
+        if (out_largest_contiguous_free_segment != NULL)
+                *out_largest_contiguous_free_segment = largest_contiguous_free_segment;
 
         if (out_total_free != NULL)
                 *out_total_free = total_free;
 
-        if (out_whole_disk_is_unitialized != NULL)
-                *out_whole_disk_is_unitialized = whole_disk_uninitialized;
+        if (out_is_unitialized != NULL)
+                *out_is_unitialized = whole_disk_uninitialized;
 
         if (out_presentable != NULL) {
                 *out_presentable = (pres != NULL ? g_object_ref (pres) : NULL);
@@ -1094,3 +1107,309 @@ _gdu_drive_rewrite_enclosing_presentable (GduDrive *drive)
  out:
         ;
 }
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+create_volume_partition_create_cb (GduDevice  *device,
+                                   gchar      *created_device_object_path,
+                                   GError     *error,
+                                   gpointer    user_data)
+{
+        GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+        if (error != NULL) {
+                g_simple_async_result_set_from_error (simple, error);
+                g_error_free (error);
+        } else {
+                GduDevice *d;
+                GduPool *pool;
+                GduPresentable *volume;
+
+                pool = gdu_device_get_pool (device);
+                d = gdu_pool_get_by_object_path (pool, created_device_object_path);
+                g_assert (d != NULL);
+
+                volume = gdu_pool_get_volume_by_device (pool, d);
+                g_assert (volume != NULL);
+
+                g_simple_async_result_set_op_res_gpointer (simple, volume, g_object_unref);
+
+                g_object_unref (pool);
+                g_object_unref (d);
+        }
+        g_simple_async_result_complete_in_idle (simple);
+}
+
+static void
+gdu_drive_create_volume_real_internal (GduDrive              *drive,
+                                       guint64                size,
+                                       const gchar           *name,
+                                       GduCreateVolumeFlags   flags,
+                                       GSimpleAsyncResult    *simple);
+
+static void
+create_volume_partition_table_create_cb (GduDevice  *device,
+                                         GError     *error,
+                                         gpointer    user_data)
+{
+        GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+        if (error != NULL) {
+                g_simple_async_result_set_from_error (simple, error);
+                g_simple_async_result_complete_in_idle (simple);
+                g_error_free (error);
+        } else {
+                GduDrive              *drive;
+                guint64                size;
+                const gchar           *name;
+                GduCreateVolumeFlags   flags;
+
+                drive = GDU_DRIVE (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
+                size = (* ((guint64 *) g_object_get_data (G_OBJECT (simple), "gdu-size")));
+                name = g_object_get_data (G_OBJECT (simple), "gdu-name");
+                flags = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (simple), "gdu-flags"));
+
+                /* now that we have a partition table... try creating the volume again */
+                gdu_drive_create_volume_real_internal (drive,
+                                                       size,
+                                                       name,
+                                                       flags,
+                                                       simple);
+
+                g_object_unref (drive);
+        }
+}
+
+static void
+gdu_drive_create_volume_real (GduDrive              *drive,
+                              guint64                size,
+                              const gchar           *name,
+                              GduCreateVolumeFlags   flags,
+                              GAsyncReadyCallback    callback,
+                              gpointer               user_data)
+{
+        GSimpleAsyncResult *simple;
+
+        g_return_if_fail (GDU_IS_DRIVE (drive));
+
+        simple = g_simple_async_result_new (G_OBJECT (drive),
+                                            callback,
+                                            user_data,
+                                            gdu_drive_create_volume);
+
+        g_object_set_data_full (G_OBJECT (simple), "gdu-size", g_memdup (&size, sizeof (guint64)), g_free);
+        g_object_set_data_full (G_OBJECT (simple), "gdu-name", g_strdup (name), g_free);
+        g_object_set_data (G_OBJECT (simple), "gdu-flags", GINT_TO_POINTER (flags));
+
+        gdu_drive_create_volume_real_internal (drive,
+                                               size,
+                                               name,
+                                               flags,
+                                               simple);
+}
+
+static void
+gdu_drive_create_volume_real_internal (GduDrive              *drive,
+                                       guint64                size,
+                                       const gchar           *name,
+                                       GduCreateVolumeFlags   flags,
+                                       GSimpleAsyncResult    *simple)
+{
+        GduPresentable *p;
+        GduDevice *d;
+        gboolean whole_disk_is_uninitialized;
+        guint64 largest_segment;
+
+        if (!gdu_drive_can_create_volume (drive,
+                                          &whole_disk_is_uninitialized,
+                                          &largest_segment,
+                                          NULL, /* total_free */
+                                          &p)) {
+                g_simple_async_result_set_error (simple,
+                                                 GDU_ERROR,
+                                                 GDU_ERROR_FAILED,
+                                                 "Insufficient space");
+                g_simple_async_result_complete_in_idle (simple);
+                g_object_unref (simple);
+                goto out;
+        }
+
+        g_assert (p != NULL);
+
+        d = gdu_presentable_get_device (GDU_PRESENTABLE (drive));
+
+        if (GDU_IS_VOLUME_HOLE (p)) {
+                guint64 offset;
+                const gchar *scheme;
+                const gchar *type;
+                gchar *label;
+
+                offset = gdu_presentable_get_offset (p);
+
+                scheme = gdu_device_partition_table_get_scheme (d);
+                type = "";
+                label = NULL;
+                if (g_strcmp0 (scheme, "mbr") == 0) {
+                        if (flags & GDU_CREATE_VOLUME_FLAGS_LINUX_MD) {
+                                type = "0xfd";
+                        } else if (flags & GDU_CREATE_VOLUME_FLAGS_LINUX_LVM2) {
+                                type = "0x8e";
+                        }
+                } else if (g_strcmp0 (scheme, "gpt") == 0) {
+                        if (flags & GDU_CREATE_VOLUME_FLAGS_LINUX_MD) {
+                                type = "A19D880F-05FC-4D3B-A006-743F0F84911E";
+                                /* Limited to 36 UTF-16LE characters according to on-disk format..
+                                 * Since a RAID array name is limited to 32 chars this should fit */
+                                if (name != NULL)
+                                        label = g_strdup_printf ("RAID: %s", name);
+                                else
+                                        label = g_strdup_printf ("RAID component");
+                        } else if (flags & GDU_CREATE_VOLUME_FLAGS_LINUX_LVM2) {
+                                type = "E6D6D379-F507-44C2-A23C-238F2A3DF928";
+                                /* Limited to 36 UTF-16LE characters according to on-disk format..
+                                 * TODO: ensure name is shorter than or equal to 32 characters */
+                                if (name != NULL)
+                                        label = g_strdup_printf ("LVM2: %s", name);
+                                else
+                                        label = g_strdup_printf ("LVM2 component");
+                        }
+                } else if (g_strcmp0 (scheme, "apt") == 0) {
+                        type = "Apple_Unix_SVR2";
+                        if (flags & GDU_CREATE_VOLUME_FLAGS_LINUX_MD) {
+                                if (name != NULL)
+                                        label = g_strdup_printf ("RAID: %s", name);
+                                else
+                                        label = g_strdup_printf ("RAID component");
+                        } else if (flags & GDU_CREATE_VOLUME_FLAGS_LINUX_LVM2) {
+                                if (name != NULL)
+                                        label = g_strdup_printf ("LVM2: %s", name);
+                                else
+                                        label = g_strdup_printf ("LVM2 component");
+                        }
+                }
+
+                gdu_device_op_partition_create (d,
+                                                offset,
+                                                size,
+                                                type,
+                                                label != NULL ? label : "",
+                                                NULL,
+                                                "",
+                                                "",
+                                                "",
+                                                FALSE,
+                                                create_volume_partition_create_cb,
+                                                simple);
+                g_free (label);
+
+        } else {
+
+                /* otherwise the whole disk must be uninitialized... */
+                g_assert (whole_disk_is_uninitialized);
+
+                /* so create a partition table...
+                 * (TODO: take a flag to determine what kind of partition table to create)
+                 */
+                gdu_device_op_partition_table_create (d,
+                                                      "mbr",
+                                                      create_volume_partition_table_create_cb,
+                                                      simple);
+        }
+
+ out:
+        if (d != NULL)
+                g_object_unref (d);
+        if (p != NULL)
+                g_object_unref (p);
+}
+
+static GduVolume *
+gdu_drive_create_volume_finish_real (GduDrive              *drive,
+                                     GAsyncResult          *res,
+                                     GError               **error)
+{
+        GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+        GduVolume *ret;
+
+        g_return_val_if_fail (GDU_IS_DRIVE (drive), NULL);
+        g_return_val_if_fail (res != NULL, NULL);
+
+        g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == gdu_drive_create_volume);
+
+        ret = NULL;
+        if (g_simple_async_result_propagate_error (simple, error))
+                goto out;
+
+        ret = GDU_VOLUME (g_simple_async_result_get_op_res_gpointer (simple));
+
+ out:
+        return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * gdu_drive_create_volume:
+ * @drive: A #GduDrive.
+ * @size: The size of the volume to create.
+ * @name: A name for the volume.
+ * @flags: Flags describing what kind of volume to create
+ * @callback: Function to call when the result is ready.
+ * @user_data: User data to pass to @callback.
+ *
+ * High-level method for creating a new volume on @drive of size @size
+ * using @name and @flags as influential hints.
+ *
+ * Depending on the actual type of @drive, different things may happen
+ * - if @drive represents a partitioned drive, then a new partition
+ * will be created (and if the partitioning scheme supports partition
+ * labels @name will be used as the label). If @drive is completely
+ * uninitialized, it may (or may not) be partitioned.
+ *
+ * If @drive represents a LVM2 volume group, a logical volume may be
+ * created (with @name being used as LV name).
+ *
+ * This is an asynchronous operation. When the result of the operation
+ * is ready, @callback will be invoked.
+ */
+void
+gdu_drive_create_volume (GduDrive              *drive,
+                         guint64                size,
+                         const gchar           *name,
+                         GduCreateVolumeFlags   flags,
+                         GAsyncReadyCallback    callback,
+                         gpointer               user_data)
+{
+        GduDriveClass *klass = GDU_DRIVE_GET_CLASS (drive);
+        klass->create_volume (drive,
+                              size,
+                              name,
+                              flags,
+                              callback,
+                              user_data);
+}
+
+/**
+ * gdu_drive_create_volume_finish:
+ * @drive: A #GduDrive.
+ * @res: A #GAsyncResult.
+ * @error: A #GError or %NULL.
+ *
+ * Finishes an operation started with gdu_drive_create_volume().
+ *
+ * Returns: A #GduVolume for the created volume or %NULL if @error is
+ * set. The returned object must be freed with g_object_unref().
+ */
+GduVolume *
+gdu_drive_create_volume_finish (GduDrive              *drive,
+                                GAsyncResult          *res,
+                                GError               **error)
+{
+        GduDriveClass *klass = GDU_DRIVE_GET_CLASS (drive);
+        return klass->create_volume_finish (drive,
+                                            res,
+                                            error);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
diff --git a/src/gdu/gdu-drive.h b/src/gdu/gdu-drive.h
index e0d7690..5559fb9 100644
--- a/src/gdu/gdu-drive.h
+++ b/src/gdu/gdu-drive.h
@@ -40,6 +40,21 @@ G_BEGIN_DECLS
 typedef struct _GduDriveClass    GduDriveClass;
 typedef struct _GduDrivePrivate  GduDrivePrivate;
 
+/* TODO: move to gdu-enums.h */
+/**
+ * GduCreateVolumeFlags:
+ * @GDU_CREATE_VOLUME_FLAGS_NONE: No flags are set.
+ * @GDU_CREATE_VOLUME_FLAGS_LINUX_MD: The volume is to be used for Linux MD RAID.
+ * @GDU_CREATE_VOLUME_FLAGS_LINUX_LVM2: The volume is to be used for Linux LVM2.
+ *
+ * Flags used in gdu_drive_create_volume().
+ */
+typedef enum {
+        GDU_CREATE_VOLUME_FLAGS_NONE = 0x00,
+        GDU_CREATE_VOLUME_FLAGS_LINUX_MD = (1<<0),
+        GDU_CREATE_VOLUME_FLAGS_LINUX_LVM2 = (1<<1)
+} GduCreateVolumeFlags;
+
 struct _GduDrive
 {
         GObject parent;
@@ -66,12 +81,22 @@ struct _GduDriveClass
                                               GduDriveDeactivateFunc callback,
                                               gpointer               user_data);
 
-        gboolean   (*has_unallocated_space) (GduDrive        *drive,
-                                             gboolean        *out_whole_disk_is_unitialized,
-                                             guint64         *out_largest_segment,
+        gboolean   (*can_create_volume)     (GduDrive        *drive,
+                                             gboolean        *out_is_uninitialized,
+                                             guint64         *out_largest_contiguous_free_segment,
                                              guint64         *out_total_free,
                                              GduPresentable **out_presentable);
 
+        void       (*create_volume)         (GduDrive              *drive,
+                                             guint64                size,
+                                             const gchar           *name,
+                                             GduCreateVolumeFlags   flags,
+                                             GAsyncReadyCallback    callback,
+                                             gpointer               user_data);
+
+        GduVolume *(*create_volume_finish) (GduDrive              *drive,
+                                            GAsyncResult          *res,
+                                            GError               **error);
 };
 
 GType       gdu_drive_get_type           (void);
@@ -88,12 +113,24 @@ void        gdu_drive_deactivate            (GduDrive              *drive,
                                              GduDriveDeactivateFunc callback,
                                              gpointer               user_data);
 
-gboolean    gdu_drive_has_unallocated_space (GduDrive        *drive,
-                                             gboolean        *out_whole_disk_is_unitialized,
-                                             guint64         *out_largest_segment,
+
+gboolean    gdu_drive_can_create_volume     (GduDrive        *drive,
+                                             gboolean        *out_is_uninitialized,
+                                             guint64         *out_largest_contiguous_free_segment,
                                              guint64         *out_total_free,
                                              GduPresentable **out_presentable);
 
+void        gdu_drive_create_volume         (GduDrive              *drive,
+                                             guint64                size,
+                                             const gchar           *name,
+                                             GduCreateVolumeFlags   flags,
+                                             GAsyncReadyCallback    callback,
+                                             gpointer               user_data);
+
+GduVolume  *gdu_drive_create_volume_finish  (GduDrive              *drive,
+                                             GAsyncResult          *res,
+                                             GError               **error);
+
 gboolean    gdu_drive_count_mbr_partitions  (GduDrive        *drive,
                                              guint           *out_num_primary_partitions,
                                              gboolean        *out_has_extended_partition);
diff --git a/src/gdu/gdu-linux-lvm2-volume-group.c b/src/gdu/gdu-linux-lvm2-volume-group.c
index 128e8a9..fe830a0 100644
--- a/src/gdu/gdu-linux-lvm2-volume-group.c
+++ b/src/gdu/gdu-linux-lvm2-volume-group.c
@@ -31,6 +31,7 @@
 #include "gdu-linux-lvm2-volume-group.h"
 #include "gdu-linux-lvm2-volume.h"
 #include "gdu-presentable.h"
+#include "gdu-volume-hole.h"
 
 struct _GduLinuxLvm2VolumeGroupPrivate
 {
@@ -59,6 +60,23 @@ static gboolean gdu_linux_lvm2_volume_group_is_activatable (GduDrive *drive);
 static gboolean gdu_linux_lvm2_volume_group_can_deactivate (GduDrive *drive);
 static gboolean gdu_linux_lvm2_volume_group_can_activate (GduDrive *drive, gboolean *out_degraded);
 
+static gboolean gdu_linux_lvm2_volume_group_can_create_volume (GduDrive        *drive,
+                                                               gboolean        *out_is_uninitialized,
+                                                               guint64         *out_largest_contiguous_free_segment,
+                                                               guint64         *out_total_free,
+                                                               GduPresentable **out_presentable);
+
+static void gdu_linux_lvm2_volume_group_create_volume (GduDrive              *drive,
+                                                       guint64                size,
+                                                       const gchar           *name,
+                                                       GduCreateVolumeFlags   flags,
+                                                       GAsyncReadyCallback    callback,
+                                                       gpointer               user_data);
+
+static GduVolume *gdu_linux_lvm2_volume_group_create_volume_finish (GduDrive              *drive,
+                                                                    GAsyncResult          *res,
+                                                                    GError               **error);
+
 static void on_device_added (GduPool *pool, GduDevice *device, gpointer user_data);
 static void on_device_removed (GduPool *pool, GduDevice *device, gpointer user_data);
 static void on_device_changed (GduPool *pool, GduDevice *device, gpointer user_data);
@@ -112,6 +130,9 @@ gdu_linux_lvm2_volume_group_class_init (GduLinuxLvm2VolumeGroupClass *klass)
         drive_class->is_activatable        = gdu_linux_lvm2_volume_group_is_activatable;
         drive_class->can_deactivate        = gdu_linux_lvm2_volume_group_can_deactivate;
         drive_class->can_activate          = gdu_linux_lvm2_volume_group_can_activate;
+        drive_class->can_create_volume     = gdu_linux_lvm2_volume_group_can_create_volume;
+        drive_class->create_volume         = gdu_linux_lvm2_volume_group_create_volume;
+        drive_class->create_volume_finish  = gdu_linux_lvm2_volume_group_create_volume_finish;
 }
 
 static void
@@ -528,6 +549,157 @@ gdu_linux_lvm2_volume_group_can_activate (GduDrive *drive,
         return FALSE;
 }
 
+static gboolean
+gdu_linux_lvm2_volume_group_can_create_volume (GduDrive        *drive,
+                                               gboolean        *out_is_uninitialized,
+                                               guint64         *out_largest_contiguous_free_segment,
+                                               guint64         *out_total_free,
+                                               GduPresentable **out_presentable)
+{
+        GList *enclosed_presentables;
+        GList *l;
+        guint64 largest_contiguous_free_segment;
+        guint64 total_free;
+        GduPresentable *pres;
+        gboolean ret;
+        GduPool *pool;
+
+        largest_contiguous_free_segment = 0;
+        total_free = 0;
+        pres = NULL;
+        ret = FALSE;
+
+        pool = gdu_presentable_get_pool (GDU_PRESENTABLE (drive));
+
+        enclosed_presentables = gdu_pool_get_enclosed_presentables (pool, GDU_PRESENTABLE (drive));
+        for (l = enclosed_presentables; l != NULL; l = l->next) {
+                GduPresentable *ep = GDU_PRESENTABLE (l->data);
+
+                if (GDU_IS_VOLUME_HOLE (ep)) {
+                        guint64 size;
+
+                        size = gdu_presentable_get_size (ep);
+
+                        if (size > largest_contiguous_free_segment) {
+                                largest_contiguous_free_segment = size;
+                                pres = ep;
+                        }
+
+                        total_free += size;
+
+                }
+        }
+        g_list_foreach (enclosed_presentables, (GFunc) g_object_unref, NULL);
+        g_list_free (enclosed_presentables);
+
+        if (out_largest_contiguous_free_segment != NULL)
+                *out_largest_contiguous_free_segment = largest_contiguous_free_segment;
+
+        if (out_total_free != NULL)
+                *out_total_free = total_free;
+
+        if (out_is_uninitialized != NULL)
+                *out_is_uninitialized = FALSE;
+
+        if (out_presentable != NULL) {
+                *out_presentable = (pres != NULL ? g_object_ref (pres) : NULL);
+        }
+
+        ret = (largest_contiguous_free_segment > 0);
+
+        g_object_unref (pool);
+
+        return ret;
+}
+
+static void
+lvm2_lv_create_op_callback (GduPool    *pool,
+                            gchar      *created_device_object_path,
+                            GError     *error,
+                            gpointer    user_data)
+{
+        GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+        if (error != NULL) {
+                g_simple_async_result_set_from_error (simple, error);
+                g_error_free (error);
+        } else {
+                GduDevice *d;
+                GduPresentable *volume;
+
+                d = gdu_pool_get_by_object_path (pool, created_device_object_path);
+                g_assert (d != NULL);
+
+                volume = gdu_pool_get_volume_by_device (pool, d);
+                g_assert (volume != NULL);
+
+                g_simple_async_result_set_op_res_gpointer (simple, volume, g_object_unref);
+
+                g_object_unref (d);
+        }
+        g_simple_async_result_complete_in_idle (simple);
+}
+
+static void
+gdu_linux_lvm2_volume_group_create_volume (GduDrive              *drive,
+                                           guint64                size,
+                                           const gchar           *name,
+                                           GduCreateVolumeFlags   flags,
+                                           GAsyncReadyCallback    callback,
+                                           gpointer               user_data)
+{
+        GduLinuxLvm2VolumeGroup *vg = GDU_LINUX_LVM2_VOLUME_GROUP (drive);
+        GSimpleAsyncResult *simple;
+        gchar *volume_name;
+
+        g_return_if_fail (GDU_IS_DRIVE (drive));
+
+        simple = g_simple_async_result_new (G_OBJECT (drive),
+                                            callback,
+                                            user_data,
+                                            gdu_linux_lvm2_volume_group_create_volume);
+
+        /* For now, just use a generic LV name - TODO: include RAID/LVM etc */
+        volume_name = gdu_linux_lvm2_volume_group_get_compute_new_lv_name (vg);
+
+        gdu_pool_op_linux_lvm2_lv_create (vg->priv->pool,
+                                          vg->priv->uuid,
+                                          volume_name,
+                                          size,
+                                          0, /* num_stripes */
+                                          0, /* stripe_size */
+                                          0, /* num_mirrors */
+                                          "", /* fs_type */
+                                          "", /* fs_label */
+                                          "", /* encrypt_passphrase */
+                                          FALSE, /* fs_take_ownership */
+                                          lvm2_lv_create_op_callback,
+                                          simple);
+}
+
+static GduVolume *
+gdu_linux_lvm2_volume_group_create_volume_finish (GduDrive              *drive,
+                                                  GAsyncResult          *res,
+                                                  GError               **error)
+{
+        GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+        GduVolume *ret;
+
+        g_return_val_if_fail (GDU_IS_DRIVE (drive), NULL);
+        g_return_val_if_fail (res != NULL, NULL);
+
+        g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == gdu_linux_lvm2_volume_group_create_volume);
+
+        ret = NULL;
+        if (g_simple_async_result_propagate_error (simple, error))
+                goto out;
+
+        ret = GDU_VOLUME (g_simple_async_result_get_op_res_gpointer (simple));
+
+ out:
+        return ret;
+}
+
 /* ---------------------------------------------------------------------------------------------------- */
 
 /**
diff --git a/src/gdu/gdu-linux-md-drive.c b/src/gdu/gdu-linux-md-drive.c
index 0b5ffc0..dbed728 100644
--- a/src/gdu/gdu-linux-md-drive.c
+++ b/src/gdu/gdu-linux-md-drive.c
@@ -597,10 +597,9 @@ get_names_and_desc (GduPresentable  *presentable,
                                        gdu_device_get_device_file (drive->priv->device),
                                        gdu_device_linux_md_get_state (drive->priv->device));
         } else {
-                g_warn_if_fail (drive->priv->device_file != NULL);
-
                 /* Translators: %s is a device file such as /dev/sda4 */
-                ret = g_strdup_printf (_("RAID device %s"), drive->priv->device_file);
+                ret = g_strdup_printf (_("RAID device %s"),
+                                       drive->priv->device_file != NULL ? drive->priv->device_file : "(unknown)");
         }
 
         /* Fallback for level_str */
@@ -687,6 +686,7 @@ gdu_linux_md_drive_get_icon (GduPresentable *presentable)
                 level = gdu_device_linux_md_get_level (drive->priv->device);
         }
 
+        emblem_name = NULL;
         if (g_strcmp0 (level, "linear") == 0) {
                 emblem_name = "gdu-emblem-raid-linear";
         } else if (g_strcmp0 (level, "raid0") == 0) {
@@ -701,8 +701,6 @@ gdu_linux_md_drive_get_icon (GduPresentable *presentable)
                 emblem_name = "gdu-emblem-raid6";
         } else if (g_strcmp0 (level, "raid10") == 0) {
                 emblem_name = "gdu-emblem-raid10";
-        } else {
-                g_warning ("Unknown level `%s'", level);
         }
 
         return gdu_util_get_emblemed_icon ("gdu-multidisk-drive", emblem_name);
diff --git a/src/gdu/gdu-pool.c b/src/gdu/gdu-pool.c
index 32b5887..5d7ee00 100644
--- a/src/gdu/gdu-pool.c
+++ b/src/gdu/gdu-pool.c
@@ -1303,6 +1303,7 @@ recompute_presentables (GduPool *pool)
                                                                                      vg_uuid,
                                                                                      uuid,
                                                                                      GDU_PRESENTABLE (vg));
+
                                                 new_presentables = g_list_prepend (new_presentables, volume);
 
                                         } else {
diff --git a/src/palimpsest/gdu-section-linux-lvm2-volume-group.c b/src/palimpsest/gdu-section-linux-lvm2-volume-group.c
index 48ac582..e3d2c5c 100644
--- a/src/palimpsest/gdu-section-linux-lvm2-volume-group.c
+++ b/src/palimpsest/gdu-section-linux-lvm2-volume-group.c
@@ -338,11 +338,11 @@ do_add_pv (AddPvData *data)
         p = NULL;
         d = NULL;
 
-        g_warn_if_fail (gdu_drive_has_unallocated_space (data->drive_to_add_to,
-                                                         &whole_disk_is_uninitialized,
-                                                         &largest_segment,
-                                                         NULL, /* total_free */
-                                                         &p));
+        g_warn_if_fail (gdu_drive_can_create_volume (data->drive_to_add_to,
+                                                     &whole_disk_is_uninitialized,
+                                                     &largest_segment,
+                                                     NULL, /* total_free */
+                                                     &p));
         g_assert (p != NULL);
         g_assert_cmpint (data->size, <=, largest_segment);
 
diff --git a/src/palimpsest/gdu-section-linux-md-drive.c b/src/palimpsest/gdu-section-linux-md-drive.c
index ec479f9..cbbb010 100644
--- a/src/palimpsest/gdu-section-linux-md-drive.c
+++ b/src/palimpsest/gdu-section-linux-md-drive.c
@@ -726,11 +726,11 @@ do_add_component (AddComponentData *data)
         if (linux_md_device == NULL)
                 goto out;
 
-        g_warn_if_fail (gdu_drive_has_unallocated_space (data->drive_to_add_to,
-                                                         &whole_disk_is_uninitialized,
-                                                         &largest_segment,
-                                                         NULL, /* total_free */
-                                                         &p));
+        g_warn_if_fail (gdu_drive_can_create_volume (data->drive_to_add_to,
+                                                     &whole_disk_is_uninitialized,
+                                                     &largest_segment,
+                                                     NULL, /* total_free */
+                                                     &p));
         g_assert (p != NULL);
         g_assert_cmpint (data->size, <=, largest_segment);
 
diff --git a/src/palimpsest/gdu-shell.c b/src/palimpsest/gdu-shell.c
index 17d9cbd..b8883e0 100644
--- a/src/palimpsest/gdu-shell.c
+++ b/src/palimpsest/gdu-shell.c
@@ -580,61 +580,35 @@ create_linux_md_data_free (CreateLinuxMdData *data)
 static void create_linux_md_do (CreateLinuxMdData *data);
 
 static void
-new_linux_md_create_part_cb (GduDevice  *device,
-                             gchar      *created_device_object_path,
-                             GError     *error,
-                             gpointer    user_data)
+md_raid_create_volume_cb (GduDrive     *drive,
+                          GAsyncResult *res,
+                          gpointer      user_data)
 {
         CreateLinuxMdData *data = user_data;
+        GduVolume *volume;
+        GError *error;
 
-        if (error != NULL) {
+        error = NULL;
+        volume = gdu_drive_create_volume_finish (drive,
+                                                 res,
+                                                 &error);
+        if (volume == NULL) {
                 gdu_shell_raise_error (data->shell,
                                        NULL,
                                        error,
                                        _("Error creating component for RAID array"));
                 g_error_free (error);
-
-                //g_debug ("Error creating component");
         } else {
                 GduDevice *d;
-                GduPool *pool;
-
-                pool = gdu_device_get_pool (device);
 
-                d = gdu_pool_get_by_object_path (pool, created_device_object_path);
+                d = gdu_presentable_get_device (GDU_PRESENTABLE (volume));
                 g_ptr_array_add (data->components, d);
-
-                //g_debug ("Done creating component");
+                g_object_unref (volume);
 
                 /* now that we have a component... carry on... */
                 create_linux_md_do (data);
-
-                g_object_unref (pool);
         }
-}
-
-static void
-new_linux_md_create_part_table_cb (GduDevice  *device,
-                                   GError     *error,
-                                   gpointer    user_data)
-{
-        CreateLinuxMdData *data = user_data;
-
-        if (error != NULL) {
-                gdu_shell_raise_error (data->shell,
-                                       NULL,
-                                       error,
-                                       _("Error creating partition table for component for RAID array"));
-                g_error_free (error);
 
-                //g_debug ("Error creating partition table");
-        } else {
-
-                //g_debug ("Done creating partition table");
-
-                /* now that we have a partition table... carry on... */
-                create_linux_md_do (data);
-        }
 }
 
 static void
@@ -699,82 +673,16 @@ create_linux_md_do (CreateLinuxMdData *data)
         } else {
                 GduDrive *drive;
                 guint num_component;
-                GduPresentable *p;
-                GduDevice *d;
-                guint64 largest_segment;
-                gboolean whole_disk_is_uninitialized;
 
                 num_component = data->components->len;
                 drive = GDU_DRIVE (data->drives->pdata[num_component]);
 
-                g_warn_if_fail (gdu_drive_has_unallocated_space (drive,
-                                                                 &whole_disk_is_uninitialized,
-                                                                 &largest_segment,
-                                                                 NULL, /* total_free */
-                                                                 &p));
-                g_assert (p != NULL);
-
-                d = gdu_presentable_get_device (GDU_PRESENTABLE (drive));
-
-                if (GDU_IS_VOLUME_HOLE (p)) {
-                        guint64 offset;
-                        guint64 size;
-                        const gchar *scheme;
-                        const gchar *type;
-                        gchar *label;
-
-                        offset = gdu_presentable_get_offset (p);
-                        size = data->component_size;
-
-                        //g_debug ("Creating component %d/%d of size %" G_GUINT64_FORMAT " bytes",
-                        //         num_component + 1,
-                        //         data->drives->len,
-                        //         size);
-
-                        scheme = gdu_device_partition_table_get_scheme (d);
-                        type = "";
-                        label = NULL;
-                        if (g_strcmp0 (scheme, "mbr") == 0) {
-                                type = "0xfd";
-                        } else if (g_strcmp0 (scheme, "gpt") == 0) {
-                                type = "A19D880F-05FC-4D3B-A006-743F0F84911E";
-                                /* Limited to 36 UTF-16LE characters according to on-disk format..
-                                 * Since a RAID array name is limited to 32 chars this should fit */
-                                label = g_strdup_printf ("RAID: %s", data->name);
-                        } else if (g_strcmp0 (scheme, "apt") == 0) {
-                                type = "Apple_Unix_SVR2";
-                                label = g_strdup_printf ("RAID: %s", data->name);
-                        }
-
-                        gdu_device_op_partition_create (d,
-                                                        offset,
-                                                        size,
-                                                        type,
-                                                        label != NULL ? label : "",
-                                                        NULL,
-                                                        "",
-                                                        "",
-                                                        "",
-                                                        FALSE,
-                                                        new_linux_md_create_part_cb,
-                                                        data);
-                        g_free (label);
-
-                } else {
-
-                        /* otherwise the whole disk must be uninitialized... */
-                        g_assert (whole_disk_is_uninitialized);
-
-                        /* so create a partition table... */
-                        gdu_device_op_partition_table_create (d,
-                                                              "mbr",
-                                                              new_linux_md_create_part_table_cb,
-                                                              data);
-                }
-
-                g_object_unref (d);
-                g_object_unref (p);
-
+                gdu_drive_create_volume (drive,
+                                         data->component_size,
+                                         data->name,
+                                         GDU_CREATE_VOLUME_FLAGS_LINUX_MD,
+                                         (GAsyncReadyCallback) md_raid_create_volume_cb,
+                                         data);
         }
 }
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]