add partition table detection
authorDavid Zeuthen <davidz@redhat.com>
Sat, 8 Mar 2008 06:43:44 +0000 (01:43 -0500)
committerDavid Zeuthen <davidz@redhat.com>
Sat, 8 Mar 2008 06:43:44 +0000 (01:43 -0500)
Right now this includes a lame part_id tool that will get called by
udev; this needs to be cleaned up and moved to udev proper (means
avoiding glib etc.).

src/99-devkit-disks.rules
src/Makefile.am
src/devkit-disks-device.c
src/org.freedesktop.DeviceKit.Disks.Device.xml
src/part-id.c [new file with mode: 0644]
src/partutil.c [new file with mode: 0644]
src/partutil.h [new file with mode: 0644]
tools/devkit-disks.c

index 64da885..17db0bf 100644 (file)
@@ -1,2 +1,18 @@
+
+ACTION!="add", GOTO="probe_parttable_end"
+SUBSYSTEM!="block", GOTO="probe_parttable_end"
+
+# ignore partitions that span the entire disk
+ATTR{whole_disk}=="*", GOTO="persistent_storage_end"
+
+# ignore non-cdrom ide drivers; these causes loops
+KERNEL=="hd*[!0-9]", ATTR{removable}=="1", DRIVERS=="ide-cs|ide-floppy", GOTO="probe_parttable_end"
+KERNEL=="hd*[0-9]", ATTR{removable}=="1", GOTO="probe_parttable_end"
+
+# scan for partition table only on if we're not a partition
+ATTR{range}=="[0-9]*", IMPORT{program}="part_id $tempnode"
+
+LABEL="probe_parttable_end"
+
 # pass all events to the DeviceKit disks daemon
 SUBSYSTEM=="block", RUN+="socket:/org/freedesktop/devicekit/disks/udev_event"
index edafa89..d70dcd6 100644 (file)
@@ -26,6 +26,15 @@ devkit-disks-device-glue.h: org.freedesktop.DeviceKit.Disks.Device.xml Makefile.
 
 libexec_PROGRAMS = devkit-disks-daemon
 
+# TODO: move to udev
+udevhelper_PROGRAMS = part_id
+udevhelperdir = /lib/udev
+
+part_id_SOURCES = part-id.c partutil.c partutil.h
+part_id_CPPFLAGS = $(AM_CPPFLAGS)
+part_id_LDADD = $(GLIB_LIBS)
+# end move to udev
+
 devkit_disks_daemon_SOURCES =                                          \
        devkit-disks-daemon.h           devkit-disks-daemon.c           \
        devkit-disks-device.h           devkit-disks-device.c           \
index a895018..ec96998 100644 (file)
@@ -61,17 +61,36 @@ struct DevkitDisksDevicePrivate
         GPtrArray *device_holders;
         GPtrArray *device_slaves;
 
+        gboolean device_is_partition;
+        gboolean device_is_partition_table;
+
         char *id_usage;
         char *id_type;
         char *id_version;
         char *id_uuid;
         char *id_label;
+
+        char *partition_slave;
+        char *partition_scheme;
+        char *partition_type;
+        char *partition_label;
+        char *partition_uuid;
+        GPtrArray *partition_flags;
+        int partition_number;
+        guint64 partition_offset;
+        guint64 partition_size;
+
+        char *partition_table_scheme;
+        int partition_table_count;
+        GPtrArray *partition_table_holders;
 };
 
 static void     devkit_disks_device_class_init  (DevkitDisksDeviceClass *klass);
 static void     devkit_disks_device_init        (DevkitDisksDevice      *seat);
 static void     devkit_disks_device_finalize    (GObject     *object);
 
+static void     set_info_clear                  (DevkitDisksDevice *device);
+
 enum
 {
         PROP_0,
@@ -84,11 +103,28 @@ enum
         PROP_DEVICE_HOLDERS,
         PROP_DEVICE_SLAVES,
 
+        PROP_DEVICE_IS_PARTITION,
+        PROP_DEVICE_IS_PARTITION_TABLE,
+
         PROP_ID_USAGE,
         PROP_ID_TYPE,
         PROP_ID_VERSION,
         PROP_ID_UUID,
         PROP_ID_LABEL,
+
+        PROP_PARTITION_SLAVE,
+        PROP_PARTITION_SCHEME,
+        PROP_PARTITION_TYPE,
+        PROP_PARTITION_LABEL,
+        PROP_PARTITION_UUID,
+        PROP_PARTITION_FLAGS,
+        PROP_PARTITION_NUMBER,
+        PROP_PARTITION_OFFSET,
+        PROP_PARTITION_SIZE,
+
+        PROP_PARTITION_TABLE_SCHEME,
+        PROP_PARTITION_TABLE_COUNT,
+        PROP_PARTITION_TABLE_HOLDERS,
 };
 
 G_DEFINE_TYPE (DevkitDisksDevice, devkit_disks_device, G_TYPE_OBJECT)
