From 7a90ae3920bd0deaf76961dcabb2ff57ac157649 Mon Sep 17 00:00:00 2001 From: Marius Vollmer Date: Mon, 12 Aug 2013 12:10:00 +0300 Subject: [PATCH] LVM2. --- data/org.freedesktop.UDisks2.xml | 421 ++++++++++++ data/org.freedesktop.udisks2.policy.in | 13 + src/Makefile.am | 16 +- src/tests/Makefile.am | 1 - src/udisksdaemontypes.h | 15 + src/udisksdaemonutil.c | 382 +++++++++++ src/udisksdaemonutil.h | 19 + src/udiskslinuxblock.c | 29 +- src/udiskslinuxblockobject.c | 37 + src/udiskslinuxblockobject.h | 4 + src/udiskslinuxlogicalvolume.c | 889 ++++++++++++++++++++++++ src/udiskslinuxlogicalvolume.h | 42 ++ src/udiskslinuxlogicalvolumeobject.c | 302 ++++++++ src/udiskslinuxlogicalvolumeobject.h | 49 ++ src/udiskslinuxmanager.c | 223 +++++- src/udiskslinuxmdraid.c | 6 + src/udiskslinuxphysicalvolume.c | 131 ++++ src/udiskslinuxphysicalvolume.h | 42 ++ src/udiskslinuxprovider.c | 164 +++++ src/udiskslinuxprovider.h | 2 + src/udiskslinuxvolumegroup.c | 1178 ++++++++++++++++++++++++++++++++ src/udiskslinuxvolumegroup.h | 40 ++ src/udiskslinuxvolumegroupobject.c | 685 +++++++++++++++++++ src/udiskslinuxvolumegroupobject.h | 48 ++ src/udiskslvmhelper.c | 245 +++++++ udisks/udisksclient.c | 146 ++++ udisks/udisksclient.h | 8 + 27 files changed, 5079 insertions(+), 58 deletions(-) create mode 100644 src/udiskslinuxlogicalvolume.c create mode 100644 src/udiskslinuxlogicalvolume.h create mode 100644 src/udiskslinuxlogicalvolumeobject.c create mode 100644 src/udiskslinuxlogicalvolumeobject.h create mode 100644 src/udiskslinuxphysicalvolume.c create mode 100644 src/udiskslinuxphysicalvolume.h create mode 100644 src/udiskslinuxvolumegroup.c create mode 100644 src/udiskslinuxvolumegroup.h create mode 100644 src/udiskslinuxvolumegroupobject.c create mode 100644 src/udiskslinuxvolumegroupobject.h create mode 100644 src/udiskslvmhelper.c diff --git a/data/org.freedesktop.UDisks2.xml b/data/org.freedesktop.UDisks2.xml index 6e40c9b..23f515a 100644 --- a/data/org.freedesktop.UDisks2.xml +++ b/data/org.freedesktop.UDisks2.xml @@ -82,6 +82,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_description>Manage LVM + <_message>Authentication is required to manage LVM + + auth_admin + auth_admin + auth_admin_keep + + + + diff --git a/src/Makefile.am b/src/Makefile.am index fc44c75..89d997c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -60,6 +60,11 @@ libudisks_daemon_la_SOURCES = \ udiskslinuxdriveata.h udiskslinuxdriveata.c \ udiskslinuxmdraidobject.h udiskslinuxmdraidobject.c \ udiskslinuxmdraid.h udiskslinuxmdraid.c \ + udiskslinuxvolumegroupobject.h udiskslinuxvolumegroupobject.c \ + udiskslinuxvolumegroup.h udiskslinuxvolumegroup.c \ + udiskslinuxlogicalvolumeobject.h udiskslinuxlogicalvolumeobject.c \ + udiskslinuxlogicalvolume.h udiskslinuxlogicalvolume.c \ + udiskslinuxphysicalvolume.h udiskslinuxphysicalvolume.c \ udiskslinuxmanager.h udiskslinuxmanager.c \ udiskslinuxfsinfo.h udiskslinuxfsinfo.c \ udisksbasejob.h udisksbasejob.c \ @@ -107,7 +112,7 @@ libudisks_daemon_la_LIBADD = \ # ---------------------------------------------------------------------------------------------------- udisksdprivdir = $(prefix)/lib/udisks2 -udisksdpriv_PROGRAMS = udisksd +udisksdpriv_PROGRAMS = udisksd udisks-lvm udisksd_SOURCES = \ main.c \ @@ -123,6 +128,15 @@ udisksd_LDADD = \ libudisks-daemon.la \ $(NULL) +udisks_lvm_SOURCES = \ + udiskslvmhelper.c \ + $(NULL) + +udisks_lvm_LDADD = \ + $(GLIB_LIBS) \ + -llvm2app \ + $(NULL) + # ---------------------------------------------------------------------------------------------------- install-data-local: diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index 3c01087..ebb4166 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -63,4 +63,3 @@ udisks_test_helper_LDADD = \ $(GLIB_LIBS) \ $(GIO_LIBS) \ $(NULL) - diff --git a/src/udisksdaemontypes.h b/src/udisksdaemontypes.h index 398581c..09870e5 100644 --- a/src/udisksdaemontypes.h +++ b/src/udisksdaemontypes.h @@ -55,6 +55,21 @@ typedef struct _UDisksLinuxMDRaidObject UDisksLinuxMDRaidObject; struct _UDisksLinuxMDRaid; typedef struct _UDisksLinuxMDRaid UDisksLinuxMDRaid; +struct _UDisksLinuxVolumeGroupObject; +typedef struct _UDisksLinuxVolumeGroupObject UDisksLinuxVolumeGroupObject; + +struct _UDisksLinuxVolumeGroup; +typedef struct _UDisksLinuxVolumeGroup UDisksLinuxVolumeGroup; + +struct _UDisksLinuxLogicalVolume; +typedef struct _UDisksLinuxLogicalVolume UDisksLinuxLogicalVolume; + +struct _UDisksLinuxLogicalVolumeObject; +typedef struct _UDisksLinuxLogicalVolumeObject UDisksLinuxLogicalVolumeObject; + +struct _UDisksLinuxPhysicalVolume; +typedef struct _UDisksLinuxPhysicalVolume UDisksLinuxPhysicalVolume; + struct _UDisksBaseJob; typedef struct _UDisksBaseJob UDisksBaseJob; diff --git a/src/udisksdaemonutil.c b/src/udisksdaemonutil.c index 4c23906..cdde57f 100644 --- a/src/udisksdaemonutil.c +++ b/src/udisksdaemonutil.c @@ -24,12 +24,16 @@ #include #include +#include #include #include #include #include #include +#include +#include +#include #include #include @@ -1459,3 +1463,381 @@ udisks_ata_identify_get_word (const guchar *identify_data, guint word_number) return ret; } +gboolean +udisks_daemon_util_block_is_unused (UDisksBlock *block, + GError **error) +{ + const gchar *device_file; + int fd; + + device_file = udisks_block_get_device (block); + fd = open (device_file, O_RDONLY | O_EXCL); + if (fd < 0) + { + g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED, + "Error opening device %s: %m", + device_file); + return FALSE; + } + close (fd); + + return TRUE; +} + +static gboolean +run_sync (const gchar *prog, ...) +{ + va_list ap; + GError **error; + enum { max_argc = 20 }; + const gchar *argv[max_argc+1]; + int argc = 0; + const gchar *arg; + gchar *standard_output; + gchar *standard_error; + gint exit_status; + + argv[argc++] = prog; + va_start (ap, prog); + while ((arg = va_arg (ap, const gchar *))) + { + if (argc < max_argc) + argv[argc] = arg; + argc++; + } + error = va_arg (ap, GError **); + va_end (ap); + + if (argc > max_argc) + { + g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED, + "Too many arguments."); + return FALSE; + } + + argv[argc] = NULL; + if (!g_spawn_sync (NULL, + (gchar **)argv, + NULL, + G_SPAWN_SEARCH_PATH, + NULL, + NULL, + &standard_output, + &standard_error, + &exit_status, + error)) + return FALSE; + + if (!g_spawn_check_exit_status (exit_status, error)) + { + g_prefix_error (error, "stdout: '%s', stderr: '%s', ", standard_output, standard_error); + g_free (standard_output); + g_free (standard_error); + return FALSE; + } + + g_free (standard_output); + g_free (standard_error); + return TRUE; +} + +gboolean +udisks_daemon_util_wipe_block (UDisksDaemon *daemon, + UDisksBlock *block, + GError **error) +{ + UDisksObject *block_object; + UDisksPhysicalVolume *physical_volume; + const gchar *volume_group_objpath; + UDisksObject *volume_group_object; + UDisksVolumeGroup *volume_group; + gchar *volume_group_name = NULL; + gboolean was_partitioned; + + const gchar *device_file; + int fd = -1; + gchar zeroes[512]; + gboolean ret = TRUE; + + // Find the name of the volume group that this device is a physical + // member of, if any. Easy. + + block_object = UDISKS_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (block))); + physical_volume = udisks_object_peek_physical_volume (block_object); + if (physical_volume) + { + volume_group_objpath = udisks_physical_volume_get_volume_group (physical_volume); + volume_group_object = udisks_daemon_find_object (daemon, volume_group_objpath); + if (volume_group_object) + { + volume_group = udisks_object_peek_volume_group (volume_group_object); + if (volume_group) + volume_group_name = g_strdup (udisks_volume_group_get_name (volume_group)); + } + } + + was_partitioned = (udisks_object_peek_partition_table (block_object) != NULL); + + device_file = udisks_block_get_device (block); + + // Remove partition table + memset (zeroes, 0, 512); + fd = open (device_file, O_RDWR | O_EXCL); + if (fd < 0) + { + g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED, + "Error opening device %s: %m", + device_file); + ret = FALSE; + goto out; + } + + if (write (fd, zeroes, 512) != 512) + { + g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED, + "Error erasing device %s: %m", + device_file); + ret = FALSE; + goto out; + } + + if (was_partitioned && ioctl (fd, BLKRRPART, NULL) < 0) + { + g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED, + "Error removing partition devices of %s: %m", + device_file); + ret = FALSE; + goto out; + } + close (fd); + fd = -1; + + // wipe other labels + if (!run_sync ("wipefs", "-a", device_file, NULL, error)) + { + ret = FALSE; + goto out; + } + + // Try to bring affected volume group back into consistency. + if (volume_group_name != NULL) + run_sync ("vgreduce", volume_group_name, "--removemissing", NULL, NULL); + + out: + if (fd >= 0) + close (fd); + g_free (volume_group_name); + return ret; +} + +/* -------------------------------------------------------------------------------- */ + +struct VariantReaderData { + const GVariantType *type; + void (*callback) (GPid pid, GVariant *result, GError *error, gpointer user_data); + gpointer user_data; + GPid pid; + GIOChannel *output_channel; + GByteArray *output; + gint output_watch; +}; + +static gboolean +variant_reader_child_output (GIOChannel *source, + GIOCondition condition, + gpointer user_data) +{ + struct VariantReaderData *data = user_data; + guint8 buf[1024]; + gsize bytes_read; + + g_io_channel_read_chars (source, (gchar *)buf, sizeof buf, &bytes_read, NULL); + g_byte_array_append (data->output, buf, bytes_read); + return TRUE; +} + +static void +variant_reader_watch_child (GPid pid, + gint status, + gpointer user_data) +{ + struct VariantReaderData *data = user_data; + guint8 *buf; + gsize buf_size; + GVariant *result; + GError *error = NULL; + + data->pid = 0; + + if (!g_spawn_check_exit_status (status, &error)) + { + data->callback (pid, NULL, error, data->user_data); + g_error_free (error); + g_byte_array_free (data->output, TRUE); + } + else + { + if (g_io_channel_read_to_end (data->output_channel, (gchar **)&buf, &buf_size, NULL) == G_IO_STATUS_NORMAL) + { + g_byte_array_append (data->output, buf, buf_size); + g_free (buf); + } + + result = g_variant_new_from_data (data->type, + data->output->data, + data->output->len, + TRUE, + g_free, NULL); + g_byte_array_free (data->output, FALSE); + data->callback (pid, result, NULL, data->user_data); + g_variant_unref (result); + } +} + +static void +variant_reader_destroy (gpointer user_data) +{ + struct VariantReaderData *data = user_data; + + g_source_remove (data->output_watch); + g_io_channel_unref (data->output_channel); + g_free (data); +} + +GPid +udisks_daemon_util_spawn_for_variant (const gchar **argv, + const GVariantType *type, + void (*callback) (GPid pid, + GVariant *result, + GError *error, + gpointer user_data), + gpointer user_data) +{ + GError *error; + struct VariantReaderData *data; + GPid pid; + gint output_fd; + + if (!g_spawn_async_with_pipes (NULL, + (gchar **)argv, + NULL, + G_SPAWN_DO_NOT_REAP_CHILD, + NULL, + NULL, + &pid, + NULL, + &output_fd, + NULL, + &error)) + { + callback (0, NULL, error, user_data); + g_error_free (error); + return 0; + } + + data = g_new0 (struct VariantReaderData, 1); + + data->type = type; + data->callback = callback; + data->user_data = user_data; + + data->pid = pid; + data->output = g_byte_array_new (); + data->output_channel = g_io_channel_unix_new (output_fd); + g_io_channel_set_encoding (data->output_channel, NULL, NULL); + g_io_channel_set_flags (data->output_channel, G_IO_FLAG_NONBLOCK, NULL); + data->output_watch = g_io_add_watch (data->output_channel, G_IO_IN, variant_reader_child_output, data); + + g_child_watch_add_full (G_PRIORITY_DEFAULT_IDLE, + pid, variant_reader_watch_child, data, variant_reader_destroy); + return pid; +} + +static gboolean +valid_lvm_name_char (gint c) +{ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '+' || c == '_' || c == '.' || c == '-'; +} + +#define LVM_ENCODING_PREFIX "+_" + +gchar * +udisks_daemon_util_encode_lvm_name (const gchar *name, gboolean for_logical_volume) +{ + const gchar *n; + GString *enc; + gchar *encoded; + + for (n = name; *n; n++) + { + if (!valid_lvm_name_char (*n)) + goto encode; + } + + if (*name == '-') + goto encode; + + if (g_str_has_prefix (name, LVM_ENCODING_PREFIX)) + goto encode; + + if (for_logical_volume + && (strstr (name, "_mlog") + || strstr (name, "_mimage") + || strstr (name, "_rimage") + || strstr (name, "_rmeta") + || strstr (name, "_tdata") + || strstr (name, "_tmeta") + || g_str_has_prefix (name, "pvmove") + || g_str_has_prefix (name, "snapshot"))) + goto encode; + + return g_strdup (name); + + encode: + enc = g_string_new (LVM_ENCODING_PREFIX); + for (n = name; *n; n++) + { + if (!valid_lvm_name_char (*n) || *n == '_') + g_string_append_printf (enc, "_%02x", *(unsigned char *)n); + else + g_string_append_c (enc, *n); + } + encoded = enc->str; + g_string_free (enc, FALSE); + return encoded; +} + +gchar * +udisks_daemon_util_decode_lvm_name (const gchar *encoded) +{ + const gchar *e; + GString *dec; + gchar *decoded; + + if (!g_str_has_prefix (encoded, LVM_ENCODING_PREFIX)) + return g_strdup (encoded); + + dec = g_string_new (""); + for (e = encoded + strlen(LVM_ENCODING_PREFIX); *e; e++) + { + if (e[0] == '_') + { + if (isxdigit(e[1]) && isxdigit(e[2])) + { + gint c = (g_ascii_xdigit_value (e[1]) << 4) | g_ascii_xdigit_value (e[2]); + g_string_append_c (dec, c); + e += 2; + } + else + { + g_string_free (dec, TRUE); + return g_strdup (encoded); + } + } + else + g_string_append_c (dec, *e); + } + decoded = dec->str; + g_string_free (dec, FALSE); + return decoded; +} diff --git a/src/udisksdaemonutil.h b/src/udisksdaemonutil.h index 1f2fac1..5a06997 100644 --- a/src/udisksdaemonutil.h +++ b/src/udisksdaemonutil.h @@ -95,6 +95,25 @@ gchar *udisks_daemon_util_get_free_mdraid_device (void); guint16 udisks_ata_identify_get_word (const guchar *identify_data, guint word_number); +gboolean udisks_daemon_util_block_is_unused (UDisksBlock *block, + GError **error); + +gboolean udisks_daemon_util_wipe_block (UDisksDaemon *daemon, + UDisksBlock *block, + GError **error); + +GPid +udisks_daemon_util_spawn_for_variant (const gchar **argv, + const GVariantType *type, + void (*callback) (GPid pid, + GVariant *result, + GError *error, + gpointer user_data), + gpointer user_data); + +gchar *udisks_daemon_util_encode_lvm_name (const gchar *name, gboolean for_logical_volume); +gchar *udisks_daemon_util_decode_lvm_name (const gchar *encoded); + G_END_DECLS #endif /* __UDISKS_DAEMON_UTIL_H__ */ diff --git a/src/udiskslinuxblock.c b/src/udiskslinuxblock.c index a1781cf..4da1268 100644 --- a/src/udiskslinuxblock.c +++ b/src/udiskslinuxblock.c @@ -57,6 +57,9 @@ #include "udiskslinuxmdraidobject.h" #include "udiskslinuxdevice.h" #include "udiskslinuxpartition.h" +#include "udiskslinuxlogicalvolume.h" +#include "udiskslinuxlogicalvolumeobject.h" +#include "udiskslinuxvolumegroupobject.h" /** * SECTION:udiskslinuxblock @@ -2145,7 +2148,6 @@ handle_format (UDisksBlock *block, gchar *mapped_name = NULL; const gchar *label = NULL; gchar *escaped_device = NULL; - gboolean was_partitioned = FALSE; UDisksInhibitCookie *inhibit_cookie = NULL; gboolean no_block = FALSE; gboolean update_partition_type = FALSE; @@ -2275,8 +2277,6 @@ handle_format (UDisksBlock *block, escaped_device = udisks_daemon_util_escape_and_quote (udisks_block_get_device (block)); - was_partitioned = (udisks_object_peek_partition_table (object) != NULL); - /* return early, if requested */ if (no_block) { @@ -2285,34 +2285,17 @@ handle_format (UDisksBlock *block, } /* First wipe the device... */ - if (!udisks_daemon_launch_spawned_job_sync (daemon, - 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", - escaped_device)) + if (!udisks_daemon_util_wipe_block (daemon, block, &error)) { if (invocation != NULL) - g_dbus_method_invocation_return_error (invocation, - UDISKS_ERROR, - UDISKS_ERROR_FAILED, - "Error wiping device: %s", - error_message); - g_free (error_message); + g_dbus_method_invocation_return_gerror (invocation, error); + g_clear_error (&error); goto out; } /* ...then wait until this change has taken effect */ wait_data = g_new0 (FormatWaitData, 1); wait_data->object = object; wait_data->type = "empty"; - udisks_linux_block_object_trigger_uevent (UDISKS_LINUX_BLOCK_OBJECT (object)); - if (was_partitioned) - udisks_linux_block_object_reread_partition_table (UDISKS_LINUX_BLOCK_OBJECT (object)); if (udisks_daemon_wait_for_object_sync (daemon, wait_for_filesystem, wait_data, diff --git a/src/udiskslinuxblockobject.c b/src/udiskslinuxblockobject.c index b907461..93c5830 100644 --- a/src/udiskslinuxblockobject.c +++ b/src/udiskslinuxblockobject.c @@ -52,6 +52,7 @@ #include "udiskslinuxencrypted.h" #include "udiskslinuxswapspace.h" #include "udiskslinuxloop.h" +#include "udiskslinuxphysicalvolume.h" #include "udiskslinuxprovider.h" #include "udisksfstabmonitor.h" #include "udisksfstabentry.h" @@ -92,6 +93,7 @@ struct _UDisksLinuxBlockObject UDisksSwapspace *iface_swapspace; UDisksEncrypted *iface_encrypted; UDisksLoop *iface_loop; + UDisksPhysicalVolume *iface_physical_volume; }; struct _UDisksLinuxBlockObjectClass @@ -140,6 +142,8 @@ udisks_linux_block_object_finalize (GObject *_object) g_object_unref (object->iface_encrypted); if (object->iface_loop != NULL) g_object_unref (object->iface_loop); + if (object->iface_physical_volume != NULL) + g_object_unref (object->iface_physical_volume); if (G_OBJECT_CLASS (udisks_linux_block_object_parent_class)->finalize != NULL) G_OBJECT_CLASS (udisks_linux_block_object_parent_class)->finalize (_object); @@ -757,6 +761,39 @@ udisks_linux_block_object_uevent (UDisksLinuxBlockObject *object, /* ---------------------------------------------------------------------------------------------------- */ +void +udisks_linux_block_object_update_lvm_pv (UDisksLinuxBlockObject *object, + UDisksLinuxVolumeGroupObject *group_object, + GVariant *info) +{ + if (group_object) + { + if (object->iface_physical_volume == NULL) + { + object->iface_physical_volume = udisks_linux_physical_volume_new (); + udisks_linux_physical_volume_update (UDISKS_LINUX_PHYSICAL_VOLUME (object->iface_physical_volume), + object, group_object, info); + g_dbus_object_skeleton_add_interface (G_DBUS_OBJECT_SKELETON (object), + G_DBUS_INTERFACE_SKELETON (object->iface_physical_volume)); + } + else + udisks_linux_physical_volume_update (UDISKS_LINUX_PHYSICAL_VOLUME (object->iface_physical_volume), + object, group_object, info); + } + else + { + if (object->iface_physical_volume != NULL) + { + g_dbus_object_skeleton_remove_interface (G_DBUS_OBJECT_SKELETON (object), + G_DBUS_INTERFACE_SKELETON (object->iface_physical_volume)); + g_object_unref (object->iface_physical_volume); + object->iface_physical_volume = NULL; + } + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + static void on_mount_monitor_mount_added (UDisksMountMonitor *monitor, UDisksMount *mount, diff --git a/src/udiskslinuxblockobject.h b/src/udiskslinuxblockobject.h index d342195..c1fe74a 100644 --- a/src/udiskslinuxblockobject.h +++ b/src/udiskslinuxblockobject.h @@ -42,6 +42,10 @@ UDisksLinuxDevice *udisks_linux_block_object_get_device (UDisksLinuxBlock void udisks_linux_block_object_trigger_uevent (UDisksLinuxBlockObject *object); void udisks_linux_block_object_reread_partition_table (UDisksLinuxBlockObject *object); +void udisks_linux_block_object_update_lvm_pv (UDisksLinuxBlockObject *object, + UDisksLinuxVolumeGroupObject *group_object, + GVariant *info); + G_END_DECLS #endif /* __UDISKS_LINUX_BLOCK_OBJECT_H__ */ diff --git a/src/udiskslinuxlogicalvolume.c b/src/udiskslinuxlogicalvolume.c new file mode 100644 index 0000000..5a12d0f --- /dev/null +++ b/src/udiskslinuxlogicalvolume.c @@ -0,0 +1,889 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2007-2010 David Zeuthen + * Copyright (C) 2013 Marius Vollmer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include + +#include "udiskslogging.h" +#include "udiskslinuxlogicalvolume.h" +#include "udiskslinuxlogicalvolumeobject.h" +#include "udiskslinuxvolumegroup.h" +#include "udiskslinuxvolumegroupobject.h" +#include "udiskslinuxblockobject.h" +#include "udisksdaemon.h" +#include "udisksstate.h" +#include "udisksdaemonutil.h" +#include "udiskslinuxdevice.h" + +/** + * SECTION:udiskslinuxlogicalvolume + * @title: UDisksLinuxLogicalVolume + * @short_description: Linux implementation of #UDisksLogicalVolume + * + * This type provides an implementation of the #UDisksLogicalVolume + * interface on Linux. + */ + +typedef struct _UDisksLinuxLogicalVolumeClass UDisksLinuxLogicalVolumeClass; + +/** + * UDisksLinuxLogicalVolume: + * + * The #UDisksLinuxLogicalVolume structure contains only private data and should + * only be accessed using the provided API. + */ +struct _UDisksLinuxLogicalVolume +{ + UDisksLogicalVolumeSkeleton parent_instance; +}; + +struct _UDisksLinuxLogicalVolumeClass +{ + UDisksLogicalVolumeSkeletonClass parent_class; +}; + +static void logical_volume_iface_init (UDisksLogicalVolumeIface *iface); + +G_DEFINE_TYPE_WITH_CODE (UDisksLinuxLogicalVolume, udisks_linux_logical_volume, + UDISKS_TYPE_LOGICAL_VOLUME_SKELETON, + G_IMPLEMENT_INTERFACE (UDISKS_TYPE_LOGICAL_VOLUME, logical_volume_iface_init)); + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +udisks_linux_logical_volume_init (UDisksLinuxLogicalVolume *logical_volume) +{ + g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (logical_volume), + G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD); +} + +static void +udisks_linux_logical_volume_class_init (UDisksLinuxLogicalVolumeClass *klass) +{ +} + +/** + * udisks_linux_logical_volume_new: + * + * Creates a new #UDisksLinuxLogicalVolume instance. + * + * Returns: A new #UDisksLinuxLogicalVolume. Free with g_object_unref(). + */ +UDisksLogicalVolume * +udisks_linux_logical_volume_new (void) +{ + return UDISKS_LOGICAL_VOLUME (g_object_new (UDISKS_TYPE_LINUX_LOGICAL_VOLUME, + NULL)); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * udisks_linux_logical_volume_update: + * @logical_volume: A #UDisksLinuxLogicalVolume. + * @vg: LVM volume group + * @lv: LVM logical volume + * + * Updates the interface. + */ +void +udisks_linux_logical_volume_update (UDisksLinuxLogicalVolume *logical_volume, + UDisksLinuxVolumeGroupObject *group_object, + GVariant *info, + gboolean *needs_polling_ret) +{ + UDisksLogicalVolume *iface; + const char *type; + const char *pool_objpath; + const char *origin_objpath; + const gchar *str; + guint64 num; + + iface = UDISKS_LOGICAL_VOLUME (logical_volume); + + if (g_variant_lookup (info, "name", "&s", &str)) + { + gchar *decoded = udisks_daemon_util_decode_lvm_name (str); + udisks_logical_volume_set_name (iface, str); + udisks_logical_volume_set_display_name (iface, decoded); + g_free (decoded); + } + + if (g_variant_lookup (info, "uuid", "&s", &str)) + udisks_logical_volume_set_uuid (iface, str); + + if (g_variant_lookup (info, "size", "t", &num)) + udisks_logical_volume_set_size (iface, num); + + type = "unsupported"; + if (g_variant_lookup (info, "lv_attr", "&s", &str) + && str && strlen (str) > 6) + { + char volume_type = str[0]; + char target_type = str[6]; + + switch (target_type) + { + case 's': + type = "snapshot"; + break; + case 'm': + type = "mirror"; + break; + case 't': + if (volume_type == 't') + type = "thin-pool"; + else + type = "thin"; + *needs_polling_ret = TRUE; + break; + case 'r': + type = "raid"; + break; + case '-': + type = "plain"; + break; + } + } + udisks_logical_volume_set_type_ (iface, type); + + if (g_variant_lookup (info, "data_percent", "t", &num) + && (int64_t)num >= 0) + udisks_logical_volume_set_data_allocated_ratio (iface, num/100000000.0); + + if (g_variant_lookup (info, "metadata_percent", "t", &num) + && (int64_t)num >= 0) + udisks_logical_volume_set_metadata_allocated_ratio (iface, num/100000000.0); + + pool_objpath = "/"; + if (g_variant_lookup (info, "pool_lv", "&s", &str) + && str != NULL && *str) + { + UDisksLinuxLogicalVolumeObject *pool_object = udisks_linux_volume_group_object_find_logical_volume_object (group_object, str); + if (pool_object) + pool_objpath = g_dbus_object_get_object_path (G_DBUS_OBJECT (pool_object)); + } + udisks_logical_volume_set_thin_pool (iface, pool_objpath); + + origin_objpath = "/"; + if (g_variant_lookup (info, "origin", "&s", &str) + && str != NULL && *str) + { + UDisksLinuxLogicalVolumeObject *origin_object = udisks_linux_volume_group_object_find_logical_volume_object (group_object, str); + if (origin_object) + origin_objpath = g_dbus_object_get_object_path (G_DBUS_OBJECT (origin_object)); + } + udisks_logical_volume_set_origin (iface, origin_objpath); + + udisks_logical_volume_set_volume_group (iface, g_dbus_object_get_object_path (G_DBUS_OBJECT (group_object))); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +handle_delete (UDisksLogicalVolume *_volume, + GDBusMethodInvocation *invocation, + GVariant *options) +{ + GError *error = NULL; + UDisksLinuxLogicalVolume *volume = UDISKS_LINUX_LOGICAL_VOLUME (_volume); + UDisksLinuxLogicalVolumeObject *object = NULL; + UDisksDaemon *daemon; + const gchar *action_id; + const gchar *message; + uid_t caller_uid; + gid_t caller_gid; + UDisksLinuxVolumeGroupObject *group_object; + gchar *escaped_group_name = NULL; + gchar *escaped_name = NULL; + gchar *error_message = NULL; + + object = udisks_daemon_util_dup_object (volume, &error); + if (object == NULL) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + daemon = udisks_linux_logical_volume_object_get_daemon (object); + + if (!udisks_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; + } + + message = N_("Authentication is required to delete a logical volume"); + action_id = "org.freedesktop.udisks2.manage-lvm"; + if (!udisks_daemon_util_check_authorization_sync (daemon, + UDISKS_OBJECT (object), + action_id, + options, + message, + invocation)) + goto out; + + group_object = udisks_linux_logical_volume_object_get_volume_group (object); + escaped_group_name = udisks_daemon_util_escape_and_quote (udisks_linux_volume_group_object_get_name (group_object)); + escaped_name = udisks_daemon_util_escape_and_quote (udisks_linux_logical_volume_object_get_name (object)); + + if (!udisks_daemon_launch_spawned_job_sync (daemon, + UDISKS_OBJECT (object), + "lvm-lvol-delete", 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 */ + "lvremove -f %s/%s", + escaped_group_name, + escaped_name)) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error deleting logical volume: %s", + error_message); + goto out; + } + + udisks_logical_volume_complete_delete (_volume, invocation); + + out: + g_free (error_message); + g_free (escaped_name); + g_free (escaped_group_name); + g_clear_object (&object); + return TRUE; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +struct WaitData { + UDisksLinuxVolumeGroupObject *group_object; + const gchar *name; +}; + +static UDisksObject * +wait_for_logical_volume_object (UDisksDaemon *daemon, + gpointer user_data) +{ + struct WaitData *data = user_data; + return UDISKS_OBJECT (udisks_linux_volume_group_object_find_logical_volume_object (data->group_object, + data->name)); +} + +static const gchar * +wait_for_logical_volume_path (UDisksLinuxVolumeGroupObject *group_object, + const gchar *name, + GError **error) +{ + struct WaitData data; + UDisksDaemon *daemon; + UDisksObject *volume_object; + + data.group_object = group_object; + data.name = name; + daemon = udisks_linux_volume_group_object_get_daemon (group_object); + volume_object = udisks_daemon_wait_for_object_sync (daemon, + wait_for_logical_volume_object, + &data, + NULL, + 10, /* timeout_seconds */ + error); + if (volume_object == NULL) + return NULL; + + return g_dbus_object_get_object_path (G_DBUS_OBJECT (volume_object)); +} + +static gboolean +handle_rename (UDisksLogicalVolume *_volume, + GDBusMethodInvocation *invocation, + const gchar *new_name, + GVariant *options) +{ + GError *error = NULL; + UDisksLinuxLogicalVolume *volume = UDISKS_LINUX_LOGICAL_VOLUME (_volume); + UDisksLinuxLogicalVolumeObject *object = NULL; + UDisksDaemon *daemon; + const gchar *action_id; + const gchar *message; + uid_t caller_uid; + gid_t caller_gid; + UDisksLinuxVolumeGroupObject *group_object; + gchar *escaped_group_name = NULL; + gchar *escaped_name = NULL; + gchar *encoded_new_name = NULL; + gchar *escaped_new_name = NULL; + gchar *error_message = NULL; + const gchar *lv_objpath; + + object = udisks_daemon_util_dup_object (volume, &error); + if (object == NULL) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + daemon = udisks_linux_logical_volume_object_get_daemon (object); + + if (!udisks_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; + } + + message = N_("Authentication is required to rename a logical volume"); + action_id = "org.freedesktop.udisks2.manage-lvm"; + if (!udisks_daemon_util_check_authorization_sync (daemon, + UDISKS_OBJECT (object), + action_id, + options, + message, + invocation)) + goto out; + + group_object = udisks_linux_logical_volume_object_get_volume_group (object); + escaped_group_name = udisks_daemon_util_escape_and_quote (udisks_linux_volume_group_object_get_name (group_object)); + escaped_name = udisks_daemon_util_escape_and_quote (udisks_linux_logical_volume_object_get_name (object)); + encoded_new_name = udisks_daemon_util_encode_lvm_name (new_name, TRUE); + escaped_new_name = udisks_daemon_util_escape_and_quote (encoded_new_name); + + if (!udisks_daemon_launch_spawned_job_sync (daemon, + UDISKS_OBJECT (object), + "lvm-vg-rename", 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 */ + "lvrename %s/%s %s", + escaped_group_name, + escaped_name, + escaped_new_name)) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error renaming volume volume: %s", + error_message); + goto out; + } + + lv_objpath = wait_for_logical_volume_path (group_object, encoded_new_name, &error); + if (lv_objpath == NULL) + { + g_prefix_error (&error, + "Error waiting for logical volume object for %s", + new_name); + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + udisks_logical_volume_complete_rename (_volume, invocation, lv_objpath); + + out: + g_free (error_message); + g_free (escaped_name); + g_free (escaped_group_name); + g_free (encoded_new_name); + g_free (escaped_new_name); + g_clear_object (&object); + return TRUE; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +handle_resize (UDisksLogicalVolume *_volume, + GDBusMethodInvocation *invocation, + guint64 new_size, + int stripes, + guint64 stripesize, + GVariant *options) +{ + GError *error = NULL; + UDisksLinuxLogicalVolume *volume = UDISKS_LINUX_LOGICAL_VOLUME (_volume); + UDisksLinuxLogicalVolumeObject *object = NULL; + UDisksDaemon *daemon; + const gchar *action_id; + const gchar *message; + uid_t caller_uid; + gid_t caller_gid; + UDisksLinuxVolumeGroupObject *group_object; + GString *cmd = NULL; + gchar *escaped_group_name = NULL; + gchar *escaped_name = NULL; + gchar *error_message = NULL; + + object = udisks_daemon_util_dup_object (volume, &error); + if (object == NULL) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + daemon = udisks_linux_logical_volume_object_get_daemon (object); + + if (!udisks_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; + } + + message = N_("Authentication is required to rename a logical volume"); + action_id = "org.freedesktop.udisks2.manage-lvm"; + if (!udisks_daemon_util_check_authorization_sync (daemon, + UDISKS_OBJECT (object), + action_id, + options, + message, + invocation)) + goto out; + + group_object = udisks_linux_logical_volume_object_get_volume_group (object); + escaped_group_name = udisks_daemon_util_escape_and_quote (udisks_linux_volume_group_object_get_name (group_object)); + escaped_name = udisks_daemon_util_escape_and_quote (udisks_linux_logical_volume_object_get_name (object)); + new_size -= new_size % 512; + + cmd = g_string_new (""); + g_string_append_printf (cmd, "lvresize %s/%s -r -L %" G_GUINT64_FORMAT "b", + escaped_group_name, escaped_name, new_size); + + if (stripes > 0) + g_string_append_printf (cmd, " -i %d", stripes); + + if (stripesize > 0) + g_string_append_printf (cmd, " -I %" G_GUINT64_FORMAT "b", stripesize); + + if (!udisks_daemon_launch_spawned_job_sync (daemon, + UDISKS_OBJECT (object), + "lvm-vg-resize", 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", cmd->str)) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error resizing logical volume: %s", + error_message); + goto out; + } + + udisks_logical_volume_complete_resize (_volume, invocation); + + out: + if (cmd) + g_string_free (cmd, TRUE); + g_free (error_message); + g_free (escaped_name); + g_free (escaped_group_name); + g_clear_object (&object); + return TRUE; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +struct WaitForBlockData { + UDisksLinuxLogicalVolumeObject *volume_object; +}; + +static UDisksObject * +wait_for_logical_volume_block_object (UDisksDaemon *daemon, + gpointer user_data) +{ + UDisksLinuxLogicalVolumeObject *volume_object = user_data; + const gchar *volume_objpath; + GList *objects, *l; + UDisksObject *ret = NULL; + + volume_objpath = g_dbus_object_get_object_path (G_DBUS_OBJECT (volume_object)); + + objects = udisks_daemon_get_objects (daemon); + for (l = objects; l != NULL; l = l->next) + { + UDisksObject *object = UDISKS_OBJECT (l->data); + UDisksBlock *block; + + block = udisks_object_peek_block (object); + if (block == NULL) + continue; + + if (g_strcmp0 (udisks_block_get_logical_volume (block), volume_objpath) == 0) + { + ret = g_object_ref (object); + goto out; + } + } + + out: + g_list_free_full (objects, g_object_unref); + return ret; +} + +static gboolean +handle_activate (UDisksLogicalVolume *_volume, + GDBusMethodInvocation *invocation, + GVariant *options) +{ + GError *error = NULL; + UDisksLinuxLogicalVolume *volume = UDISKS_LINUX_LOGICAL_VOLUME (_volume); + UDisksLinuxLogicalVolumeObject *object = NULL; + UDisksDaemon *daemon; + const gchar *action_id; + const gchar *message; + uid_t caller_uid; + gid_t caller_gid; + UDisksLinuxVolumeGroupObject *group_object; + gchar *escaped_group_name = NULL; + gchar *escaped_name = NULL; + gchar *error_message = NULL; + UDisksObject *block_object = NULL; + + object = udisks_daemon_util_dup_object (volume, &error); + if (object == NULL) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + daemon = udisks_linux_logical_volume_object_get_daemon (object); + + if (!udisks_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; + } + + message = N_("Authentication is required to activate a logical volume"); + action_id = "org.freedesktop.udisks2.manage-lvm"; + if (!udisks_daemon_util_check_authorization_sync (daemon, + UDISKS_OBJECT (object), + action_id, + options, + message, + invocation)) + goto out; + + group_object = udisks_linux_logical_volume_object_get_volume_group (object); + escaped_group_name = udisks_daemon_util_escape_and_quote (udisks_linux_volume_group_object_get_name (group_object)); + escaped_name = udisks_daemon_util_escape_and_quote (udisks_linux_logical_volume_object_get_name (object)); + + if (!udisks_daemon_launch_spawned_job_sync (daemon, + UDISKS_OBJECT (object), + "lvm-lvol-activate", 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 */ + "lvchange %s/%s -a y", + escaped_group_name, + escaped_name)) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error deleting logical volume: %s", + error_message); + goto out; + } + + block_object = udisks_daemon_wait_for_object_sync (daemon, + wait_for_logical_volume_block_object, + object, + NULL, + 10, /* timeout_seconds */ + &error); + if (block_object == NULL) + { + g_prefix_error (&error, + "Error waiting for block object for %s", + udisks_logical_volume_get_name (_volume)); + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + udisks_logical_volume_complete_activate (_volume, invocation, + g_dbus_object_get_object_path (G_DBUS_OBJECT (block_object))); + + out: + g_free (error_message); + g_free (escaped_name); + g_free (escaped_group_name); + g_clear_object (&block_object); + g_clear_object (&object); + return TRUE; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +handle_deactivate (UDisksLogicalVolume *_volume, + GDBusMethodInvocation *invocation, + GVariant *options) +{ + GError *error = NULL; + UDisksLinuxLogicalVolume *volume = UDISKS_LINUX_LOGICAL_VOLUME (_volume); + UDisksLinuxLogicalVolumeObject *object = NULL; + UDisksDaemon *daemon; + const gchar *action_id; + const gchar *message; + uid_t caller_uid; + gid_t caller_gid; + UDisksLinuxVolumeGroupObject *group_object; + gchar *escaped_group_name = NULL; + gchar *escaped_name = NULL; + gchar *error_message = NULL; + + object = udisks_daemon_util_dup_object (volume, &error); + if (object == NULL) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + daemon = udisks_linux_logical_volume_object_get_daemon (object); + + if (!udisks_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; + } + + message = N_("Authentication is required to deactivate a logical volume"); + action_id = "org.freedesktop.udisks2.manage-lvm"; + if (!udisks_daemon_util_check_authorization_sync (daemon, + UDISKS_OBJECT (object), + action_id, + options, + message, + invocation)) + goto out; + + group_object = udisks_linux_logical_volume_object_get_volume_group (object); + escaped_group_name = udisks_daemon_util_escape_and_quote (udisks_linux_volume_group_object_get_name (group_object)); + escaped_name = udisks_daemon_util_escape_and_quote (udisks_linux_logical_volume_object_get_name (object)); + + if (!udisks_daemon_launch_spawned_job_sync (daemon, + UDISKS_OBJECT (object), + "lvm-lvol-deactivate", 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 */ + "lvchange %s/%s -a n", + escaped_group_name, + escaped_name)) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error deleting logical volume: %s", + error_message); + goto out; + } + + udisks_logical_volume_complete_deactivate (_volume, invocation); + + out: + g_free (error_message); + g_free (escaped_name); + g_free (escaped_group_name); + g_clear_object (&object); + return TRUE; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +handle_create_snapshot (UDisksLogicalVolume *_volume, + GDBusMethodInvocation *invocation, + const gchar *name, + guint64 size, + GVariant *options) +{ + GError *error = NULL; + UDisksLinuxLogicalVolume *volume = UDISKS_LINUX_LOGICAL_VOLUME (_volume); + UDisksLinuxLogicalVolumeObject *object = NULL; + UDisksDaemon *daemon; + const gchar *action_id; + const gchar *message; + uid_t caller_uid; + gid_t caller_gid; + UDisksLinuxVolumeGroupObject *group_object; + gchar *encoded_volume_name = NULL; + gchar *escaped_volume_name = NULL; + gchar *escaped_group_name = NULL; + gchar *escaped_origin_name = NULL; + GString *cmd = NULL; + gchar *error_message = NULL; + const gchar *lv_objpath = NULL; + + object = udisks_daemon_util_dup_object (volume, &error); + if (object == NULL) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + daemon = udisks_linux_logical_volume_object_get_daemon (object); + + if (!udisks_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; + } + + message = N_("Authentication is required to create a snapshot of a logical volume"); + action_id = "org.freedesktop.udisks2.manage-lvm"; + if (!udisks_daemon_util_check_authorization_sync (daemon, + UDISKS_OBJECT (object), + action_id, + options, + message, + invocation)) + goto out; + + encoded_volume_name = udisks_daemon_util_encode_lvm_name (name, TRUE); + escaped_volume_name = udisks_daemon_util_escape_and_quote (encoded_volume_name); + group_object = udisks_linux_logical_volume_object_get_volume_group (object); + escaped_group_name = udisks_daemon_util_escape_and_quote (udisks_linux_volume_group_object_get_name (group_object)); + escaped_origin_name = udisks_daemon_util_escape_and_quote (udisks_linux_logical_volume_object_get_name (object)); + + cmd = g_string_new ("lvcreate"); + g_string_append_printf (cmd, " -s %s/%s -n %s", + escaped_group_name, escaped_origin_name, escaped_volume_name); + + if (size > 0) + { + size -= size % 512; + g_string_append_printf (cmd, " -L %" G_GUINT64_FORMAT "b", size); + } + + if (!udisks_daemon_launch_spawned_job_sync (daemon, + UDISKS_OBJECT (object), + "lvm-lvol-snapshot", 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", cmd->str)) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error creating snapshot: %s", + error_message); + goto out; + } + + lv_objpath = wait_for_logical_volume_path (group_object, encoded_volume_name, &error); + if (lv_objpath == NULL) + { + g_prefix_error (&error, + "Error waiting for logical volume object for %s", + name); + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + udisks_logical_volume_complete_create_snapshot (_volume, invocation, lv_objpath); + + out: + g_free (error_message); + g_free (encoded_volume_name); + g_free (escaped_volume_name); + g_free (escaped_origin_name); + g_free (escaped_group_name); + if (cmd) + g_string_free (cmd, TRUE); + g_clear_object (&object); + return TRUE; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +logical_volume_iface_init (UDisksLogicalVolumeIface *iface) +{ + iface->handle_delete = handle_delete; + iface->handle_rename = handle_rename; + iface->handle_resize = handle_resize; + iface->handle_activate = handle_activate; + iface->handle_deactivate = handle_deactivate; + iface->handle_create_snapshot = handle_create_snapshot; +} diff --git a/src/udiskslinuxlogicalvolume.h b/src/udiskslinuxlogicalvolume.h new file mode 100644 index 0000000..21de4f0 --- /dev/null +++ b/src/udiskslinuxlogicalvolume.h @@ -0,0 +1,42 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2007-2010 David Zeuthen + * Copyright (C) 2013 Marius Vollmer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __UDISKS_LINUX_LOGICAL_VOLUME_H__ +#define __UDISKS_LINUX_LOGICAL_VOLUME_H__ + +#include "udisksdaemontypes.h" + +G_BEGIN_DECLS + +#define UDISKS_TYPE_LINUX_LOGICAL_VOLUME (udisks_linux_logical_volume_get_type ()) +#define UDISKS_LINUX_LOGICAL_VOLUME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), UDISKS_TYPE_LINUX_LOGICAL_VOLUME, UDisksLinuxLogicalVolume)) +#define UDISKS_IS_LINUX_LOGICAL_VOLUME(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), UDISKS_TYPE_LINUX_LOGICAL_VOLUME)) + +GType udisks_linux_logical_volume_get_type (void) G_GNUC_CONST; +UDisksLogicalVolume *udisks_linux_logical_volume_new (void); +void udisks_linux_logical_volume_update (UDisksLinuxLogicalVolume *logical_volume, + UDisksLinuxVolumeGroupObject *group_object, + GVariant *info, + gboolean *needs_polling_ret); + +G_END_DECLS + +#endif /* __UDISKS_LINUX_LOGICAL_VOLUME_H__ */ diff --git a/src/udiskslinuxlogicalvolumeobject.c b/src/udiskslinuxlogicalvolumeobject.c new file mode 100644 index 0000000..f42fd6a --- /dev/null +++ b/src/udiskslinuxlogicalvolumeobject.c @@ -0,0 +1,302 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2007-2010 David Zeuthen + * Copyright (C) 2013 Marius Vollmer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include + +#include +#include +#include + +#include "udiskslogging.h" +#include "udisksdaemon.h" +#include "udisksdaemonutil.h" +#include "udiskslinuxprovider.h" +#include "udiskslinuxvolumegroup.h" +#include "udiskslinuxvolumegroupobject.h" +#include "udiskslinuxlogicalvolumeobject.h" +#include "udiskslinuxlogicalvolume.h" +#include "udiskslinuxblockobject.h" +#include "udiskslinuxdevice.h" + +/** + * SECTION:udiskslinuxlogicalvolumeobject + * @title: UDisksLinuxLogicalVolumeObject + * @short_description: Object representing a LVM2 logical volume + */ + +typedef struct _UDisksLinuxLogicalVolumeObjectClass UDisksLinuxLogicalVolumeObjectClass; + +/** + * UDisksLinuxLogicalVolumeObject: + * + * The #UDisksLinuxLogicalVolumeObject structure contains only private data and + * should only be accessed using the provided API. + */ +struct _UDisksLinuxLogicalVolumeObject +{ + UDisksObjectSkeleton parent_instance; + + UDisksDaemon *daemon; + + gchar *name; + UDisksLinuxVolumeGroupObject *volume_group; + + UDisksLogicalVolume *iface_logical_volume; +}; + +struct _UDisksLinuxLogicalVolumeObjectClass +{ + UDisksObjectSkeletonClass parent_class; +}; + +enum +{ + PROP_0, + PROP_NAME, + PROP_VOLUME_GROUP, + PROP_DAEMON, +}; + +G_DEFINE_TYPE (UDisksLinuxLogicalVolumeObject, udisks_linux_logical_volume_object, UDISKS_TYPE_OBJECT_SKELETON); + +static void +udisks_linux_logical_volume_object_finalize (GObject *_object) +{ + UDisksLinuxLogicalVolumeObject *object = UDISKS_LINUX_LOGICAL_VOLUME_OBJECT (_object); + + /* note: we don't hold a ref to object->daemon */ + + if (object->iface_logical_volume != NULL) + g_object_unref (object->iface_logical_volume); + + g_free (object->name); + + if (G_OBJECT_CLASS (udisks_linux_logical_volume_object_parent_class)->finalize != NULL) + G_OBJECT_CLASS (udisks_linux_logical_volume_object_parent_class)->finalize (_object); +} + +static void +udisks_linux_logical_volume_object_get_property (GObject *__object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + UDisksLinuxLogicalVolumeObject *object = UDISKS_LINUX_LOGICAL_VOLUME_OBJECT (__object); + + switch (prop_id) + { + case PROP_DAEMON: + g_value_set_object (value, udisks_linux_logical_volume_object_get_daemon (object)); + break; + + case PROP_VOLUME_GROUP: + g_value_set_object (value, udisks_linux_logical_volume_object_get_volume_group (object)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +udisks_linux_logical_volume_object_set_property (GObject *__object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + UDisksLinuxLogicalVolumeObject *object = UDISKS_LINUX_LOGICAL_VOLUME_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_NAME: + object->name = g_value_dup_string (value); + break; + + case PROP_VOLUME_GROUP: + g_assert (object->volume_group == NULL); + object->volume_group = g_value_get_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +udisks_linux_logical_volume_object_init (UDisksLinuxLogicalVolumeObject *object) +{ +} + +static void +udisks_linux_logical_volume_object_constructed (GObject *_object) +{ + UDisksLinuxLogicalVolumeObject *object = UDISKS_LINUX_LOGICAL_VOLUME_OBJECT (_object); + GString *s; + + if (G_OBJECT_CLASS (udisks_linux_logical_volume_object_parent_class)->constructed != NULL) + G_OBJECT_CLASS (udisks_linux_logical_volume_object_parent_class)->constructed (_object); + + /* compute the object path */ + + s = g_string_new (g_dbus_object_get_object_path (G_DBUS_OBJECT (object->volume_group))); + g_string_append_c (s, '/'); + udisks_safe_append_to_object_path (s, object->name); + g_dbus_object_skeleton_set_object_path (G_DBUS_OBJECT_SKELETON (object), s->str); + g_string_free (s, TRUE); + + /* create the DBus interface */ + object->iface_logical_volume = udisks_linux_logical_volume_new (); + g_dbus_object_skeleton_add_interface (G_DBUS_OBJECT_SKELETON (object), + G_DBUS_INTERFACE_SKELETON (object->iface_logical_volume)); +} + +static void +udisks_linux_logical_volume_object_class_init (UDisksLinuxLogicalVolumeObjectClass *klass) +{ + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = udisks_linux_logical_volume_object_finalize; + gobject_class->constructed = udisks_linux_logical_volume_object_constructed; + gobject_class->set_property = udisks_linux_logical_volume_object_set_property; + gobject_class->get_property = udisks_linux_logical_volume_object_get_property; + + /** + * UDisksLinuxLogicalVolumeObject:daemon: + * + * The #UDisksDaemon the object is for. + */ + g_object_class_install_property (gobject_class, + PROP_DAEMON, + g_param_spec_object ("daemon", + "Daemon", + "The daemon the object is for", + UDISKS_TYPE_DAEMON, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * UDisksLinuxLogicalVolumeObject:name: + * + * The name of the logical volume. + */ + g_object_class_install_property (gobject_class, + PROP_NAME, + g_param_spec_string ("name", + "Name", + "The name of the volume group", + NULL, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * UDisksLinuxLogicalVolumeObject:volume_group: + * + * The volume group. + */ + g_object_class_install_property (gobject_class, + PROP_VOLUME_GROUP, + g_param_spec_object ("volumegroup", + "Volume Group", + "The volume group", + UDISKS_TYPE_LINUX_VOLUME_GROUP_OBJECT, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + +/** + * udisks_linux_logical_volume_object_new: + * + * Create a new LogicalVolume object. + * + * Returns: A #UDisksLinuxLogicalVolumeObject object. Free with g_object_unref(). + */ +UDisksLinuxLogicalVolumeObject * +udisks_linux_logical_volume_object_new (UDisksDaemon *daemon, + UDisksLinuxVolumeGroupObject *volume_group, + const gchar *name) +{ + g_return_val_if_fail (UDISKS_IS_DAEMON (daemon), NULL); + g_return_val_if_fail (UDISKS_IS_LINUX_VOLUME_GROUP_OBJECT (volume_group), NULL); + g_return_val_if_fail (name != NULL, NULL); + + return UDISKS_LINUX_LOGICAL_VOLUME_OBJECT (g_object_new (UDISKS_TYPE_LINUX_LOGICAL_VOLUME_OBJECT, + "daemon", daemon, + "volumegroup", volume_group, + "name", name, + NULL)); +} + +/** + * udisks_linux_logical_volume_object_get_daemon: + * @object: A #UDisksLinuxLogicalVolumeObject. + * + * Gets the daemon used by @object. + * + * Returns: A #UDisksDaemon. Do not free, the object is owned by @object. + */ +UDisksDaemon * +udisks_linux_logical_volume_object_get_daemon (UDisksLinuxLogicalVolumeObject *object) +{ + g_return_val_if_fail (UDISKS_IS_LINUX_LOGICAL_VOLUME_OBJECT (object), NULL); + return object->daemon; +} + +UDisksLinuxVolumeGroupObject * +udisks_linux_logical_volume_object_get_volume_group (UDisksLinuxLogicalVolumeObject *object) +{ + g_return_val_if_fail (UDISKS_IS_LINUX_LOGICAL_VOLUME_OBJECT (object), NULL); + return object->volume_group; +} + +const gchar * +udisks_linux_logical_volume_object_get_name (UDisksLinuxLogicalVolumeObject *object) +{ + g_return_val_if_fail (UDISKS_IS_LINUX_LOGICAL_VOLUME_OBJECT (object), NULL); + return object->name; +} + +void +udisks_linux_logical_volume_object_update (UDisksLinuxLogicalVolumeObject *object, + GVariant *info, + gboolean *needs_polling_ret) +{ + g_return_if_fail (UDISKS_IS_LINUX_LOGICAL_VOLUME_OBJECT (object)); + + udisks_linux_logical_volume_update (UDISKS_LINUX_LOGICAL_VOLUME (object->iface_logical_volume), + object->volume_group, + info, + needs_polling_ret); +} diff --git a/src/udiskslinuxlogicalvolumeobject.h b/src/udiskslinuxlogicalvolumeobject.h new file mode 100644 index 0000000..e8dac17 --- /dev/null +++ b/src/udiskslinuxlogicalvolumeobject.h @@ -0,0 +1,49 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2007-2010 David Zeuthen + * Copyright (C) 2013 Marius Vollmer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __UDISKS_LINUX_MDRAID_OBJECT_H__ +#define __UDISKS_LINUX_MDRAID_OBJECT_H__ + +#include "udisksdaemontypes.h" + +#include + +G_BEGIN_DECLS + +#define UDISKS_TYPE_LINUX_LOGICAL_VOLUME_OBJECT (udisks_linux_logical_volume_object_get_type ()) +#define UDISKS_LINUX_LOGICAL_VOLUME_OBJECT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), UDISKS_TYPE_LINUX_LOGICAL_VOLUME_OBJECT, UDisksLinuxLogicalVolumeObject)) +#define UDISKS_IS_LINUX_LOGICAL_VOLUME_OBJECT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), UDISKS_TYPE_LINUX_LOGICAL_VOLUME_OBJECT)) + +GType udisks_linux_logical_volume_object_get_type (void) G_GNUC_CONST; +UDisksLinuxLogicalVolumeObject *udisks_linux_logical_volume_object_new (UDisksDaemon *daemon, + UDisksLinuxVolumeGroupObject *vg_object, + const gchar *name); +UDisksDaemon *udisks_linux_logical_volume_object_get_daemon (UDisksLinuxLogicalVolumeObject *object); +UDisksLinuxVolumeGroupObject *udisks_linux_logical_volume_object_get_volume_group (UDisksLinuxLogicalVolumeObject *object); +const gchar *udisks_linux_logical_volume_object_get_name (UDisksLinuxLogicalVolumeObject *object); + +void udisks_linux_logical_volume_object_update (UDisksLinuxLogicalVolumeObject *object, GVariant *info, gboolean *needs_polling_ret); + + + +G_END_DECLS + +#endif /* __UDISKS_LINUX_LOGICAL_VOLUME_OBJECT_H__ */ diff --git a/src/udiskslinuxmanager.c b/src/udiskslinuxmanager.c index 2e2034f..388487c 100644 --- a/src/udiskslinuxmanager.c +++ b/src/udiskslinuxmanager.c @@ -43,6 +43,8 @@ #include "udisksstate.h" #include "udiskslinuxblockobject.h" #include "udiskslinuxdevice.h" +#include "udiskslinuxprovider.h" +#include "udiskslinuxvolumegroup.h" /** * SECTION:udiskslinuxmanager @@ -680,42 +682,11 @@ handle_mdraid_create (UDisksManager *_object, /* wipe existing devices */ for (l = blocks; l != NULL; l = l->next) { - UDisksBlock *block = UDISKS_BLOCK (l->data); - UDisksObject *object_for_block; - gchar *escaped_device; - object_for_block = udisks_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 = udisks_daemon_util_escape (udisks_block_get_device (block)); - if (!udisks_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)) + if (!udisks_daemon_util_wipe_block (manager->daemon, UDISKS_BLOCK (l->data), &error)) { - g_dbus_method_invocation_return_error (invocation, - UDISKS_ERROR, - UDISKS_ERROR_FAILED, - "Error wiping device %s to be used in a RAID array: %s", - udisks_block_get_device (block), - error_message); - g_free (error_message); - g_object_unref (object_for_block); - g_free (escaped_device); + g_dbus_method_invocation_take_error (invocation, error); goto out; } - g_object_unref (object_for_block); - g_free (escaped_device); } /* Create the array... */ @@ -867,9 +838,195 @@ handle_mdraid_create (UDisksManager *_object, /* ---------------------------------------------------------------------------------------------------- */ +static UDisksObject * +wait_for_volume_group_object (UDisksDaemon *daemon, + gpointer user_data) +{ + const gchar *name = user_data; + UDisksLinuxProvider *provider; + + provider = udisks_daemon_get_linux_provider (daemon); + return UDISKS_OBJECT (udisks_linux_provider_find_volume_group_object (provider, name)); +} + +static gboolean +handle_volume_group_create (UDisksManager *_object, + GDBusMethodInvocation *invocation, + const gchar *const *arg_blocks, + const gchar *arg_name, + guint64 arg_extent_size, + GVariant *arg_options) +{ + UDisksLinuxManager *manager = UDISKS_LINUX_MANAGER (_object); + uid_t caller_uid; + GError *error = NULL; + const gchar *message; + const gchar *action_id; + GList *blocks = NULL; + GList *l; + guint n; + gchar *encoded_name = NULL; + gchar *escaped_name = NULL; + GString *str = NULL; + gint status; + gchar *error_message = NULL; + UDisksObject *group_object = NULL; + + error = NULL; + if (!udisks_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.freedesktop.udisks2.manage-lvm"; + if (!udisks_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++) + { + UDisksObject *object = NULL; + UDisksBlock *block = NULL; + + object = udisks_daemon_find_object (manager->daemon, arg_blocks[n]); + if (object == NULL) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Invalid object path %s at index %d", + arg_blocks[n], n); + goto out; + } + + block = udisks_object_get_block (object); + if (block == NULL) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Object path %s for index %d is not a block device", + arg_blocks[n], n); + goto out; + } + + if (!udisks_daemon_util_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 (!udisks_daemon_util_wipe_block (manager->daemon, UDISKS_BLOCK (l->data), &error)) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + } + + /* Create the volume group... */ + encoded_name = udisks_daemon_util_encode_lvm_name (arg_name, FALSE); + escaped_name = udisks_daemon_util_escape_and_quote (encoded_name); + str = g_string_new ("vgcreate"); + g_string_append_printf (str, " %s", escaped_name); + if (arg_extent_size > 0) + g_string_append_printf (str, " -s %" G_GUINT64_FORMAT "b", arg_extent_size); + for (l = blocks; l != NULL; l = l->next) + { + UDisksBlock *block = UDISKS_BLOCK (l->data); + gchar *escaped_device; + escaped_device = udisks_daemon_util_escape_and_quote (udisks_block_get_device (block)); + g_string_append_printf (str, " %s", escaped_device); + g_free (escaped_device); + } + + if (!udisks_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, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error creating volume group: %s", + error_message); + g_free (error_message); + goto out; + } + + for (l = blocks; l != NULL; l = l->next) + { + UDisksBlock *block = UDISKS_BLOCK (l->data); + UDisksObject *object_for_block; + object_for_block = udisks_daemon_util_dup_object (block, &error); + if (object_for_block != NULL) + udisks_linux_block_object_trigger_uevent (UDISKS_LINUX_BLOCK_OBJECT (object_for_block)); + g_object_unref (object_for_block); + } + + /* ... then, sit and wait for the object to show up */ + group_object = udisks_daemon_wait_for_object_sync (manager->daemon, + wait_for_volume_group_object, + (gpointer)encoded_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; + } + + udisks_manager_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); + g_free (encoded_name); + + return TRUE; /* returning TRUE means that we handled the method invocation */ +} + +/* ---------------------------------------------------------------------------------------------------- */ + static void manager_iface_init (UDisksManagerIface *iface) { iface->handle_loop_setup = handle_loop_setup; iface->handle_mdraid_create = handle_mdraid_create; + iface->handle_volume_group_create = handle_volume_group_create; } diff --git a/src/udiskslinuxmdraid.c b/src/udiskslinuxmdraid.c index d7c507a..4fabe44 100644 --- a/src/udiskslinuxmdraid.c +++ b/src/udiskslinuxmdraid.c @@ -1217,6 +1217,12 @@ handle_add_device (UDisksMDRaid *_mdraid, goto out; } + if (!udisks_daemon_util_wipe_block (daemon, new_member_device, &error)) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + device_file = g_udev_device_get_device_file (raid_device->udev_device); escaped_device_file = udisks_daemon_util_escape_and_quote (device_file); diff --git a/src/udiskslinuxphysicalvolume.c b/src/udiskslinuxphysicalvolume.c new file mode 100644 index 0000000..13ed174 --- /dev/null +++ b/src/udiskslinuxphysicalvolume.c @@ -0,0 +1,131 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2007-2010 David Zeuthen + * Copyright (C) 2013 Marius Vollmer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include + +#include "udiskslogging.h" +#include "udiskslinuxphysicalvolume.h" +#include "udiskslinuxvolumegroup.h" +#include "udiskslinuxvolumegroupobject.h" +#include "udiskslinuxblockobject.h" +#include "udisksdaemon.h" +#include "udisksstate.h" +#include "udisksdaemonutil.h" +#include "udiskslinuxdevice.h" +#include "udiskslinuxprovider.h" + +/** + * SECTION:udiskslinuxphysicalvolume + * @title: UDisksLinuxPhysicalVolume + * @short_description: Linux implementation of #UDisksPhysicalVolume + * + * This type provides an implementation of the #UDisksPhysicalVolume + * interface on Linux. + */ + +typedef struct _UDisksLinuxPhysicalVolumeClass UDisksLinuxPhysicalVolumeClass; + +/** + * UDisksLinuxPhysicalVolume: + * + * The #UDisksLinuxPhysicalVolume structure contains only private data and should + * only be accessed using the provided API. + */ +struct _UDisksLinuxPhysicalVolume +{ + UDisksPhysicalVolumeSkeleton parent_instance; +}; + +struct _UDisksLinuxPhysicalVolumeClass +{ + UDisksPhysicalVolumeSkeletonClass parent_class; +}; + +static void physical_volume_iface_init (UDisksPhysicalVolumeIface *iface); + +G_DEFINE_TYPE_WITH_CODE (UDisksLinuxPhysicalVolume, udisks_linux_physical_volume, + UDISKS_TYPE_PHYSICAL_VOLUME_SKELETON, + G_IMPLEMENT_INTERFACE (UDISKS_TYPE_PHYSICAL_VOLUME, physical_volume_iface_init)); + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +udisks_linux_physical_volume_init (UDisksLinuxPhysicalVolume *physical_volume) +{ + g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (physical_volume), + G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD); +} + +static void +udisks_linux_physical_volume_class_init (UDisksLinuxPhysicalVolumeClass *klass) +{ +} + +/** + * udisks_linux_physical_volume_new: + * + * Creates a new #UDisksLinuxPhysicalVolume instance. + * + * Returns: A new #UDisksLinuxPhysicalVolume. Free with g_object_unref(). + */ +UDisksPhysicalVolume * +udisks_linux_physical_volume_new (void) +{ + return UDISKS_PHYSICAL_VOLUME (g_object_new (UDISKS_TYPE_LINUX_PHYSICAL_VOLUME, + NULL)); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * udisks_linux_physical_volume_update: + * @physical_volume: A #UDisksLinuxPhysicalVolume. + * @object: The enclosing #UDisksLinuxBlockObject instance. + * + * Updates the interface. + */ +void +udisks_linux_physical_volume_update (UDisksLinuxPhysicalVolume *physical_volume, + UDisksLinuxBlockObject *object, + UDisksLinuxVolumeGroupObject *group_object, + GVariant *info) +{ + UDisksPhysicalVolume *iface; + guint64 num; + + iface = UDISKS_PHYSICAL_VOLUME (physical_volume); + + udisks_physical_volume_set_volume_group (iface, g_dbus_object_get_object_path (G_DBUS_OBJECT (group_object))); + + if (g_variant_lookup (info, "size", "t", &num)) + udisks_physical_volume_set_size (iface, num); + + if (g_variant_lookup (info, "free-size", "t", &num)) + udisks_physical_volume_set_free_size (iface, num); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +physical_volume_iface_init (UDisksPhysicalVolumeIface *iface) +{ +} diff --git a/src/udiskslinuxphysicalvolume.h b/src/udiskslinuxphysicalvolume.h new file mode 100644 index 0000000..7e1d2a7 --- /dev/null +++ b/src/udiskslinuxphysicalvolume.h @@ -0,0 +1,42 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2007-2010 David Zeuthen + * Copyright (C) 2013 Marius Vollmer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __UDISKS_LINUX_PHYSICAL_VOLUME_H__ +#define __UDISKS_LINUX_PHYSICAL_VOLUME_H__ + +#include "udisksdaemontypes.h" + +G_BEGIN_DECLS + +#define UDISKS_TYPE_LINUX_PHYSICAL_VOLUME (udisks_linux_physical_volume_get_type ()) +#define UDISKS_LINUX_PHYSICAL_VOLUME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), UDISKS_TYPE_LINUX_PHYSICAL_VOLUME, UDisksLinuxPhysicalVolume)) +#define UDISKS_IS_LINUX_PHYSICAL_VOLUME(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), UDISKS_TYPE_LINUX_PHYSICAL_VOLUME)) + +GType udisks_linux_physical_volume_get_type (void) G_GNUC_CONST; +UDisksPhysicalVolume *udisks_linux_physical_volume_new (void); +void udisks_linux_physical_volume_update (UDisksLinuxPhysicalVolume *physical_volume, + UDisksLinuxBlockObject *object, + UDisksLinuxVolumeGroupObject *group_object, + GVariant *info); + +G_END_DECLS + +#endif /* __UDISKS_LINUX_PHYSICAL_VOLUME_H__ */ diff --git a/src/udiskslinuxprovider.c b/src/udiskslinuxprovider.c index 0c247fe..904e718 100644 --- a/src/udiskslinuxprovider.c +++ b/src/udiskslinuxprovider.c @@ -25,11 +25,16 @@ #include "udiskslogging.h" #include "udisksdaemon.h" +#include "udisksdaemonutil.h" #include "udisksprovider.h" #include "udiskslinuxprovider.h" #include "udiskslinuxblockobject.h" #include "udiskslinuxdriveobject.h" #include "udiskslinuxmdraidobject.h" +#include "udiskslinuxvolumegroupobject.h" +#include "udiskslinuxblock.h" +#include "udiskslinuxlogicalvolume.h" +#include "udiskslinuxphysicalvolume.h" #include "udiskslinuxmanager.h" #include "udisksstate.h" #include "udiskslinuxdevice.h" @@ -73,6 +78,11 @@ struct _UDisksLinuxProvider GHashTable *sysfs_path_to_mdraid; GHashTable *sysfs_path_to_mdraid_members; + /* maps from volume group name to UDisksLinuxVolumeGroupObject + instances. + */ + GHashTable *name_to_volume_group; + GFileMonitor *etc_udisks2_dir_monitor; /* set to TRUE only in the coldplug phase */ @@ -81,6 +91,8 @@ struct _UDisksLinuxProvider guint housekeeping_timeout; guint64 housekeeping_last; gboolean housekeeping_running; + + gint lvm_delayed_update_id; }; G_LOCK_DEFINE_STATIC (provider_lock); @@ -118,6 +130,8 @@ static void on_etc_udisks2_dir_monitor_changed (GFileMonitor *monitor, GFileMonitorEvent event_type, gpointer user_data); +static void do_delayed_lvm_update_now (UDisksLinuxProvider *provider); + gpointer probe_request_thread_func (gpointer user_data); G_DEFINE_TYPE (UDisksLinuxProvider, udisks_linux_provider, UDISKS_TYPE_PROVIDER); @@ -149,6 +163,7 @@ udisks_linux_provider_finalize (GObject *object) g_hash_table_unref (provider->uuid_to_mdraid); g_hash_table_unref (provider->sysfs_path_to_mdraid); g_hash_table_unref (provider->sysfs_path_to_mdraid_members); + g_hash_table_unref (provider->name_to_volume_group); g_object_unref (provider->gudev_client); udisks_object_skeleton_set_manager (provider->manager_object, NULL); @@ -435,6 +450,10 @@ udisks_linux_provider_start (UDisksProvider *_provider) g_str_equal, g_free, NULL); + provider->name_to_volume_group = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) g_object_unref); devices = g_udev_client_query_by_subsystem (provider->gudev_client, "block"); @@ -463,6 +482,7 @@ udisks_linux_provider_start (UDisksProvider *_provider) } g_list_free_full (devices, g_object_unref); g_list_free_full (udisks_devices, g_object_unref); + do_delayed_lvm_update_now (provider); udisks_info ("Initialization complete"); /* schedule housekeeping for every 10 minutes */ @@ -707,6 +727,148 @@ handle_block_uevent_for_mdraid (UDisksLinuxProvider *provider, handle_block_uevent_for_mdraid_with_uuid (provider, action, device, NULL, FALSE); } +/* ------------------- */ + +static void +lvm_update_from_variant (GPid pid, + GVariant *volume_groups, + GError *error, + gpointer user_data) +{ + UDisksLinuxProvider *provider = user_data; + UDisksDaemon *daemon; + GDBusObjectManagerServer *manager; + GVariantIter var_iter; + GHashTableIter vg_name_iter; + gpointer key, value; + const gchar *name; + + daemon = udisks_provider_get_daemon (UDISKS_PROVIDER (provider)); + manager = udisks_daemon_get_object_manager (daemon); + + // Remove obsolete groups + g_hash_table_iter_init (&vg_name_iter, provider->name_to_volume_group); + while (g_hash_table_iter_next (&vg_name_iter, &key, &value)) + { + const gchar *vg; + UDisksLinuxVolumeGroupObject *group; + gboolean found = FALSE; + + name = key; + group = value; + + g_variant_iter_init (&var_iter, volume_groups); + while (g_variant_iter_next (&var_iter, "&s", &vg)) + if (g_strcmp0 (vg, name) == 0) + { + found = TRUE; + break; + } + + if (!found) + { + udisks_linux_volume_group_object_destroy (group); + g_dbus_object_manager_server_unexport (manager, + g_dbus_object_get_object_path (G_DBUS_OBJECT (group))); + g_hash_table_iter_remove (&vg_name_iter); + } + } + + // Add new groups and update existing groups + g_variant_iter_init (&var_iter, volume_groups); + while (g_variant_iter_next (&var_iter, "&s", &name)) + { + UDisksLinuxVolumeGroupObject *group; + group = g_hash_table_lookup (provider->name_to_volume_group, name); + + if (group == NULL) + { + group = udisks_linux_volume_group_object_new (daemon, name); + g_hash_table_insert (provider->name_to_volume_group, g_strdup (name), group); + } + udisks_linux_volume_group_object_update (group); + } +} + +static void +lvm_update (UDisksLinuxProvider *provider) +{ + const gchar *args[] = { "/usr/lib/udisks2/udisks-lvm", "-b", "list", NULL }; + udisks_daemon_util_spawn_for_variant (args, G_VARIANT_TYPE("as"), + lvm_update_from_variant, provider); +} + +static gboolean +delayed_lvm_update (gpointer user_data) +{ + UDisksLinuxProvider *provider = UDISKS_LINUX_PROVIDER (user_data); + lvm_update (provider); + provider->lvm_delayed_update_id = 0; + return FALSE; +} + +static void +trigger_delayed_lvm_update (UDisksLinuxProvider *provider) +{ + if (provider->lvm_delayed_update_id > 0) + return; + + provider->lvm_delayed_update_id = + g_timeout_add (100, delayed_lvm_update, provider); +} + +static void +do_delayed_lvm_update_now (UDisksLinuxProvider *provider) +{ + if (provider->lvm_delayed_update_id > 0) + { + g_source_remove (provider->lvm_delayed_update_id); + provider->lvm_delayed_update_id = 0; + lvm_update (provider); + } +} + +UDisksLinuxVolumeGroupObject * +udisks_linux_provider_find_volume_group_object (UDisksLinuxProvider *provider, + const gchar *name) +{ + return g_hash_table_lookup (provider->name_to_volume_group, name); +} + +static gboolean +is_logical_volume (UDisksLinuxDevice *device) +{ + const gchar *dm_vg_name = g_udev_device_get_property (device->udev_device, "DM_VG_NAME"); + return dm_vg_name && *dm_vg_name; +} + +static gboolean +has_physical_volume_label (UDisksLinuxDevice *device) +{ + const gchar *id_fs_type = g_udev_device_get_property (device->udev_device, "ID_FS_TYPE"); + return g_strcmp0 (id_fs_type, "LVM2_member") == 0; +} + +static gboolean +is_recorded_as_physical_volume (UDisksDaemon *daemon, + UDisksLinuxDevice *device) +{ + UDisksObject *object = udisks_daemon_find_block (daemon, g_udev_device_get_device_number (device->udev_device)); + return object && udisks_object_peek_physical_volume (object) != NULL; +} + +static void +handle_block_uevent_for_lvm (UDisksLinuxProvider *provider, + const gchar *action, + UDisksLinuxDevice *device) +{ + if (is_logical_volume (device) + || has_physical_volume_label (device) + || is_recorded_as_physical_volume (udisks_provider_get_daemon (UDISKS_PROVIDER (provider)), + device)) + trigger_delayed_lvm_update (provider); +} + /* ---------------------------------------------------------------------------------------------------- */ /* called with lock held */ @@ -858,6 +1020,7 @@ handle_block_uevent (UDisksLinuxProvider *provider, handle_block_uevent_for_block (provider, action, device); handle_block_uevent_for_drive (provider, action, device); handle_block_uevent_for_mdraid (provider, action, device); + handle_block_uevent_for_lvm (provider, action, device); } else { @@ -874,6 +1037,7 @@ handle_block_uevent (UDisksLinuxProvider *provider, } else { + handle_block_uevent_for_lvm (provider, action, device); handle_block_uevent_for_mdraid (provider, action, device); handle_block_uevent_for_drive (provider, action, device); handle_block_uevent_for_block (provider, action, device); diff --git a/src/udiskslinuxprovider.h b/src/udiskslinuxprovider.h index 9980f0b..42c718d 100644 --- a/src/udiskslinuxprovider.h +++ b/src/udiskslinuxprovider.h @@ -35,6 +35,8 @@ UDisksLinuxProvider *udisks_linux_provider_new (UDisksDaemon GUdevClient *udisks_linux_provider_get_udev_client (UDisksLinuxProvider *provider); gboolean udisks_linux_provider_get_coldplug (UDisksLinuxProvider *provider); +UDisksLinuxVolumeGroupObject *udisks_linux_provider_find_volume_group_object (UDisksLinuxProvider *provider, const gchar *name); + G_END_DECLS #endif /* __UDISKS_LINUX_PROVIDER_H__ */ diff --git a/src/udiskslinuxvolumegroup.c b/src/udiskslinuxvolumegroup.c new file mode 100644 index 0000000..417a51b --- /dev/null +++ b/src/udiskslinuxvolumegroup.c @@ -0,0 +1,1178 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2007-2010 David Zeuthen + * Copyright (C) 2013 Marius Vollmer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "udiskslogging.h" +#include "udiskslinuxprovider.h" +#include "udiskslinuxblock.h" +#include "udiskslinuxblockobject.h" +#include "udiskslinuxvolumegroup.h" +#include "udiskslinuxvolumegroupobject.h" +#include "udiskslinuxlogicalvolume.h" +#include "udiskslinuxlogicalvolumeobject.h" +#include "udisksdaemon.h" +#include "udisksstate.h" +#include "udisksdaemonutil.h" +#include "udiskslinuxdevice.h" + +/** + * SECTION:udiskslinuxvolume_group + * @title: UDisksLinuxVolumeGroup + * @short_description: Linux implementation of #UDisksVolumeGroup + * + * This type provides an implementation of the #UDisksVolumeGroup interface + * on Linux. + */ + +typedef struct _UDisksLinuxVolumeGroupClass UDisksLinuxVolumeGroupClass; + +/** + * UDisksLinuxVolumeGroup: + * + * The #UDisksLinuxVolumeGroup structure contains only private data and should + * only be accessed using the provided API. + */ +struct _UDisksLinuxVolumeGroup +{ + UDisksVolumeGroupSkeleton parent_instance; +}; + +struct _UDisksLinuxVolumeGroupClass +{ + UDisksVolumeGroupSkeletonClass parent_class; +}; + +static void volume_group_iface_init (UDisksVolumeGroupIface *iface); + +G_DEFINE_TYPE_WITH_CODE (UDisksLinuxVolumeGroup, udisks_linux_volume_group, UDISKS_TYPE_VOLUME_GROUP_SKELETON, + G_IMPLEMENT_INTERFACE (UDISKS_TYPE_VOLUME_GROUP, volume_group_iface_init)); + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +udisks_linux_volume_group_finalize (GObject *object) +{ + if (G_OBJECT_CLASS (udisks_linux_volume_group_parent_class)->finalize != NULL) + G_OBJECT_CLASS (udisks_linux_volume_group_parent_class)->finalize (object); +} + +static void +udisks_linux_volume_group_init (UDisksLinuxVolumeGroup *volume_group) +{ + g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (volume_group), + G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD); +} + +static void +udisks_linux_volume_group_class_init (UDisksLinuxVolumeGroupClass *klass) +{ + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = udisks_linux_volume_group_finalize; +} + +/** + * udisks_linux_volume_group_new: + * + * Creates a new #UDisksLinuxVolumeGroup instance. + * + * Returns: A new #UDisksLinuxVolumeGroup. Free with g_object_unref(). + */ +UDisksVolumeGroup * +udisks_linux_volume_group_new (void) +{ + return UDISKS_VOLUME_GROUP (g_object_new (UDISKS_TYPE_LINUX_VOLUME_GROUP, + NULL)); +} + +/** + * udisks_linux_volume_group_update: + * @volume_group: A #UDisksLinuxVolumeGroup. + * @object: The enclosing #UDisksLinuxVolumeGroupObject instance. + * + * Updates the interface. + */ +void +udisks_linux_volume_group_update (UDisksLinuxVolumeGroup *volume_group, + GVariant *info, + gboolean *needs_polling_ret) +{ + UDisksVolumeGroup *iface = UDISKS_VOLUME_GROUP (volume_group); + const gchar *str; + guint64 num; + + if (g_variant_lookup (info, "name", "&s", &str)) + { + gchar *decoded = udisks_daemon_util_decode_lvm_name (str); + udisks_volume_group_set_name (iface, str); + udisks_volume_group_set_display_name (iface, decoded); + g_free (decoded); + } + + if (g_variant_lookup (info, "uuid", "&s", &str)) + udisks_volume_group_set_uuid (iface, str); + + if (g_variant_lookup (info, "size", "t", &num)) + udisks_volume_group_set_size (iface, num); + + if (g_variant_lookup (info, "free-size", "t", &num)) + udisks_volume_group_set_free_size (iface, num); + + if (g_variant_lookup (info, "extent-size", "t", &num)) + udisks_volume_group_set_extent_size (iface, num); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +handle_poll (UDisksVolumeGroup *_group, + GDBusMethodInvocation *invocation) +{ + UDisksLinuxVolumeGroup *group = UDISKS_LINUX_VOLUME_GROUP (_group); + UDisksLinuxVolumeGroupObject *object = NULL; + GError *error = NULL; + + object = udisks_daemon_util_dup_object (group, &error); + if (object == NULL) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + udisks_linux_volume_group_object_poll (object); + + udisks_volume_group_complete_poll (_group, invocation); + + out: + g_clear_object (&object); + return TRUE; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +handle_delete (UDisksVolumeGroup *_group, + GDBusMethodInvocation *invocation, + GVariant *arg_options) +{ + GError *error = NULL; + UDisksLinuxVolumeGroup *group = UDISKS_LINUX_VOLUME_GROUP (_group); + UDisksLinuxVolumeGroupObject *object = NULL; + UDisksDaemon *daemon; + const gchar *action_id; + const gchar *message; + uid_t caller_uid; + gid_t caller_gid; + gchar *escaped_name = NULL; + gchar *error_message = NULL; + gboolean opt_wipe = FALSE; + GList *objects_to_wipe = NULL; + GList *l; + + object = udisks_daemon_util_dup_object (group, &error); + if (object == NULL) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + daemon = udisks_linux_volume_group_object_get_daemon (object); + + // Find physical volumes to wipe. + + g_variant_lookup (arg_options, "wipe", "b", &opt_wipe); + if (opt_wipe) + { + GList *objects = udisks_daemon_get_objects (daemon); + for (l = objects; l; l = l->next) + { + UDisksPhysicalVolume *physical_volume; + physical_volume = udisks_object_peek_physical_volume (UDISKS_OBJECT (l->data)); + if (physical_volume + && g_strcmp0 (udisks_physical_volume_get_volume_group (physical_volume), + g_dbus_object_get_object_path (G_DBUS_OBJECT (object))) == 0) + objects_to_wipe = g_list_append (objects_to_wipe, g_object_ref (l->data)); + } + g_list_free_full (objects, g_object_unref); + } + + if (!udisks_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; + } + + message = N_("Authentication is required to delete a volume group"); + action_id = "org.freedesktop.udisks2.manage-lvm"; + if (!udisks_daemon_util_check_authorization_sync (daemon, + UDISKS_OBJECT (object), + action_id, + arg_options, + message, + invocation)) + goto out; + + escaped_name = udisks_daemon_util_escape_and_quote (udisks_linux_volume_group_object_get_name (object)); + + if (!udisks_daemon_launch_spawned_job_sync (daemon, + UDISKS_OBJECT (object), + "lvm-vg-delete", 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 */ + "vgremove -f %s", + escaped_name)) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error deleting volume group: %s", + error_message); + goto out; + } + + for (l = objects_to_wipe; l; l = l->next) + { + UDisksBlock *block = udisks_object_peek_block (l->data); + if (block) + udisks_daemon_util_wipe_block (daemon, block, NULL); + } + + udisks_volume_group_complete_delete (_group, invocation); + + out: + g_list_free_full (objects_to_wipe, g_object_unref); + g_free (error_message); + g_free (escaped_name); + g_clear_object (&object); + return TRUE; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static UDisksObject * +wait_for_volume_group_object (UDisksDaemon *daemon, + gpointer user_data) +{ + const gchar *name = user_data; + UDisksLinuxProvider *provider; + + provider = udisks_daemon_get_linux_provider (daemon); + return UDISKS_OBJECT (udisks_linux_provider_find_volume_group_object (provider, name)); +} + +static gboolean +handle_rename (UDisksVolumeGroup *_group, + GDBusMethodInvocation *invocation, + const gchar *new_name, + GVariant *options) +{ + GError *error = NULL; + UDisksLinuxVolumeGroup *group = UDISKS_LINUX_VOLUME_GROUP (_group); + UDisksLinuxVolumeGroupObject *object = NULL; + UDisksDaemon *daemon; + const gchar *action_id; + const gchar *message; + uid_t caller_uid; + gid_t caller_gid; + gchar *escaped_name = NULL; + gchar *encoded_new_name = NULL; + gchar *escaped_new_name = NULL; + gchar *error_message = NULL; + UDisksObject *group_object = NULL; + + object = udisks_daemon_util_dup_object (group, &error); + if (object == NULL) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + daemon = udisks_linux_volume_group_object_get_daemon (object); + + if (!udisks_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; + } + + message = N_("Authentication is required to rename a volume group"); + action_id = "org.freedesktop.udisks2.manage-lvm"; + if (!udisks_daemon_util_check_authorization_sync (daemon, + UDISKS_OBJECT (object), + action_id, + options, + message, + invocation)) + goto out; + + escaped_name = udisks_daemon_util_escape_and_quote (udisks_linux_volume_group_object_get_name (object)); + encoded_new_name = udisks_daemon_util_encode_lvm_name (new_name, FALSE); + escaped_new_name = udisks_daemon_util_escape_and_quote (encoded_new_name); + + if (!udisks_daemon_launch_spawned_job_sync (daemon, + UDISKS_OBJECT (object), + "lvm-vg-rename", 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 */ + "vgrename %s %s", + escaped_name, + escaped_new_name)) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error renaming volume group: %s", + error_message); + goto out; + } + + group_object = udisks_daemon_wait_for_object_sync (daemon, + wait_for_volume_group_object, + (gpointer)encoded_new_name, + NULL, + 10, /* timeout_seconds */ + &error); + if (group_object == NULL) + { + g_prefix_error (&error, + "Error waiting for volume group object for %s", + new_name); + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + udisks_volume_group_complete_rename (_group, + invocation, + g_dbus_object_get_object_path (G_DBUS_OBJECT (group_object))); + + out: + g_free (error_message); + g_free (escaped_name); + g_free (encoded_new_name); + g_free (escaped_new_name); + g_clear_object (&object); + return TRUE; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +handle_add_device (UDisksVolumeGroup *_group, + GDBusMethodInvocation *invocation, + const gchar *new_member_device_objpath, + GVariant *options) +{ + UDisksLinuxVolumeGroup *group = UDISKS_LINUX_VOLUME_GROUP (_group); + UDisksDaemon *daemon; + UDisksLinuxVolumeGroupObject *object; + const gchar *action_id; + const gchar *message; + uid_t caller_uid; + gid_t caller_gid; + const gchar *new_member_device_file = NULL; + gchar *escaped_new_member_device_file = NULL; + GError *error = NULL; + gchar *error_message = NULL; + UDisksObject *new_member_device_object = NULL; + UDisksBlock *new_member_device = NULL; + gchar *escaped_name = NULL; + + object = udisks_daemon_util_dup_object (group, &error); + if (object == NULL) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + daemon = udisks_linux_volume_group_object_get_daemon (object); + + error = NULL; + if (!udisks_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; + } + + new_member_device_object = udisks_daemon_find_object (daemon, new_member_device_objpath); + if (new_member_device_object == NULL) + { + g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED, + "No device for given object path"); + goto out; + } + + new_member_device = udisks_object_get_block (new_member_device_object); + if (new_member_device == NULL) + { + g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED, + "No block interface on given object"); + goto out; + } + + message = N_("Authentication is required to add a device to a volume group"); + action_id = "org.freedesktop.udisks2.manage-lvm"; + if (!udisks_daemon_util_check_authorization_sync (daemon, + UDISKS_OBJECT (object), + action_id, + options, + message, + invocation)) + goto out; + + if (!udisks_daemon_util_block_is_unused (new_member_device, &error)) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + if (!udisks_daemon_util_wipe_block (daemon, new_member_device, &error)) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + escaped_name = udisks_daemon_util_escape_and_quote (udisks_linux_volume_group_object_get_name (object)); + new_member_device_file = udisks_block_get_device (new_member_device); + escaped_new_member_device_file = udisks_daemon_util_escape_and_quote (new_member_device_file); + + if (!udisks_daemon_launch_spawned_job_sync (daemon, + UDISKS_OBJECT (object), + "lvm-vg-add-device", 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 */ + "vgextend %s %s", + escaped_name, + escaped_new_member_device_file)) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error adding %s to volume group: %s", + new_member_device_file, + error_message); + goto out; + } + + udisks_volume_group_complete_add_device (_group, invocation); + + out: + g_free (error_message); + g_free (escaped_name); + g_free (escaped_new_member_device_file); + g_clear_object (&new_member_device_object); + g_clear_object (&new_member_device); + g_clear_object (&object); + return TRUE; /* returning TRUE means that we handled the method invocation */ +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +handle_remove_device (UDisksVolumeGroup *_group, + GDBusMethodInvocation *invocation, + const gchar *member_device_objpath, + GVariant *options) +{ + UDisksLinuxVolumeGroup *group = UDISKS_LINUX_VOLUME_GROUP (_group); + UDisksDaemon *daemon; + UDisksLinuxVolumeGroupObject *object; + const gchar *action_id; + const gchar *message; + uid_t caller_uid; + gid_t caller_gid; + const gchar *member_device_file = NULL; + gchar *escaped_member_device_file = NULL; + GError *error = NULL; + gchar *error_message = NULL; + UDisksObject *member_device_object = NULL; + UDisksBlock *member_device = NULL; + gchar *escaped_name = NULL; + gboolean opt_wipe = FALSE; + + object = udisks_daemon_util_dup_object (group, &error); + if (object == NULL) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + daemon = udisks_linux_volume_group_object_get_daemon (object); + + g_variant_lookup (options, "wipe", "b", &opt_wipe); + + error = NULL; + if (!udisks_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; + } + + member_device_object = udisks_daemon_find_object (daemon, member_device_objpath); + if (member_device_object == NULL) + { + g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED, + "No device for given object path"); + goto out; + } + + member_device = udisks_object_get_block (member_device_object); + if (member_device == NULL) + { + g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED, + "No block interface on given object"); + goto out; + } + + message = N_("Authentication is required to remove a device from a volume group"); + action_id = "org.freedesktop.udisks2.manage-lvm"; + if (!udisks_daemon_util_check_authorization_sync (daemon, + UDISKS_OBJECT (object), + action_id, + options, + message, + invocation)) + goto out; + + escaped_name = udisks_daemon_util_escape_and_quote (udisks_linux_volume_group_object_get_name (object)); + member_device_file = udisks_block_get_device (member_device); + escaped_member_device_file = udisks_daemon_util_escape_and_quote (member_device_file); + + if (!udisks_daemon_launch_spawned_job_sync (daemon, + UDISKS_OBJECT (object), + "lvm-vg-rem-device", 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 */ + "vgreduce %s %s", + escaped_name, + escaped_member_device_file)) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error remove %s from volume group: %s", + member_device_file, + error_message); + goto out; + } + + if (opt_wipe) + { + if (!udisks_daemon_launch_spawned_job_sync (daemon, + UDISKS_OBJECT (member_device_object), + "format-erase", 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_member_device_file)) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error wiping %s after removal from volume group %s: %s", + member_device_file, + udisks_linux_volume_group_object_get_name (object), + error_message); + goto out; + } + } + + udisks_volume_group_complete_remove_device (_group, invocation); + + out: + g_free (error_message); + g_free (escaped_name); + g_free (escaped_member_device_file); + g_clear_object (&member_device_object); + g_clear_object (&member_device); + g_clear_object (&object); + return TRUE; /* returning TRUE means that we handled the method invocation */ +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +handle_empty_device (UDisksVolumeGroup *_group, + GDBusMethodInvocation *invocation, + const gchar *member_device_objpath, + GVariant *options) +{ + UDisksLinuxVolumeGroup *group = UDISKS_LINUX_VOLUME_GROUP (_group); + UDisksDaemon *daemon; + UDisksLinuxVolumeGroupObject *object; + const gchar *action_id; + const gchar *message; + uid_t caller_uid; + gid_t caller_gid; + const gchar *member_device_file = NULL; + gchar *escaped_member_device_file = NULL; + GError *error = NULL; + gchar *error_message = NULL; + UDisksObject *member_device_object = NULL; + UDisksBlock *member_device = NULL; + gboolean no_block = FALSE; + + object = udisks_daemon_util_dup_object (group, &error); + if (object == NULL) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + daemon = udisks_linux_volume_group_object_get_daemon (object); + + g_variant_lookup (options, "no-block", "b", &no_block); + + error = NULL; + if (!udisks_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; + } + + member_device_object = udisks_daemon_find_object (daemon, member_device_objpath); + if (member_device_object == NULL) + { + g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED, + "No device for given object path"); + goto out; + } + + member_device = udisks_object_get_block (member_device_object); + if (member_device == NULL) + { + g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED, + "No block interface on given object"); + goto out; + } + + message = N_("Authentication is required to empty a device in a volume group"); + action_id = "org.freedesktop.udisks2.manage-lvm"; + if (!udisks_daemon_util_check_authorization_sync (daemon, + UDISKS_OBJECT (object), + action_id, + options, + message, + invocation)) + goto out; + + member_device_file = udisks_block_get_device (member_device); + escaped_member_device_file = udisks_daemon_util_escape_and_quote (member_device_file); + + if (!udisks_daemon_launch_spawned_job_sync (daemon, + UDISKS_OBJECT (member_device_object), + "lvm-vg-empty-device", 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 */ + "pvmove %s%s", + no_block? "-b " : "", + escaped_member_device_file)) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error emptying %s: %s", + member_device_file, + error_message); + goto out; + } + + if (!no_block) + udisks_volume_group_complete_remove_device (_group, invocation); + + out: + g_free (error_message); + g_free (escaped_member_device_file); + g_clear_object (&member_device_object); + g_clear_object (&member_device); + g_clear_object (&object); + return TRUE; /* returning TRUE means that we handled the method invocation */ +} + +/* ---------------------------------------------------------------------------------------------------- */ + +struct WaitData { + UDisksLinuxVolumeGroupObject *group_object; + const gchar *name; +}; + +static UDisksObject * +wait_for_logical_volume_object (UDisksDaemon *daemon, + gpointer user_data) +{ + struct WaitData *data = user_data; + return UDISKS_OBJECT (udisks_linux_volume_group_object_find_logical_volume_object (data->group_object, + data->name)); +} + +static const gchar * +wait_for_logical_volume_path (UDisksLinuxVolumeGroupObject *group_object, + const gchar *name, + GError **error) +{ + struct WaitData data; + UDisksDaemon *daemon; + UDisksObject *volume_object; + + data.group_object = group_object; + data.name = name; + daemon = udisks_linux_volume_group_object_get_daemon (group_object); + volume_object = udisks_daemon_wait_for_object_sync (daemon, + wait_for_logical_volume_object, + &data, + NULL, + 10, /* timeout_seconds */ + error); + if (volume_object == NULL) + return NULL; + + return g_dbus_object_get_object_path (G_DBUS_OBJECT (volume_object)); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +handle_create_plain_volume (UDisksVolumeGroup *_group, + GDBusMethodInvocation *invocation, + const gchar *arg_name, + guint64 arg_size, + gint arg_stripes, + guint64 arg_stripesize, + GVariant *options) +{ + GError *error = NULL; + UDisksLinuxVolumeGroup *group = UDISKS_LINUX_VOLUME_GROUP (_group); + UDisksLinuxVolumeGroupObject *object = NULL; + UDisksDaemon *daemon; + const gchar *action_id; + const gchar *message; + uid_t caller_uid; + gid_t caller_gid; + gchar *encoded_volume_name = NULL; + gchar *escaped_volume_name = NULL; + gchar *escaped_group_name = NULL; + GString *cmd = NULL; + gchar *error_message = NULL; + const gchar *lv_objpath; + + object = udisks_daemon_util_dup_object (group, &error); + if (object == NULL) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + daemon = udisks_linux_volume_group_object_get_daemon (object); + + if (!udisks_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; + } + + message = N_("Authentication is required to create a logical volume"); + action_id = "org.freedesktop.udisks2.manage-lvm"; + if (!udisks_daemon_util_check_authorization_sync (daemon, + UDISKS_OBJECT (object), + action_id, + options, + message, + invocation)) + goto out; + + encoded_volume_name = udisks_daemon_util_encode_lvm_name (arg_name, TRUE); + escaped_volume_name = udisks_daemon_util_escape_and_quote (encoded_volume_name); + escaped_group_name = udisks_daemon_util_escape_and_quote (udisks_linux_volume_group_object_get_name (object)); + arg_size -= arg_size % 512; + + cmd = g_string_new (""); + g_string_append_printf (cmd, "lvcreate %s -L %" G_GUINT64_FORMAT "b -n %s", + escaped_group_name, arg_size, escaped_volume_name); + + if (arg_stripes > 0) + g_string_append_printf (cmd, " -i %d", arg_stripes); + + if (arg_stripesize > 0) + g_string_append_printf (cmd, " -I %" G_GUINT64_FORMAT "b", arg_stripesize); + + if (!udisks_daemon_launch_spawned_job_sync (daemon, + UDISKS_OBJECT (object), + "lvm-vg-create-volume", 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", cmd->str)) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error creating volume: %s", + error_message); + goto out; + } + + lv_objpath = wait_for_logical_volume_path (object, encoded_volume_name, &error); + if (lv_objpath == NULL) + { + g_prefix_error (&error, + "Error waiting for logical volume object for %s", + arg_name); + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + udisks_volume_group_complete_create_plain_volume (_group, invocation, lv_objpath); + + out: + g_free (error_message); + g_free (escaped_group_name); + g_free (encoded_volume_name); + g_free (escaped_volume_name); + g_string_free (cmd, TRUE); + g_clear_object (&object); + return TRUE; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +handle_create_thin_pool_volume (UDisksVolumeGroup *_group, + GDBusMethodInvocation *invocation, + const gchar *arg_name, + guint64 arg_size, + GVariant *options) +{ + GError *error = NULL; + UDisksLinuxVolumeGroup *group = UDISKS_LINUX_VOLUME_GROUP (_group); + UDisksLinuxVolumeGroupObject *object = NULL; + UDisksDaemon *daemon; + const gchar *action_id; + const gchar *message; + uid_t caller_uid; + gid_t caller_gid; + gchar *encoded_volume_name = NULL; + gchar *escaped_volume_name = NULL; + gchar *escaped_group_name = NULL; + GString *cmd = NULL; + gchar *error_message = NULL; + const gchar *lv_objpath; + + object = udisks_daemon_util_dup_object (group, &error); + if (object == NULL) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + daemon = udisks_linux_volume_group_object_get_daemon (object); + + if (!udisks_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; + } + + message = N_("Authentication is required to create a logical volume"); + action_id = "org.freedesktop.udisks2.manage-lvm"; + if (!udisks_daemon_util_check_authorization_sync (daemon, + UDISKS_OBJECT (object), + action_id, + options, + message, + invocation)) + goto out; + + encoded_volume_name = udisks_daemon_util_encode_lvm_name (arg_name, TRUE); + escaped_volume_name = udisks_daemon_util_escape_and_quote (encoded_volume_name); + escaped_group_name = udisks_daemon_util_escape_and_quote (udisks_linux_volume_group_object_get_name (object)); + arg_size -= arg_size % 512; + + cmd = g_string_new (""); + g_string_append_printf (cmd, "lvcreate %s -T -L %" G_GUINT64_FORMAT "b --thinpool %s", + escaped_group_name, arg_size, escaped_volume_name); + + if (!udisks_daemon_launch_spawned_job_sync (daemon, + UDISKS_OBJECT (object), + "lvm-vg-create-volume", 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", cmd->str)) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error creating volume: %s", + error_message); + goto out; + } + + lv_objpath = wait_for_logical_volume_path (object, encoded_volume_name, &error); + if (lv_objpath == NULL) + { + g_prefix_error (&error, + "Error waiting for logical volume object for %s", + arg_name); + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + udisks_volume_group_complete_create_thin_pool_volume (_group, invocation, lv_objpath); + + out: + g_free (error_message); + g_free (encoded_volume_name); + g_free (escaped_volume_name); + g_free (escaped_group_name); + g_string_free (cmd, TRUE); + g_clear_object (&object); + return TRUE; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +handle_create_thin_volume (UDisksVolumeGroup *_group, + GDBusMethodInvocation *invocation, + const gchar *arg_name, + guint64 arg_size, + const gchar *arg_pool, + GVariant *options) +{ + GError *error = NULL; + UDisksLinuxVolumeGroup *group = UDISKS_LINUX_VOLUME_GROUP (_group); + UDisksLinuxVolumeGroupObject *object = NULL; + UDisksDaemon *daemon; + const gchar *action_id; + const gchar *message; + uid_t caller_uid; + gid_t caller_gid; + UDisksLinuxLogicalVolumeObject *pool_object; + gchar *encoded_volume_name = NULL; + gchar *escaped_volume_name = NULL; + gchar *escaped_group_name = NULL; + gchar *escaped_pool_name = NULL; + GString *cmd = NULL; + gchar *error_message = NULL; + const gchar *lv_objpath; + + object = udisks_daemon_util_dup_object (group, &error); + if (object == NULL) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + daemon = udisks_linux_volume_group_object_get_daemon (object); + + if (!udisks_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; + } + + message = N_("Authentication is required to create a logical volume"); + action_id = "org.freedesktop.udisks2.manage-lvm"; + if (!udisks_daemon_util_check_authorization_sync (daemon, + UDISKS_OBJECT (object), + action_id, + options, + message, + invocation)) + goto out; + + pool_object = UDISKS_LINUX_LOGICAL_VOLUME_OBJECT (udisks_daemon_find_object (daemon, arg_pool)); + if (pool_object == NULL || !UDISKS_IS_LINUX_LOGICAL_VOLUME_OBJECT (pool_object)) + { + g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED, + "Not a logical volume"); + goto out; + } + + encoded_volume_name = udisks_daemon_util_encode_lvm_name (arg_name, TRUE); + escaped_volume_name = udisks_daemon_util_escape_and_quote (encoded_volume_name); + escaped_group_name = udisks_daemon_util_escape_and_quote (udisks_linux_volume_group_object_get_name (object)); + arg_size -= arg_size % 512; + escaped_pool_name = udisks_daemon_util_escape_and_quote (udisks_linux_logical_volume_object_get_name (pool_object)); + + cmd = g_string_new (""); + g_string_append_printf (cmd, "lvcreate %s --thinpool %s -V %" G_GUINT64_FORMAT "b -n %s", + escaped_group_name, escaped_pool_name, arg_size, escaped_volume_name); + + if (!udisks_daemon_launch_spawned_job_sync (daemon, + UDISKS_OBJECT (object), + "lvm-vg-create-volume", 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", cmd->str)) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error creating volume: %s", + error_message); + goto out; + } + + lv_objpath = wait_for_logical_volume_path (object, encoded_volume_name, &error); + if (lv_objpath == NULL) + { + g_prefix_error (&error, + "Error waiting for logical volume object for %s", + arg_name); + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + udisks_volume_group_complete_create_thin_pool_volume (_group, invocation, lv_objpath); + + out: + g_free (error_message); + g_free (encoded_volume_name); + g_free (escaped_volume_name); + g_free (escaped_group_name); + g_free (escaped_pool_name); + g_string_free (cmd, TRUE); + g_clear_object (&pool_object); + g_clear_object (&object); + return TRUE; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +volume_group_iface_init (UDisksVolumeGroupIface *iface) +{ + iface->handle_poll = handle_poll; + + iface->handle_delete = handle_delete; + iface->handle_rename = handle_rename; + + iface->handle_add_device = handle_add_device; + iface->handle_remove_device = handle_remove_device; + iface->handle_empty_device = handle_empty_device; + + iface->handle_create_plain_volume = handle_create_plain_volume; + iface->handle_create_thin_pool_volume = handle_create_thin_pool_volume; + iface->handle_create_thin_volume = handle_create_thin_volume; +} diff --git a/src/udiskslinuxvolumegroup.h b/src/udiskslinuxvolumegroup.h new file mode 100644 index 0000000..624f193 --- /dev/null +++ b/src/udiskslinuxvolumegroup.h @@ -0,0 +1,40 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2013 Marius Vollmer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __UDISKS_LINUX_VOLUME_GROUP_H__ +#define __UDISKS_LINUX_VOLUME_GROUP_H__ + +#include "udisksdaemontypes.h" + +G_BEGIN_DECLS + +#define UDISKS_TYPE_LINUX_VOLUME_GROUP (udisks_linux_volume_group_get_type ()) +#define UDISKS_LINUX_VOLUME_GROUP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), UDISKS_TYPE_LINUX_VOLUME_GROUP, UDisksLinuxVolumeGroup)) +#define UDISKS_IS_LINUX_VOLUME_GROUP(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), UDISKS_TYPE_LINUX_VOLUME_GROUP)) + +GType udisks_linux_volume_group_get_type (void) G_GNUC_CONST; +UDisksVolumeGroup *udisks_linux_volume_group_new (void); +void udisks_linux_volume_group_update (UDisksLinuxVolumeGroup *volume_group, + GVariant *info, + gboolean *needs_polling_ret); + +G_END_DECLS + +#endif /* __UDISKS_LINUX_VOLUME_GROUP_H__ */ diff --git a/src/udiskslinuxvolumegroupobject.c b/src/udiskslinuxvolumegroupobject.c new file mode 100644 index 0000000..92a06b9 --- /dev/null +++ b/src/udiskslinuxvolumegroupobject.c @@ -0,0 +1,685 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2007-2010 David Zeuthen + * Copyright (C) 2013 Marius Vollmer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include + +#include +#include +#include + +#include "udiskslogging.h" +#include "udisksdaemon.h" +#include "udisksdaemonutil.h" +#include "udiskslinuxprovider.h" +#include "udiskslinuxvolumegroupobject.h" +#include "udiskslinuxvolumegroup.h" +#include "udiskslinuxlogicalvolumeobject.h" +#include "udiskslinuxblockobject.h" +#include "udiskslinuxdevice.h" + +/** + * SECTION:udiskslinuxvolumegroupobject + * @title: UDisksLinuxVolumeGroupObject + * @short_description: Object representing a LVM volume group + */ + +typedef struct _UDisksLinuxVolumeGroupObjectClass UDisksLinuxVolumeGroupObjectClass; + +/** + * UDisksLinuxVolumeGroupObject: + * + * The #UDisksLinuxVolumeGroupObject structure contains only private data and + * should only be accessed using the provided API. + */ +struct _UDisksLinuxVolumeGroupObject +{ + UDisksObjectSkeleton parent_instance; + + UDisksDaemon *daemon; + + gchar *name; + + GHashTable *logical_volumes; + GPid poll_pid; + guint poll_timeout_id; + gboolean poll_requested; + + /* interface */ + UDisksVolumeGroup *iface_volume_group; +}; + +struct _UDisksLinuxVolumeGroupObjectClass +{ + UDisksObjectSkeletonClass parent_class; +}; + +enum +{ + PROP_0, + PROP_DAEMON, + PROP_NAME, +}; + +G_DEFINE_TYPE (UDisksLinuxVolumeGroupObject, udisks_linux_volume_group_object, UDISKS_TYPE_OBJECT_SKELETON); + +static void +udisks_linux_volume_group_object_finalize (GObject *_object) +{ + UDisksLinuxVolumeGroupObject *object = UDISKS_LINUX_VOLUME_GROUP_OBJECT (_object); + + /* note: we don't hold a ref to object->daemon */ + + if (object->iface_volume_group != NULL) + g_object_unref (object->iface_volume_group); + + g_hash_table_unref (object->logical_volumes); + g_free (object->name); + + if (G_OBJECT_CLASS (udisks_linux_volume_group_object_parent_class)->finalize != NULL) + G_OBJECT_CLASS (udisks_linux_volume_group_object_parent_class)->finalize (_object); +} + +static void +udisks_linux_volume_group_object_get_property (GObject *__object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + UDisksLinuxVolumeGroupObject *object = UDISKS_LINUX_VOLUME_GROUP_OBJECT (__object); + + switch (prop_id) + { + case PROP_DAEMON: + g_value_set_object (value, udisks_linux_volume_group_object_get_daemon (object)); + break; + + case PROP_NAME: + g_value_set_string (value, udisks_linux_volume_group_object_get_name (object)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +udisks_linux_volume_group_object_set_property (GObject *__object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + UDisksLinuxVolumeGroupObject *object = UDISKS_LINUX_VOLUME_GROUP_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_NAME: + g_assert (object->name == NULL); + object->name = g_value_dup_string (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +udisks_linux_volume_group_object_init (UDisksLinuxVolumeGroupObject *object) +{ +} + +static void +udisks_linux_volume_group_object_constructed (GObject *_object) +{ + UDisksLinuxVolumeGroupObject *object = UDISKS_LINUX_VOLUME_GROUP_OBJECT (_object); + GString *s; + + if (G_OBJECT_CLASS (udisks_linux_volume_group_object_parent_class)->constructed != NULL) + G_OBJECT_CLASS (udisks_linux_volume_group_object_parent_class)->constructed (_object); + + object->logical_volumes = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) g_object_unref); + + /* compute the object path */ + s = g_string_new ("/org/freedesktop/UDisks2/lvm/"); + udisks_safe_append_to_object_path (s, object->name); + g_dbus_object_skeleton_set_object_path (G_DBUS_OBJECT_SKELETON (object), s->str); + g_string_free (s, TRUE); + + /* create the DBus interface */ + object->iface_volume_group = udisks_linux_volume_group_new (); + g_dbus_object_skeleton_add_interface (G_DBUS_OBJECT_SKELETON (object), + G_DBUS_INTERFACE_SKELETON (object->iface_volume_group)); +} + +static void +udisks_linux_volume_group_object_class_init (UDisksLinuxVolumeGroupObjectClass *klass) +{ + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = udisks_linux_volume_group_object_finalize; + gobject_class->constructed = udisks_linux_volume_group_object_constructed; + gobject_class->set_property = udisks_linux_volume_group_object_set_property; + gobject_class->get_property = udisks_linux_volume_group_object_get_property; + + /** + * UDisksLinuxVolumeGroupObject:daemon: + * + * The #UDisksDaemon the object is for. + */ + g_object_class_install_property (gobject_class, + PROP_DAEMON, + g_param_spec_object ("daemon", + "Daemon", + "The daemon the object is for", + UDISKS_TYPE_DAEMON, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * UDisksLinuxVolumeGroupObject:name: + * + * The name of the volume group. + */ + g_object_class_install_property (gobject_class, + PROP_NAME, + g_param_spec_string ("name", + "Name", + "The name of the volume group", + NULL, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + +/** + * udisks_linux_volume_group_object_new: + * @daemon: A #UDisksDaemon. + * @name: The name of the volume group. + * + * Create a new VolumeGroup object. + * + * Returns: A #UDisksLinuxVolumeGroupObject object. Free with g_object_unref(). + */ +UDisksLinuxVolumeGroupObject * +udisks_linux_volume_group_object_new (UDisksDaemon *daemon, + const gchar *name) +{ + g_return_val_if_fail (UDISKS_IS_DAEMON (daemon), NULL); + g_return_val_if_fail (name != NULL, NULL); + return UDISKS_LINUX_VOLUME_GROUP_OBJECT (g_object_new (UDISKS_TYPE_LINUX_VOLUME_GROUP_OBJECT, + "daemon", daemon, + "name", name, + NULL)); +} + +/** + * udisks_linux_volume_group_object_get_daemon: + * @object: A #UDisksLinuxVolumeGroupObject. + * + * Gets the daemon used by @object. + * + * Returns: A #UDisksDaemon. Do not free, the object is owned by @object. + */ +UDisksDaemon * +udisks_linux_volume_group_object_get_daemon (UDisksLinuxVolumeGroupObject *object) +{ + g_return_val_if_fail (UDISKS_IS_LINUX_VOLUME_GROUP_OBJECT (object), NULL); + return object->daemon; +} + +static gboolean +lv_is_pvmove_volume (const gchar *name) +{ + return name && g_str_has_prefix (name, "pvmove"); +} + +static gboolean +lv_is_visible (const gchar *name) +{ + // XXX - get this from lvm2app + + return (name + && strstr (name, "_mlog") == NULL + && strstr (name, "_mimage") == NULL + && strstr (name, "_rimage") == NULL + && strstr (name, "_rmeta") == NULL + && strstr (name, "_tdata") == NULL + && strstr (name, "_tmeta") == NULL + && !g_str_has_prefix (name, "pvmove") + && !g_str_has_prefix (name, "snapshot")); +} + +static void +update_progress_for_device (UDisksDaemon *daemon, + const gchar *operation, + const gchar *dev, + double progress) +{ + GDBusObjectManager *object_manager; + GList *objects, *l; + + object_manager = G_DBUS_OBJECT_MANAGER (udisks_daemon_get_object_manager (daemon)); + objects = g_dbus_object_manager_get_objects (object_manager); + + for (l = objects; l; l = l->next) + { + UDisksObject *object = UDISKS_OBJECT (l->data); + UDisksJob *job; + const gchar *const *job_objects; + int i; + + job = udisks_object_peek_job (object); + if (job == NULL) + continue; + + if (g_strcmp0 (udisks_job_get_operation (job), operation) != 0) + continue; + + job_objects = udisks_job_get_objects (job); + for (i = 0; job_objects[i]; i++) + { + UDisksBlock *block = UDISKS_BLOCK (g_dbus_object_manager_get_interface (object_manager, + job_objects[i], + "org.freedesktop.UDisks2.Block")); + + if (block) + { + const gchar *const *symlinks; + int j; + if (g_strcmp0 (udisks_block_get_device (block), dev) == 0) + goto found; + symlinks = udisks_block_get_symlinks (block); + for (j = 0; symlinks[j]; j++) + if (g_strcmp0 (symlinks[j], dev) == 0) + goto found; + + continue; + found: + udisks_job_set_progress (job, progress); + udisks_job_set_progress_valid (job, TRUE); + } + } + + } + g_list_free_full (objects, g_object_unref); +} + +static void +update_operations (UDisksDaemon *daemon, + const gchar *lv_name, GVariant *lv_info, + gboolean *needs_polling_ret) +{ + const gchar *move_pv; + guint64 copy_percent; + + if (lv_is_pvmove_volume (lv_name) + && g_variant_lookup (lv_info, "move_pv", "&s", &move_pv) + && g_variant_lookup (lv_info, "copy_percent", "t", ©_percent)) + { + update_progress_for_device (daemon, + "lvm-vg-empty-device", + move_pv, + copy_percent/100000000.0); + *needs_polling_ret = TRUE; + } +} + +static void +update_block (UDisksLinuxBlockObject *block_object, + UDisksLinuxVolumeGroupObject *group_object, + GHashTable *new_lvs, + GHashTable *new_pvs) +{ + UDisksBlock *block; + GVariant *pv_info; + + block = udisks_object_peek_block (UDISKS_OBJECT (block_object)); + if (block == NULL) + return; + + // XXX - move this elsewhere? + { + UDisksLinuxDevice *device; + UDisksLinuxLogicalVolumeObject *lv_object; + const gchar *block_vg_name; + const gchar *block_lv_name; + + device = udisks_linux_block_object_get_device (block_object); + if (device) + { + block_vg_name = g_udev_device_get_property (device->udev_device, "DM_VG_NAME"); + block_lv_name = g_udev_device_get_property (device->udev_device, "DM_LV_NAME"); + + if (g_strcmp0 (block_vg_name, udisks_linux_volume_group_object_get_name (group_object)) == 0 + && (lv_object = g_hash_table_lookup (new_lvs, block_lv_name))) + { + udisks_block_set_logical_volume (block, g_dbus_object_get_object_path (G_DBUS_OBJECT (lv_object))); + } + } + } + + pv_info = g_hash_table_lookup (new_pvs, udisks_block_get_device (block)); + if (!pv_info) + { + const gchar *const *symlinks; + int i; + symlinks = udisks_block_get_symlinks (block); + for (i = 0; symlinks[i]; i++) + { + pv_info = g_hash_table_lookup (new_pvs, symlinks[i]); + if (pv_info) + break; + } + } + + if (pv_info) + { + udisks_linux_block_object_update_lvm_pv (block_object, group_object, pv_info); + } + else + { + UDisksPhysicalVolume *pv = udisks_object_peek_physical_volume (UDISKS_OBJECT (block_object)); + if (pv && g_strcmp0 (udisks_physical_volume_get_volume_group (pv), + g_dbus_object_get_object_path (G_DBUS_OBJECT (group_object))) == 0) + udisks_linux_block_object_update_lvm_pv (block_object, NULL, NULL); + } +} + +static void +update_with_variant (GPid pid, + GVariant *info, + GError *error, + gpointer user_data) +{ + UDisksLinuxVolumeGroupObject *object = user_data; + UDisksDaemon *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 = udisks_linux_volume_group_object_get_daemon (object); + manager = udisks_daemon_get_object_manager (daemon); + + if (error) + { + udisks_warning ("Failed to update LVM volume group %s: %s", + udisks_linux_volume_group_object_get_name (object), + error->message); + g_object_unref (object); + return; + } + + udisks_linux_volume_group_update (UDISKS_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; + UDisksLinuxLogicalVolumeObject *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 (!lv_is_visible (name)) + continue; + + volume = g_hash_table_lookup (object->logical_volumes, name); + if (volume == NULL) + { + volume = udisks_linux_logical_volume_object_new (daemon, object, name); + udisks_linux_logical_volume_object_update (volume, lv_info, &needs_polling); + 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 + udisks_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; + UDisksLinuxLogicalVolumeObject *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); + } + } + + udisks_volume_group_set_needs_polling (UDISKS_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 (UDISKS_IS_LINUX_BLOCK_OBJECT (l->data)) + update_block (UDISKS_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); +} + +void +udisks_linux_volume_group_object_update (UDisksLinuxVolumeGroupObject *object) +{ + const gchar *args[] = { "/usr/lib/udisks2/udisks-lvm", "-b", "show", object->name, NULL }; + udisks_daemon_util_spawn_for_variant (args, G_VARIANT_TYPE("a{sv}"), + update_with_variant, g_object_ref (object)); +} + +static void +poll_with_variant (GPid pid, + GVariant *info, + GError *error, + gpointer user_data) +{ + UDisksLinuxVolumeGroupObject *object = user_data; + UDisksDaemon *daemon; + GVariantIter *iter; + gboolean needs_polling; + + if (pid != object->poll_pid) + { + g_object_unref (object); + return; + } + + object->poll_pid = 0; + + if (error) + { + udisks_warning ("Failed to poll LVM volume group %s: %s", + udisks_linux_volume_group_object_get_name (object), + error->message); + g_object_unref (object); + return; + } + + daemon = udisks_linux_volume_group_object_get_daemon (object); + + udisks_linux_volume_group_update (UDISKS_LINUX_VOLUME_GROUP (object->iface_volume_group), info, &needs_polling); + + 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; + UDisksLinuxLogicalVolumeObject *volume; + + g_variant_lookup (lv_info, "name", "&s", &name); + update_operations (daemon, name, lv_info, &needs_polling); + volume = g_hash_table_lookup (object->logical_volumes, name); + if (volume) + udisks_linux_logical_volume_object_update (volume, lv_info, &needs_polling); + } + g_variant_iter_free (iter); + } + + g_object_unref (object); +} + +static void poll_now (UDisksLinuxVolumeGroupObject *object); + +static gboolean +poll_in_main_thread (gpointer user_data) +{ + UDisksLinuxVolumeGroupObject *object = user_data; + + if (object->poll_timeout_id) + object->poll_requested = TRUE; + else + poll_now (object); + + g_object_unref (object); + return FALSE; +} + +static gboolean +poll_timeout (gpointer user_data) +{ + UDisksLinuxVolumeGroupObject *object = user_data; + + object->poll_timeout_id = 0; + if (object->poll_requested) + { + object->poll_requested = FALSE; + poll_now (object); + } + + g_object_unref (object); + return FALSE; +} + +static void +poll_now (UDisksLinuxVolumeGroupObject *object) +{ + const gchar *args[] = { "/usr/lib/udisks2/udisks-lvm", "-b", "show", object->name, NULL }; + + object->poll_timeout_id = g_timeout_add (5000, poll_timeout, g_object_ref (object)); + + if (object->poll_pid) + kill (object->poll_pid, SIGINT); + + object->poll_pid = udisks_daemon_util_spawn_for_variant (args, G_VARIANT_TYPE("a{sv}"), + poll_with_variant, g_object_ref (object)); +} + +void +udisks_linux_volume_group_object_poll (UDisksLinuxVolumeGroupObject *object) +{ + g_idle_add (poll_in_main_thread, g_object_ref (object)); +} + +void +udisks_linux_volume_group_object_destroy (UDisksLinuxVolumeGroupObject *object) +{ + GHashTableIter volume_iter; + gpointer key, value; + + g_hash_table_iter_init (&volume_iter, object->logical_volumes); + while (g_hash_table_iter_next (&volume_iter, &key, &value)) + { + UDisksLinuxLogicalVolumeObject *volume = value; + g_dbus_object_manager_server_unexport (udisks_daemon_get_object_manager (object->daemon), + g_dbus_object_get_object_path (G_DBUS_OBJECT (volume))); + } +} + +UDisksLinuxLogicalVolumeObject * +udisks_linux_volume_group_object_find_logical_volume_object (UDisksLinuxVolumeGroupObject *object, + const gchar *name) +{ + return g_hash_table_lookup (object->logical_volumes, name); +} + +/** + * udisks_linux_volume_group_object_get_name: + * @object: A #UDisksLinuxVolumeGroupObject. + * + * Gets the name for @object. + * + * Returns: (transfer none): The name for object. Do not free, the string belongs to @object. + */ +const gchar * +udisks_linux_volume_group_object_get_name (UDisksLinuxVolumeGroupObject *object) +{ + g_return_val_if_fail (UDISKS_IS_LINUX_VOLUME_GROUP_OBJECT (object), NULL); + return object->name; +} diff --git a/src/udiskslinuxvolumegroupobject.h b/src/udiskslinuxvolumegroupobject.h new file mode 100644 index 0000000..61f765c --- /dev/null +++ b/src/udiskslinuxvolumegroupobject.h @@ -0,0 +1,48 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2013 Marius Vollmer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __UDISKS_LINUX_VOLUME_GROUP_OBJECT_H__ +#define __UDISKS_LINUX_VOLUME_GROUP_OBJECT_H__ + +#include "udisksdaemontypes.h" +#include + +G_BEGIN_DECLS + +#define UDISKS_TYPE_LINUX_VOLUME_GROUP_OBJECT (udisks_linux_volume_group_object_get_type ()) +#define UDISKS_LINUX_VOLUME_GROUP_OBJECT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), UDISKS_TYPE_LINUX_VOLUME_GROUP_OBJECT, UDisksLinuxVolumeGroupObject)) +#define UDISKS_IS_LINUX_VOLUME_GROUP_OBJECT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), UDISKS_TYPE_LINUX_VOLUME_GROUP_OBJECT)) + +GType udisks_linux_volume_group_object_get_type (void) G_GNUC_CONST; +UDisksLinuxVolumeGroupObject *udisks_linux_volume_group_object_new (UDisksDaemon *daemon, + const gchar *name); +const gchar *udisks_linux_volume_group_object_get_name (UDisksLinuxVolumeGroupObject *object); +UDisksDaemon *udisks_linux_volume_group_object_get_daemon (UDisksLinuxVolumeGroupObject *object); +void udisks_linux_volume_group_object_update (UDisksLinuxVolumeGroupObject *object); + +void udisks_linux_volume_group_object_poll (UDisksLinuxVolumeGroupObject *object); + +void udisks_linux_volume_group_object_destroy (UDisksLinuxVolumeGroupObject *object); + +UDisksLinuxLogicalVolumeObject *udisks_linux_volume_group_object_find_logical_volume_object (UDisksLinuxVolumeGroupObject *object, const gchar *name); + +G_END_DECLS + +#endif /* __UDISKS_LINUX_VOLUME_GROUP_OBJECT_H__ */ diff --git a/src/udiskslvmhelper.c b/src/udiskslvmhelper.c new file mode 100644 index 0000000..c0c9813 --- /dev/null +++ b/src/udiskslvmhelper.c @@ -0,0 +1,245 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2013 Marius Vollmer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +/* This is a helper program used by UDisks2 to query the state of + LVM2 via the lvm2app library. + + The reasoning for doing this in a separate process goes like this: + Calling lvm_vg_open might block for a long time when the volume + group is locked by someone else. When this happens, only that one + volume group should be affected. No other part of UDisks and no + other volume group updates should wait for it. It doesn't seem to + be possible to set "wait_for_locks" temporarily when using lvm2app, + and we actually do want to wait the short amounts of time that a + volume group is usually locked. Thus, we need to query each volume + group in its own thread or process. The lvm2app library doesn't + seem to be thread-safe, so we use processes. + + The program can list all volume groups or can return all needed + information for a single volume group. Output is a GVariant, by + default as text (mostly for debugging and because it is impolite to + output binary data to a terminal) or serialized. +*/ + +#include +#include +#include +#include + +static void +usage (void) +{ + fprintf (stderr, "Usage: udisks-lvm [-b] list\n"); + fprintf (stderr, " udisks-lvm [-b] show VG\n"); + exit (1); +} + +static GVariant * +list_volume_groups (void) +{ + lvm_t lvm; + struct dm_list *vg_names; + struct lvm_str_list *vg_name; + GVariantBuilder result; + + lvm = lvm_init (NULL); + + g_variant_builder_init (&result, G_VARIANT_TYPE ("as")); + vg_names = lvm_list_vg_names (lvm); + dm_list_iterate_items (vg_name, vg_names) + { + g_variant_builder_add (&result, "s", vg_name->str); + } + + lvm_quit (lvm); + return g_variant_builder_end (&result); +} + +static void +add_string (GVariantBuilder *bob, const gchar *key, const gchar *val) +{ + g_variant_builder_add (bob, "{sv}", key, g_variant_new_string (val)); +} + +static void +add_uint64 (GVariantBuilder *bob, const gchar *key, guint64 val) +{ + g_variant_builder_add (bob, "{sv}", key, g_variant_new_uint64 (val)); +} + +static void +add_lvprop (GVariantBuilder *bob, const gchar *key, lv_t lv) +{ + lvm_property_value_t p = lvm_lv_get_property (lv, key); + if (p.is_valid) + { + if (p.is_string && p.value.string) + add_string (bob, key, p.value.string); + else if (p.is_integer) + add_uint64 (bob, key, p.value.integer); + } +} + +static GVariant * +show_logical_volume (vg_t vg, lv_t lv) +{ + GVariantBuilder result; + g_variant_builder_init (&result, G_VARIANT_TYPE ("a{sv}")); + + add_string (&result, "name", lvm_lv_get_name (lv)); + add_string (&result, "uuid", lvm_lv_get_uuid (lv)); + add_uint64 (&result, "size", lvm_lv_get_size (lv)); + + add_lvprop (&result, "lv_attr", lv); + add_lvprop (&result, "move_pv", lv); + add_lvprop (&result, "pool_lv", lv); + add_lvprop (&result, "origin", lv); + add_lvprop (&result, "data_percent", lv); + add_lvprop (&result, "metadata_percent", lv); + add_lvprop (&result, "copy_percent", lv); + + return g_variant_builder_end (&result); +} + +static GVariant * +show_physical_volume (vg_t vg, pv_t pv) +{ + GVariantBuilder result; + g_variant_builder_init (&result, G_VARIANT_TYPE ("a{sv}")); + + add_string (&result, "device", lvm_pv_get_name (pv)); + add_string (&result, "uuid", lvm_pv_get_uuid (pv)); + add_uint64 (&result, "size", lvm_pv_get_size (pv)); + add_uint64 (&result, "free-size", lvm_pv_get_free (pv)); + + return g_variant_builder_end (&result); +} + +static GVariant * +show_volume_group (const char *name) +{ + lvm_t lvm; + vg_t vg; + GVariantBuilder result; + + lvm = lvm_init (NULL); + vg = lvm_vg_open (lvm, name, "r", 0); + + g_variant_builder_init (&result, G_VARIANT_TYPE ("a{sv}")); + if (vg) + { + struct dm_list *list; + struct lvm_lv_list *lv_entry; + struct lvm_pv_list *pv_entry; + GVariantBuilder lvs; + GVariantBuilder pvs; + + add_string (&result, "name", lvm_vg_get_name (vg)); + add_string (&result, "uuid", lvm_vg_get_uuid (vg)); + add_uint64 (&result, "size", lvm_vg_get_size (vg)); + add_uint64 (&result, "free-size", lvm_vg_get_free_size (vg)); + add_uint64 (&result, "extent-size", lvm_vg_get_extent_size (vg)); + + g_variant_builder_init (&lvs, G_VARIANT_TYPE("aa{sv}")); + list = lvm_vg_list_lvs (vg); + if (list) + { + dm_list_iterate_items (lv_entry, list) + g_variant_builder_add (&lvs, "@a{sv}", show_logical_volume (vg, lv_entry->lv)); + } + g_variant_builder_add (&result, "{sv}", "lvs", g_variant_builder_end (&lvs)); + + g_variant_builder_init (&pvs, G_VARIANT_TYPE("aa{sv}")); + list = lvm_vg_list_pvs (vg); + if (list) + { + dm_list_iterate_items (pv_entry, list) + g_variant_builder_add (&pvs, "@a{sv}", show_physical_volume (vg, pv_entry->pv)); + } + g_variant_builder_add (&result, "{sv}", "pvs", g_variant_builder_end (&pvs)); + + lvm_vg_close (vg); + } + else + { + lvm_quit (lvm); + exit (2); + } + + lvm_quit (lvm); + return g_variant_builder_end (&result); +} + +static void +write_all (int fd, const char *mem, size_t size) +{ + while (size > 0) + { + int r = write (fd, mem, size); + if (r < 0) + { + fprintf (stderr, "Write error: %m\n"); + exit (1); + } + size -= r; + mem += r; + } +} + +int +main (int argc, char **argv) +{ + gboolean opt_binary = FALSE; + GVariant *result; + + if (argv[1] && strcmp (argv[1], "-b") == 0) + { + opt_binary = TRUE; + argv++; + } + + if (argv[1] && strcmp (argv[1], "list") == 0) + result = list_volume_groups (); + else if (argv[1] && strcmp (argv[1], "show") == 0) + { + if (argv[2]) + result = show_volume_group (argv[2]); + else + usage (); + } + else + usage (); + + if (opt_binary) + { + GVariant *normal = g_variant_get_normal_form (result); + gsize size = g_variant_get_size (normal); + gconstpointer data = g_variant_get_data (normal); + write_all (1, data, size); + } + else + { + gchar *text = g_variant_print (result, FALSE); + printf ("%s\n", text); + g_free (text); + } + + exit (0); +} diff --git a/udisks/udisksclient.c b/udisks/udisksclient.c index 18f3590..6328b9e 100644 --- a/udisks/udisksclient.c +++ b/udisks/udisksclient.c @@ -1038,6 +1038,152 @@ udisks_client_get_members_for_mdraid (UDisksClient *client, /* ---------------------------------------------------------------------------------------------------- */ +/** + * udisks_client_get_block_for_logical_volume: + * @client: A #UDisksClient. + * @volume: A #UDisksLogicalVolume. + * + * Gets the block device for @volume. + * + * Returns: (transfer full): A #UDisksBlock or %NULL if the volume has no block device. + * + * Since: x.x + */ +UDisksBlock * +udisks_client_get_block_for_logical_volume (UDisksClient *client, + UDisksLogicalVolume *volume) +{ + UDisksBlock *ret = NULL; + GList *l, *object_proxies = NULL; + GDBusObject *volume_object; + const gchar *volume_objpath; + + g_return_val_if_fail (UDISKS_IS_CLIENT (client), NULL); + g_return_val_if_fail (UDISKS_IS_LOGICAL_VOLUME (volume), NULL); + + volume_object = g_dbus_interface_get_object (G_DBUS_INTERFACE (volume)); + if (volume_object == NULL) + goto out; + + volume_objpath = g_dbus_object_get_object_path (volume_object); + + object_proxies = g_dbus_object_manager_get_objects (client->object_manager); + for (l = object_proxies; l != NULL; l = l->next) + { + UDisksObject *object = UDISKS_OBJECT (l->data); + UDisksBlock *block; + + block = udisks_object_get_block (object); + if (block == NULL) + continue; + + /* ignore partitions */ + if (udisks_object_peek_partition (object) != NULL) + continue; + + if (g_strcmp0 (udisks_block_get_logical_volume (block), volume_objpath) == 0) + { + ret = block; + goto out; + } + g_object_unref (block); + } + + out: + g_list_foreach (object_proxies, (GFunc) g_object_unref, NULL); + g_list_free (object_proxies); + return ret; +} + +/** + * udisks_client_get_volume_group_for_logical_volume: + * @client: A #UDisksClient. + * @volume: A #UDisksLogicalVolume. + * + * Gets the #UDisksVolumeGroup that @volume is a member of. + * + * Returns: (transfer full): A #UDisksVolumeGroup or %NULL if + * something went wrong. Free the returned object with + * g_object_unref(). + * + * Since: x.x + */ +UDisksVolumeGroup * +udisks_client_get_volume_group_for_logical_volume (UDisksClient *client, + UDisksLogicalVolume *volume) +{ + UDisksVolumeGroup *ret = NULL; + GDBusObject *object; + + g_return_val_if_fail (UDISKS_IS_CLIENT (client), NULL); + g_return_val_if_fail (UDISKS_IS_LOGICAL_VOLUME (volume), NULL); + + object = g_dbus_object_manager_get_object (client->object_manager, udisks_logical_volume_get_volume_group (volume)); + if (object != NULL) + ret = udisks_object_get_volume_group (UDISKS_OBJECT (object)); + return ret; +} + +/** + * udisks_client_get_logical_volumes_for_volume_group: + * @client: A #UDisksClient. + * @group: A #UDisksVolumeGroup. + * + * Gets the logical volumes that are part of @group. + * + * Returns: (transfer full) (element-type UDisksLogicalVolume): A list + * of #UDisksLogicalVolume instances. The returned list should be + * freed with g_list_free() after each element has been freed with + * g_object_unref(). + * + * Since: x.x + */ +GList * +udisks_client_get_logical_volumes_for_volume_group (UDisksClient *client, + UDisksVolumeGroup *group) +{ + GList *ret = NULL; + GList *l, *object_proxies = NULL; + GDBusObject *group_object; + const gchar *group_objpath; + + g_return_val_if_fail (UDISKS_IS_CLIENT (client), NULL); + g_return_val_if_fail (UDISKS_IS_VOLUME_GROUP (group), NULL); + + group_object = g_dbus_interface_get_object (G_DBUS_INTERFACE (group)); + if (group_object == NULL) + goto out; + + group_objpath = g_dbus_object_get_object_path (group_object); + + object_proxies = g_dbus_object_manager_get_objects (client->object_manager); + for (l = object_proxies; l != NULL; l = l->next) + { + UDisksObject *object = UDISKS_OBJECT (l->data); + UDisksLogicalVolume *volume; + + volume = udisks_object_get_logical_volume (object); + if (volume == NULL) + continue; + + if (g_strcmp0 (udisks_logical_volume_get_volume_group (volume), group_objpath) == 0) + { + ret = g_list_prepend (ret, volume); /* adopts reference to block */ + } + else + { + g_object_unref (volume); + } + } + + out: + g_list_foreach (object_proxies, (GFunc) g_object_unref, NULL); + g_list_free (object_proxies); + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + static void add_item (gchar **items_str, const gchar *item) diff --git a/udisks/udisksclient.h b/udisks/udisksclient.h index 93dfddf..1d0a55f 100644 --- a/udisks/udisksclient.h +++ b/udisks/udisksclient.h @@ -78,6 +78,14 @@ GList *udisks_client_get_all_blocks_for_mdraid (UDisksClient *clie GList *udisks_client_get_members_for_mdraid (UDisksClient *client, UDisksMDRaid *raid); +UDisksBlock *udisks_client_get_block_for_logical_volume (UDisksClient *client, + UDisksLogicalVolume *volume); + +UDisksVolumeGroup *udisks_client_get_volume_group_for_logical_volume (UDisksClient *client, + UDisksLogicalVolume *volume); +GList *udisks_client_get_logical_volumes_for_volume_group (UDisksClient *client, + UDisksVolumeGroup *group); + UDisksPartitionTable *udisks_client_get_partition_table (UDisksClient *client, UDisksPartition *partition); -- 1.8.3.1