static void
storaged_linux_block_object_set_property (GObject      *__object,
        guint         prop_id,
        const GValue *value,
        GParamSpec   *pspec)
{
    StoragedLinuxBlockObject *object = STORAGED_LINUX_BLOCK_OBJECT (__object);

    switch (prop_id)
    {
    case PROP_DAEMON:
        g_assert (object->daemon == NULL);
        /* we don't take a reference to the daemon */
        object->daemon = g_value_get_object (value);
        break;

    case PROP_DEVICE:
        g_assert (object->device == NULL);
        object->device = g_value_dup_object (value);
        break;

    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
    }
}
static gboolean
filesystem_check (StoragedObject *object)
{
    StoragedLinuxBlockObject *block_object = STORAGED_LINUX_BLOCK_OBJECT (object);
    gboolean ret = FALSE;
    gboolean detected_as_filesystem = FALSE;
    StoragedMountType mount_type;

    /* if blkid(8) has detected the device as a filesystem, trust that */
    if (g_strcmp0 (storaged_block_get_id_usage (block_object->iface_block_device), "filesystem") == 0)
    {
        detected_as_filesystem = TRUE;
        /* except, if we are a whole-disk device and the kernel has already partitioned us...
         * in that case, don't pretend we're a filesystem
         *
         * (see partition_table_check() above for the similar case where we don't pretend
         * to be a partition table)
         */
        if (g_strcmp0 (g_udev_device_get_devtype (block_object->device->udev_device), "disk") == 0 &&
                disk_is_partitioned_by_kernel (block_object->device->udev_device))
        {
            detected_as_filesystem = FALSE;
        }
    }

    if (drive_does_not_detect_media_change (block_object) ||
            detected_as_filesystem ||
            (storaged_mount_monitor_is_dev_in_use (block_object->mount_monitor,
                    g_udev_device_get_device_number (block_object->device->udev_device),
                    &mount_type) &&
             mount_type == STORAGED_MOUNT_TYPE_FILESYSTEM))
        ret = TRUE;

    return ret;
}
static void
storaged_linux_block_object_finalize (GObject *_object)
{
    StoragedLinuxBlockObject *object = STORAGED_LINUX_BLOCK_OBJECT (_object);

    /* note: we don't hold a ref to block->daemon or block->mount_monitor */
    g_signal_handlers_disconnect_by_func (object->mount_monitor, on_mount_monitor_mount_added, object);
    g_signal_handlers_disconnect_by_func (object->mount_monitor, on_mount_monitor_mount_removed, object);

    g_object_unref (object->device);

    if (object->iface_block_device != NULL)
        g_object_unref (object->iface_block_device);
    if (object->iface_partition != NULL)
        g_object_unref (object->iface_partition);
    if (object->iface_partition_table != NULL)
        g_object_unref (object->iface_partition_table);
    if (object->iface_filesystem != NULL)
        g_object_unref (object->iface_filesystem);
    if (object->iface_swapspace != NULL)
        g_object_unref (object->iface_swapspace);
    if (object->iface_encrypted != NULL)
        g_object_unref (object->iface_encrypted);
    if (object->iface_loop != NULL)
        g_object_unref (object->iface_loop);
    if (object->module_ifaces != NULL)
        g_hash_table_destroy (object->module_ifaces);

    if (G_OBJECT_CLASS (storaged_linux_block_object_parent_class)->finalize != NULL)
        G_OBJECT_CLASS (storaged_linux_block_object_parent_class)->finalize (_object);
}
static void
storaged_linux_block_object_constructed (GObject *_object)
{
    StoragedLinuxBlockObject *object = STORAGED_LINUX_BLOCK_OBJECT (_object);
    GString *str;

    object->mount_monitor = storaged_daemon_get_mount_monitor (object->daemon);
    g_signal_connect (object->mount_monitor,
                      "mount-added",
                      G_CALLBACK (on_mount_monitor_mount_added),
                      object);
    g_signal_connect (object->mount_monitor,
                      "mount-removed",
                      G_CALLBACK (on_mount_monitor_mount_removed),
                      object);

    /* initial coldplug */
    storaged_linux_block_object_uevent (object, "add", NULL);

    /* compute the object path */
    str = g_string_new ("/org/storaged/Storaged/block_devices/");
    storaged_safe_append_to_object_path (str, g_udev_device_get_name (object->device->udev_device));
    g_dbus_object_skeleton_set_object_path (G_DBUS_OBJECT_SKELETON (object), str->str);
    g_string_free (str, TRUE);

    if (G_OBJECT_CLASS (storaged_linux_block_object_parent_class)->constructed != NULL)
        G_OBJECT_CLASS (storaged_linux_block_object_parent_class)->constructed (_object);
}
static gboolean
bcache_block_update (StoragedObject  *object,
                     const gchar     *uevent_action,
                     GDBusInterface  *_iface)
{
  return storaged_linux_block_bcache_update (STORAGED_LINUX_BLOCK_BCACHE (_iface),
                                             STORAGED_LINUX_BLOCK_OBJECT (object));
}
예제 #6
0
static gboolean
btrfs_block_update (StoragedObject *object,
                    const gchar    *uevent_action,
                    GDBusInterface *_iface)
{
  return storaged_linux_filesystem_btrfs_update (STORAGED_LINUX_FILESYSTEM_BTRFS (_iface),
                                                 STORAGED_LINUX_BLOCK_OBJECT (object));
}
static gboolean
encrypted_update (StoragedObject   *object,
                  const gchar      *uevent_action,
                  GDBusInterface   *_iface)
{
    storaged_linux_encrypted_update (STORAGED_LINUX_ENCRYPTED (_iface), STORAGED_LINUX_BLOCK_OBJECT (object));
    return TRUE;
}
static gboolean
loop_update (StoragedObject   *object,
             const gchar      *uevent_action,
             GDBusInterface   *_iface)
{
    storaged_linux_loop_update (STORAGED_LINUX_LOOP (_iface), STORAGED_LINUX_BLOCK_OBJECT (object));
    return TRUE;
}
static gboolean
swapspace_update (StoragedObject   *object,
                  const gchar    *uevent_action,
                  GDBusInterface *_iface)
{
    storaged_linux_swapspace_update (STORAGED_LINUX_SWAPSPACE (_iface), STORAGED_LINUX_BLOCK_OBJECT (object));
    return TRUE;
}
예제 #10
0
static gboolean
filesystem_update (StoragedObject   *object,
                   const gchar      *uevent_action,
                   GDBusInterface   *_iface)
{
    storaged_linux_filesystem_update (STORAGED_LINUX_FILESYSTEM (_iface), STORAGED_LINUX_BLOCK_OBJECT (object));
    return TRUE;
}
예제 #11
0
static gboolean
partition_update (StoragedObject   *object,
                  const gchar      *uevent_action,
                  GDBusInterface   *_iface)
{
    storaged_linux_partition_update (STORAGED_LINUX_PARTITION (_iface), STORAGED_LINUX_BLOCK_OBJECT (object));
    return TRUE;
}
예제 #12
0
static void
on_mount_monitor_mount_removed (StoragedMountMonitor  *monitor,
                                StoragedMount         *mount,
                                gpointer               user_data)
{
    StoragedLinuxBlockObject *object = STORAGED_LINUX_BLOCK_OBJECT (user_data);
    if (storaged_mount_get_dev (mount) == g_udev_device_get_device_number (object->device->udev_device))
        storaged_linux_block_object_uevent (object, NULL, NULL);
}
예제 #13
0
/**
 * storaged_linux_block_object_new:
 * @daemon: A #StoragedDaemon.
 * @device: The #StoragedLinuxDevice for the device.
 *
 * Create a new block object.
 *
 * Returns: A #StoragedLinuxBlockObject object. Free with g_object_unref().
 */