@@ -177,6 +213,13 @@ get_property (GObject         *object,
                g_value_set_boxed (value, device->priv->device_slaves);
                break;
 
+       case PROP_DEVICE_IS_PARTITION:
+               g_value_set_boolean (value, device->priv->device_is_partition);
+               break;
+       case PROP_DEVICE_IS_PARTITION_TABLE:
+               g_value_set_boolean (value, device->priv->device_is_partition_table);
+               break;
+
         case PROP_ID_USAGE:
                 g_value_set_string (value, device->priv->id_usage);
                 break;
@@ -193,6 +236,44 @@ get_property (GObject         *object,
                 g_value_set_string (value, device->priv->id_label);
                 break;
 
+       case PROP_PARTITION_SLAVE:
+               g_value_set_string (value, device->priv->partition_slave);
+               break;
+       case PROP_PARTITION_SCHEME:
+               g_value_set_string (value, device->priv->partition_scheme);
+               break;
+       case PROP_PARTITION_TYPE:
+               g_value_set_string (value, device->priv->partition_type);
+               break;
+       case PROP_PARTITION_LABEL:
+               g_value_set_string (value, device->priv->partition_label);
+               break;
+       case PROP_PARTITION_UUID:
+               g_value_set_string (value, device->priv->partition_uuid);
+               break;
+       case PROP_PARTITION_FLAGS:
+               g_value_set_boxed (value, device->priv->partition_flags);
+               break;
+       case PROP_PARTITION_NUMBER:
+               g_value_set_int (value, device->priv->partition_number);
+               break;
+       case PROP_PARTITION_OFFSET:
+               g_value_set_uint64 (value, device->priv->partition_offset);
+               break;
+       case PROP_PARTITION_SIZE:
+               g_value_set_uint64 (value, device->priv->partition_size);
+               break;
+
+       case PROP_PARTITION_TABLE_SCHEME:
+               g_value_set_string (value, device->priv->partition_table_scheme);
+               break;
+       case PROP_PARTITION_TABLE_COUNT:
+               g_value_set_int (value, device->priv->partition_table_count);
+               break;
+       case PROP_PARTITION_TABLE_HOLDERS:
+               g_value_set_boxed (value, device->priv->partition_table_holders);
+               break;
+
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                 break;
@@ -254,6 +335,16 @@ devkit_disks_device_class_init (DevkitDisksDeviceClass *klass)
 
         g_object_class_install_property (
                 object_class,
+                PROP_DEVICE_IS_PARTITION,
+                g_param_spec_boolean ("device-is-partition", NULL, NULL, FALSE, G_PARAM_READABLE));
+
+        g_object_class_install_property (
+                object_class,
+                PROP_DEVICE_IS_PARTITION_TABLE,
+                g_param_spec_boolean ("device-is-partition-table", NULL, NULL, FALSE, G_PARAM_READABLE));
+
+        g_object_class_install_property (
+                object_class,
                 PROP_ID_USAGE,
                 g_param_spec_string ("id-usage", NULL, NULL, NULL, G_PARAM_READABLE));
 
@@ -277,7 +368,59 @@ devkit_disks_device_class_init (DevkitDisksDeviceClass *klass)
                 PROP_ID_LABEL,
                 g_param_spec_string ("id-label", NULL, NULL, NULL, G_PARAM_READABLE));
 
+        g_object_class_install_property (
+                object_class,
+                PROP_PARTITION_SLAVE,
+                g_param_spec_string ("partition-slave", NULL, NULL, NULL, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_PARTITION_SCHEME,
+                g_param_spec_string ("partition-scheme", NULL, NULL, NULL, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_PARTITION_TYPE,
+                g_param_spec_string ("partition-type", NULL, NULL, NULL, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_PARTITION_LABEL,
+                g_param_spec_string ("partition-label", NULL, NULL, NULL, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_PARTITION_UUID,
+                g_param_spec_string ("partition-uuid", NULL, NULL, NULL, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_PARTITION_FLAGS,
+                g_param_spec_boxed ("partition-flags", NULL, NULL,
+                                    dbus_g_type_get_collection ("GPtrArray", G_TYPE_STRING),
+                                    G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_PARTITION_NUMBER,
+                g_param_spec_int ("partition-number", NULL, NULL, 0, G_MAXINT, 0, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_PARTITION_OFFSET,
+                g_param_spec_uint64 ("partition-offset", NULL, NULL, 0, G_MAXUINT64, 0, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_PARTITION_SIZE,
+                g_param_spec_uint64 ("partition-size", NULL, NULL, 0, G_MAXUINT64, 0, G_PARAM_READABLE));
 
+        g_object_class_install_property (
+                object_class,
+                PROP_PARTITION_TABLE_SCHEME,
+                g_param_spec_string ("partition-table-scheme", NULL, NULL, NULL, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_PARTITION_TABLE_COUNT,
+                g_param_spec_int ("partition-table-count", NULL, NULL, 0, G_MAXINT, 0, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_PARTITION_TABLE_HOLDERS,
+                g_param_spec_boxed ("partition-table-holders", NULL, NULL,
+                                    dbus_g_type_get_collection ("GPtrArray", G_TYPE_STRING),
+                                    G_PARAM_READABLE));
 }
 
 static void
@@ -288,6 +431,8 @@ devkit_disks_device_init (DevkitDisksDevice *device)
         device->priv->device_file_by_path = g_ptr_array_new ();
         device->priv->device_holders = g_ptr_array_new ();
         device->priv->device_slaves = g_ptr_array_new ();
+        device->priv->partition_flags = g_ptr_array_new ();
+        device->priv->partition_table_holders = g_ptr_array_new ();
 }
 
 static void
@@ -306,23 +451,7 @@ devkit_disks_device_finalize (GObject *object)
 
         g_free (device->priv->native_path);
 
-        g_free (device->priv->device_file);
-
-        g_ptr_array_foreach (device->priv->device_file_by_id, (GFunc) g_free, NULL);
-        g_ptr_array_foreach (device->priv->device_file_by_path, (GFunc) g_free, NULL);
-        g_ptr_array_foreach (device->priv->device_holders, (GFunc) g_free, NULL);
-        g_ptr_array_foreach (device->priv->device_slaves, (GFunc) g_free, NULL);
-        g_ptr_array_free (device->priv->device_file_by_id, TRUE);
-        g_ptr_array_free (device->priv->device_file_by_path, TRUE);
-        g_ptr_array_free (device->priv->device_holders, TRUE);
-        g_ptr_array_free (device->priv->device_slaves, TRUE);
-
-        g_free (device->priv->id_usage);
-        g_free (device->priv->id_type);
-        g_free (device->priv->id_version);
-        g_free (device->priv->id_uuid);
-        g_free (device->priv->id_label);
-
+        set_info_clear (device);
 
         G_OBJECT_CLASS (devkit_disks_device_parent_class)->finalize (object);
 }
@@ -393,6 +522,58 @@ error:
         return FALSE;
 }
 
+static int
+sysfs_get_int (const char *dir, const char *attribute)
+{
+        int result;
+        char *contents;
+        char *filename;
+
+        result = 0;
+        filename = g_build_filename (dir, attribute, NULL);
+        if (g_file_get_contents (filename, &contents, NULL, NULL)) {
+                result = atoi (contents);
+                g_free (contents);
+        }
+        g_free (filename);
+
+
+        return result;
+}
+
+static void
+set_info_clear (DevkitDisksDevice *device)
+{
+        g_free (device->priv->device_file);
+
+        g_ptr_array_foreach (device->priv->device_file_by_id, (GFunc) g_free, NULL);
+        g_ptr_array_foreach (device->priv->device_file_by_path, (GFunc) g_free, NULL);
+        g_ptr_array_foreach (device->priv->device_holders, (GFunc) g_free, NULL);
+        g_ptr_array_foreach (device->priv->device_slaves, (GFunc) g_free, NULL);
+        g_ptr_array_free (device->priv->device_file_by_id, TRUE);
+        g_ptr_array_free (device->priv->device_file_by_path, TRUE);
+        g_ptr_array_free (device->priv->device_holders, TRUE);
+        g_ptr_array_free (device->priv->device_slaves, TRUE);
+
+        g_free (device->priv->id_usage);
+        g_free (device->priv->id_type);
+        g_free (device->priv->id_version);
+        g_free (device->priv->id_uuid);
+        g_free (device->priv->id_label);
+
+        g_free (device->priv->partition_slave);
+        g_free (device->priv->partition_scheme);
+        g_free (device->priv->partition_type);
+        g_free (device->priv->partition_label);
+        g_free (device->priv->partition_uuid);
+        g_ptr_array_foreach (device->priv->partition_flags, (GFunc) g_free, NULL);
+        g_ptr_array_free (device->priv->partition_flags, TRUE);
+
+        g_free (device->priv->partition_table_scheme);
+        g_ptr_array_foreach (device->priv->partition_table_holders, (GFunc) g_free, NULL);
+        g_ptr_array_free (device->priv->partition_table_holders, TRUE);
+}
+
 static gboolean
 set_info (DevkitDisksDevice *device)
 {
@@ -402,11 +583,14 @@ set_info (DevkitDisksDevice *device)
         char *standard_output;
         char **lines;
         unsigned int n;
+        unsigned int m;
         GDir *dir;
         char *path;
-        gboolean is_partition;
         char *s;
+        int range;
         const char *name;
+        gboolean is_partitioned_by_kernel;
+        gboolean is_partitioned_by_kernel_and_is_partition;
 
         ret = FALSE;
 
@@ -420,52 +604,24 @@ set_info (DevkitDisksDevice *device)
                 goto out;
         }
 
-        g_ptr_array_foreach (device->priv->device_file_by_id, (GFunc) g_free, NULL);
-        g_ptr_array_foreach (device->priv->device_file_by_path, (GFunc) g_free, NULL);
-        g_ptr_array_foreach (device->priv->device_holders, (GFunc) g_free, NULL);
-        g_ptr_array_foreach (device->priv->device_slaves, (GFunc) g_free, NULL);
+        /* free all info and prepare arrays for new info */
+        set_info_clear (device);
         device->priv->device_file_by_id = g_ptr_array_new ();
         device->priv->device_file_by_path = g_ptr_array_new ();
         device->priv->device_holders = g_ptr_array_new ();
         device->priv->device_slaves = g_ptr_array_new ();
+        device->priv->partition_flags = g_ptr_array_new ();
+        device->priv->partition_table_holders = g_ptr_array_new ();
 
-        lines = g_strsplit (standard_output, "\n", 0);
-        for (n = 0; lines[n] != NULL; n++) {
-                char *line = lines[n];
+        is_partitioned_by_kernel = FALSE;
+        is_partitioned_by_kernel_and_is_partition = FALSE;
 
-                if (g_str_has_prefix (line, "N: ")) {
-                        g_free (device->priv->device_file);
-                        device->priv->device_file = g_build_filename ("/dev", line + 3, NULL);
-                } else if (g_str_has_prefix (line, "S: ")) {
-                        if (g_str_has_prefix (line + 3, "disk/by-id/") ||
-                            g_str_has_prefix (line + 3, "disk/by-uuid/")) {
-                                g_ptr_array_add (device->priv->device_file_by_id,
-                                                 g_build_filename ("/dev", line + 3, NULL));
-                        } else if (g_str_has_prefix (line + 3, "disk/by-path/")) {
-                                g_ptr_array_add (device->priv->device_file_by_path,
-                                                 g_build_filename ("/dev", line + 3, NULL));
-                        }
-
-                } else if (g_str_has_prefix (line, "E: ")) {
-                        if (g_str_has_prefix (line + 3, "ID_FS_USAGE=")) {
-                                g_free (device->priv->id_usage);
-                                device->priv->id_usage   = g_strdup (line + 3 + sizeof ("ID_FS_USAGE=") - 1);
-                        } else if (g_str_has_prefix (line + 3, "ID_FS_TYPE=")) {
-                                g_free (device->priv->id_type);
-                                device->priv->id_type    = g_strdup (line + 3 + sizeof ("ID_FS_TYPE=") - 1);
-                        } else if (g_str_has_prefix (line + 3, "ID_FS_VERSION=")) {
-                                g_free (device->priv->id_version);
-                                device->priv->id_version = g_strdup (line + 3 + sizeof ("ID_FS_VERSION=") - 1);
-                        } else if (g_str_has_prefix (line + 3, "ID_FS_UUID=")) {
-                                g_free (device->priv->id_uuid);
-                                device->priv->id_uuid    = g_strdup (line + 3 + sizeof ("ID_FS_UUID=") - 1);
-                        } else if (g_str_has_prefix (line + 3, "ID_FS_LABEL=")) {
-                                g_free (device->priv->id_label);
-                                device->priv->id_label   = g_strdup (line + 3 + sizeof ("ID_FS_LABEL=") - 1);
-                        }
-                }
-        }
-        g_strfreev (lines);
+        /* devices partitioned by in-kernel partioning have range
+         * set to > 1 - that's how we identify them
+         */
+        range = sysfs_get_int (device->priv->native_path, "range");
+        if (range > 1)
+                is_partitioned_by_kernel = TRUE;
 
         path = g_build_filename (device->priv->native_path, "holders", NULL);
         dir = g_dir_open (path, 0, NULL);
@@ -474,12 +630,12 @@ set_info (DevkitDisksDevice *device)
         }
         g_free (path);
 
+        path = g_build_filename (device->priv->native_path, "slaves", NULL);
         /* block devices created by in-kernel partioning don't have
-         * the slaves/ directory; that's one way to identify them
+         * the slaves/ directory; that's how we identify them
          */
-        path = g_build_filename (device->priv->native_path, "slaves", NULL);
         dir = g_dir_open (path, 0, NULL);
-        is_partition = (dir == NULL);
+        is_partitioned_by_kernel_and_is_partition = (dir == NULL);
         while (dir != NULL && (name = g_dir_read_name (dir)) != NULL) {
                 g_ptr_array_add (device->priv->device_slaves, compute_object_path_from_basename (name));
         }
@@ -489,28 +645,127 @@ set_info (DevkitDisksDevice *device)
          * you do userspace partitioning, via kpartx(8), then you get
          * these. So manually add this ourselves to be consistent.
          */
-        if (is_partition) {
+        if (is_partitioned_by_kernel_and_is_partition) {
                 /* cut the number off */
                 s = g_path_get_basename (device->priv->native_path);
                 for (n = strlen (s) - 1; g_ascii_isdigit (s[n]) && n >= 0; n--)
                         s[n] = '\0';
-                g_ptr_array_add (device->priv->device_slaves,
-                                 compute_object_path_from_basename (s));
+                g_ptr_array_add (device->priv->device_slaves, compute_object_path_from_basename (s));
+                device->priv->partition_slave = compute_object_path_from_basename (s);
                 g_free (s);
-        } else {
-                char *s;
+
+                n = strlen (device->priv->native_path) - 1;
+                while (g_ascii_isdigit (device->priv->native_path[n]))
+                        n--;
+                device->priv->partition_number = atoi (device->priv->native_path + n + 1);
+                device->priv->device_is_partition = TRUE;
+
+        } else if (is_partitioned_by_kernel) {
                 s = g_path_get_basename (device->priv->native_path);
                 dir = g_dir_open (device->priv->native_path, 0, NULL);
-                is_partition = (dir != NULL);
                 while (dir != NULL && (name = g_dir_read_name (dir)) != NULL) {
                         if (g_str_has_prefix (name, s) && g_ascii_isdigit (name[strlen (s)])) {
                                 g_ptr_array_add (device->priv->device_holders,
                                                  compute_object_path_from_basename (name));
+
+                                g_ptr_array_add (device->priv->partition_table_holders,
+                                                 compute_object_path_from_basename (name));
                         }
                 }
                 g_free (s);
 
+                device->priv->device_is_partition_table = TRUE;
+        }
+
+        /* TODO: right now we only support partitions and partition tables
+         *       created by the kernel; it's a bit hard to determine in the
+         *       general (kpartx) case
+         */
+        if (is_partitioned_by_kernel) {
+
+        } else if (is_partitioned_by_kernel_and_is_partition) {
+        }
+
+        /* set other properties from the udev database */
+        lines = g_strsplit (standard_output, "\n", 0);
+        for (n = 0; lines[n] != NULL; n++) {
+                char *line = lines[n];
+
+                if (g_str_has_prefix (line, "N: ")) {
+                        g_free (device->priv->device_file);
+                        device->priv->device_file = g_build_filename ("/dev", line + 3, NULL);
+                } else if (g_str_has_prefix (line, "S: ")) {
+                        if (g_str_has_prefix (line + 3, "disk/by-id/") ||
+                            g_str_has_prefix (line + 3, "disk/by-uuid/")) {
+                                g_ptr_array_add (device->priv->device_file_by_id,
+                                                 g_build_filename ("/dev", line + 3, NULL));
+                        } else if (g_str_has_prefix (line + 3, "disk/by-path/")) {
+                                g_ptr_array_add (device->priv->device_file_by_path,
+                                                 g_build_filename ("/dev", line + 3, NULL));
+                        }
+
+                } else if (g_str_has_prefix (line, "E: ")) {
+                        if (g_str_has_prefix (line + 3, "ID_FS_USAGE=")) {
+                                g_free (device->priv->id_usage);
+                                device->priv->id_usage   = g_strdup (line + 3 + sizeof ("ID_FS_USAGE=") - 1);
+                        } else if (g_str_has_prefix (line + 3, "ID_FS_TYPE=")) {
+                                g_free (device->priv->id_type);
+                                device->priv->id_type    = g_strdup (line + 3 + sizeof ("ID_FS_TYPE=") - 1);
+                        } else if (g_str_has_prefix (line + 3, "ID_FS_VERSION=")) {
+                                g_free (device->priv->id_version);
+                                device->priv->id_version = g_strdup (line + 3 + sizeof ("ID_FS_VERSION=") - 1);
+                        } else if (g_str_has_prefix (line + 3, "ID_FS_UUID=")) {
+                                g_free (device->priv->id_uuid);
+                                device->priv->id_uuid    = g_strdup (line + 3 + sizeof ("ID_FS_UUID=") - 1);
+                        } else if (g_str_has_prefix (line + 3, "ID_FS_LABEL=")) {
+                                g_free (device->priv->id_label);
+                                device->priv->id_label   = g_strdup (line + 3 + sizeof ("ID_FS_LABEL=") - 1);
+                        } else if (g_str_has_prefix (line + 3, "ID_PART_SCHEME")) {
+                                if (device->priv->device_is_partition_table) {
+                                        device->priv->partition_table_scheme =
+                                                g_strdup (line + 3 + sizeof ("ID_PART_SCHEME=") - 1);
+                                } else if (device->priv->device_is_partition) {
+                                        device->priv->partition_scheme =
+                                                g_strdup (line + 3 + sizeof ("ID_PART_SCHEME=") - 1);
+                                }
+                        } else if (g_str_has_prefix (line + 3, "ID_PART_COUNT")) {
+                                if (device->priv->device_is_partition_table) {
+                                        device->priv->partition_table_count =
+                                                atoi (line + 3 + sizeof ("ID_PART_COUNT=") - 1);
+                                }
+                        } else if (device->priv->device_is_partition &&
+                                   g_str_has_prefix (line + 3, "ID_PART_P")) {
+                                char *endp;
+                                int given_part = strtol (line + 3 + sizeof ("ID_PART_P") - 1, &endp, 10);
+                                if (given_part == device->priv->partition_number && *endp == '_') {
+                                        if (g_str_has_prefix (endp, "_TYPE="))
+                                                device->priv->partition_type =
+                                                        g_strdup (endp + sizeof ("_TYPE=") - 1);
+                                        else if (g_str_has_prefix (endp, "_LABEL="))
+                                                device->priv->partition_label =
+                                                        g_strdup (endp + sizeof ("_LABEL=") - 1);
+                                        else if (g_str_has_prefix (endp, "_UUID="))
+                                                device->priv->partition_uuid =
+                                                        g_strdup (endp + sizeof ("_UUID=") - 1);
+                                        else if (g_str_has_prefix (endp, "_FLAGS=")) {
+                                                char **tokens;
+                                                tokens = g_strsplit (endp + sizeof ("_FLAGS=") - 1, " ", 0);
+                                                for (m = 0; tokens[m] != NULL; m++)
+                                                        g_ptr_array_add (device->priv->partition_flags, tokens[m]);
+                                                g_free (tokens); /* ptrarray takes ownership of strings */
+                                        }
+                                        else if (g_str_has_prefix (endp, "_OFFSET="))
+                                                device->priv->partition_offset =
+                                                        atoll (endp + sizeof ("_OFFSET=") - 1);
+                                        else if (g_str_has_prefix (endp, "_SIZE="))
+                                                device->priv->partition_size =
+                                                        atoll (endp + sizeof ("_SIZE=") - 1);
+                                        /* TODO: slave */
+                                }
+                        }
+                }
         }
+        g_strfreev (lines);
 
 
         /* check for required keys */
index cb36dee..e758792 100644 (file)
     <!-- holders -->
     <property name="device-holders" type="as" access="read"/>
 
+    <!-- true if the device is a partition -->
+    <property name="device-is-partition" type="b" access="read"/>
+
+    <!-- true if the device contains a partition table -->
+    <property name="device-is-partition-table" type="b" access="read"/>
+
     <!-- the usage is a result of probing for signatures on the block
          device; known values are
 
@@ -49,7 +55,7 @@
            other           - other signature found on the device -->
     <property name="id-usage" type="s" access="read"/>
 
-    <!-- More detailed result of signature probing. The value depends
+    <!-- more detailed result of signature probing. The value depends
          on the value of 'id-usage'
 
            filesystem      - the filesystem type e.g. vfat, ext3 and so forth
            other           - known types are: swap -->
     <property name="id-type" type="s" access="read"/>
 
-    <!-- If set, the version of the format specified by 'id-type' -->
+    <!-- the version of the format specified by 'id-type' -->
     <property name="id-version" type="s" access="read"/>
 
-    <!-- If set, an UUID for the format specified by 'id-type' -->
+    <!-- an UUID for the format specified by 'id-type' -->
     <property name="id-uuid" type="s" access="read"/>
 
-    <!-- If set, a label for the format specified by 'id-type' -->
+    <!-- a label for the format specified by 'id-type' -->
     <property name="id-label" type="s" access="read"/>
 
+    <!-- the following properties are only set if the device is
+         a partition (see 'device-is-partition'). Known values
+         for the partitioning scheme are
+
+           mbr  - Master Boot Record
+           embr - Extended Master Boot Record
+           apm  - Apple Partitoning Map
+           gpt  - GUID Partition Table
+
+         The 'partition-slave' property specifies what device
+         the device in question is a partition of. The property
+         'partition-number' is the number of the partition starting
+         from 1 -->
+    <property name="partition-slave" type="s" access="read"/>
+    <property name="partition-scheme" type="s" access="read"/>
+    <property name="partition-type" type="s" access="read"/>
+    <property name="partition-label" type="s" access="read"/>
+    <property name="partition-uuid" type="s" access="read"/>
+    <property name="partition-flags" type="as" access="read"/>
+    <property name="partition-number" type="i" access="read"/>
+    <property name="partition-offset" type="t" access="read"/>
+    <property name="partition-size" type="t" access="read"/>
+
+    <!-- the type of partition table the deice contains; only set if
+         'device-is-partition-table' is true. Known values are
+
+           mbr  - Master Boot Record
+           embr - Extended Master Boot Record
+           apm  - Apple Partitoning Map
+           gpt  - GUID Partition Table
+
+         The 'partition-table-count' property specifies the number
+         of partitions in the partition table. The property
+         'partition-table-holders' specifies what devices is
+         part of the device in question.
+      -->
+    <property name="partition-table-scheme" type="s" access="read"/>
+    <property name="partition-table-count" type="i" access="read"/>
+    <property name="partition-table-holders" type="as" access="read"/>
+
   </interface>
 
 </node>
diff --git a/src/part-id.c b/src/part-id.c
new file mode 100644 (file)
index 0000000..6181627
--- /dev/null
@@ -0,0 +1,116 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "partutil.h"
+
+static void
+usage (int argc, char *argv[])
+{
+        execlp ("man", "man", "part_id", NULL);
+        fprintf (stderr, "Cannot show man page: %m\n");
+        exit (1);
+}
+
+static void
+print_entry (PartitionTable *p, int entry, int print_number)
+{
+        char *type;
+        char *label;
+        char *uuid;
+        char **flags;
+        char *flags_combined;
+        guint64 offset;
+        guint64 size;
+
+        type = part_table_entry_get_type (p, entry);
+        label = part_table_entry_get_label (p, entry);
+        uuid = part_table_entry_get_uuid (p, entry);
+        flags = part_table_entry_get_flags (p, entry);
+        offset = part_table_entry_get_offset (p, entry);
+        size = part_table_entry_get_size (p, entry);
+
+        flags_combined = g_strjoinv (" ", flags);
+
+        printf ("ID_PART_P%d_TYPE=%s\n", print_number, type != NULL ? type : "");
+        printf ("ID_PART_P%d_OFFSET=%lld\n", print_number, offset);
+        printf ("ID_PART_P%d_SIZE=%lld\n", print_number, size);
+        printf ("ID_PART_P%d_LABEL=%s\n", print_number, label != NULL ? label : "");
+        printf ("ID_PART_P%d_UUID=%s\n", print_number, uuid != NULL ? uuid : "");
+        printf ("ID_PART_P%d_FLAGS=%s\n", print_number, flags_combined);
+
+        g_free (type);
+        g_free (label);
+        g_free (uuid);
+        g_strfreev (flags);
+        g_free (flags_combined);
+}
+
+int
+main (int argc, char *argv[])
+{
+        int n;
+        int ret;
+        char *device_file;
+        PartitionTable *table;
+        PartitionTable *nested_table;
+        int num_entries;
+        int num_nested_entries;
+
+        ret = 1;
+
+        device_file = NULL;
+        for (n = 1; n < argc; n++) {
+                if (strcmp (argv[n], "--help") == 0) {
+                        usage (argc, argv);
+                        return 0;
+                } else {
+                        if (device_file != NULL)
+                                usage (argc, argv);
+                        device_file = argv[n];
+                }
+       }
+
+        if (device_file == NULL) {
+                fprintf (stderr, "no device\n");
+                goto out;
+        }
+
+        table = part_table_load_from_disk (device_file);
+        if (table == NULL) {
+                fprintf (stderr, "%s: unknown partition table type\n", device_file);
+                goto out;
+        }
+
+        num_entries = part_table_get_num_entries (table);
+
+        /* we only support a single nested partition table */
+        num_nested_entries = 0;
+        for (n = 0; n < num_entries; n++) {
+                nested_table = part_table_entry_get_nested (table, n);
+                if (nested_table != NULL) {
+                        num_nested_entries = part_table_get_num_entries (nested_table);
+                        break;
+                }
+        }
+
+
+        printf ("ID_PART_SCHEME=%s\n", part_get_scheme_name (part_table_get_scheme (table)));
+        printf ("ID_PART_COUNT=%d\n", num_entries + num_nested_entries);
+
+        for (n = 0; n < num_entries; n++) {
+                print_entry (table, n, n + 1);
+        }
+
+        for (n = 0; n < num_nested_entries; n++) {
+                print_entry (nested_table, n, n + 5);
+        }
+
+        ret = 0;
+
+out:
+        return ret;
+}
+
diff --git a/src/partutil.c b/src/partutil.c
new file mode 100644 (file)
index 0000000..f81441b
--- /dev/null
@@ -0,0 +1,1836 @@
+/***************************************************************************
+ *
+ * part.c : library for reading and writing partition tables - uses
+ *          libparted for the heavy lifting
+ *
+ * Copyright (C) 2006 David Zeuthen, <david@fubar.dk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <ctype.h>
+
+#include <linux/hdreg.h>
+
+#define BLKGETSIZE64 _IOR(0x12,114,size_t)
+
+#include "partutil.h"
+
+static void
+DEBUG (const gchar *essage, ...)
+{
+#if 0
+  va_list args;
+  va_start (args, message);
+  g_vfprintf (stderr, message, args);
+  va_end (args);
+  g_fprintf (stderr, "\n");
+  fflush (stderr);
+#endif
+}
+
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#ifdef USE_PARTED
+#include <parted/parted.h>
+#endif
+
+const char *
+part_get_scheme_name (PartitionScheme scheme)
+{
+       const char *s;
+
+       switch (scheme) {
+       case PART_TYPE_GPT:
+               s = "gpt";
+               break;
+       case PART_TYPE_MSDOS:
+               s = "mbr";
+               break;
+       case PART_TYPE_MSDOS_EXTENDED:
+               s = "embr";
+               break;
+       case PART_TYPE_APPLE:
+               s = "apm";
+               break;
+       default:
+               s = NULL;
+               break;
+       }
+
+       return s;
+}
+
+struct PartitionEntry_s;
+typedef struct PartitionEntry_s PartitionEntry;
+
+struct PartitionEntry_s
+{
+       gboolean is_part_table;
+
+       /* NULL iff is_part_table==FALSE */
+       PartitionTable *part_table;
+
+       /* these are always set */
+       guint8 *data;
+       int length;
+
+       /* offset _on disk_ where the entry starts */
+       guint64 offset;
+};
+
+struct PartitionTable_s
+{
+       /* partitioning scheme used */
+       PartitionScheme scheme;
+
+       /* offset of table on disk */
+       guint64 offset;
+       guint64 size;
+
+       /* entries in partition table */
+       GSList *entries;
+};
+
+void
+part_table_find (PartitionTable *p, guint64 offset,
+                PartitionTable **out_part_table, int *out_entry)
+{
+       int n;
+       int num_entries;
+
+       *out_part_table = p;
+       *out_entry = -1;
+
+       num_entries = part_table_get_num_entries (p);
+       for (n = 0; n < num_entries; n++) {
+               guint64 pe_offset;
+               guint64 pe_size;
+
+               pe_offset = part_table_entry_get_offset (p, n);
+               pe_size = part_table_entry_get_size (p, n);
+
+               if ((offset >= pe_offset) && (offset < pe_offset + pe_size)) {
+                       PartitionTable *part_table_nested;
+
+                       part_table_nested = part_table_entry_get_nested (p, n);
+                       /* return the extended partition only if the offset points to it - otherwise
+                        * look for a logical partition
+                        */
+                       if (part_table_nested != NULL && offset > pe_offset) {
+                               part_table_find (part_table_nested, offset, out_part_table, out_entry);
+                       } else {
+                               *out_entry = n;
+                       }
+
+                       /* and we're done... */
+                       break;
+               }
+       }
+}
+
+
+static guint16
+get_le16 (const void *buf)
+{
+       return GUINT16_FROM_LE ( * ((guint16 *) buf) );
+}
+
+
+static guint32
+get_le32 (const void *buf)
+{
+       return GUINT32_FROM_LE ( * ((guint32 *) buf) );
+}
+
+static guint64
+get_le64 (const void *buf)
+{
+       return GUINT64_FROM_LE ( * ((guint64 *) buf) );
+}
+
+
+static guint32
+get_be32 (const void *buf)
+{
+       return GUINT32_FROM_BE ( * ((guint32 *) buf) );
+}
+
+/* see http://en.wikipedia.org/wiki/Globally_Unique_Identifier - excerpt
+ *
+ * Guids are most commonly written in text as a sequence of hexadecimal digits as such:
+ *
+ *   3F2504E0-4F89-11D3-9A0C-0305E82C3301
+ *
+ * This text notation follows from the data structure defined above. The sequence is
+ *
+ *  1. Data1 (8 characters)
+ *  2. Hyphen
+ *  3. Data2 (4 characters)
+ *  4. Hyphen
+ *  5. Data3 (4 characters)
+ *  6. Hyphen
+ *  7. Initial two items from Data4 (4 characters)
+ *  8. Hyphen
+ *  9. Remaining six items from Data4 (12 characters)
+ *
+ * Often braces are added to enclose the above format, as such:
+ *
+ *   {3F2504E0-4F89-11D3-9A0C-0305E82C3301}
+ *
+ * When printing fewer characters is desired guids are sometimes encoded
+ * into a base64 string of 22 to 24 characters (depending on
+ * padding). For instance:
+ *
+ *   7QDBkvCA1+B9K/U0vrQx1A
+ *   7QDBkvCA1+B9K/U0vrQx1A==
+ */
+
+typedef struct efi_guid_s {
+       guint32 data1;
+       guint16 data2;
+       guint16 data3;
+       guint8  data4[8];
+} __attribute__ ((packed)) efi_guid;
+
+static char *
+get_le_guid (const guint8 *buf)
+{
+       efi_guid *guid = (efi_guid *) buf;
+
+       return g_strdup_printf("%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
+                              get_le32 (&(guid->data1)),
+                              get_le16 (&(guid->data2)),
+                              get_le16 (&(guid->data3)),
+                              guid->data4[0],
+                              guid->data4[1],
+                              guid->data4[2],
+                              guid->data4[3],
+                              guid->data4[4],
+                              guid->data4[5],
+                              guid->data4[6],
+                              guid->data4[7]);
+}
+
+#ifdef USE_PARTED
+static gboolean
+set_le_guid (guint8 *buf, const char *source)
+{
+       efi_guid *guid = (efi_guid *) buf;
+       guint32 __attribute__((__unused__)) data1;
+       guint16 __attribute__((__unused__)) data2;
+       guint16 __attribute__((__unused__)) data3;
+       guint8  __attribute__((__unused__)) data4[8];
+       gboolean ret;
+       int n;
+
+       n = sscanf (source, "%x-%hx-%hx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
+                   &guid->data1,
+                   &guid->data2,
+                   &guid->data3,
+                   &(guid->data4[0]),
+                   &(guid->data4[1]),
+                   &(guid->data4[2]),
+                   &(guid->data4[3]),
+                   &(guid->data4[4]),
+                   &(guid->data4[5]),
+                   &(guid->data4[6]),
+                   &(guid->data4[7]));
+
+       if (n != 11) {
+               DEBUG ("guid '%s' is not valid", source);
+               goto out;
+       }
+
+#if 0
+       DEBUG ("source = %s", source));
+       DEBUG ("data1 = %08x", guid->data1));
+       DEBUG ("data2 = %04x", guid->data2));
+       DEBUG ("data3 = %04x", guid->data3));
+       DEBUG ("data4[0] = %02x", guid->data4[0]));
+       DEBUG ("data4[1] = %02x", guid->data4[1]));
+       DEBUG ("data4[2] = %02x", guid->data4[2]));
+       DEBUG ("data4[3] = %02x", guid->data4[3]));
+       DEBUG ("data4[4] = %02x", guid->data4[4]));
+       DEBUG ("data4[5] = %02x", guid->data4[5]));
+       DEBUG ("data4[6] = %02x", guid->data4[6]));
+       DEBUG ("data4[7] = %02x", guid->data4[7]));
+#endif
+
+       guid->data1 = GUINT32_TO_LE (guid->data1);
+       guid->data2 = GUINT16_TO_LE (guid->data2);
+       guid->data3 = GUINT16_TO_LE (guid->data3);
+
+       ret = TRUE;
+
+out:
+       return ret;
+}
+#endif
+
+static PartitionEntry *
+part_entry_new (PartitionTable *e_part_table, const guint8 *data, int length, guint64 offset)
+{
+       PartitionEntry *pe;
+
+       pe = g_new0 (PartitionEntry, 1);
+       pe->is_part_table = (e_part_table != NULL);
+       pe->part_table = e_part_table;
+       pe->offset = offset;
+       pe->length = length;
+       pe->data = g_new0 (guint8, length);
+       memcpy (pe->data, data, length);
+
+       return pe;
+}
+
+static void
+part_entry_free (PartitionEntry *pe)
+{
+       if (pe->part_table != NULL) {
+               part_table_free (pe->part_table);
+       }
+       g_free (pe->data);
+       g_free (pe);
+}
+
+static PartitionTable *
+part_table_new_empty (PartitionScheme scheme)
+{
+       PartitionTable *p;
+
+       p = g_new0 (PartitionTable, 1);
+       p->scheme = scheme;
+       p->offset = 0;
+       p->entries = NULL;
+
+       return p;
+}
+
+void
+part_table_free (PartitionTable *p)
+{
+       GSList *i;
+
+       if (p == NULL)
+               return;
+
+       for (i = p->entries; i != NULL; i = i->next) {
+               PartitionEntry *pe = i->data;
+               part_entry_free (pe);
+       }
+       g_slist_free (p->entries);
+       g_free (p);
+}
+
+#if 0
+static PartitionTable *
+part_table_parse_bsd (int fd, guint64 offset, guint64 size)
+{
+       PartitionTable *p;
+
+       p = NULL;
+
+       /* TODO */
+
+       return p;
+}
+#endif
+
+
+#define MSDOS_MAGIC                    "\x55\xaa"
+#define MSDOS_PARTTABLE_OFFSET         0x1be
+#define MSDOS_SIG_OFF                  0x1fe
+
+#if 1
+static void
+hexdump (const guint8 *mem, int size)
+{
+       int i;
+       int j;
+       int n;
+       const guint8 *buf = (const guint8 *) mem;
+
+       n = 0;
+       printf ("Dumping %d=0x%x bytes\n", size, size);
+       while (n < size) {
+
+               printf ("0x%04x: ", n);
+
+               j = n;
+               for (i = 0; i < 16; i++) {
+                       if (j >= size)
+                               break;
+                       printf ("%02x ", buf[j]);
+                       j++;
+               }
+
+               for ( ; i < 16; i++) {
+                       printf ("   ");
+               }
+
+               printf ("   ");
+
+               j = n;
+               for (i = 0; i < 16; i++) {
+                       if (j >= size)
+                               break;
+                       printf ("%c", isprint(buf[j]) ? buf[j] : '.');
+                       j++;
+               }
+
+               printf ("\n");
+
+               n += 16;
+       }
+}
+#endif
+
+static PartitionTable *
+part_table_parse_msdos_extended (int fd, guint64 offset, guint64 size)
+{
+       int n;
+       PartitionTable *p;
+       guint64 next;
+
+       DEBUG ("Entering MS-DOS extended parser (offset=%lld, size=%lld)", offset, size);
+
+       p = NULL;
+
+       next = offset;
+
+       while (next != 0) {
+               guint64 readfrom;
+               const guint8 embr[512];
+
+               readfrom = next;
+               next = 0;
+
+               DEBUG ("readfrom = %lld", readfrom);
+
+               if (lseek (fd, readfrom, SEEK_SET) < 0) {
+                       DEBUG ("lseek failed (%s)", strerror (errno));
+                       goto out;
+               }
+               if (read (fd, &embr, sizeof (embr)) != sizeof (embr)) {
+                       DEBUG ("read failed (%s)", strerror (errno));
+                       goto out;
+               }
+
+                //hexdump (embr, 512);
+
+               if (memcmp (&embr[MSDOS_SIG_OFF], MSDOS_MAGIC, 2) != 0) {
+                       DEBUG ("No MSDOS_MAGIC found");
+                       goto out;
+               }
+
+               DEBUG ("MSDOS_MAGIC found");
+
+               if (p == NULL) {
+                       p = part_table_new_empty (PART_TYPE_MSDOS_EXTENDED);
+                       p->offset = offset;
+                       p->size = size;
+               }
+
+
+               for (n = 0; n < 2; n++) {
+                       PartitionEntry *pe;
+                       guint64 pstart;
+                       guint64 psize;
+
+                       pstart = 0x200 * ((guint64) get_le32 (&(embr[MSDOS_PARTTABLE_OFFSET + n * 16 + 8])));
+                       psize  = 0x200 * ((guint64) get_le32 (&(embr[MSDOS_PARTTABLE_OFFSET + n * 16 + 12])));
+
+                       if (psize == 0)
+                               continue;
+
+                       pe = NULL;
+
+                       if (n == 0) {
+                               //DEBUG ("part %d (offset %lld, size %lld, type 0x%02x)",
+                               //     n, readfrom + pstart, psize, ptype));
+
+                               //DEBUG ("pstart = %lld", pstart));
+
+                               //hexdump (&(embr[MSDOS_PARTTABLE_OFFSET + n * 16]), 16);
+
+                               pe = part_entry_new (NULL,
+                                                    &(embr[MSDOS_PARTTABLE_OFFSET + n * 16]),
+                                                    16,
+                                                    readfrom + MSDOS_PARTTABLE_OFFSET + n * 16);
+                       } else {
+                               if (pstart != 0) {
+                                       //DEBUG ("found chain at offset %lld", offset + pstart);
+                                       next = offset + pstart;
+                               }
+                       }
+
+                       //DEBUG ("pe = %p", pe));
+
+                       if (pe != NULL) {
+                               p->entries = g_slist_append (p->entries, pe);
+                       }
+               }
+
+       }
+
+out:
+       DEBUG ("Exiting MS-DOS extended parser");
+       return p;
+}
+
+static PartitionTable *
+part_table_parse_msdos (int fd, guint64 offset, guint64 size, gboolean *found_gpt)
+{
+       int n;
+       const guint8 mbr[512] __attribute__ ((aligned));
+       PartitionTable *p;
+
+       DEBUG ("Entering MS-DOS parser (offset=%lld, size=%lld)", offset, size);
+
+       *found_gpt = FALSE;
+
+       p = NULL;
+
+       if (lseek (fd, offset, SEEK_SET) < 0) {
+               DEBUG ("lseek failed (%s)", strerror (errno));
+               goto out;
+       }
+       if (read (fd, &mbr, sizeof (mbr)) != sizeof (mbr)) {
+               DEBUG ("read failed (%s)", strerror (errno));
+               goto out;
+       }
+
+        //hexdump (mbr, 512);
+
+       if (memcmp (&mbr[MSDOS_SIG_OFF], MSDOS_MAGIC, 2) != 0) {
+               DEBUG ("No MSDOS_MAGIC found");
+               goto out;
+       }
+
+       DEBUG ("MSDOS_MAGIC found");
+
+       /* sanity checks */
+       for (n = 0; n < 4; n++) {
+               if (mbr[MSDOS_PARTTABLE_OFFSET + n * 16 + 0] != 0 &&
+                   mbr[MSDOS_PARTTABLE_OFFSET + n * 16 + 0] != 0x80) {
+                       DEBUG ("partitioning flag for part %d is not 0x00 or 0x80", n);
+                       goto out;
+               }
+               /* protective MBR for GPT => GPT, not MS-DOS */
+               if (mbr[MSDOS_PARTTABLE_OFFSET + n * 16 + 4] == 0xee) {
+                       DEBUG ("found partition type 0xee => protective MBR for GPT");
+                       *found_gpt = TRUE;
+                       goto out;
+               }
+       }
+
+       p = part_table_new_empty (PART_TYPE_MSDOS);
+       p->offset = offset;
+       p->size = size;
+
+       /* we _always_ want to create four partitions */
+       for (n = 0; n < 4; n++) {
+               PartitionEntry *pe;
+               guint64 pstart;
+               guint64 psize;
+               guint8 ptype;
+               PartitionTable *e_part_table;
+
+               pstart = 0x200 * ((guint64) get_le32 (&(mbr[MSDOS_PARTTABLE_OFFSET + n * 16 + 8])));
+               psize  = 0x200 * ((guint64) get_le32 (&(mbr[MSDOS_PARTTABLE_OFFSET + n * 16 + 12])));
+               ptype = mbr[MSDOS_PARTTABLE_OFFSET + n * 16 + 4];
+
+               DEBUG ("looking at part %d (offset %lld, size %lld, type 0x%02x)", n, pstart, psize, ptype);
+
+               pe = NULL;
+               e_part_table = NULL;
+
+               /* look for embedded partition tables */
+               switch (ptype) {
+
+                /* extended partitions */
+               case 0x05: /* MS-DOS */
+               case 0x0f: /* Win95 */
+               case 0x85: /* Linux */
+                       e_part_table = part_table_parse_msdos_extended (fd, pstart, psize);
+                       if (e_part_table != NULL) {
+                               pe = part_entry_new (e_part_table,
+                                                    &(mbr[MSDOS_PARTTABLE_OFFSET + n * 16]),
+                                                    16,
+                                                    offset + MSDOS_PARTTABLE_OFFSET + n * 16);
+                       }
+                       break;
+
+               case 0xa5: /* FreeBSD */
+               case 0xa6: /* OpenBSD */
+               case 0xa9: /* NetBSD */
+                       //e_part_table = part_table_parse_bsd (fd, pstart, psize);
+                       //break;
+
+               default:
+                       DEBUG ("new part entry");
+                       pe = part_entry_new (NULL,
+                                            &(mbr[MSDOS_PARTTABLE_OFFSET + n * 16]),
+                                            16,
+                                            offset + MSDOS_PARTTABLE_OFFSET + n * 16);
+                       break;
+               }
+
+               //DEBUG ("pe = %p", pe));
+
+               if (pe != NULL) {
+                       p->entries = g_slist_append (p->entries, pe);
+               }
+       }
+
+out:
+       DEBUG ("Exiting MS-DOS parser");
+       return p;
+}
+
+#define GPT_MAGIC "EFI PART"
+
+#define GPT_PART_TYPE_GUID_EMPTY "00000000-0000-0000-0000-000000000000"
+
+static PartitionTable *
+part_table_parse_gpt (int fd, guint64 offset, guint64 size)
+{
+       int n;
+       PartitionTable *p;
+       guint8 buf[16];
+       guint64 partition_entry_lba;
+       int num_entries;
+       int size_of_entry;
+
+       DEBUG ("Entering EFI GPT parser");
+
+       /* by way of getting here, we've already checked for a protective MBR */
+
+       p = NULL;
+
+       /* Check GPT signature */
+       if (lseek (fd, offset + 512 + 0, SEEK_SET) < 0) {
+               DEBUG ("lseek failed (%s)", strerror (errno));
+               goto out;
+       }
+       if (read (fd, buf, 8) != 8) {
+               DEBUG ("read failed (%s)", strerror (errno));
+               goto out;
+       }
+       if (memcmp (buf, GPT_MAGIC, 8) != 0) {
+               DEBUG ("No GPT_MAGIC found");
+               goto out;
+       }
+
+       DEBUG ("GPT magic found");
+
+       /* Disk UUID */
+       if (lseek (fd, offset + 512 + 56, SEEK_SET) < 0) {
+               DEBUG ("lseek failed (%s)", strerror (errno));
+               goto out;
+       }
+       if (read (fd, buf, 16) != 16) {
+               DEBUG ("read failed (%s)", strerror (errno));
+               goto out;
+       }
+       //hexdump ((guint8*) buf, 16);
+
+       if (lseek (fd, offset + 512 + 72, SEEK_SET) < 0) {
+               DEBUG ("lseek failed (%s)", strerror (errno));
+               goto out;
+       }
+       if (read (fd, buf, 8) != 8) {
+               DEBUG ("read failed (%s)", strerror (errno));
+               goto out;
+       }
+       partition_entry_lba = get_le64 (buf);
+
+       if (lseek (fd, offset + 512 + 80, SEEK_SET) < 0) {
+               DEBUG ("lseek failed (%s)", strerror (errno));
+               goto out;
+       }
+       if (read (fd, buf, 4) != 4) {
+               DEBUG ("read failed (%s)", strerror (errno));
+               goto out;
+       }
+       num_entries = get_le32 (buf);
+
+       if (lseek (fd, offset + 512 + 84, SEEK_SET) < 0) {
+               DEBUG ("lseek failed (%s)", strerror (errno));
+               goto out;
+       }
+       if (read (fd, buf, 4) != 4) {
+               DEBUG ("read failed (%s)", strerror (errno));
+               goto out;
+       }
+       size_of_entry = get_le32(buf);
+
+
+       p = part_table_new_empty (PART_TYPE_GPT);
+       p->offset = offset;
+       p->size = size;
+
+       DEBUG ("partition_entry_lba=%lld", partition_entry_lba);
+       DEBUG ("num_entries=%d", num_entries);
+       DEBUG ("size_of_entry=%d", size_of_entry);
+
+       for (n = 0; n < num_entries; n++) {
+               PartitionEntry *pe;
+               struct {
+                       guint8 partition_type_guid[16];
+                       guint8 partition_guid[16];
+                       guint8 starting_lba[8];
+                       guint8 ending_lba[8];
+                       guint8 attributes[8];
+                       guint8 partition_name[72];
+               } gpt_part_entry;
+               char *partition_type_guid;
+
+               if (lseek (fd, offset + partition_entry_lba * 512 + n * size_of_entry, SEEK_SET) < 0) {
+                       DEBUG ("lseek failed (%s)", strerror (errno));
+                       goto out;
+               }
+               if (read (fd, &gpt_part_entry, 128) != 128) {
+                       DEBUG ("read failed (%s)", strerror (errno));
+                       goto out;
+               }
+
+               partition_type_guid = get_le_guid (gpt_part_entry.partition_type_guid);
+
+               if (strcmp (partition_type_guid, GPT_PART_TYPE_GUID_EMPTY) == 0)
+                       continue;
+
+               pe = part_entry_new (NULL,
+                                    (guint8*) &gpt_part_entry,
+                                    128,
+                                    offset + partition_entry_lba * 512 + n * size_of_entry);
+               p->entries = g_slist_append (p->entries, pe);
+
+               g_free (partition_type_guid);
+
+               //hexdump ((guint8 *) &gpt_part_entry, 128);
+
+       }
+
+
+out:
+       DEBUG ("Leaving EFI GPT parser");
+       return p;
+}
+
+#define MAC_MAGIC "ER"
+#define MAC_PART_MAGIC "PM"
+
+static PartitionTable *
+part_table_parse_apple (int fd, guint64 offset, guint64 size)
+{
+       int n;
+       PartitionTable *p;
+       struct {
+               guint16 signature;
+               guint16 block_size;
+               guint32 block_count;
+               /* more stuff */
+       } __attribute__ ((packed)) mac_header;
+       struct {
+               guint16 signature;
+               guint16 res1;
+               guint32 map_count;
+               guint32 start_block;
+               guint32 block_count;
+               char name[32];
+               char type[32];
+               guint32 data_start;
+               guint32 data_count;
+               guint32 status;
+               guint32 boot_start;
+               guint32 boot_size;
+               guint32 boot_load;
+               guint32 boot_load2;
+               guint32 boot_entry;
+               guint32 boot_entry2;
+               guint32 boot_cksum;
+               char processor[16]; /* identifies ISA of boot */
+               /* more stuff */
+       } __attribute__ ((packed)) mac_part;
+       int block_size;
+       int block_count;
+       int map_count;
+
+       DEBUG ("Entering Apple parser");
+
+       p = NULL;
+
+       /* Check Mac start of disk signature */
+       if (lseek (fd, offset + 0, SEEK_SET) < 0) {
+               DEBUG ("lseek failed (%s)", strerror (errno));
+               goto out;
+       }
+       if (read (fd, &mac_header, sizeof (mac_header)) != sizeof (mac_header)) {
+               DEBUG ("read failed (%s)", strerror (errno));
+               goto out;
+       }
+       if (memcmp (&(mac_header.signature), MAC_MAGIC, 2) != 0) {
+               DEBUG ("No MAC_MAGIC found");
+               goto out;
+       }
+
+       block_size = GUINT16_FROM_BE (mac_header.block_size);
+       block_count = GUINT32_FROM_BE (mac_header.block_count); /* num blocks on whole disk */
+
+       DEBUG ("Mac MAGIC found, block_size=%d", block_size);
+
+       p = part_table_new_empty (PART_TYPE_APPLE);
+       p->offset = offset;
+       p->size = size;
+
+       /* get number of entries from first entry   */
+       if (lseek (fd, offset + block_size, SEEK_SET) < 0) {
+               DEBUG ("lseek failed (%s)", strerror (errno));
+               goto out;
+       }
+       if (read (fd, &mac_part, sizeof (mac_part)) != sizeof (mac_part)) {
+               DEBUG ("read failed (%s)", strerror (errno));
+               goto out;
+       }
+       map_count = GUINT32_FROM_BE (mac_part.map_count); /* num blocks in part map */
+
+       DEBUG ("map_count = %d", map_count);
+
+       for (n = 0; n < map_count; n++) {
+               PartitionEntry *pe;
+
+               if (memcmp (&(mac_part.signature), MAC_PART_MAGIC, 2) != 0) {
+                       DEBUG ("No MAC_PART_MAGIC found");
+                       break;
+               }
+
+               if (lseek (fd, offset + (n + 1) * block_size, SEEK_SET) < 0) {
+                       DEBUG ("lseek failed (%s)", strerror (errno));
+                       goto out;
+               }
+               if (read (fd, &mac_part, sizeof (mac_part)) != sizeof (mac_part)) {
+                       DEBUG ("read failed (%s)", strerror (errno));
+                       goto out;
+               }
+
+               pe = part_entry_new (NULL,
+                                    (guint8*) &mac_part,
+                                    sizeof (mac_part),
+                                    offset + (n + 1) * block_size);
+               p->entries = g_slist_append (p->entries, pe);
+
+       }
+
+out:
+       DEBUG ("Leaving Apple parser");
+       return p;
+}
+
+PartitionTable *
+part_table_load_from_disk (char *device)
+{
+       int fd;
+       guint64 size;
+       PartitionTable *p;
+       gboolean found_gpt;
+
+       p = NULL;
+
+       fd = open (device, O_RDONLY);
+       if (fd < 0) {
+               DEBUG ("Cannot open device %s", device);
+               goto out;
+       }
+
+       if (ioctl (fd, BLKGETSIZE64, &size) != 0) {
+               DEBUG ("Cannot determine size of device");
+               goto out;
+       }
+
+       p = part_table_parse_msdos (fd, 0, size, &found_gpt);
+       if (p != NULL) {
+               DEBUG ("MSDOS partition table detected");
+               goto out;
+       }
+
+       if (found_gpt) {
+               p = part_table_parse_gpt (fd, 0, size);
+               if (p != NULL) {
+                       DEBUG ("EFI GPT partition table detected");
+                       goto out;
+               }
+       }
+
+       p = part_table_parse_apple (fd, 0, size);
+       if (p != NULL) {
+               DEBUG ("Apple partition table detected");
+               goto out;
+       }
+
+       DEBUG ("No known partition table found");
+
+
+out:
+       if (fd >= 0)
+               close (fd);
+
+       return p;
+}
+
+
+
+PartitionScheme
+part_table_get_scheme (PartitionTable *p)
+{
+       return p->scheme;
+}
+
+int
+part_table_get_num_entries (PartitionTable *p)
+{
+       return g_slist_length (p->entries);
+}
+
+guint64
+part_table_get_offset (PartitionTable *p)
+{
+       return p->offset;
+}
+
+guint64
+part_table_get_size (PartitionTable *p)
+{
+       return p->size;
+}
+
+PartitionTable *
+part_table_entry_get_nested (PartitionTable *p, int entry)
+{
+       PartitionEntry *pe;
+
+       if (p == NULL)
+               return NULL;
+
+       if ((pe = g_slist_nth_data (p->entries, entry)) == NULL)
+               return NULL;
+
+       if (pe->is_part_table)
+               return pe->part_table;
+       else
+               return NULL;
+}
+
+/**************************************************************************/
+
+char *
+part_table_entry_get_type (PartitionTable *p, int entry)
+{
+       char *s = NULL;
+       PartitionEntry *pe;
+
+       if (p == NULL)
+               goto out;
+
+       if ((pe = g_slist_nth_data (p->entries, entry)) == NULL)
+               goto out;
+
+       switch (p->scheme) {
+       case PART_TYPE_GPT:
+               s = get_le_guid (&(pe->data[0]));
+               break;
+       case PART_TYPE_MSDOS:
+       case PART_TYPE_MSDOS_EXTENDED:
+               s = g_strdup_printf ("0x%02x", pe->data[4]);
+               break;
+       case PART_TYPE_APPLE:
+               s = g_strdup ((char *) pe->data + 2*2 + 3*4 + 32);
+               g_strchomp (s);
+               break;
+       default:
+               break;
+       }
+out:
+       if (s != NULL) {
+               g_strchomp (s);
+       }
+       return s;
+}
+
+char *
+part_table_entry_get_uuid (PartitionTable *p, int entry)
+{
+       char *s = NULL;
+       PartitionEntry *pe;
+
+       if (p == NULL)
+               goto out;
+
+       if ((pe = g_slist_nth_data (p->entries, entry)) == NULL)
+               goto out;
+
+       switch (p->scheme) {
+       case PART_TYPE_GPT:
+               s = get_le_guid (&(pe->data[16]));
+               break;
+       default:
+               break;
+       }
+out:
+       if (s != NULL) {
+               g_strchomp (s);
+       }
+       return s;
+}
+
+char *
+part_table_entry_get_label (PartitionTable *p, int entry)
+{
+       char *s = NULL;
+       PartitionEntry *pe;
+
+       if (p == NULL)
+               goto out;
+
+       if ((pe = g_slist_nth_data (p->entries, entry)) == NULL)
+               goto out;
+
+       switch (p->scheme) {
+       case PART_TYPE_GPT:
+               s = g_utf16_to_utf8 ((const gunichar2 *) &(pe->data[56]), 36, NULL, NULL, NULL);
+               break;
+       case PART_TYPE_APPLE:
+               s = g_strdup ((char *) pe->data + 2*2 + 3*4);
+               g_strchomp (s);
+               break;
+       default:
+               break;
+       }
+out:
+       if (s != NULL) {
+               g_strchomp (s);
+       }
+       return s;
+}
+
+char **
+part_table_entry_get_flags (PartitionTable *p, int entry)
+{
+       int n;
+       char **ss = NULL;
+       guint32 apm_status;
+       guint64 gpt_attributes;
+       PartitionEntry *pe;
+
+       if (p == NULL)
+               goto out;
+
+       if ((pe = g_slist_nth_data (p->entries, entry)) == NULL)
+               goto out;
+
+       ss = g_new0 (char*, 6 + 1); /* hard coded to max items we'll return */
+       ss[0] = NULL;
+       n = 0;
+
+       switch (p->scheme) {
+       case PART_TYPE_GPT:
+               gpt_attributes = get_le64 (&(pe->data[48]));
+
+               /* From Table 16 of EFI 2.0 spec, bit zero means:
+                *
+                * "Required for the platform to function. The system
+                * cannot function normally if this partition is
+                * removed. This partition should be considered as
+                * part of the hardware of the system, and if it is
+                * removed the system may not boot. It may contain
+                * diagnostics, recovery tools, or other code or data
+                * that is critical to the functioning of a system
+                * independent of any OS."
+                *
+                */
+               if (gpt_attributes & (1<<0)) {
+                       ss[n++] = g_strdup ("required");
+               }
+
+               /* TODO: handle partition type specific attributes
+                *
+                * Found on the Internet: "For basic data partitions, the following attribute is
+                * defined:0x8000000000000000 prevents the partition from having a drive letter automatically
+                * assigned. By default, each partition is assigned a new drive letter. Setting this
+                * attribute ensures that when a disk is moved to a new computer, a new drive letter
+                * will not be automatically generated. Instead, the user can manually assign drive
+                * letters. Note: Other attributes can be added at any time."
+                */
+               break;
+
+       case PART_TYPE_MSDOS:
+       case PART_TYPE_MSDOS_EXTENDED:
+               if (pe->data[0] == 0x80) {
+                       ss[n++] = g_strdup ("boot");
+               }
+               break;
+
+       case PART_TYPE_APPLE:
+               apm_status = get_be32 (&(pe->data[2*2 + 3*4 + 2*32 + 2*4]));
+               if (apm_status&(1<<1))
+                       ss[n++] = g_strdup ("allocated");
+               if (apm_status&(1<<2))
+                       ss[n++] = g_strdup ("in_use");
+               if (apm_status&(1<<3))
+                       ss[n++] = g_strdup ("boot");
+               if (apm_status&(1<<4))
+                       ss[n++] = g_strdup ("allow_read");
+               if (apm_status&(1<<5))
+                       ss[n++] = g_strdup ("allow_write");
+               if (apm_status&(1<<6))
+                       ss[n++] = g_strdup ("boot_code_is_pic");
+               break;
+       default:
+               break;
+       }
+       ss[n] = NULL;
+
+out:
+       return ss;
+}
+
+guint64
+part_table_entry_get_offset (PartitionTable *p, int entry)
+{
+       guint64 val;
+       PartitionEntry *pe;
+
+       val = G_MAXUINT64;
+       if (p == NULL)
+               goto out;
+
+       if ((pe = g_slist_nth_data (p->entries, entry)) == NULL)
+               goto out;
+
+       switch (p->scheme) {
+       case PART_TYPE_GPT:
+               val = 0x200 * ((guint64) get_le64 (pe->data + 32));
+               break;
+
+       case PART_TYPE_MSDOS:
+               val = 0x200 * ((guint64) get_le32 (pe->data + 8));
+               break;
+       case PART_TYPE_MSDOS_EXTENDED:
+               /* tricky here.. the offset in the EMBR is from the start of the EMBR and they are
+                * scattered around the ext partition... Hence, just use the entry's offset and subtract
+                * it's offset from the EMBR..
+                */
+               val = 0x200 * ((guint64) get_le32 (pe->data + 8)) + pe->offset - MSDOS_PARTTABLE_OFFSET;
+               break;
+       case PART_TYPE_APPLE:
+               val = 0x200 * ((guint64) get_be32 (pe->data + 2*2 + 1*4));
+               break;
+       default:
+               break;
+       }
+out:
+       return val;
+}
+
+guint64
+part_table_entry_get_size (PartitionTable *p, int entry)
+{
+       guint64 val;
+       PartitionEntry *pe;
+
+       val = G_MAXUINT64;
+       if (p == NULL)
+               goto out;
+
+       if ((pe = g_slist_nth_data (p->entries, entry)) == NULL)
+               goto out;
+
+       switch (p->scheme) {
+       case PART_TYPE_GPT:
+               val = 0x200 * (((guint64) get_le64 (pe->data + 40)) - ((guint64) get_le64 (pe->data + 32)) + 1);
+               break;
+       case PART_TYPE_MSDOS:
+       case PART_TYPE_MSDOS_EXTENDED:
+               val = 0x200 * ((guint64) get_le32 (pe->data + 12));
+               break;
+       case PART_TYPE_APPLE:
+               val = 0x200 * ((guint64) get_be32 (pe->data + 2*2 + 2*4));
+               break;
+       default:
+               break;
+       }
+out:
+       return val;
+}
+
+/**************************************************************************/
+
+#ifdef USE_PARTED
+
+/* internal function to both add OR change a partition - if size==0,
+ * then we're changing, otherwise we're adding
+ */
+
+static gboolean
+part_add_change_partition (char *device_file,
+                          guint64 start, guint64 size,
+                          guint64 new_start, guint64 new_size,
+                          guint64 *out_start, guint64 *out_size,
+                          char *type, char *label, char **flags,
+                          int geometry_hps, int geometry_spt)
+{
+       int n;
+       gboolean is_change;
+       gboolean res;
+       PedDevice *device;
+       PedDisk *disk;
+       PedPartition *part;
+       PedConstraint* constraint;
+       PedPartitionType ped_type;
+       guint64 start_sector;
+       guint64 end_sector;
+       guint64 new_start_sector;
+       guint64 new_end_sector;
+       PartitionTable *p;
+       PartitionTable *container_p;
+       int container_entry;
+       PartitionScheme scheme;
+       guint8 mbr_flags = 0;
+       guint8 mbr_part_type = 0;
+       char *endp;
+       guint64 gpt_attributes = 0;
+       guint32 apm_status = 0;
+
+       res = FALSE;
+
+       is_change = FALSE;
+       if (size == 0) {
+               is_change = TRUE;
+       }
+
+       if (is_change) {
+               DEBUG ("In part_change_partition: device_file=%s, start=%lld, new_start=%lld, new_size=%lld, type=%s", device_file, start, new_start, new_size, type);
+       } else {
+               DEBUG ("In part_add_partition: device_file=%s, start=%lld, size=%lld, type=%s", device_file, start, size, type);
+       }
+
+       /* first, find the kind of (embedded) partition table the new partition is going to be part of */
+       p = part_table_load_from_disk (device_file);
+       if (p == NULL) {
+               DEBUG ("Cannot load partition table from %s", device_file);
+               goto out;
+       }
+
+       part_table_find (p, start + 512, &container_p, &container_entry);
+       scheme = part_table_get_scheme (container_p);
+
+       if (is_change) {
+               /* if changing, make sure there is a partition to change */
+               if (container_entry < 0) {
+                       DEBUG ("Couldn't find partition to change");
+                       goto out;
+               }
+       } else {
+               /* if adding, make sure there is no partition in the way... */
+               if (container_entry >= 0) {
+                       char *part_type;
+
+                       /* this might be Apple_Free if we're on PART_TYPE_APPLE */
+                       part_type = part_table_entry_get_type (p, container_entry);
+                       if (! (p->scheme == PART_TYPE_APPLE && part_type != NULL && (strcmp (part_type, "Apple_Free") == 0))) {
+                               part_table_free (p);
+                               DEBUG ("There is a partition in the way on %s", device_file);
+                               goto out;
+                       }
+               }
+       }
+
+       DEBUG ("containing partition table scheme = %d", scheme);
+
+       part_table_free (p);
+       p = NULL;
+
+       if (!is_change) {
+               if (type == NULL) {
+                       DEBUG ("No type specified");
+                       goto out;
+               }
+       }
+
+       /* now that we know the partitoning scheme, sanity check type and flags */
+       switch (scheme) {
+       case PART_TYPE_MSDOS:
+       case PART_TYPE_MSDOS_EXTENDED:
+               mbr_flags = 0;
+               if (flags != NULL) {
+                       for (n = 0; flags[n] != NULL; n++) {
+                               if (strcmp (flags[n], "boot") == 0) {
+                                       mbr_flags |= 0x80;
+                               } else {
+                                       DEBUG ("unknown flag '%s'", flags[n]);
+                                       goto out;
+                               }
+                       }
+               }
+
+               if (type != NULL) {
+                       mbr_part_type = (guint8) (strtol (type, &endp, 0));
+                       if (*endp != '\0') {
+                               DEBUG ("invalid type '%s' given", type);
+                               goto out;
+                       }
+               }
+
+               if (label != NULL) {
+                       DEBUG ("labeled partitions not supported on MSDOS or MSDOS_EXTENDED");
+                       goto out;
+               }
+
+               break;
+
+       case PART_TYPE_GPT:
+               gpt_attributes = 0;
+               if (flags != NULL) {
+                       for (n = 0; flags[n] != NULL; n++) {
+                               if (strcmp (flags[n], "required") == 0) {
+                                       gpt_attributes |= 1;
+                               } else {
+                                       DEBUG ("unknown flag '%s'", flags[n]);
+                                       goto out;
+                               }
+                       }
+               }
+               break;
+
+       case PART_TYPE_APPLE:
+               apm_status = 0;
+               if (flags != NULL) {
+                       for (n = 0; flags[n] != NULL; n++) {
+                               if (strcmp (flags[n], "allocated") == 0) {
+                                       apm_status |= (1<<1);
+                               } else if (strcmp (flags[n], "in_use") == 0) {
+                                       apm_status |= (1<<2);
+                               } else if (strcmp (flags[n], "boot") == 0) {
+                                       apm_status |= (1<<3);
+                               } else if (strcmp (flags[n], "allow_read") == 0) {
+                                       apm_status |= (1<<4);
+                               } else if (strcmp (flags[n], "allow_write") == 0) {
+                                       apm_status |= (1<<5);
+                               } else if (strcmp (flags[n], "boot_code_is_pic") == 0) {
+                                       apm_status |= (1<<6);
+                               } else {
+                                       DEBUG ("unknown flag '%s'", flags[n]);
+                                       goto out;
+                               }
+                       }
+               }
+               break;
+
+       default:
+               DEBUG ("partitioning scheme %d not supported", scheme);
+               goto out;
+       }
+
+       switch (scheme) {
+       case PART_TYPE_MSDOS:
+               if (mbr_part_type == 0x05 || mbr_part_type == 0x85 || mbr_part_type == 0x0f) {
+                       ped_type = PED_PARTITION_EXTENDED;
+               } else {
+                       ped_type = PED_PARTITION_NORMAL;
+               }
+               break;
+
+       case PART_TYPE_MSDOS_EXTENDED:
+               ped_type = PED_PARTITION_LOGICAL;
+               if (mbr_part_type == 0x05 || mbr_part_type == 0x85 || mbr_part_type == 0x0f) {
+                       DEBUG ("Cannot create an extended partition inside an extended partition");
+                       goto out;
+               }
+               break;
+
+       default:
+               ped_type = PED_PARTITION_NORMAL;
+               break;
+       }
+
+       /* now, create the partition */
+
+       start_sector = start / 512;
+       end_sector = (start + size) / 512 - 1;
+       new_start_sector = new_start / 512;
+       new_end_sector = (new_start + new_size) / 512 - 1;
+
+       device = ped_device_get (device_file);
+       if (device == NULL) {
+               DEBUG ("ped_device_get() failed");
+               goto out;
+       }
+       DEBUG ("got it");
+
+       /* set drive geometry on libparted object if the user requested it */
+       if (geometry_hps > 0 && geometry_spt > 0 ) {
+               /* not sure this is authorized use of libparted, but, eh, it seems to work */
+               device->hw_geom.cylinders = device->bios_geom.cylinders = device->length / geometry_hps / geometry_spt;
+               device->hw_geom.heads = device->bios_geom.heads = geometry_hps;
+               device->hw_geom.sectors = device->bios_geom.sectors = geometry_spt;
+       }
+
+       disk = ped_disk_new (device);
+       if (disk == NULL) {
+               DEBUG ("ped_disk_new() failed");
+               goto out_ped_device;
+       }
+       DEBUG ("got disk");
+
+       if (!is_change) {
+               part = ped_partition_new (disk,
+                                         ped_type,
+                                         NULL,
+                                         start_sector,
+                                         end_sector);
+               if (part == NULL) {
+                       DEBUG ("ped_partition_new() failed");
+                       goto out_ped_disk;
+               }
+               DEBUG ("new partition");
+       } else {
+               part = ped_disk_get_partition_by_sector (disk,
+                                                        start_sector);
+               if (part == NULL) {
+                       DEBUG ("ped_partition_get_by_sector() failed");
+                       goto out_ped_disk;
+               }
+               DEBUG ("got partition");
+       }
+
+
+       /* TODO HACK XXX FIXME UGLY BAD: This is super ugly abuse of
+        * libparted - we poke at their internal data structures - but
+        * there ain't nothing we can do about it until libparted
+        * provides API for this...
+        */
+       if (scheme == PART_TYPE_GPT) {
+               struct {
+                       efi_guid        type;
+                       efi_guid        uuid;
+                       char            name[37];
+                       int             lvm;
+                       int             raid;
+                       int             boot;
+                       int             hp_service;
+                       int             hidden;
+                       /* more stuff */
+               } *gpt_data = (void *) part->disk_specific;
+
+               if (type != NULL) {
+                       if (!set_le_guid ((guint8*) &gpt_data->type, type)) {
+                               DEBUG ("type '%s' for GPT appear to be malformed", type);
+                               goto out_ped_partition;
+                       }
+               }
+
+               if (flags != NULL) {
+                       if (gpt_attributes & 1) {
+                               gpt_data->hidden = 1;
+                       } else {
+                               gpt_data->hidden = 0;
+                       }
+               }
+
+       } else if (scheme == PART_TYPE_MSDOS || scheme == PART_TYPE_MSDOS_EXTENDED) {
+               struct {
+                       unsigned char   system;
+                       int             boot;
+                       /* more stuff */
+               } *dos_data = (void *) part->disk_specific;
+
+               if (type != NULL) {
+                       dos_data->system = mbr_part_type;
+               }
+               if (flags != NULL) {
+                       if (mbr_flags & 0x80) {
+                               dos_data->boot = 1;
+                       } else {
+                               dos_data->boot = 0;
+                       }
+               }
+
+       } else if (scheme == PART_TYPE_APPLE) {
+               struct {
+                       char            volume_name[33];        /* eg: "Games" */
+                       char            system_name[33];        /* eg: "Apple_Unix_SVR2" */
+                       char            processor_name[17];
+                       int             is_boot;
+                       int             is_driver;
+                       int             has_driver;
+                       int             is_root;
+                       int             is_swap;
+                       int             is_lvm;
+                       int             is_raid;
+                       PedSector       data_region_length;
+                       PedSector       boot_region_length;
+                       guint32         boot_base_address;
+                       guint32         boot_entry_address;
+                       guint32         boot_checksum;
+                       guint32         status;
+                       /* more stuff */
+               } *mac_data = (void *) part->disk_specific;
+
+               if (type != NULL) {
+                       memset (mac_data->system_name, 0, 33);
+                       strncpy (mac_data->system_name, type, 32);
+               }
+
+               if (flags != NULL) {
+                       mac_data->status = apm_status;
+               }
+       }
+
+       if (label != NULL) {
+               ped_partition_set_name (part, label);
+       }
+
+       if (geometry_hps > 0 && geometry_spt > 0 ) {
+               /* respect drive geometry */
+               constraint = ped_constraint_any (device);
+       } else if (geometry_hps == -1 && geometry_spt == -1 ) {
+
+               /* undocumented (or is it?) libparted usage again.. it appears that
+                * the probed geometry is stored in hw_geom
+                */
+               device->bios_geom.cylinders = device->hw_geom.cylinders;
+               device->bios_geom.heads     = device->hw_geom.heads;
+               device->bios_geom.sectors   = device->hw_geom.sectors;
+
+               constraint = ped_constraint_any (device);
+       } else {
+               PedGeometry *geo_start;
+               PedGeometry *geo_end;
+
+               /* ignore drive geometry */
+               if (is_change) {
+                       geo_start = ped_geometry_new (device, new_start_sector, 1);
+                       geo_end = ped_geometry_new (device, new_end_sector, 1);
+               } else {
+                       geo_start = ped_geometry_new (device, start_sector, 1);
+                       geo_end = ped_geometry_new (device, end_sector, 1);
+               }
+
+               constraint = ped_constraint_new (ped_alignment_any, ped_alignment_any,
+                                                geo_start, geo_end, 1, device->length);
+       }
+
+try_change_again:
+       if (is_change) {
+               if (ped_disk_set_partition_geom (disk,
+                                                part,
+                                                constraint,
+                                                new_start_sector, new_end_sector) == 0) {
+                       DEBUG ("ped_disk_set_partition_geom() failed");
+                       goto out_ped_constraint;
+               }
+       } else {
+               if (ped_disk_add_partition (disk,
+                                           part,
+                                           constraint) == 0) {
+                       DEBUG ("ped_disk_add_partition() failed");
+                       goto out_ped_constraint;
+               }
+       }
+
+       *out_start = part->geom.start * 512;
+       *out_size = part->geom.length * 512;
+
+       if (is_change) {
+               /* make sure the resulting size is never smaller than requested
+                * (this is because one will resize the FS and *then* change the partition table)
+                */
+               if (*out_size < new_size) {
+                       DEBUG ("new_size=%lld but resulting size, %lld, smaller than requested", new_size, *out_size);
+                       new_end_sector++;
+                       goto try_change_again;
+               } else {
+                       DEBUG ("changed partition to start=%lld size=%lld", *out_start, *out_size);
+               }
+       } else {
+               DEBUG ("added partition start=%lld size=%lld", *out_start, *out_size);
+       }
+
+
+       /* hmm, if we don't do this libparted crashes.. I assume that
+        * ped_disk_add_partition assumes ownership of the
+        * PedPartition when adding it... sadly this is not documented
+        * anywhere.. sigh..
+        */
+       part = NULL;
+
+       /* use commit_to_dev rather than just commit to avoid
+        * libparted sending BLKRRPART to the kernel - we want to do
+        * this ourselves...
+        */
+       if (ped_disk_commit_to_dev (disk) == 0) {
+               DEBUG ("ped_disk_commit_to_dev() failed");
+               goto out_ped_constraint;
+       }
+       DEBUG ("committed to disk");
+
+       res = TRUE;
+
+       ped_constraint_destroy (constraint);
+       ped_disk_destroy (disk);
+       ped_device_destroy (device);
+       goto out;
+
+out_ped_constraint:
+       ped_constraint_destroy (constraint);
+
+out_ped_partition:
+       if (part != NULL) {
+               ped_partition_destroy (part);
+       }
+
+out_ped_disk:
+       ped_disk_destroy (disk);
+
+out_ped_device:
+       ped_device_destroy (device);
+
+out:
+       return res;
+}
+
+gboolean
+part_add_partition (char *device_file,
+                   guint64 start, guint64 size,
+                   guint64 *out_start, guint64 *out_size,
+                   char *type, char *label, char **flags,
+                   int geometry_hps, int geometry_spt)
+{
+       return part_add_change_partition (device_file,
+                                         start, size,
+                                         0, 0,
+                                         out_start, out_size,
+                                         type, label, flags,
+                                         geometry_hps, geometry_spt);
+}
+
+gboolean
+part_change_partition (char *device_file,
+                      guint64 start,
+                      guint64 new_start, guint64 new_size,
+                      guint64 *out_start, guint64 *out_size,
+                      char *type, char *label, char **flags,
+                      int geometry_hps, int geometry_spt)
+{
+       return part_add_change_partition (device_file,
+                                         start, 0,
+                                         new_start, new_size,
+                                         out_start, out_size,
+                                         type, label, flags,
+                                         geometry_hps, geometry_spt);
+}
+
+gboolean
+part_del_partition (char *device_file, guint64 offset)
+{
+       gboolean ret;
+       PedDevice *device;
+       PedDisk *disk;
+       PedPartition *part;
+       PartitionTable *p;
+       gboolean is_extended;
+       int n;
+
+       DEBUG ("In part_del_partition: device_file=%s, offset=%lld", device_file, offset);
+       ret = FALSE;
+
+
+       /* sigh.. one would think that if you passed the sector of where the
+        * the beginning of the extended partition starts, then _by_sector
+        * would return the same as _extended_partition.
+        *
+        * Sadly it's not so..
+        *
+        * So, check if the passed offset actually corresponds to a nested
+        * partition table...
+        */
+       is_extended = FALSE;
+       p = part_table_load_from_disk (device_file);
+       if (p == NULL) {
+               DEBUG ("Cannot load partition table from %s", device_file);
+               goto out;
+       }
+       for (n = 0; n < part_table_get_num_entries (p); n++) {
+               PartitionTable *nested;
+               nested = part_table_entry_get_nested (p, n);
+               if (nested != NULL) {
+                       if (part_table_get_offset (nested) == offset) {
+                               DEBUG ("partition to delete is an extended partition");
+                               is_extended = TRUE;
+                       }
+               }
+       }
+       part_table_free (p);
+
+       device = ped_device_get (device_file);
+       if (device == NULL) {
+               DEBUG ("ped_device_get() failed");
+               goto out;
+       }
+       DEBUG ("got it");
+
+       disk = ped_disk_new (device);
+       if (disk == NULL) {
+               DEBUG ("ped_disk_new() failed");
+               goto out_ped_device;
+       }
+       DEBUG ("got disk");
+
+       if (is_extended) {
+               part = ped_disk_extended_partition (disk);
+       } else {
+               part = ped_disk_get_partition_by_sector (disk, offset / 512);
+       }
+
+       if (part == NULL) {
+               DEBUG ("ped_disk_get_partition_by_sector() failed");
+               goto out_ped_disk;
+       }
+
+       DEBUG ("got partition - part->type=%d", part->type);
+       /* allow only to delete primary, logical and extended partitions */
+       if (! ((part->type == PED_PARTITION_NORMAL) ||
+              (part->type == PED_PARTITION_LOGICAL) ||
+              (part->type == PED_PARTITION_EXTENDED))) {
+               DEBUG ("no data partition at given offset %lld for device %s", offset, device_file);
+               goto out_ped_disk;
+       }
+
+       if (ped_disk_delete_partition (disk, part) == 0) {
+               DEBUG ("ped_disk_delete_partition() failed");
+               goto out_ped_disk;
+       }
+
+       /* use commit_to_dev rather than just commit to avoid
+        * libparted sending BLKRRPART to the kernel - we want to do
+        * this ourselves...
+        */
+
+       if (ped_disk_commit_to_dev (disk) == 0) {
+               DEBUG ("ped_disk_commit_to_dev() failed");
+               goto out_ped_disk;
+       }
+       DEBUG ("committed to disk");
+
+       ret = TRUE;
+
+       ped_disk_destroy (disk);
+       ped_device_destroy (device);
+       goto out;
+
+out_ped_disk:
+       ped_disk_destroy (disk);
+
+out_ped_device:
+       ped_device_destroy (device);
+
+out:
+       return ret;
+}
+
+gboolean
+part_create_partition_table (char *device_file, PartitionScheme scheme)
+{
+       PedDevice *device;
+       PedDisk *disk;
+       PedDiskType *disk_type;
+       gboolean ret;
+
+       ret = FALSE;
+
+       DEBUG ("In part_create_partition_table: device_file=%s, scheme=%d", device_file, scheme);
+
+       device = ped_device_get (device_file);
+       if (device == NULL) {
+               DEBUG ("ped_device_get() failed");
+               goto out;
+       }
+       DEBUG ("got it");
+
+       switch (scheme) {
+       case PART_TYPE_MSDOS:
+               disk_type = ped_disk_type_get ("msdos");
+               break;
+       case PART_TYPE_APPLE:
+               disk_type = ped_disk_type_get ("mac");
+               break;
+       case PART_TYPE_GPT:
+               disk_type = ped_disk_type_get ("gpt");
+               break;
+       default:
+               disk_type = NULL;
+               break;
+       }
+
+       if (disk_type == NULL) {
+               DEBUG ("Unknown or unsupported partitioning scheme %d", scheme);
+               goto out;
+       }
+
+        disk = ped_disk_new_fresh (device, disk_type);
+       if (disk == NULL) {
+               DEBUG ("ped_disk_new_fresh() failed");
+               goto out_ped_device;
+       }
+       DEBUG ("got disk");
+
+       if (ped_disk_commit_to_dev (disk) == 0) {
+               DEBUG ("ped_disk_commit_to_dev() failed");
+               goto out_ped_disk;
+       }
+       DEBUG ("committed to disk");
+
+       ret = TRUE;
+
+       ped_disk_destroy (disk);
+       ped_device_destroy (device);
+       goto out;
+
+out_ped_disk:
+       ped_disk_destroy (disk);
+
+out_ped_device:
+       ped_device_destroy (device);
+
+out:
+       return ret;
+}
+
+#endif /* USE_PARTED */
+
+/**************************************************************************/
diff --git a/src/partutil.h b/src/partutil.h
new file mode 100644 (file)
index 0000000..9b6a9c5
--- /dev/null
@@ -0,0 +1,413 @@
+/***************************************************************************
+ *
+ * part.h : library for reading and writing partition tables - uses
+ *          libparted for the heavy lifting
+ *
+ * Copyright (C) 2006 David Zeuthen, <david@fubar.dk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifndef PARTUTIL_H
+#define PARTUTIL_H
+
+#include <stdio.h>
+#include <glib.h>
+
+/* Partition schemes understood by this library */
+typedef enum {
+       PART_TYPE_MSDOS           = 0,
+       PART_TYPE_MSDOS_EXTENDED  = 1,
+       PART_TYPE_APPLE           = 2,
+       PART_TYPE_GPT             = 3
+} PartitionScheme;
+
+/**
+ * part_get_scheme_name:
+ * @scheme: the partitioning scheme
+ *
+ * Get a name for the partitioning scheme. The current mapping is used
+ *
+ *  PART_TYPE_MSDOS          -> mbr
+ *  PART_TYPE_MSDOS_EXTENDED -> embr
+ *  PART_TYPE_APPLE          -> apm
+ *  PART_TYPE_GPT            -> gpt
+ *
+ * Returns: Name of scheme or NULL for unknown scheme. Caller shall not free this string.
+ */
+const char           *part_get_scheme_name (PartitionScheme scheme);
+
+struct PartitionTable_s;
+typedef struct PartitionTable_s PartitionTable;
+
+
+/**
+ * part_table_load_from_disk:
+ * @device: name of device file for entire disk, e.g. /dev/sda
+ *
+ * Scans a disk and collect all partition entries and nested partition tables.
+ *
+ * Returns: A partition table object. Use part_table_free() to free this object.
+ */
+PartitionTable       *part_table_load_from_disk   (char *device);
+
+/**
+ * part_table_free:
+ * @part_table: the partition table
+ *
+ * Frees the partition table returned from part_table_load_from_disk().
+ */
+void                  part_table_free             (PartitionTable *part_table);
+
+/* partition table inspection */
+
+/**
+ * part_table_get_scheme:
+ * @part_table: the partition table
+ *
+ * Get partitioning scheme. 
+ *
+ * Returns: The partitioning scheme.
+ */
+PartitionScheme       part_table_get_scheme       (PartitionTable *part_table);
+
+/**
+ * part_table_get_num_entries:
+ * @part_table: the partition table
+ *
+ * Get number of entries in partition table.
+ *
+ * Returns: Number of entries.
+ */
+int                   part_table_get_num_entries  (PartitionTable *part_table);
+
+/**
+ * part_table_get_offset:
+ * @part_table: the partition table
+ *
+ * Get offset from start of disk where partition table starts (as
+ * referenced in the partition table entry if it's an embedded
+ * partition table, otherwise zero for the full disk)
+ *
+ * Returns: offset, from start of disk, in bytes
+ */
+guint64               part_table_get_offset (PartitionTable *part_table);
+
+/**
+ * part_table_get_size:
+ * @part_table: the partition table
+ *
+ * Get size of partition table (as referenced in the partition table
+ * entry if it's an embedded partition table, otherwise the size of
+ * the full disk)
+ *
+ * Returns: size of partition, in bytes
+ */
+guint64               part_table_get_size   (PartitionTable *part_table);
+
+/**
+ * part_table_find:
+ * @part_table: the partition table
+ * @offset: the offset to test for
+ * @out_part_table: where the (embedded) enclosing the entry will be stored
+ * @out_entry: there the partition table entry number will be stored
+ *
+ * This function finds the entry that a certain byte of the disk belongs to.
+ * As partition tables can be embedded (think MS-DOS extended partitions)
+ * the returned partition table (out_part_table) might be different from
+ * the one passed. If the offset belongs to a primary partition then the
+ * return partition_table will be the same as the passed one.
+ *
+ * If there is no partition at the given offset (might be free space), 
+ * out_entry will be set to -1. Note that out_part_table will always
+ * be set though and free space in the primary disk space and the 
+ * extended partition space differs.
+ *
+ * This is a convenience function.
+ */
+void                  part_table_find (PartitionTable *part_table, 
+                                      guint64 offset,
+                                      PartitionTable **out_part_table, 
+                                      int *out_entry);
+
+
+/**
+ * part_table_entry_get_nested:
+ * @part_table: the partition table
+ * @entry: zero-based index of entry in partition table
+ *
+ * If the partition table entry points to an embedded partition table this
+ * function will return a PartitionTable object representing it.
+ *
+ * Returns: NULL if the entry does not point to a an embedded partition table.
+ *          Do not free with part_table_free() - the object will be freed when
+ *          freeing the root object.
+ */
+PartitionTable       *part_table_entry_get_nested (PartitionTable *part_table, int entry);
+
+/**
+ * part_table_entry_get_type:
+ * @part_table: the partition table
+ * @entry: zero-based index of entry in partition table
+ *
+ * Get the partition table type - the type itself is partitioning scheme
+ * specific as described below.
+ *
+ * For PART_TYPE_MSDOS and PART_TYPE_MSDOS_EXTENDED, the type is an integer
+ * encoded in a string, e.g. 0x83 is Linux. Use atoi() to convert back to
+ * an integer. See http://www.win.tue.nl/~aeb/partitions/partition_types-1.html
+ * for details.
+ *
+ * For PART_TYPE_GPT, this is the GUID encoded as a string, see
+ * http://en.wikipedia.org/wiki/GUID_Partition_Table for details.
+ *
+ * For PART_TYPE_APPLE, this is a string as defined in 
+ * http://developer.apple.com/documentation/mac/Devices/Devices-126.html.
+ * For FAT file systems, it appears that "DOS_FAT_32", "DOS_FAT_16" and
+ * "DOS_FAT_12" are also recognized under Mac OS X (I've tested this too) cf. 
+ * http://lists.apple.com/archives/Darwin-drivers/2003/May/msg00021.html
+ *
+ * Returns: The partition table type. Caller shall free this with g_free().
+ */
+char                 *part_table_entry_get_type   (PartitionTable *part_table, int entry);
+
+/**
+ * part_table_entry_get_label:
+ * @part_table: the partition table
+ * @entry: zero-based index of entry in partition table
+ *
+ * Label of the partition. This is only supported for PART_TYPE_APPLE and
+ * PART_TYPE_GPT. Note that this is not the same as the file system label
+ * in a file system in the partition.
+ *
+ * Returns: The label or NULL if the partitioning scheme does not support
+ *          labels. Caller shall free this with g_free().
+ */
+char                 *part_table_entry_get_label  (PartitionTable *part_table, int entry);
+
+/**
+ * part_table_entry_get_uuid:
+ * @part_table: the partition table
+ * @entry: zero-based index of entry in partition table
+ *
+ * Some UUID/GUID of the partition. This is only supported for PART_TYPE_GPT.
+ *
+ * Returns: The UUID or NULL if the partitioning scheme does not support
+ *          UUID/GUID. Caller shall free this with g_free().
+ */
+char                 *part_table_entry_get_uuid  (PartitionTable *part_table, int entry);
+
+/**
+ * part_table_entry_get_flags:
+ * @part_table: the partition table
+ * @entry: zero-based index of entry in partition table
+ *
+ * Get flags of partition table entry. This is dependent on the partitioning
+ * scheme.
+ *
+ * For PART_TYPE_MSDOS and PART_TYPE_MSDOS_EXTENDED the following flags are
+ * recognized: 
+ * - "boot"; meaning that the bootable flag is set. This is used by some
+ *   BIOS'es and boot loaders to populate a boot menu.
+ *
+ * For PART_TYPE_GPT the following flags are recognized:
+ * - "required" which corresponds to bit 0 of the attibutes
+ *   (offset 48), meaning "Required for the platform to function. The
+ *   system cannot function normally if this partition is removed. This
+ *   partition should be considered as part of the hardware of the
+ *   system, and if it is removed the system may not boot. It may
+ *   contain diagnostics, recovery tools, or other code or data that is
+ *   critical to the functioning of a system independent of any OS."
+ *
+ *
+ * For PART_TYPE_APPLE the following flags are recognized:
+ * - "allocated"; if the partition is already allocated
+ * - "in_use"; if the partition is in use; may be cleared after a system reset
+ * - "boot"; if partition contains valid boot information
+ * - "allow_read"; if partition allows reading
+ * - "allow_write"; if partition allows writing
+ * - "boot_code_is_pic"; if boot code is position independent
+ *
+ * Returns: An array of strings, one per flag, terminated by NULL. Caller
+ *          shall free this with g_strfreev().
+ */
+char                **part_table_entry_get_flags  (PartitionTable *part_table, int entry);
+
+/**
+ * part_table_entry_get_offset:
+ * @part_table: the partition table
+ * @entry: zero-based index of entry in partition table
+ *
+ * Get offset from start of disk where partition starts (as referenced in the
+ * partition table entry)
+ *
+ * Returns: offset, from start of disk, in bytes
+ */
+guint64               part_table_entry_get_offset (PartitionTable *part_table, int entry);
+
+/**
+ * part_table_entry_get_size:
+ * @part_table: the partition table
+ * @entry: zero-based index of entry in partition table
+ *
+ * Get size of partition (as referenced in the partition table entry)
+ *
+ * Returns: size of partition, in bytes
+ */
+guint64               part_table_entry_get_size   (PartitionTable *part_table, int entry);
+
+
+/**
+ * part_create_partition_table:
+ * @device: name of device file for entire disk, e.g. /dev/sda
+ * @scheme: the partitioning scheme
+ *
+ * Create a new fresh partition on a disk.
+ * 
+ * Returns: TRUE if the operation was succesful, otherwise FALSE
+ */
+gboolean              part_create_partition_table (char *device, PartitionScheme scheme);
+
+
+/**
+ * part_add_partition:
+ * @device: name of device file for entire disk, e.g. /dev/sda
+ * @start: start offset of partition, in bytes
+ * @size: size of partition, in bytes
+ * @out_start: where partition will start, after satisfying disk geometry constraints
+ * @out_size: size of partition, after satisfying disk geometry constraints
+ * @type: the partition type as defined in part_table_entry_get_type()
+ * @flags: the partition flags as defined in part_table_entry_get_flags()
+ * @label: the partition label as defined in part_table_entry_get_label()
+ * @geometry_hps: heads-per-sector used for LBA<->CHS conversions
+ * @geometry_spt: sectors-per-track used for LBA<->CHS conversions
+ *
+ * Adds a new partition to a disk. 
+ *
+ * If geometry_hps and geomtry_spt are both positive, they will be
+ * used as the geometry of the disk for CHS<->LBA conversions. Notably
+ * this is only applicable for MSDOS / MSDOS_EXTENDED partition
+ * tables. Also, in this case, geometry is enforced to ensure that
+ * partitions start and end at cylinder boundaries.
+ * 
+ * If either geometry_hps or geomtry_spt are zero, geometry is
+ * simply ignored and partitions will only be aligned to blocks, e.g.
+ * normally 512 byte boundaries.
+ *
+ * If both geometry_hps or geomtry_spt are -1, then geometry information
+ * probed from existing partition table entries / file systems on the
+ * disk. This is not always reliable.
+ *
+ * As such, the caller cannot always expect that the partition created
+ * will be at the requested offset and size due to e.g. geometry and
+ * block boundary alignment. Therefore, the start and size where the
+ * partition ends up is passed in the out_start and out_size
+ * arguments.
+ *
+ * As embedded partition tables are supported, the caller should use
+ * part_table_find() in advance to make sure that the passed type,
+ * label and flags match the (embedded) partition table that this
+ * partition will be part of.
+ *
+ * To create an MSDOS extended partition table in a MSDOS partition
+ * table, simply pass 0x05, 0x0f or 0x85 as the partition type.
+ *
+ * In order for changes to take effect, the caller needs to poke the
+ * OS kernel himself to make it reload the partition table. It is not
+ * automatically done by this function.
+ *
+ * NOTE: After calling this function you need to discard any partition table
+ * obtained with part_table_load_from_disk() since the in-memory data structure
+ * is not updated.
+ *
+ * Returns: TRUE if the operation was succesful, otherwise FALSE
+ */
+gboolean              part_add_partition (char *device, 
+                                         guint64 start, guint64 size, 
+                                         guint64 *out_start, guint64 *out_size, 
+                                         char *type, char *label, char **flags,
+                                         int geometry_hps, int geometry_spt);
+
+/**
+ * part_change_partition:
+ * @device_file: name of device file for entire disk, e.g. /dev/sda
+ * @start: start offset of existing partition, in bytes
+ * @new_start: new start offset of partition, in bytes
+ * @new_size: new size of partition, in bytes
+ * @out_start: where partition will start, after satisfying disk geometry constraints
+ * @out_size: size of partition, after satisfying disk geometry constraints
+ * @type: the partition type as defined in part_table_entry_get_type() or NULL to not change
+ * @flags: the partition flags as defined in part_table_entry_get_flags() or NULL to not change
+ * @label: the partition label as defined in part_table_entry_get_label() or NULL to not change
+ * @geometry_hps: heads-per-sector used for LBA<->CHS conversions
+ * @geometry_spt: sectors-per-track used for LBA<->CHS conversions
+ *
+ * Changes an existing partition table entry on disk. The contents of
+ * the partition will not be touched. 
+ * 
+ * XXX TODO FIXME: probably be careful with overlapping partitions as
+ * e.g. extended MS-DOS partitions have the partition information just
+ * before the partition data itself. Need to look into this.
+ *
+ * If new_start and new_size matches the existing start and size, only
+ * flags, label and type are changed. Any of flags, label and type can
+ * be set to NULL to signal there should be no change. Thus, this
+ * function serves two purposes. It can be used to both change offset and/or
+ * size and it can be used to change type and/or flags and/or label.
+ *
+ * See part_add_partition() for information about geometry_hps and
+ * geometry_spt and how it affects the resulting partition offset and
+ * size. This function gives one guarantee though: the resulting size
+ * will never be smaller than the requested size. This is useful for
+ * two-step operations by which a file system is first shrinked and
+ * then the partition table is updated.
+ *
+ * In order for changes to take effect, the caller needs to poke the
+ * OS kernel himself to make it reload the partition table. It is not
+ * automatically done by this function.
+ *
+ * NOTE: After calling this function you need to discard any partition
+ * table obtained with part_table_load_from_disk() since the in-memory
+ * data structure is not updated.
+ *
+ * Returns: TRUE if the operation was succesful, otherwise FALSE
+ */
+gboolean              part_change_partition (char *device_file, 
+                                            guint64 start, 
+                                            guint64 new_start, guint64 new_size,
+                                            guint64 *out_start, guint64 *out_size, 
+                                            char *type, char *label, char **flags,
+                                            int geometry_hps, int geometry_spt);
+
+/**
+ * part_del_partition:
+ * @device: name of device file for entire disk, e.g. /dev/sda
+ * @offset: offset of somewhere within the partition to delete, in bytes
+ *
+ * Deletes a partition. Just pass the offset of the partition. If you
+ * delete an extended partition all logical partitions will be deleted
+ * too.
+ *
+ * NOTE: After calling this function you need to discard any partition table
+ * obtained with part_table_load_from_disk() since the in-memory data structure
+ * is not updated.
+ *
+ * Returns: TRUE if the operation was succesful, otherwise FALSE
+ */
+gboolean              part_del_partition (char *device, guint64 offset);
+
+
+#endif /* PARTUTIL_H */
index 1ab5c01..dd442de 100644 (file)
@@ -57,6 +57,12 @@ device_removed_signal_handler (DBusGProxy *proxy, const char *object_path, gpoin
   g_print ("removed: %s\n", object_path);
 }
 
