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 */
}
/* runs in thread dedicated to handling @invocation */
static gboolean
handle_loop_setup (StoragedManager          *object,
                   GDBusMethodInvocation    *invocation,
                   GUnixFDList              *fd_list,
                   GVariant                 *fd_index,
                   GVariant                 *options)
{
  StoragedLinuxManager *manager = STORAGED_LINUX_MANAGER (object);
  GError *error;
  gint fd_num;
  gint fd = -1;
  gchar proc_path[64];
  gchar path[8192];
  ssize_t path_len;
  gint loop_fd = -1;
  gint loop_control_fd = -1;
  gint allocated_loop_number = -1;
  gchar *loop_device = NULL;
  struct loop_info64 li64;
  StoragedObject *loop_object = NULL;
  gboolean option_read_only = FALSE;
  gboolean option_no_part_scan = FALSE;
  guint64 option_offset = 0;
  guint64 option_size = 0;
  uid_t caller_uid;
  struct stat fd_statbuf;
  gboolean fd_statbuf_valid = FALSE;
  WaitForLoopData wait_data;

  /* we need the uid of the caller for the loop file */
  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_error_free (error);
      goto out;
    }

  /* Check if the user is authorized to create a loop device */
  if (!storaged_daemon_util_check_authorization_sync (manager->daemon,
                                                      NULL,
                                                      "org.storaged.Storaged.loop-setup",
                                                      options,
                                                      /* Translators: Shown in authentication dialog when the user
                                                       * requests setting up a loop device.
                                                       */
                                                      N_("Authentication is required to set up a loop device"),
                                                      invocation))
    goto out;

  fd_num = g_variant_get_handle (fd_index);
  if (fd_list == NULL || fd_num >= g_unix_fd_list_get_length (fd_list))
    {
      g_dbus_method_invocation_return_error (invocation,
                                             STORAGED_ERROR,
                                             STORAGED_ERROR_FAILED,
                                             "Expected to use fd at index %d, but message has only %d fds",
                                             fd_num,
                                             fd_list == NULL ? 0 : g_unix_fd_list_get_length (fd_list));
      goto out;
    }
  error = NULL;
  fd = g_unix_fd_list_get (fd_list, fd_num, &error);
  if (fd == -1)
    {
      g_prefix_error (&error, "Error getting file descriptor %d from message: ", fd_num);
      g_dbus_method_invocation_take_error (invocation, error);
      goto out;
    }

  snprintf (proc_path, sizeof (proc_path), "/proc/%d/fd/%d", getpid (), fd);
  path_len = readlink (proc_path, path, sizeof (path) - 1);
  if (path_len < 1)
    {
      g_dbus_method_invocation_return_error (invocation,
                                             STORAGED_ERROR,
                                             STORAGED_ERROR_FAILED,
                                             "Error determing path: %m");
      goto out;
    }
  path[path_len] = '\0';

  g_variant_lookup (options, "read-only", "b", &option_read_only);
  g_variant_lookup (options, "offset", "t", &option_offset);
  g_variant_lookup (options, "size", "t", &option_size);
  g_variant_lookup (options, "no-part-scan", "b", &option_no_part_scan);

  /* it's not a problem if fstat fails... for example, this can happen if the user
   * passes a fd to a file on the GVfs fuse mount
   */
  if (fstat (fd, &fd_statbuf) == 0)
    fd_statbuf_valid = TRUE;

  /* serialize access to /dev/loop-control */
  g_mutex_lock (&(manager->lock));

  loop_control_fd = open ("/dev/loop-control", O_RDWR);
  if (loop_control_fd == -1)
    {
      g_dbus_method_invocation_return_error (invocation,
                                             STORAGED_ERROR,
                                             STORAGED_ERROR_FAILED,
                                             "Error opening /dev/loop-control: %m");
      g_mutex_unlock (&(manager->lock));
      goto out;
    }

  allocated_loop_number = ioctl (loop_control_fd, LOOP_CTL_GET_FREE);
  if (allocated_loop_number < 0)
    {
      g_dbus_method_invocation_return_error (invocation,
                                             STORAGED_ERROR,
                                             STORAGED_ERROR_FAILED,
                                             "Error allocating free loop device: %m");
      g_mutex_unlock (&(manager->lock));
      goto out;
    }

  loop_device = g_strdup_printf ("/dev/loop%d", allocated_loop_number);
  loop_fd = open (loop_device, option_read_only ? O_RDONLY : O_RDWR);
  if (loop_fd == -1)
    {
      g_dbus_method_invocation_return_error (invocation,
                                             STORAGED_ERROR,
                                             STORAGED_ERROR_FAILED,
                                             "Cannot open %s: %m", loop_device);
      g_mutex_unlock (&(manager->lock));
      goto out;
    }

  /* update the loop file - need to do this before getting the uevent for the device  */
  storaged_state_add_loop (storaged_daemon_get_state (manager->daemon),
                           loop_device,
                           path,
                           fd_statbuf_valid ? fd_statbuf.st_dev : 0,
                           caller_uid);

  memset (&li64, '\0', sizeof (li64));
  strncpy ((char *) li64.lo_file_name, path, LO_NAME_SIZE - 1);
  if (option_read_only)
    li64.lo_flags |= LO_FLAGS_READ_ONLY;
  if (!option_no_part_scan)
    li64.lo_flags |= 8; /* Use LO_FLAGS_PARTSCAN when 3.2 has been out for a while */
  li64.lo_offset = option_offset;
  li64.lo_sizelimit = option_size;
  if (ioctl (loop_fd, LOOP_SET_FD, fd) < 0 || ioctl (loop_fd, LOOP_SET_STATUS64, &li64) < 0)
    {
      g_dbus_method_invocation_return_error (invocation,
                                             STORAGED_ERROR,
                                             STORAGED_ERROR_FAILED,
                                             "Error setting up loop device %s: %m",
                                             loop_device);
      g_mutex_unlock (&(manager->lock));
      goto out;
    }
  g_mutex_unlock (&(manager->lock));

  /* Determine the resulting object */
  error = NULL;
  wait_data.loop_device = loop_device;
  wait_data.path = path;
  loop_object = storaged_daemon_wait_for_object_sync (manager->daemon,
                                                    wait_for_loop_object,
                                                    &wait_data,
                                                    NULL,
                                                    10, /* timeout_seconds */
                                                    &error);
  if (loop_object == NULL)
    {
      g_prefix_error (&error,
                      "Error waiting for loop object after creating %s",
                      loop_device);
      g_dbus_method_invocation_take_error (invocation, error);
      goto out;
    }

  storaged_notice ("Set up loop device %s (backed by %s)",
                   loop_device,
                   path);

  storaged_manager_complete_loop_setup (object,
                                        invocation,
                                        NULL, /* fd_list */
                                        g_dbus_object_get_object_path (G_DBUS_OBJECT (loop_object)));

 out:
  if (loop_object != NULL)
    g_object_unref (loop_object);
  g_free (loop_device);
  if (loop_control_fd != -1)
    close (loop_control_fd);
  if (loop_fd != -1)
    close (loop_fd);
  if (fd != -1)
    close (fd);
  return TRUE; /* returning TRUE means that we handled the method invocation */
}
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 */
}
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;
}