StoragedLinuxBlockObject *
storaged_linux_block_object_new (StoragedDaemon      *daemon,
                                 StoragedLinuxDevice *device)
{
    g_return_val_if_fail (STORAGED_IS_DAEMON (daemon), NULL);
    return STORAGED_LINUX_BLOCK_OBJECT (g_object_new (STORAGED_TYPE_LINUX_BLOCK_OBJECT,
                                        "daemon", daemon,
                                        "device", device,
                                        NULL));
}
static gboolean
have_partition_in_range (StoragedPartitionTable     *table,
                         StoragedObject             *object,
                         guint64                     start,
                         guint64                     end,
                         gboolean                    ignore_container)
{
  gboolean ret = FALSE;
  StoragedDaemon *daemon = NULL;
  GDBusObjectManager *object_manager = NULL;
  const gchar *table_object_path;
  GList *objects = NULL, *l;

  daemon = storaged_linux_block_object_get_daemon (STORAGED_LINUX_BLOCK_OBJECT (object));
  object_manager = G_DBUS_OBJECT_MANAGER (storaged_daemon_get_object_manager (daemon));

  table_object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (object));

  objects = g_dbus_object_manager_get_objects (object_manager);
  for (l = objects; l != NULL; l = l->next)
    {
      StoragedObject *i_object = STORAGED_OBJECT (l->data);
      StoragedPartition *i_partition = NULL;

      i_partition = storaged_object_get_partition (i_object);

      if (i_partition == NULL)
        goto cont;

      if (g_strcmp0 (storaged_partition_get_table (i_partition), table_object_path) != 0)
        goto cont;

      if (ignore_container && storaged_partition_get_is_container (i_partition))
        goto cont;

      if (!ranges_overlap (start, end - start,
                           storaged_partition_get_offset (i_partition), storaged_partition_get_size (i_partition)))
        goto cont;

      ret = TRUE;
      g_clear_object (&i_partition);
      goto out;

    cont:
      g_clear_object (&i_partition);
    }

 out:
  g_list_foreach (objects, (GFunc) g_object_unref, NULL);
  g_list_free (objects);
  return ret;
}
예제 #15
0
static gboolean
encrypted_check (StoragedObject *object)
{
    StoragedLinuxBlockObject *block_object = STORAGED_LINUX_BLOCK_OBJECT (object);
    gboolean ret;

    ret = FALSE;
    if (g_strcmp0 (storaged_block_get_id_usage (block_object->iface_block_device), "crypto") == 0 &&
            g_strcmp0 (storaged_block_get_id_type (block_object->iface_block_device), "crypto_LUKS") == 0)
        ret = TRUE;

    return ret;
}
예제 #16
0
static gboolean
loop_check (StoragedObject *object)
{
    StoragedLinuxBlockObject *block_object = STORAGED_LINUX_BLOCK_OBJECT (object);
    gboolean ret;

    ret = FALSE;
    if (g_str_has_prefix (g_udev_device_get_name (block_object->device->udev_device), "loop") &&
            g_strcmp0 (g_udev_device_get_devtype (block_object->device->udev_device), "disk") == 0)
        ret = TRUE;

    return ret;
}
예제 #17
0
static gboolean
partition_table_check (StoragedObject *object)
{
    StoragedLinuxBlockObject *block_object = STORAGED_LINUX_BLOCK_OBJECT (object);
    gboolean ret = FALSE;

    /* only consider whole disks, never partitions */
    if (g_strcmp0 (g_udev_device_get_devtype (block_object->device->udev_device), "disk") != 0)
        goto out;

    /* if blkid(8) already identified the device as a partition table, it's all good */
    if (g_udev_device_has_property (block_object->device->udev_device, "ID_PART_TABLE_TYPE"))
    {
        /* however, if blkid(8) also think that we're a filesystem... then don't
         * mark us as a partition table ... except if we are partitioned by the
         * kernel
         *
         * (see filesystem_check() for the similar case where we don't pretend
         * to be a filesystem)
         */
        if (g_strcmp0 (g_udev_device_get_property (block_object->device->udev_device, "ID_FS_USAGE"), "filesystem") == 0)
        {
            if (!disk_is_partitioned_by_kernel (block_object->device->udev_device))
            {
                goto out;
            }
        }
        ret = TRUE;
        goto out;
    }

    /* Note that blkid(8) might not detect all partition table
     * formats that the kernel knows about.... so we need to
     * double check...
     *
     * Fortunately, note that the kernel guarantees that all children
     * block devices that are partitions are created before the uevent
     * for the parent block device.... so if the parent block device has
     * children... then it must be partitioned by the kernel, hence it
     * must contain a partition table.
     */
    if (disk_is_partitioned_by_kernel (block_object->device->udev_device))
    {
        ret = TRUE;
        goto out;
    }

out:
    return ret;
}
예제 #18
0
static gboolean
btrfs_block_check (StoragedObject *object)
{
  const gchar *fs_type = NULL;
  StoragedLinuxDevice *device = NULL;

  g_return_val_if_fail (STORAGED_IS_LINUX_BLOCK_OBJECT (object), FALSE);

  /* Check filesystem type from udev property. */
  device = storaged_linux_block_object_get_device (STORAGED_LINUX_BLOCK_OBJECT (object));
  fs_type = g_udev_device_get_property (device->udev_device, "ID_FS_TYPE");

  return g_strcmp0 (fs_type, "btrfs") == 0;
}
예제 #19
0
/**
 * storaged_linux_drive_object_get_block:
 * @object: A #StoragedLinuxDriveObject.
 * @get_hw: If the drive is multipath, set to %TRUE to get a path device instead of the multipath device.
 *
 * Gets a #StoragedLinuxBlockObject representing a block device associated with @object.
 *
 * Returns: A #StoragedLinuxBlockObject or %NULL. The returned object
 * must be freed with g_object_unref().
 */