+/* --- SUCKY CODE BEGIN --- */
+
+/* This totally sucks; dbus-bindings-tool and dbus-glib should be able
+ * to do this for us.
+ */
+
 static char *
 get_property_string (DBusGConnection *bus,
                      const char *svc_name,
@@ -97,6 +103,126 @@ out:
         return ret;
 }
 
+static gboolean
+get_property_boolean (DBusGConnection *bus,
+                      const char *svc_name,
+                      const char *obj_path,
+                      const char *if_name,
+                      const char *prop_name)
+{
+        gboolean ret;
+        DBusGProxy *proxy;
+        GValue value = { 0 };
+        GError *error = NULL;
+
+        ret = FALSE;
+       proxy = dbus_g_proxy_new_for_name (bus,
+                                           svc_name,
+                                           obj_path,
+                                           "org.freedesktop.DBus.Properties");
+        if (!dbus_g_proxy_call (proxy,
+                                "Get",
+                                &error,
+                                G_TYPE_STRING,
+                                if_name,
+                                G_TYPE_STRING,
+                                prop_name,
+                                G_TYPE_INVALID,
+                                G_TYPE_VALUE,
+                                &value,
+                                G_TYPE_INVALID)) {
+                g_warning ("error: %s\n", error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        ret = (gboolean) g_value_get_boolean (&value);
+
+out:
+        g_object_unref (proxy);
+        return ret;
+}
+
+static guint64
+get_property_uint64 (DBusGConnection *bus,
+                     const char *svc_name,
+                     const char *obj_path,
+                     const char *if_name,
+                     const char *prop_name)
+{
+        guint64 ret;
+        DBusGProxy *proxy;
+        GValue value = { 0 };
+        GError *error = NULL;
+
+        ret = 0;
+       proxy = dbus_g_proxy_new_for_name (bus,
+                                           svc_name,
+                                           obj_path,
+                                           "org.freedesktop.DBus.Properties");
+        if (!dbus_g_proxy_call (proxy,
+                                "Get",
+                                &error,
+                                G_TYPE_STRING,
+                                if_name,
+                                G_TYPE_STRING,
+                                prop_name,
+                                G_TYPE_INVALID,
+                                G_TYPE_VALUE,
+                                &value,
+                                G_TYPE_INVALID)) {
+                g_warning ("error: %s\n", error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        ret = (guint64) g_value_get_uint64 (&value);
+
+out:
+        g_object_unref (proxy);
+        return ret;
+}
+
+static int
+get_property_int (DBusGConnection *bus,
+                  const char *svc_name,
+                  const char *obj_path,
+                  const char *if_name,
+                  const char *prop_name)
+{
+        int ret;
+        DBusGProxy *proxy;
+        GValue value = { 0 };
+        GError *error = NULL;
+
+        ret = 0;
+       proxy = dbus_g_proxy_new_for_name (bus,
+                                           svc_name,
+                                           obj_path,
+                                           "org.freedesktop.DBus.Properties");
+        if (!dbus_g_proxy_call (proxy,
+                                "Get",
+                                &error,
+                                G_TYPE_STRING,
+                                if_name,
+                                G_TYPE_STRING,
+                                prop_name,
+                                G_TYPE_INVALID,
+                                G_TYPE_VALUE,
+                                &value,
+                                G_TYPE_INVALID)) {
+                g_warning ("error: %s\n", error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        ret = (guint64) g_value_get_int (&value);
+
+out:
+        g_object_unref (proxy);
+        return ret;
+}
+
 static char **
 get_property_strlist (DBusGConnection *bus,
                       const char *svc_name,
@@ -147,17 +273,33 @@ typedef struct
 {
         char *native_path;
 
-        char *device_file;
-        char **device_file_by_id;
-        char **device_file_by_path;
-        char **device_holders;
-        char **device_slaves;
-
-        char *id_usage;
-        char *id_type;
-        char *id_version;
-        char *id_uuid;
-        char *id_label;
+        char    *device_file;
+        char   **device_file_by_id;
+        char   **device_file_by_path;
+        char   **device_holders;
+        char   **device_slaves;
+        gboolean device_is_partition;
+        gboolean device_is_partition_table;
+
+        char    *id_usage;
+        char    *id_type;
+        char    *id_version;
+        char    *id_uuid;
+        char    *id_label;
+
+        char    *partition_slave;
+        char    *partition_scheme;
+        int      partition_number;
+        char    *partition_type;
+        char    *partition_label;
+        char    *partition_uuid;
+        char   **partition_flags;
+        guint64  partition_offset;
+        guint64  partition_size;
+
+        char    *partition_table_scheme;
+        int      partition_table_count;
+        char   **partition_table_holders;
 } DeviceProperties;
 
 static DeviceProperties *
@@ -203,6 +345,18 @@ device_properties_get (DBusGConnection *bus, const char *object_path)
                 object_path,
                 "org.freedesktop.DeviceKit.Disks.Device",
                 "device-slaves");
+        props->device_is_partition = get_property_boolean (
+                bus,
+                "org.freedesktop.DeviceKit.Disks",
+                object_path,
+                "org.freedesktop.DeviceKit.Disks.Device",
+                "device-is-partition");
+        props->device_is_partition_table = get_property_boolean (
+                bus,
+                "org.freedesktop.DeviceKit.Disks",
+                object_path,
+                "org.freedesktop.DeviceKit.Disks.Device",
+                "device-is-partition-table");
 
         props->id_usage = get_property_string (
                 bus,
@@ -234,6 +388,80 @@ device_properties_get (DBusGConnection *bus, const char *object_path)
                 object_path,
                 "org.freedesktop.DeviceKit.Disks.Device",
                 "id-label");
+
+        props->partition_slave = get_property_string (
+                bus,
+                "org.freedesktop.DeviceKit.Disks",
+                object_path,
+                "org.freedesktop.DeviceKit.Disks.Device",
+                "partition-slave");
+        props->partition_scheme = get_property_string (
+                bus,
+                "org.freedesktop.DeviceKit.Disks",
+                object_path,
+                "org.freedesktop.DeviceKit.Disks.Device",
+                "partition-scheme");
+        props->partition_number = get_property_int (
+                bus,
+                "org.freedesktop.DeviceKit.Disks",
+                object_path,
+                "org.freedesktop.DeviceKit.Disks.Device",
+                "partition-number");
+        props->partition_type = get_property_string (
+                bus,
+                "org.freedesktop.DeviceKit.Disks",
+                object_path,
+                "org.freedesktop.DeviceKit.Disks.Device",
+                "partition-type");
+        props->partition_label = get_property_string (
+                bus,
+                "org.freedesktop.DeviceKit.Disks",
+                object_path,
+                "org.freedesktop.DeviceKit.Disks.Device",
+                "partition-label");
+        props->partition_uuid = get_property_string (
+                bus,
+                "org.freedesktop.DeviceKit.Disks",
+                object_path,
+                "org.freedesktop.DeviceKit.Disks.Device",
+                "partition-uuid");
+        props->partition_flags = get_property_strlist (
+                bus,
+                "org.freedesktop.DeviceKit.Disks",
+                object_path,
+                "org.freedesktop.DeviceKit.Disks.Device",
+                "partition-flags");
+        props->partition_offset = get_property_uint64 (
+                bus,
+                "org.freedesktop.DeviceKit.Disks",
+                object_path,
+                "org.freedesktop.DeviceKit.Disks.Device",
+                "partition-offset");
+        props->partition_size = get_property_uint64 (
+                bus,
+                "org.freedesktop.DeviceKit.Disks",
+                object_path,
+                "org.freedesktop.DeviceKit.Disks.Device",
+                "partition-size");
+
+        props->partition_table_scheme = get_property_string (
+                bus,
+                "org.freedesktop.DeviceKit.Disks",
+                object_path,
+                "org.freedesktop.DeviceKit.Disks.Device",
+                "partition-table-scheme");
+        props->partition_table_count = get_property_int (
+                bus,
+                "org.freedesktop.DeviceKit.Disks",
+                object_path,
+                "org.freedesktop.DeviceKit.Disks.Device",
+                "partition-table-count");
+        props->partition_table_holders = get_property_strlist (
+                bus,
+                "org.freedesktop.DeviceKit.Disks",
+                object_path,
+                "org.freedesktop.DeviceKit.Disks.Device",
+                "partition-table-holders");
         return props;
 }
 
@@ -251,9 +479,18 @@ device_properties_free (DeviceProperties *props)
         g_free (props->id_version);
         g_free (props->id_uuid);
         g_free (props->id_label);
+        g_free (props->partition_slave);
+        g_free (props->partition_type);
+        g_free (props->partition_label);
+        g_free (props->partition_uuid);
+        g_strfreev (props->partition_flags);
+        g_free (props->partition_table_scheme);
+        g_strfreev (props->partition_table_holders);
         g_free (props);
 }
 
+/* --- SUCKY CODE END --- */
+
 int
 main (int argc, char **argv)
 {
@@ -338,21 +575,45 @@ main (int argc, char **argv)
 
                 props = device_properties_get (bus, show_info);
                 g_print ("Showing information for %s\n", show_info);
-                g_print ("  native-path: %s\n", props->native_path);
+                g_print ("  native-path:   %s\n", props->native_path);
                 for (n = 0; props->device_holders[n] != NULL; n++)
-                        g_print ("  holder:      %s\n", (char *) props->device_holders[n]);
+                        g_print ("  holder:        %s\n", (char *) props->device_holders[n]);
                 for (n = 0; props->device_slaves[n] != NULL; n++)
-                        g_print ("  slave:       %s\n", (char *) props->device_slaves[n]);
-                g_print ("  device-file: %s\n", props->device_file);
+                        g_print ("  slave:         %s\n", (char *) props->device_slaves[n]);
+                g_print ("  device-file:   %s\n", props->device_file);
                 for (n = 0; props->device_file_by_id[n] != NULL; n++)
-                        g_print ("    by-id:     %s\n", (char *) props->device_file_by_id[n]);
+                        g_print ("    by-id:       %s\n", (char *) props->device_file_by_id[n]);
                 for (n = 0; props->device_file_by_path[n] != NULL; n++)
-                        g_print ("    by-path:   %s\n", (char *) props->device_file_by_path[n]);
-                g_print ("  id-usage:    %s\n", props->id_usage);
-                g_print ("  id-type:     %s\n", props->id_type);
-                g_print ("  id-version:  %s\n", props->id_version);
-                g_print ("  id-uuid:     %s\n", props->id_uuid);
-                g_print ("  id-label:    %s\n", props->id_label);
+                        g_print ("    by-path:     %s\n", (char *) props->device_file_by_path[n]);
+                g_print ("  usage:         %s\n", props->id_usage);
+                g_print ("  type:          %s\n", props->id_type);
+                g_print ("  version:       %s\n", props->id_version);
+                g_print ("  uuid:          %s\n", props->id_uuid);
+                g_print ("  label:         %s\n", props->id_label);
+                if (props->device_is_partition_table) {
+                        g_print ("  partition table:\n");
+                        g_print ("    scheme:      %s\n", props->partition_table_scheme);
+                        g_print ("    count:       %d\n", props->partition_table_count);
+                        for (n = 0; props->partition_table_holders[n] != NULL; n++)
+                                g_print ("    partition:   %s\n", (char *) props->partition_table_holders[n]);
+
+                }
+                if (props->device_is_partition) {
+                        g_print ("  partition:\n");
+                        g_print ("    part of:     %s\n", props->partition_slave);
+                        g_print ("    scheme:      %s\n", props->partition_scheme);
+                        g_print ("    number:      %d\n", props->partition_number);
+                        g_print ("    type:        %s\n", props->partition_type);
+                        g_print ("    flags:      ");
+                        for (n = 0; props->partition_flags[n] != NULL; n++)
+                                g_print (" %s", (char *) props->partition_flags[n]);
+                        g_print ("\n");
+                        g_print ("    offset:      %lld\n", props->partition_offset);
+                        g_print ("    size:        %lld\n", props->partition_size);
+                        g_print ("    label:       %s\n", props->partition_label);
+                        g_print ("    uuid:        %s\n", props->partition_uuid);
+
+                }
                 device_properties_free (props);
         }