From 8be656571835afa0ce3338f4ccb80ae74b19bc92 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Sat, 8 Mar 2008 01:43:44 -0500 Subject: [PATCH] add partition table detection 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 | 16 + src/Makefile.am | 9 + src/devkit-disks-device.c | 389 ++++- src/org.freedesktop.DeviceKit.Disks.Device.xml | 54 +- src/part-id.c | 116 ++ src/partutil.c | 1836 ++++++++++++++++++++++++ src/partutil.h | 413 ++++++ tools/devkit-disks.c | 305 +++- 8 files changed, 3045 insertions(+), 93 deletions(-) create mode 100644 src/part-id.c create mode 100644 src/partutil.c create mode 100644 src/partutil.h diff --git a/src/99-devkit-disks.rules b/src/99-devkit-disks.rules index 64da885..17db0bf 100644 --- a/src/99-devkit-disks.rules +++ b/src/99-devkit-disks.rules @@ -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" diff --git a/src/Makefile.am b/src/Makefile.am index edafa89..d70dcd6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/devkit-disks-device.c b/src/devkit-disks-device.c index a895018..ec96998 100644 --- a/src/devkit-disks-device.c +++ b/src/devkit-disks-device.c @@ -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 */ diff --git a/src/org.freedesktop.DeviceKit.Disks.Device.xml b/src/org.freedesktop.DeviceKit.Disks.Device.xml index cb36dee..e758792 100644 --- a/src/org.freedesktop.DeviceKit.Disks.Device.xml +++ b/src/org.freedesktop.DeviceKit.Disks.Device.xml @@ -38,6 +38,12 @@ + + + + + + - - + - + - + + + + + + + + + + + + + + + + + diff --git a/src/part-id.c b/src/part-id.c new file mode 100644 index 0000000..6181627 --- /dev/null +++ b/src/part-id.c @@ -0,0 +1,116 @@ + +#include +#include +#include +#include + +#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 index 0000000..f81441b --- /dev/null +++ b/src/partutil.c @@ -0,0 +1,1836 @@ +/*************************************************************************** + * + * part.c : library for reading and writing partition tables - uses + * libparted for the heavy lifting + * + * Copyright (C) 2006 David Zeuthen, + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 +#endif + +#ifdef USE_PARTED +#include +#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 index 0000000..9b6a9c5 --- /dev/null +++ b/src/partutil.h @@ -0,0 +1,413 @@ +/*************************************************************************** + * + * part.h : library for reading and writing partition tables - uses + * libparted for the heavy lifting + * + * Copyright (C) 2006 David Zeuthen, + * + * 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 +#include + +/* 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 */ diff --git a/tools/devkit-disks.c b/tools/devkit-disks.c index 1ab5c01..dd442de 100644 --- a/tools/devkit-disks.c +++ b/tools/devkit-disks.c @@ -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); } -- 2.7.4