StoragedLinuxBlockObject *
storaged_linux_drive_object_get_block (StoragedLinuxDriveObject   *object,
                                       gboolean                    get_hw)
{
    GDBusObjectManagerServer *object_manager;
    StoragedLinuxBlockObject *ret;
    GList *objects;
    GList *l;

    /* TODO: actually look at @get_hw */

    ret = NULL;

    object_manager = storaged_daemon_get_object_manager (object->daemon);
    objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (object_manager));
    for (l = objects; l != NULL; l = l->next)
    {
        GDBusObjectSkeleton *iter_object = G_DBUS_OBJECT_SKELETON (l->data);
        StoragedBlock *block;
        StoragedLinuxDevice *device;
        gboolean is_disk;

        if (!STORAGED_IS_LINUX_BLOCK_OBJECT (iter_object))
            continue;

        device = storaged_linux_block_object_get_device (STORAGED_LINUX_BLOCK_OBJECT (iter_object));
        is_disk = (g_strcmp0 (g_udev_device_get_devtype (device->udev_device), "disk") == 0);
        g_object_unref (device);

        if (!is_disk)
            continue;

        block = storaged_object_peek_block (STORAGED_OBJECT (iter_object));
        if (g_strcmp0 (storaged_block_get_drive (block),
                       g_dbus_object_get_object_path (G_DBUS_OBJECT (object))) == 0)
        {
            ret = g_object_ref (iter_object);
            goto out;
        }
    }

