static StoragedObject * wait_for_array_object (StoragedDaemon *daemon, gpointer user_data) { const gchar *raid_device_file = user_data; StoragedObject *object = NULL; StoragedBlock *block = NULL; gchar *mdraid_objpath = NULL; StoragedObject *ret = NULL; /* First see if we have the right array object */ object = storaged_daemon_find_block_by_device_file (daemon, raid_device_file); if (object == NULL) goto out; block = storaged_object_get_block (object); if (block == NULL) goto out; mdraid_objpath = storaged_block_dup_mdraid (block); if (g_strcmp0 (mdraid_objpath, "/") == 0) goto out; ret = storaged_daemon_find_object (daemon, mdraid_objpath); out: g_free (mdraid_objpath); g_clear_object (&block); g_clear_object (&object); return ret; }
static gboolean drive_does_not_detect_media_change (StoragedLinuxBlockObject *object) { gboolean ret = FALSE; StoragedObject *drive_object; drive_object = storaged_daemon_find_object (object->daemon, storaged_block_get_drive (object->iface_block_device)); if (drive_object != NULL) { StoragedDrive *drive = storaged_object_get_drive (drive_object); if (drive != NULL) { ret = ! storaged_drive_get_media_change_detected (drive); g_object_unref (drive); } g_object_unref (drive_object); } return ret; }
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 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 */ }