out:
    g_list_foreach (objects, (GFunc) g_object_unref, NULL);
    g_list_free (objects);
    return ret;
}
예제 #20
0
static gboolean
swapspace_check (StoragedObject *object)
{
    StoragedLinuxBlockObject *block_object = STORAGED_LINUX_BLOCK_OBJECT (object);
    gboolean ret;
    StoragedMountType mount_type;

    ret = FALSE;
    if ((g_strcmp0 (storaged_block_get_id_usage (block_object->iface_block_device), "other") == 0 &&
            g_strcmp0 (storaged_block_get_id_type (block_object->iface_block_device), "swap") == 0)
            || (storaged_mount_monitor_is_dev_in_use (block_object->mount_monitor,
                    g_udev_device_get_device_number (block_object->device->udev_device),
                    &mount_type)
                && mount_type == STORAGED_MOUNT_TYPE_SWAP))
        ret = TRUE;

    return ret;
}
예제 #21
0
static gboolean
bcache_block_check (StoragedObject *object)
{
  const gchar *devname = NULL;
  StoragedLinuxDevice *device = NULL;
  gboolean rval = FALSE;

  g_return_val_if_fail (STORAGED_IS_LINUX_BLOCK_OBJECT (object), FALSE);

  /* Check device name */
  device = storaged_linux_block_object_get_device (STORAGED_LINUX_BLOCK_OBJECT (object));
  devname = g_strdup (g_udev_device_get_device_file (device->udev_device));

  rval = g_str_has_prefix (devname, "/dev/bcache");

  g_free ((gpointer) devname);

  return rval;
}
예제 #22
0
static void
storaged_linux_block_object_get_property (GObject    *__object,
        guint       prop_id,
        GValue     *value,
        GParamSpec *pspec)
{
    StoragedLinuxBlockObject *object = STORAGED_LINUX_BLOCK_OBJECT (__object);

    switch (prop_id)
    {
    case PROP_DAEMON:
        g_value_set_object (value, storaged_linux_block_object_get_daemon (object));
        break;

    case PROP_DEVICE:
        g_value_set_object (value, storaged_linux_block_object_get_device (object));
        break;

    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
    }
}
예제 #23
0
static gboolean
partition_check (StoragedObject *object)
{
    StoragedLinuxBlockObject *block_object = STORAGED_LINUX_BLOCK_OBJECT (object);
    gboolean ret = FALSE;

    /* could be partitioned by the kernel */
    if (g_strcmp0 (g_udev_device_get_devtype (block_object->device->udev_device), "partition") == 0)
    {
        ret = TRUE;
        goto out;
    }

    /* if blkid(8) already identified the device as a partition, it's all good */
    if (g_udev_device_has_property (block_object->device->udev_device, "ID_PART_ENTRY_SCHEME"))
    {
        ret = TRUE;
        goto out;
    }

out:
    return ret;
}
예제 #24
0
static gboolean
handle_volume_group_create (StoragedManagerLVM2     *_object,
                            GDBusMethodInvocation   *invocation,
                            const gchar             *arg_name,
                            const gchar *const      *arg_blocks,
                            GVariant                *arg_options)
{
  StoragedLinuxManagerLVM2 *manager = STORAGED_LINUX_MANAGER_LVM2(_object);
  uid_t caller_uid;
  GError *error = NULL;
  const gchar *message;
  const gchar *action_id;
  GList *blocks = NULL;
  GList *l;
  guint n;
  gchar *escaped_name = NULL;
  GString *str = NULL;
  gint status;
  gchar *error_message = NULL;
  StoragedObject *group_object = NULL;

  error = NULL;
  if (!storaged_daemon_util_get_caller_uid_sync (manager->daemon, invocation, NULL /* GCancellable */, &caller_uid, NULL, NULL, &error))
    {
      g_dbus_method_invocation_return_gerror (invocation, error);
      g_clear_error (&error);
      goto out;
    }

  message = N_("Authentication is required to create a volume group");
  action_id = "org.storaged.Storaged.lvm2.manage-lvm";
  if (!storaged_daemon_util_check_authorization_sync (manager->daemon,
                                                      NULL,
                                                      action_id,
                                                      arg_options,
                                                      message,
                                                      invocation))
    goto out;

  /* Collect and validate block objects
   *
   * Also, check we can open the block devices at the same time - this
   * is to avoid start deleting half the block devices while the other
   * half is already in use.
   */
  for (n = 0; arg_blocks != NULL && arg_blocks[n] != NULL; n++)
    {
      StoragedObject *object = NULL;
      StoragedBlock *block = NULL;

      object = storaged_daemon_find_object (manager->daemon, arg_blocks[n]);
      if (object == NULL)
        {
          g_dbus_method_invocation_return_error (invocation,
                                                 STORAGED_ERROR,
                                                 STORAGED_ERROR_FAILED,
                                                 "Invalid object path %s at index %u",
                                                 arg_blocks[n], n);
          goto out;
        }

      block = storaged_object_get_block (object);
      if (block == NULL)
        {
          g_dbus_method_invocation_return_error (invocation,
                                                 STORAGED_ERROR,
                                                 STORAGED_ERROR_FAILED,
                                                 "Object path %s for index %u is not a block device",
                                                 arg_blocks[n], n);
          goto out;
        }

      if (!storaged_daemon_util_lvm2_block_is_unused (block, &error))
        {
          g_dbus_method_invocation_take_error (invocation, error);
          goto out;
        }

      blocks = g_list_prepend (blocks, block); /* adopts ownership */
      g_object_unref (object);
    }
  blocks = g_list_reverse (blocks);

  /* wipe existing devices */
  for (l = blocks; l != NULL; l = l->next)
    {
      if (!storaged_daemon_util_lvm2_wipe_block (manager->daemon, STORAGED_BLOCK (l->data), &error))
        {
          g_dbus_method_invocation_take_error (invocation, error);
          goto out;
        }
    }

  /* Create the volume group... */
  escaped_name = storaged_daemon_util_escape_and_quote (arg_name);
  str = g_string_new ("vgcreate");
  g_string_append_printf (str, " %s", escaped_name);
  for (l = blocks; l != NULL; l = l->next)
    {
      StoragedBlock *block = STORAGED_BLOCK (l->data);
      gchar *escaped_device;
      escaped_device = storaged_daemon_util_escape_and_quote (storaged_block_get_device (block));
      g_string_append_printf (str, " %s", escaped_device);
      g_free (escaped_device);
    }

  if (!storaged_daemon_launch_spawned_job_sync (manager->daemon,
                                                NULL,
                                                "lvm-vg-create", caller_uid,
                                                NULL, /* cancellable */
                                                0,    /* uid_t run_as_uid */
                                                0,    /* uid_t run_as_euid */
                                                &status,
                                                &error_message,
                                                NULL, /* input_string */
                                                "%s",
                                                str->str))
    {
      g_dbus_method_invocation_return_error (invocation,
                                             STORAGED_ERROR,
                                             STORAGED_ERROR_FAILED,
                                             "Error creating volume group: %s",
                                             error_message);
      g_free (error_message);
      goto out;
    }

  for (l = blocks; l != NULL; l = l->next)
    {
      StoragedBlock *block = STORAGED_BLOCK (l->data);
      StoragedObject *object_for_block;
      object_for_block = storaged_daemon_util_dup_object (block, &error);
      if (object_for_block != NULL)
        storaged_linux_block_object_trigger_uevent (STORAGED_LINUX_BLOCK_OBJECT (object_for_block));
      g_object_unref (object_for_block);
    }

  /* ... then, sit and wait for the object to show up */
  group_object = storaged_daemon_wait_for_object_sync (manager->daemon,
                                                       wait_for_volume_group_object,
                                                       (gpointer) arg_name,
                                                       NULL,
                                                       10, /* timeout_seconds */
                                                       &error);
  if (group_object == NULL)
    {
      g_prefix_error (&error,
                      "Error waiting for volume group object for %s",
                      arg_name);
      g_dbus_method_invocation_take_error (invocation, error);
      goto out;
    }

  storaged_manager_lvm2_complete_volume_group_create (_object,
                                                      invocation,
                                                      g_dbus_object_get_object_path (G_DBUS_OBJECT (group_object)));

 out:
  if (str != NULL)
    g_string_free (str, TRUE);
  g_list_free_full (blocks, g_object_unref);
  g_free (escaped_name);

  return TRUE; /* returning TRUE means that we handled the method invocation */
}
static void
update_with_variant (GPid pid,
                     GVariant *info,
                     GError *error,
                     gpointer user_data)
{
    StoragedLinuxVolumeGroupObject *object = user_data;
    StoragedDaemon *daemon;
    GDBusObjectManagerServer *manager;
    GVariantIter *iter;
    GHashTableIter volume_iter;
    gpointer key, value;
    GHashTable *new_lvs;
    GHashTable *new_pvs;
    GList *objects, *l;
    gboolean needs_polling = FALSE;

    daemon = storaged_linux_volume_group_object_get_daemon (object);
    manager = storaged_daemon_get_object_manager (daemon);

    if (error)
    {
        storaged_warning ("Failed to update LVM volume group %s: %s",
                          storaged_linux_volume_group_object_get_name (object),
                          error->message);
        g_object_unref (object);
        return;
    }

    storaged_linux_volume_group_update (STORAGED_LINUX_VOLUME_GROUP (object->iface_volume_group), info, &needs_polling);

    if (!g_dbus_object_manager_server_is_exported (manager, G_DBUS_OBJECT_SKELETON (object)))
        g_dbus_object_manager_server_export_uniquely (manager, G_DBUS_OBJECT_SKELETON (object));

    new_lvs = g_hash_table_new (g_str_hash, g_str_equal);

    if (g_variant_lookup (info, "lvs", "aa{sv}", &iter))
    {
        GVariant *lv_info = NULL;
        while (g_variant_iter_loop (iter, "@a{sv}", &lv_info))
        {
            const gchar *name;
            StoragedLinuxLogicalVolumeObject *volume;

            g_variant_lookup (lv_info, "name", "&s", &name);

            update_operations (daemon, name, lv_info, &needs_polling);

            if (lv_is_pvmove_volume (name))
                needs_polling = TRUE;

            if (storaged_daemon_util_lvm2_name_is_reserved (name))
                continue;

            volume = g_hash_table_lookup (object->logical_volumes, name);
            if (volume == NULL)
            {
                volume = storaged_linux_logical_volume_object_new (daemon, object, name);
                storaged_linux_logical_volume_object_update (volume, lv_info, &needs_polling);
                storaged_linux_logical_volume_object_update_etctabs (volume);
                g_dbus_object_manager_server_export_uniquely (manager, G_DBUS_OBJECT_SKELETON (volume));
                g_hash_table_insert (object->logical_volumes, g_strdup (name), g_object_ref (volume));
            }
            else
                storaged_linux_logical_volume_object_update (volume, lv_info, &needs_polling);

            g_hash_table_insert (new_lvs, (gchar *)name, volume);
        }
        g_variant_iter_free (iter);
    }

    g_hash_table_iter_init (&volume_iter, object->logical_volumes);
    while (g_hash_table_iter_next (&volume_iter, &key, &value))
    {
        const gchar *name = key;
        StoragedLinuxLogicalVolumeObject *volume = value;

        if (!g_hash_table_contains (new_lvs, name))
        {
            g_dbus_object_manager_server_unexport (manager,
                                                   g_dbus_object_get_object_path (G_DBUS_OBJECT (volume)));
            g_hash_table_iter_remove (&volume_iter);

            g_object_unref (G_OBJECT (volume));
        }
    }

    storaged_volume_group_set_needs_polling (STORAGED_VOLUME_GROUP (object->iface_volume_group),
            needs_polling);

    /* Update block objects. */

    new_pvs = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)g_variant_unref);
    if (g_variant_lookup (info, "pvs", "aa{sv}", &iter))
    {
        const gchar *name;
        GVariant *pv_info;
        while (g_variant_iter_next (iter, "@a{sv}", &pv_info))
        {
            if (g_variant_lookup (pv_info, "device", "&s", &name))
                g_hash_table_insert (new_pvs, (gchar *)name, pv_info);
            else
                g_variant_unref (pv_info);
        }
    }

    objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (manager));
    for (l = objects; l != NULL; l = l->next)
    {
        if (STORAGED_IS_LINUX_BLOCK_OBJECT (l->data))
            update_block (STORAGED_LINUX_BLOCK_OBJECT (l->data), object, new_lvs, new_pvs);
    }
    g_list_free_full (objects, g_object_unref);

    g_hash_table_destroy (new_lvs);
    g_hash_table_destroy (new_pvs);

    g_object_unref (object);
}
예제 #26
0
static gboolean
handle_mdraid_create (StoragedManager         *_object,
                      GDBusMethodInvocation   *invocation,
                      const gchar *const      *arg_blocks,
                      const gchar             *arg_level,
                      const gchar             *arg_name,
                      guint64                  arg_chunk,
                      GVariant                *arg_options)
{
  StoragedLinuxManager *manager = STORAGED_LINUX_MANAGER (_object);
  StoragedObject *array_object = NULL;
  uid_t caller_uid;
  GError *error = NULL;
  const gchar *message;
  const gchar *action_id;
  guint num_devices = 0;
  GList *blocks = NULL;
  GList *l;
  guint n;
  gchar *escaped_name = NULL;
  GString *str = NULL;
  gint status;
  gchar *error_message = NULL;
  gchar *raid_device_file = NULL;
  struct stat statbuf;
  dev_t raid_device_num;

  error = NULL;
  if (!storaged_daemon_util_get_caller_uid_sync (manager->daemon, invocation, NULL /* GCancellable */, &caller_uid, NULL, NULL, &error))
    {
      g_dbus_method_invocation_return_gerror (invocation, error);
      g_clear_error (&error);
      goto out;
    }

  /* Translators: Shown in authentication dialog when the user
   * attempts to start a RAID Array.
   */
  /* TODO: variables */
  message = N_("Authentication is required to create a RAID array");
  action_id = "org.storaged.Storaged.manage-md-raid";
  if (!storaged_daemon_util_check_authorization_sync (manager->daemon,
                                                      NULL,
                                                      action_id,
                                                      arg_options,
                                                      message,
                                                      invocation))
    goto out;

  /* validate level */
  for (n = 0; raid_level_whitelist[n] != NULL; n++)
    {
      if (g_strcmp0 (raid_level_whitelist[n], arg_level) == 0)
        break;
    }
  if (raid_level_whitelist[n] == NULL)
    {
      g_dbus_method_invocation_return_error (invocation, STORAGED_ERROR, STORAGED_ERROR_FAILED,
                                             "Unsupported RAID level %s", arg_level);
      goto out;
    }

  /* validate chunk (TODO: check that it's a power of 2) */
  if ((arg_chunk & 0x0fff) != 0)
    {
      g_dbus_method_invocation_return_error (invocation, STORAGED_ERROR, STORAGED_ERROR_FAILED,
                                             "Chunk %" G_GUINT64_FORMAT " is not a multiple of 4KiB", arg_chunk);
      goto out;
    }

  /* validate name */
  if (g_strcmp0 (arg_level, "raid1") == 0 && arg_chunk != 0)
    {
      g_dbus_method_invocation_return_error (invocation, STORAGED_ERROR, STORAGED_ERROR_FAILED,
                                             "Chunk must be zero for level 'raid1'");
      goto out;
    }

  /* validate name */
  if (strlen (arg_name) > 32)
    {
      g_dbus_method_invocation_return_error (invocation, STORAGED_ERROR, STORAGED_ERROR_FAILED,
                                             "Name is invalid");
      goto out;
    }

  num_devices = g_strv_length ((gchar **) arg_blocks);

  /* validate number of devices */
  if (num_devices < 2)
    {
      g_dbus_method_invocation_return_error (invocation, STORAGED_ERROR, STORAGED_ERROR_FAILED,
                                             "Must have at least two devices");
      goto out;
    }

  /* Collect and validate block objects
   *
   * Also, check we can open the block devices at the same time - this
   * is to avoid start deleting half the block devices while the other
   * half is already in use.
   */
  for (n = 0; arg_blocks != NULL && arg_blocks[n] != NULL; n++)
    {
      StoragedObject *object = NULL;
      StoragedBlock *block = NULL;
      gchar *device_file = NULL;
      int fd;

      object = storaged_daemon_find_object (manager->daemon, arg_blocks[n]);
      if (object == NULL)
        {
          g_dbus_method_invocation_return_error (invocation,
                                                 STORAGED_ERROR,
                                                 STORAGED_ERROR_FAILED,
                                                 "Invalid object path %s at index %u",
                                                 arg_blocks[n], n);
          goto out;
        }

      block = storaged_object_get_block (object);
      if (block == NULL)
        {
          g_dbus_method_invocation_return_error (invocation,
                                                 STORAGED_ERROR,
                                                 STORAGED_ERROR_FAILED,
                                                 "Object path %s for index %u is not a block device",
                                                 arg_blocks[n], n);
          goto out;
        }

      device_file = storaged_block_dup_device (block);
      fd = open (device_file, O_RDWR | O_EXCL);
      if (fd < 0)
        {
          g_dbus_method_invocation_return_error (invocation,
                                                 STORAGED_ERROR,
                                                 STORAGED_ERROR_FAILED,
                                                 "Error opening device %s: %m",
                                                 device_file);
          g_free (device_file);
          goto out;
        }
      close (fd);
      g_free (device_file);

      blocks = g_list_prepend (blocks, block); /* adopts ownership */
      g_object_unref (object);
    }
  blocks = g_list_reverse (blocks);

  /* wipe existing devices */
  for (l = blocks; l != NULL; l = l->next)
    {
      StoragedBlock *block = STORAGED_BLOCK (l->data);
      StoragedObject *object_for_block;
      gchar *escaped_device;
      object_for_block = storaged_daemon_util_dup_object (block, &error);
      if (object_for_block == NULL)
        {
          g_dbus_method_invocation_return_gerror (invocation, error);
          g_clear_error (&error);
          goto out;
        }
      escaped_device = storaged_daemon_util_escape (storaged_block_get_device (block));
      if (!storaged_daemon_launch_spawned_job_sync (manager->daemon,
                                                  object_for_block,
                                                  "format-erase", caller_uid,
                                                  NULL, /* cancellable */
                                                  0,    /* uid_t run_as_uid */
                                                  0,    /* uid_t run_as_euid */
                                                  &status,
                                                  &error_message,
                                                  NULL, /* input_string */
                                                  "wipefs -a \"%s\"",
                                                  escaped_device))
        {
          g_dbus_method_invocation_return_error (invocation,
                                                 STORAGED_ERROR,
                                                 STORAGED_ERROR_FAILED,
                                                 "Error wiping device %s to be used in a RAID array: %s",
                                                 storaged_block_get_device (block),
                                                 error_message);
          g_free (error_message);
          g_object_unref (object_for_block);
          g_free (escaped_device);
          goto out;
        }
      g_object_unref (object_for_block);
      g_free (escaped_device);
    }

  /* Create the array... */
  escaped_name = storaged_daemon_util_escape (arg_name);
  str = g_string_new ("mdadm");
  raid_device_file = storaged_daemon_util_get_free_mdraid_device ();
  if (raid_device_file == NULL)
    {
      g_dbus_method_invocation_return_error (invocation, STORAGED_ERROR, STORAGED_ERROR_FAILED,
                                             "Unable to find free MD device");
      goto out;
    }
  g_string_append_printf (str, " --create %s", raid_device_file);
  g_string_append_printf (str, " --run");
  if (arg_chunk > 0)
    g_string_append_printf (str, " --chunk %" G_GUINT64_FORMAT, (guint64) (arg_chunk / 1024LL));
  g_string_append_printf (str, " --level %s", arg_level);
  if (strlen (arg_name) > 0)
    g_string_append_printf (str, " --name \"%s\"", escaped_name);
  g_string_append_printf (str, " --raid-devices %u", num_devices);
  for (l = blocks; l != NULL; l = l->next)
    {
      StoragedBlock *block = STORAGED_BLOCK (l->data);
      gchar *escaped_device;
      escaped_device = storaged_daemon_util_escape (storaged_block_get_device (block));
      g_string_append_printf (str, " \"%s\"", escaped_device);
      g_free (escaped_device);
    }

  if (!storaged_daemon_launch_spawned_job_sync (manager->daemon,
                                                NULL,
                                                "mdraid-create", caller_uid,
                                                NULL, /* cancellable */
                                                0,    /* uid_t run_as_uid */
                                                0,    /* uid_t run_as_euid */
                                                &status,
                                                &error_message,
                                                NULL, /* input_string */
                                                "%s",
                                                str->str))
    {
      g_dbus_method_invocation_return_error (invocation,
                                             STORAGED_ERROR,
                                             STORAGED_ERROR_FAILED,
                                             "Error creating RAID array: %s",
                                             error_message);
      g_free (error_message);
      goto out;
    }

  /* ... then, sit and wait for raid array object to show up */
  array_object = storaged_daemon_wait_for_object_sync (manager->daemon,
                                                       wait_for_array_object,
                                                       raid_device_file,
                                                       NULL,
                                                       10, /* timeout_seconds */
                                                       &error);
  if (array_object == NULL)
    {
      g_prefix_error (&error,
                      "Error waiting for array object after creating %s",
                      raid_device_file);
      g_dbus_method_invocation_take_error (invocation, error);
      goto out;
    }

  if (stat (raid_device_file, &statbuf) != 0)
    {
      g_dbus_method_invocation_return_error (invocation,
                                             STORAGED_ERROR,
                                             STORAGED_ERROR_FAILED,
                                             "Error calling stat(2) on %s: %m",
                                             raid_device_file);
      goto out;
    }
  if (!S_ISBLK (statbuf.st_mode))
    {
      g_dbus_method_invocation_return_error (invocation,
                                             STORAGED_ERROR,
                                             STORAGED_ERROR_FAILED,
                                             "Device file %s is not a block device",
                                             raid_device_file);
      goto out;
    }
  raid_device_num = statbuf.st_rdev;

  /* update the mdraid file */
  storaged_state_add_mdraid (storaged_daemon_get_state (manager->daemon),
                             raid_device_num,
                             caller_uid);

  /* ... wipe the created RAID array */
  if (!storaged_daemon_launch_spawned_job_sync (manager->daemon,
                                                array_object,
                                                "format-erase", caller_uid,
                                                NULL, /* cancellable */
                                                0,    /* uid_t run_as_uid */
                                                0,    /* uid_t run_as_euid */
                                                &status,
                                                &error_message,
                                                NULL, /* input_string */
                                                "wipefs -a %s",
                                                raid_device_file))
    {
      g_dbus_method_invocation_return_error (invocation,
                                             STORAGED_ERROR,
                                             STORAGED_ERROR_FAILED,
                                             "Error wiping raid device %s: %s",
                                             raid_device_file,
                                             error_message);
      goto out;
    }

  /* ... finally trigger uevents on the members - we want this so the
   * udev database is updated for them with e.g. ID_FS_TYPE. Ideally
   * mdadm(8) or whatever thing is writing out the RAID metadata would
   * ensure this, but that's not how things currently work :-/
   */
  for (l = blocks; l != NULL; l = l->next)
    {
      StoragedBlock *block = STORAGED_BLOCK (l->data);
      StoragedObject *object_for_block;
      object_for_block = storaged_daemon_util_dup_object (block, &error);
      if (object_for_block == NULL)
        {
          g_dbus_method_invocation_return_gerror (invocation, error);
          g_clear_error (&error);
          goto out;
        }
      storaged_linux_block_object_trigger_uevent (STORAGED_LINUX_BLOCK_OBJECT (object_for_block));
      g_object_unref (object_for_block);
    }

  /* ... and, we're done! */
  storaged_manager_complete_mdraid_create (_object,
                                           invocation,
                                           g_dbus_object_get_object_path (G_DBUS_OBJECT (array_object)));

 out:
  g_free (raid_device_file);
  if (str != NULL)
    g_string_free (str, TRUE);
  g_list_free_full (blocks, g_object_unref);
  g_free (escaped_name);
  g_clear_object (&array_object);

  return TRUE; /* returning TRUE means that we handled the method invocation */
}
예제 #27
0
static StoragedObject *
wait_for_loop_object (StoragedDaemon *daemon,
                      gpointer        user_data)
{
  WaitForLoopData *data = user_data;
  StoragedObject *ret = NULL;
  StoragedObject *object = NULL;
  StoragedBlock *block;
  StoragedLoop *loop;
  StoragedLinuxDevice *device = NULL;
  GDir *dir;

  /* First see if we have the right loop object */
  object = storaged_daemon_find_block_by_device_file (daemon, data->loop_device);
  if (object == NULL)
    goto out;
  block = storaged_object_peek_block (object);
  loop = storaged_object_peek_loop (object);
  if (block == NULL || loop == NULL)
    goto out;
  if (g_strcmp0 (storaged_loop_get_backing_file (loop), data->path) != 0)
    goto out;

  /* We also need to wait for all partitions to be in place in case
   * the loop device is partitioned... we can do it like this because
   * we are guaranteed that partitions are in sysfs when receiving the
   * uevent for the main block device...
   */
  device = storaged_linux_block_object_get_device (STORAGED_LINUX_BLOCK_OBJECT (object));
  if (device == NULL)
    goto out;

  dir = g_dir_open (g_udev_device_get_sysfs_path (device->udev_device), 0 /* flags */, NULL /* GError */);
  if (dir != NULL)
    {
      const gchar *name;
      const gchar *device_name;
      device_name = g_udev_device_get_name (device->udev_device);
      while ((name = g_dir_read_name (dir)) != NULL)
        {
          if (g_str_has_prefix (name, device_name))
            {
              gchar *sysfs_path;
              StoragedObject *partition_object;
              sysfs_path = g_strconcat (g_udev_device_get_sysfs_path (device->udev_device), "/", name, NULL);
              partition_object = storaged_daemon_find_block_by_sysfs_path (daemon, sysfs_path);
              if (partition_object == NULL)
                {
                  /* nope, not there, bail */
                  g_free (sysfs_path);
                  g_dir_close (dir);
                  goto out;
                }
              g_object_unref (partition_object);
              g_free (sysfs_path);
            }
        }
      g_dir_close (dir);
    }

  /* all, good return the loop object */
  ret = g_object_ref (object);

 out:
  g_clear_object (&object);
  g_clear_object (&device);
  return ret;
}
static StoragedObject *
storaged_linux_partition_table_handle_create_partition (StoragedPartitionTable   *table,
                                                        GDBusMethodInvocation    *invocation,
                                                        guint64                   offset,
                                                        guint64                   size,
                                                        const gchar              *type,
                                                        const gchar              *name,
                                                        GVariant                 *options)
{
  const gchar *action_id = NULL;
  const gchar *message = NULL;
  StoragedBlock *block = NULL;
  StoragedObject *object = NULL;
  StoragedDaemon *daemon = NULL;
  gchar *error_message = NULL;
  gchar *escaped_device = NULL;
  gchar *command_line = NULL;
  WaitForPartitionData *wait_data = NULL;
  StoragedObject *partition_object = NULL;
  StoragedBlock *partition_block = NULL;
  gchar *escaped_partition_device = NULL;
  const gchar *table_type;
  uid_t caller_uid;
  gid_t caller_gid;
  gboolean do_wipe = TRUE;
  GError *error;

  error = NULL;
  object = storaged_daemon_util_dup_object (table, &error);
  if (object == NULL)
    {
      g_dbus_method_invocation_take_error (invocation, error);
      goto out;
    }

  daemon = storaged_linux_block_object_get_daemon (STORAGED_LINUX_BLOCK_OBJECT (object));
  block = storaged_object_get_block (object);
  if (block == NULL)
    {
      g_dbus_method_invocation_return_error (invocation, STORAGED_ERROR, STORAGED_ERROR_FAILED,
                                             "Partition table object is not a block device");
      goto out;
    }

  error = NULL;
  if (!storaged_daemon_util_get_caller_uid_sync (daemon,
                                                 invocation,
                                                 NULL /* GCancellable */,
                                                 &caller_uid,
                                                 &caller_gid,
                                                 NULL,
                                                 &error))
    {
      g_dbus_method_invocation_return_gerror (invocation, error);
      g_error_free (error);
      goto out;
    }

  action_id = "org.storaged.Storaged.modify-device";
  /* Translators: Shown in authentication dialog when the user
   * requests creating a new partition.
   *
   * Do not translate $(drive), it's a placeholder and
   * will be replaced by the name of the drive/device in question
   */
  message = N_("Authentication is required to create a partition on $(drive)");
  if (!storaged_daemon_util_setup_by_user (daemon, object, caller_uid))
    {
      if (storaged_block_get_hint_system (block))
        {
          action_id = "org.storaged.Storaged.modify-device-system";
        }
      else if (!storaged_daemon_util_on_user_seat (daemon, object, caller_uid))
        {
          action_id = "org.storaged.Storaged.modify-device-other-seat";
        }
    }

  if (!storaged_daemon_util_check_authorization_sync (daemon,
                                                    object,
                                                    action_id,
                                                    options,
                                                    message,
                                                    invocation))
    goto out;

  escaped_device = storaged_daemon_util_escape_and_quote (storaged_block_get_device (block));

  table_type = storaged_partition_table_get_type_ (table);
  wait_data = g_new0 (WaitForPartitionData, 1);
  if (g_strcmp0 (table_type, "dos") == 0)
    {
      guint64 start_mib;
      guint64 end_bytes;
      guint64 max_end_bytes;
      const gchar *part_type;
      char *endp;
      gint type_as_int;
      gboolean is_logical = FALSE;

      max_end_bytes = storaged_block_get_size (block);

      if (strlen (name) > 0)
        {
          g_dbus_method_invocation_return_error (invocation, STORAGED_ERROR, STORAGED_ERROR_FAILED,
                                                 "MBR partition table does not support names");
          goto out;
        }

      /* Determine whether we are creating a primary, extended or logical partition */
      type_as_int = strtol (type, &endp, 0);
      if (type[0] != '\0' && *endp == '\0' &&
          (type_as_int == 0x05 || type_as_int == 0x0f || type_as_int == 0x85))
        {
          part_type = "extended";
          do_wipe = FALSE;  // wiping an extended partition destroys it
          if (have_partition_in_range (table, object, offset, offset + size, FALSE))
            {
              g_dbus_method_invocation_return_error (invocation, STORAGED_ERROR, STORAGED_ERROR_FAILED,
                                                     "Requested range is already occupied by a partition");
              goto out;
            }
        }
      else
        {
          if (have_partition_in_range (table, object, offset, offset + size, FALSE))
            {
              if (have_partition_in_range (table, object, offset, offset + size, TRUE))
                {
                  g_dbus_method_invocation_return_error (invocation, STORAGED_ERROR, STORAGED_ERROR_FAILED,
                                                         "Requested range is already occupied by a partition");
                  goto out;
                }
              else
                {
                  StoragedPartition *container = find_container_partition (table, object,
                                                                           offset, offset + size);
                  g_assert (container != NULL);
                  is_logical = TRUE;
                  part_type = "logical ext2";
                  max_end_bytes = (storaged_partition_get_offset(container)
                                   + storaged_partition_get_size(container));
                }
            }
          else
            {
              part_type = "primary ext2";
            }
        }

      /* Ensure we _start_ at MiB granularity since that ensures optimal IO...
       * Also round up size to nearest multiple of 512
       */
      start_mib = offset / MIB_SIZE + 1L;
      end_bytes = start_mib * MIB_SIZE + ((size + 511L) & (~511L));

      /* Now reduce size until we are not
       *
       *  - overlapping neighboring partitions; or
       *  - exceeding the end of the disk
       */
      while (end_bytes > start_mib * MIB_SIZE && (have_partition_in_range (table,
                                                                           object,
                                                                           start_mib * MIB_SIZE,
                                                                           end_bytes, is_logical) ||
                                                  end_bytes > max_end_bytes))
        {
          /* TODO: if end_bytes is sufficiently big this could be *a lot* of loop iterations
           *       and thus a potential DoS attack...
           */
          end_bytes -= 512L;
        }
      wait_data->pos_to_wait_for = (start_mib*MIB_SIZE + end_bytes) / 2L;
      wait_data->ignore_container = is_logical;

      command_line = g_strdup_printf ("parted --align optimal --script %s "
                                      "\"mkpart %s %" G_GUINT64_FORMAT "MiB %" G_GUINT64_FORMAT "b\"",
                                      escaped_device,
                                      part_type,
                                      start_mib,
                                      end_bytes - 1); /* end_bytes is *INCLUSIVE* (!) */
    }
  else if (g_strcmp0 (table_type, "gpt") == 0)
    {
      guint64 start_mib;
      guint64 end_bytes;
      gchar *escaped_name;
      gchar *escaped_escaped_name;

      /* GPT is easy, no extended/logical crap */
      if (have_partition_in_range (table, object, offset, offset + size, FALSE))
        {
          g_dbus_method_invocation_return_error (invocation, STORAGED_ERROR, STORAGED_ERROR_FAILED,
                                                 "Requested range is already occupied by a partition");
          goto out;
        }

      /* bah, parted(8) is broken with empty names (it sets the name to 'ext2' in that case)
       * TODO: file bug
       */
      if (strlen (name) == 0)
        {
          name = " ";
        }

      escaped_name = storaged_daemon_util_escape (name);
      escaped_escaped_name = storaged_daemon_util_escape (escaped_name);

      /* Ensure we _start_ at MiB granularity since that ensures optimal IO...
       * Also round up size to nearest multiple of 512
       */
      start_mib = offset / MIB_SIZE + 1L;
      end_bytes = start_mib * MIB_SIZE + ((size + 511L) & (~511L));

      /* Now reduce size until we are not
       *
       *  - overlapping neighboring partitions; or
       *  - exceeding the end of the disk (note: the 33 LBAs is the Secondary GPT)
       */
      while (end_bytes > start_mib * MIB_SIZE && (have_partition_in_range (table,
                                                                           object,
                                                                           start_mib * MIB_SIZE,
                                                                           end_bytes, FALSE) ||
                                                  (end_bytes > storaged_block_get_size (block) - 33*512)))
        {
          /* TODO: if end_bytes is sufficiently big this could be *a lot* of loop iterations
           *       and thus a potential DoS attack...
           */
          end_bytes -= 512L;
        }
      wait_data->pos_to_wait_for = (start_mib*MIB_SIZE + end_bytes) / 2L;
      command_line = g_strdup_printf ("parted --align optimal --script %s "
                                      "\"mkpart \\\"%s\\\" ext2 %" G_GUINT64_FORMAT "MiB %" G_GUINT64_FORMAT "b\"",
                                      escaped_device,
                                      escaped_escaped_name,
                                      start_mib,
                                      end_bytes - 1); /* end_bytes is *INCLUSIVE* (!) */
      g_free (escaped_escaped_name);
      g_free (escaped_name);
    }
  else
    {
      g_dbus_method_invocation_return_error (invocation, STORAGED_ERROR, STORAGED_ERROR_FAILED,
                                             "Don't know how to create partitions this partition table of type `%s'",
                                             table_type);
      goto out;
    }

  if (!storaged_daemon_launch_spawned_job_sync (daemon,
                                              object,
                                              "partition-create", caller_uid,
                                              NULL, /* GCancellable */
                                              0,    /* uid_t run_as_uid */
                                              0,    /* uid_t run_as_euid */
                                              NULL, /* gint *out_status */
                                              &error_message,
                                              NULL,  /* input_string */
                                              "%s",
                                              command_line))
    {
      g_dbus_method_invocation_return_error (invocation,
                                             STORAGED_ERROR,
                                             STORAGED_ERROR_FAILED,
                                             "Error creating partition on %s: %s",
                                             storaged_block_get_device (block),
                                             error_message);
      goto out;
    }
  /* this is sometimes needed because parted(8) does not generate the uevent itself */
  storaged_linux_block_object_trigger_uevent (STORAGED_LINUX_BLOCK_OBJECT (object));

  /* sit and wait for the partition to show up */
  g_warn_if_fail (wait_data->pos_to_wait_for > 0);
  wait_data->partition_table_object = object;
  error = NULL;
  partition_object = storaged_daemon_wait_for_object_sync (daemon,
                                                           wait_for_partition,
                                                           wait_data,
                                                           NULL,
                                                           30,
                                                           &error);
  if (partition_object == NULL)
    {
      g_prefix_error (&error, "Error waiting for partition to appear: ");
      g_dbus_method_invocation_take_error (invocation, error);
      goto out;
    }
  partition_block = storaged_object_get_block (partition_object);
  if (partition_block == NULL)
    {
      g_dbus_method_invocation_return_error (invocation, STORAGED_ERROR, STORAGED_ERROR_FAILED,
                                             "Partition object is not a block device");
      g_clear_object (&partition_object);
      goto out;
    }
  escaped_partition_device = storaged_daemon_util_escape_and_quote (storaged_block_get_device (partition_block));

  /* TODO: set partition type */

  /* wipe the newly created partition if wanted */
  if (do_wipe)
    {
      if (!storaged_daemon_launch_spawned_job_sync (daemon,
                                                    partition_object,
                                                    "partition-create", caller_uid,
                                                    NULL, /* GCancellable */
                                                    0,    /* uid_t run_as_uid */
                                                    0,    /* uid_t run_as_euid */
                                                    NULL, /* gint *out_status */
                                                    &error_message,
                                                    NULL,  /* input_string */
                                                    "wipefs -a %s",
                                                    escaped_partition_device))
        {
          g_dbus_method_invocation_return_error (invocation,
                                                 STORAGED_ERROR,
                                                 STORAGED_ERROR_FAILED,
                                                 "Error wiping newly created partition %s: %s",
                                                 storaged_block_get_device (partition_block),
                                                 error_message);
          g_clear_object (&partition_object);
          goto out;
        }
    }

  /* this is sometimes needed because parted(8) does not generate the uevent itself */
  storaged_linux_block_object_trigger_uevent (STORAGED_LINUX_BLOCK_OBJECT (partition_object));

 out:
  g_free (escaped_partition_device);
  g_free (wait_data);
  g_clear_object (&partition_block);
  g_free (command_line);
  g_free (escaped_device);
  g_free (error_message);
  g_clear_object (&object);
  g_clear_object (&block);
  return partition_object;
}