1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2  *
3  * Copyright (C) 2007-2010 David Zeuthen <zeuthen@gmail.com>
4  * Copyright (C) 2013 Marius Vollmer <marius.vollmer@gmail.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21 
22 #include "config.h"
23 #include <glib/gi18n-lib.h>
24 
25 #include <sys/types.h>
26 #include <pwd.h>
27 #include <grp.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <mntent.h>
32 
33 #include <glib/gstdio.h>
34 
35 #include <blockdev/fs.h>
36 #include <blockdev/lvm.h>
37 
38 #include <src/udiskslogging.h>
39 #include <src/udiskslinuxprovider.h>
40 #include <src/udisksdaemon.h>
41 #include <src/udisksstate.h>
42 #include <src/udisksdaemonutil.h>
43 #include <src/udiskslinuxdevice.h>
44 #include <src/udiskslinuxblock.h>
45 #include <src/udiskslinuxblockobject.h>
46 
47 #include "udiskslinuxvolumegroup.h"
48 #include "udiskslinuxvolumegroupobject.h"
49 #include "udiskslinuxlogicalvolume.h"
50 #include "udiskslinuxlogicalvolumeobject.h"
51 
52 #include "udiskslvm2daemonutil.h"
53 #include "jobhelpers.h"
54 
55 /**
56  * SECTION:udiskslinuxvolume_group
57  * @title: UDisksLinuxVolumeGroup
58  * @short_description: Linux implementation of #UDisksVolumeGroup
59  *
60  * This type provides an implementation of the #UDisksVolumeGroup interface
61  * on Linux.
62  */
63 
64 typedef struct _UDisksLinuxVolumeGroupClass   UDisksLinuxVolumeGroupClass;
65 
66 /**
67  * UDisksLinuxVolumeGroup:
68  *
69  * The #UDisksLinuxVolumeGroup structure contains only private data and should
70  * only be accessed using the provided API.
71  */
72 struct _UDisksLinuxVolumeGroup
73 {
74   UDisksVolumeGroupSkeleton parent_instance;
75 };
76 
77 struct _UDisksLinuxVolumeGroupClass
78 {
79   UDisksVolumeGroupSkeletonClass parent_class;
80 };
81 
82 static void volume_group_iface_init (UDisksVolumeGroupIface *iface);
83 
84 G_DEFINE_TYPE_WITH_CODE (UDisksLinuxVolumeGroup, udisks_linux_volume_group, UDISKS_TYPE_VOLUME_GROUP_SKELETON,
85                          G_IMPLEMENT_INTERFACE (UDISKS_TYPE_VOLUME_GROUP, volume_group_iface_init));
86 
87 /* ---------------------------------------------------------------------------------------------------- */
88 
89 static void
udisks_linux_volume_group_finalize(GObject * object)90 udisks_linux_volume_group_finalize (GObject *object)
91 {
92   if (G_OBJECT_CLASS (udisks_linux_volume_group_parent_class)->finalize != NULL)
93     G_OBJECT_CLASS (udisks_linux_volume_group_parent_class)->finalize (object);
94 }
95 
96 static void
udisks_linux_volume_group_init(UDisksLinuxVolumeGroup * volume_group)97 udisks_linux_volume_group_init (UDisksLinuxVolumeGroup *volume_group)
98 {
99   g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (volume_group),
100                                        G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD);
101 }
102 
103 static void
udisks_linux_volume_group_class_init(UDisksLinuxVolumeGroupClass * klass)104 udisks_linux_volume_group_class_init (UDisksLinuxVolumeGroupClass *klass)
105 {
106   GObjectClass *gobject_class;
107 
108   gobject_class = G_OBJECT_CLASS (klass);
109   gobject_class->finalize = udisks_linux_volume_group_finalize;
110 }
111 
112 /**
113  * udisks_linux_volume_group_new:
114  *
115  * Creates a new #UDisksLinuxVolumeGroup instance.
116  *
117  * Returns: A new #UDisksLinuxVolumeGroup. Free with g_object_unref().
118  */
119 UDisksVolumeGroup *
udisks_linux_volume_group_new(void)120 udisks_linux_volume_group_new (void)
121 {
122   return UDISKS_VOLUME_GROUP (g_object_new (UDISKS_TYPE_LINUX_VOLUME_GROUP,
123                                             NULL));
124 }
125 
126 /**
127  * udisks_linux_volume_group_update:
128  * @volume_group: A #UDisksLinuxVolumeGroup.
129  * @object: The enclosing #UDisksLinuxVolumeGroupObject instance.
130  *
131  * Updates the interface.
132  */
133 void
udisks_linux_volume_group_update(UDisksLinuxVolumeGroup * volume_group,BDLVMVGdata * vg_info,gboolean * needs_polling_ret)134 udisks_linux_volume_group_update (UDisksLinuxVolumeGroup *volume_group,
135                                   BDLVMVGdata            *vg_info,
136                                   gboolean               *needs_polling_ret)
137 {
138   UDisksVolumeGroup *iface = UDISKS_VOLUME_GROUP (volume_group);
139 
140   udisks_volume_group_set_name (iface, vg_info->name);
141   udisks_volume_group_set_uuid (iface, vg_info->uuid);
142   udisks_volume_group_set_size (iface, vg_info->size);
143   udisks_volume_group_set_free_size (iface, vg_info->free);
144   udisks_volume_group_set_extent_size (iface, vg_info->extent_size);
145 
146   g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (iface));
147 }
148 
149 /* ---------------------------------------------------------------------------------------------------- */
150 
151 static gboolean
handle_poll(UDisksVolumeGroup * _group,GDBusMethodInvocation * invocation)152 handle_poll (UDisksVolumeGroup *_group,
153              GDBusMethodInvocation *invocation)
154 {
155   UDisksLinuxVolumeGroup *group = UDISKS_LINUX_VOLUME_GROUP (_group);
156   UDisksLinuxVolumeGroupObject *object = NULL;
157   GError *error = NULL;
158 
159   object = udisks_daemon_util_dup_object (group, &error);
160   if (object == NULL)
161     {
162       g_dbus_method_invocation_take_error (invocation, error);
163       goto out;
164     }
165 
166   udisks_linux_volume_group_object_poll (object);
167 
168   udisks_volume_group_complete_poll (_group, invocation);
169 
170  out:
171   g_clear_object (&object);
172   return TRUE;
173 }
174 
175 /* ---------------------------------------------------------------------------------------------------- */
176 
177 /*  transfer-full  */
178 GList *
udisks_linux_volume_group_get_logical_volumes(UDisksVolumeGroup * group,UDisksDaemon * daemon)179 udisks_linux_volume_group_get_logical_volumes (UDisksVolumeGroup *group,
180                                                UDisksDaemon      *daemon)
181 {
182   GList *ret = NULL;
183   GList *l, *objects = NULL;
184   GDBusObject *object;
185   UDisksLogicalVolume *volume;
186 
187   object = g_dbus_interface_get_object (G_DBUS_INTERFACE (group));
188   if (object == NULL)
189     goto out;
190 
191   objects = udisks_daemon_get_objects (daemon);
192   for (l = objects; l != NULL; l = l->next)
193     {
194       volume = udisks_object_peek_logical_volume (UDISKS_OBJECT (l->data));
195       if (volume &&
196           g_strcmp0 (udisks_logical_volume_get_volume_group (volume),
197                      g_dbus_object_get_object_path (object)) == 0)
198         ret = g_list_append (ret, g_object_ref (volume));
199     }
200 
201  out:
202   g_list_free_full (objects, g_object_unref);
203   return ret;
204 }
205 
206 /* ---------------------------------------------------------------------------------------------------- */
207 
208 static gboolean
teardown_volume_group(UDisksVolumeGroup * group,UDisksDaemon * daemon,GDBusMethodInvocation * invocation,GVariant * options,GError ** error)209 teardown_volume_group (UDisksVolumeGroup     *group,
210                        UDisksDaemon          *daemon,
211                        GDBusMethodInvocation *invocation,
212                        GVariant              *options,
213                        GError               **error)
214 {
215   GList *volumes;
216   GList *l;
217   UDisksLogicalVolume *volume;
218 
219   volumes = udisks_linux_volume_group_get_logical_volumes (group, daemon);
220   for (l = volumes; l; l = l->next)
221     {
222       volume = UDISKS_LOGICAL_VOLUME (l->data);
223       if (g_strcmp0 (udisks_logical_volume_get_type_ (volume), "pool") != 0)
224         {
225           if (!udisks_linux_logical_volume_teardown_block (volume,
226                                                            daemon,
227                                                            invocation,
228                                                            options,
229                                                            error))
230             {
231               g_list_free_full (volumes, g_object_unref);
232               return FALSE;
233             }
234         }
235     }
236   g_list_free_full (volumes, g_object_unref);
237 
238   return TRUE;
239 }
240 
241 static gboolean
handle_delete(UDisksVolumeGroup * _group,GDBusMethodInvocation * invocation,gboolean arg_wipe,GVariant * arg_options)242 handle_delete (UDisksVolumeGroup     *_group,
243                GDBusMethodInvocation *invocation,
244                gboolean               arg_wipe,
245                GVariant              *arg_options)
246 {
247   GError *error = NULL;
248   UDisksLinuxVolumeGroup *group = UDISKS_LINUX_VOLUME_GROUP (_group);
249   UDisksLinuxVolumeGroupObject *object = NULL;
250   UDisksLinuxModuleLVM2 *module;
251   UDisksDaemon *daemon;
252   uid_t caller_uid;
253   gboolean teardown_flag = FALSE;
254   GList *objects_to_wipe = NULL;
255   GList *l;
256   VGJobData data;
257 
258   g_variant_lookup (arg_options, "tear-down", "b", &teardown_flag);
259 
260   object = udisks_daemon_util_dup_object (group, &error);
261   if (object == NULL)
262     {
263       g_dbus_method_invocation_take_error (invocation, error);
264       goto out;
265     }
266 
267   module = udisks_linux_volume_group_object_get_module (object);
268   daemon = udisks_module_get_daemon (UDISKS_MODULE (module));
269 
270   /* Find physical volumes to wipe. */
271   if (arg_wipe)
272     {
273       GList *objects = udisks_daemon_get_objects (daemon);
274       for (l = objects; l; l = l->next)
275         {
276           UDisksPhysicalVolume *physical_volume;
277           physical_volume = udisks_object_peek_physical_volume (UDISKS_OBJECT (l->data));
278           if (physical_volume
279               && g_strcmp0 (udisks_physical_volume_get_volume_group (physical_volume),
280                             g_dbus_object_get_object_path (G_DBUS_OBJECT (object))) == 0)
281             objects_to_wipe = g_list_append (objects_to_wipe, g_object_ref (l->data));
282         }
283       g_list_free_full (objects, g_object_unref);
284     }
285 
286   if (!udisks_daemon_util_get_caller_uid_sync (daemon,
287                                                invocation,
288                                                NULL /* GCancellable */,
289                                                &caller_uid,
290                                                &error))
291     {
292       g_dbus_method_invocation_return_gerror (invocation, error);
293       g_clear_error (&error);
294       goto out;
295     }
296 
297   /* Policy check. */
298   UDISKS_DAEMON_CHECK_AUTHORIZATION (daemon,
299                                      UDISKS_OBJECT (object),
300                                      LVM2_POLICY_ACTION_ID,
301                                      arg_options,
302                                      N_("Authentication is required to delete a volume group"),
303                                      invocation);
304 
305   if (teardown_flag &&
306       !teardown_volume_group (_group,
307                               daemon,
308                               invocation,
309                               arg_options,
310                               &error))
311     {
312       g_dbus_method_invocation_take_error (invocation, error);
313       goto out;
314     }
315 
316   data.vg_name = udisks_linux_volume_group_object_get_name (object);
317 
318   if (!udisks_daemon_launch_threaded_job_sync (daemon,
319                                                UDISKS_OBJECT (object),
320                                                "lvm-vg-delete",
321                                                caller_uid,
322                                                vgremove_job_func,
323                                                &data,
324                                                NULL, /* user_data_free_func */
325                                                NULL, /* GCancellable */
326                                                &error))
327     {
328       g_dbus_method_invocation_return_error (invocation,
329                                              UDISKS_ERROR,
330                                              UDISKS_ERROR_FAILED,
331                                              "Error deleting volume group: %s",
332                                              error->message);
333       g_clear_error (&error);
334       goto out;
335     }
336 
337   for (l = objects_to_wipe; l; l = l->next)
338     {
339       UDisksBlock *block = udisks_object_peek_block (l->data);
340       if (block)
341         udisks_daemon_util_lvm2_wipe_block (daemon, block, NULL);
342     }
343 
344   udisks_volume_group_complete_delete (_group, invocation);
345 
346  out:
347   g_list_free_full (objects_to_wipe, g_object_unref);
348   g_clear_object (&object);
349   return TRUE;
350 }
351 
352 /* ---------------------------------------------------------------------------------------------------- */
353 
354 typedef struct
355 {
356   UDisksLinuxModuleLVM2 *module;
357   const gchar *name;
358 } WaitForVolumeGroupObjectData;
359 
360 static UDisksObject *
wait_for_volume_group_object(UDisksDaemon * daemon,gpointer user_data)361 wait_for_volume_group_object (UDisksDaemon *daemon,
362                               gpointer      user_data)
363 {
364   WaitForVolumeGroupObjectData *data = user_data;
365   UDisksLinuxVolumeGroupObject *object;
366 
367   object = udisks_linux_module_lvm2_find_volume_group_object (data->module, data->name);
368 
369   if (object == NULL)
370     return NULL;
371 
372   return g_object_ref (UDISKS_OBJECT (object));
373 }
374 
375 static gboolean
handle_rename(UDisksVolumeGroup * _group,GDBusMethodInvocation * invocation,const gchar * new_name,GVariant * options)376 handle_rename (UDisksVolumeGroup     *_group,
377                GDBusMethodInvocation *invocation,
378                const gchar           *new_name,
379                GVariant              *options)
380 {
381   GError *error = NULL;
382   UDisksLinuxVolumeGroup *group = UDISKS_LINUX_VOLUME_GROUP (_group);
383   UDisksLinuxVolumeGroupObject *object = NULL;
384   UDisksLinuxModuleLVM2 *module;
385   UDisksDaemon *daemon;
386   uid_t caller_uid;
387   UDisksObject *group_object = NULL;
388   VGJobData data;
389   WaitForVolumeGroupObjectData wait_data;
390 
391   object = udisks_daemon_util_dup_object (group, &error);
392   if (object == NULL)
393     {
394       g_dbus_method_invocation_take_error (invocation, error);
395       goto out;
396     }
397 
398   module = udisks_linux_volume_group_object_get_module (object);
399   daemon = udisks_module_get_daemon (UDISKS_MODULE (module));
400 
401   if (!udisks_daemon_util_get_caller_uid_sync (daemon,
402                                                invocation,
403                                                NULL /* GCancellable */,
404                                                &caller_uid,
405                                                &error))
406     {
407       g_dbus_method_invocation_return_gerror (invocation, error);
408       g_clear_error (&error);
409       goto out;
410     }
411 
412   /* Policy check. */
413   UDISKS_DAEMON_CHECK_AUTHORIZATION (daemon,
414                                      UDISKS_OBJECT (object),
415                                      LVM2_POLICY_ACTION_ID,
416                                      options,
417                                      N_("Authentication is required to rename a volume group"),
418                                      invocation);
419 
420   data.vg_name = udisks_linux_volume_group_object_get_name (object);
421   data.new_vg_name = new_name;
422 
423   if (!udisks_daemon_launch_threaded_job_sync (daemon,
424                                                UDISKS_OBJECT (object),
425                                                "lvm-vg-rename",
426                                                caller_uid,
427                                                vgrename_job_func,
428                                                &data,
429                                                NULL, /* user_data_free_func */
430                                                NULL, /* GCancellable */
431                                                &error))
432     {
433       g_dbus_method_invocation_return_error (invocation,
434                                              UDISKS_ERROR,
435                                              UDISKS_ERROR_FAILED,
436                                              "Error renaming volume group: %s",
437                                              error->message);
438       g_clear_error (&error);
439       goto out;
440     }
441 
442   wait_data.module = module;
443   wait_data.name = new_name;
444   group_object = udisks_daemon_wait_for_object_sync (daemon,
445                                                      wait_for_volume_group_object,
446                                                      &wait_data,
447                                                      NULL,
448                                                      UDISKS_DEFAULT_WAIT_TIMEOUT,
449                                                      &error);
450   if (group_object == NULL)
451     {
452       g_prefix_error (&error,
453                       "Error waiting for volume group object for '%s': ",
454                       new_name);
455       g_dbus_method_invocation_take_error (invocation, error);
456       goto out;
457     }
458 
459   udisks_volume_group_complete_rename (_group,
460                                        invocation,
461                                        g_dbus_object_get_object_path (G_DBUS_OBJECT (group_object)));
462   g_clear_object (&group_object);
463 
464  out:
465   g_clear_object (&object);
466   return TRUE;
467 }
468 
469 /* ---------------------------------------------------------------------------------------------------- */
470 
471 static gboolean
handle_add_device(UDisksVolumeGroup * _group,GDBusMethodInvocation * invocation,const gchar * new_member_device_objpath,GVariant * options)472 handle_add_device (UDisksVolumeGroup     *_group,
473                    GDBusMethodInvocation *invocation,
474                    const gchar           *new_member_device_objpath,
475                    GVariant              *options)
476 {
477   UDisksLinuxVolumeGroup *group = UDISKS_LINUX_VOLUME_GROUP (_group);
478   UDisksLinuxModuleLVM2 *module;
479   UDisksDaemon *daemon;
480   UDisksLinuxVolumeGroupObject *object;
481   uid_t caller_uid;
482   GError *error = NULL;
483   UDisksObject *new_member_device_object = NULL;
484   UDisksBlock *new_member_device = NULL;
485   UDisksPhysicalVolume *physical_volume = NULL;
486   VGJobData data;
487 
488   object = udisks_daemon_util_dup_object (group, &error);
489   if (object == NULL)
490     {
491       g_dbus_method_invocation_take_error (invocation, error);
492       goto out;
493     }
494 
495   module = udisks_linux_volume_group_object_get_module (object);
496   daemon = udisks_module_get_daemon (UDISKS_MODULE (module));
497 
498   error = NULL;
499   if (!udisks_daemon_util_get_caller_uid_sync (daemon,
500                                                invocation,
501                                                NULL /* GCancellable */,
502                                                &caller_uid,
503                                                &error))
504     {
505       g_dbus_method_invocation_return_gerror (invocation, error);
506       g_clear_error (&error);
507       goto out;
508     }
509 
510   new_member_device_object = udisks_daemon_find_object (daemon, new_member_device_objpath);
511   if (new_member_device_object == NULL)
512     {
513       g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
514                                              "No device for given object path");
515       goto out;
516     }
517 
518   new_member_device = udisks_object_get_block (new_member_device_object);
519   if (new_member_device == NULL)
520     {
521       g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
522                                              "No block interface on given object");
523       goto out;
524     }
525 
526   /* Policy check. */
527   UDISKS_DAEMON_CHECK_AUTHORIZATION (daemon,
528                                      UDISKS_OBJECT (object),
529                                      LVM2_POLICY_ACTION_ID,
530                                      options,
531                                      N_("Authentication is required to add a device to a volume group"),
532                                      invocation);
533 
534   if (!udisks_daemon_util_lvm2_block_is_unused (new_member_device, &error))
535     {
536       g_dbus_method_invocation_take_error (invocation, error);
537       goto out;
538     }
539 
540   if (!udisks_daemon_util_lvm2_wipe_block (daemon, new_member_device, &error))
541     {
542       g_dbus_method_invocation_take_error (invocation, error);
543       goto out;
544     }
545 
546   physical_volume = udisks_object_peek_physical_volume (new_member_device_object);
547   if (!physical_volume)
548     {
549       PVJobData pv_data;
550       pv_data.path = udisks_block_get_device (new_member_device);
551       if (!udisks_daemon_launch_threaded_job_sync (daemon,
552                                                    UDISKS_OBJECT (object),
553                                                    "lvm-pv-create",
554                                                    caller_uid,
555                                                    pvcreate_job_func,
556                                                    &pv_data,
557                                                    NULL, /* user_data_free_func */
558                                                    NULL, /* GCancellable */
559                                                    &error))
560         {
561           g_dbus_method_invocation_return_error (invocation,
562                                                  UDISKS_ERROR,
563                                                  UDISKS_ERROR_FAILED,
564                                                  "Error creating LVM metadata on %s: %s",
565                                                  pv_data.path,
566                                                  error->message);
567           g_clear_error (&error);
568           goto out;
569         }
570     }
571 
572 
573   data.vg_name = udisks_linux_volume_group_object_get_name (object);
574   data.pv_path = udisks_block_get_device (new_member_device);
575 
576   if (!udisks_daemon_launch_threaded_job_sync (daemon,
577                                                UDISKS_OBJECT (object),
578                                                "lvm-vg-add-device",
579                                                caller_uid,
580                                                vgextend_job_func,
581                                                &data,
582                                                NULL, /* user_data_free_func */
583                                                NULL, /* GCancellable */
584                                                &error))
585     {
586       g_dbus_method_invocation_return_error (invocation,
587                                              UDISKS_ERROR,
588                                              UDISKS_ERROR_FAILED,
589                                              "Error adding %s to volume group: %s",
590                                              data.pv_path,
591                                              error->message);
592       g_clear_error (&error);
593       goto out;
594     }
595 
596   udisks_volume_group_complete_add_device (_group, invocation);
597 
598  out:
599   g_clear_object (&new_member_device_object);
600   g_clear_object (&new_member_device);
601   g_clear_object (&object);
602   return TRUE; /* returning TRUE means that we handled the method invocation */
603 }
604 
605 /* ---------------------------------------------------------------------------------------------------- */
606 static gboolean
handle_remove_common(UDisksVolumeGroup * _group,GDBusMethodInvocation * invocation,const gchar * member_device_objpath,GVariant * options,gboolean is_remove,gboolean arg_wipe)607 handle_remove_common (UDisksVolumeGroup     *_group,
608                       GDBusMethodInvocation *invocation,
609                       const gchar           *member_device_objpath,
610                       GVariant              *options,
611                       gboolean               is_remove,
612                       gboolean               arg_wipe)
613 {
614   UDisksLinuxVolumeGroup *group = UDISKS_LINUX_VOLUME_GROUP (_group);
615   UDisksLinuxModuleLVM2 *module;
616   UDisksDaemon *daemon;
617   UDisksLinuxVolumeGroupObject *object;
618   uid_t caller_uid;
619   GError *error = NULL;
620   UDisksObject *member_device_object = NULL;
621   UDisksBlock *member_device = NULL;
622   VGJobData data;
623   const gchar *authentication_error_msg = NULL;
624   const gchar *job_operation = NULL;
625   UDisksThreadedJobFunc job_func = NULL;
626   gboolean do_wipe = FALSE;
627 
628   if (is_remove)
629     {
630       authentication_error_msg = N_("Authentication is required to remove a device from a volume group");
631       job_operation = "lvm-vg-rem-device";
632       job_func = vgreduce_job_func;
633 
634       if (arg_wipe)
635         do_wipe = TRUE;
636     }
637   else
638     {
639       authentication_error_msg = N_("Authentication is required to empty a device in a volume group");
640       job_operation = "lvm-vg-empty-device";
641       job_func = pvmove_job_func;
642     }
643 
644   object = udisks_daemon_util_dup_object (group, &error);
645   if (object == NULL)
646     {
647       g_dbus_method_invocation_take_error (invocation, error);
648       goto out;
649     }
650 
651   module = udisks_linux_volume_group_object_get_module (object);
652   daemon = udisks_module_get_daemon (UDISKS_MODULE (module));
653 
654   error = NULL;
655   if (!udisks_daemon_util_get_caller_uid_sync (daemon,
656                                                invocation,
657                                                NULL /* GCancellable */,
658                                                &caller_uid,
659                                                &error))
660     {
661       g_dbus_method_invocation_return_gerror (invocation, error);
662       g_clear_error (&error);
663       goto out;
664     }
665 
666   member_device_object = udisks_daemon_find_object (daemon, member_device_objpath);
667   if (member_device_object == NULL)
668     {
669       g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
670                                              "No device for given object path");
671       goto out;
672     }
673 
674   member_device = udisks_object_get_block (member_device_object);
675   if (member_device == NULL)
676     {
677       g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
678                                              "No block interface on given object");
679       goto out;
680     }
681 
682   /* Policy check. */
683   UDISKS_DAEMON_CHECK_AUTHORIZATION (daemon,
684                                      UDISKS_OBJECT (object),
685                                      LVM2_POLICY_ACTION_ID,
686                                      options,
687                                      authentication_error_msg,
688                                      invocation);
689 
690   if (is_remove)
691     data.vg_name = udisks_linux_volume_group_object_get_name (object);
692 
693   data.pv_path = udisks_block_get_device (member_device);
694 
695   if (!udisks_daemon_launch_threaded_job_sync (daemon,
696                                                UDISKS_OBJECT (object),
697                                                job_operation,
698                                                caller_uid,
699                                                job_func,
700                                                &data,
701                                                NULL, /* user_data_free_func */
702                                                NULL, /* GCancellable */
703                                                &error))
704     {
705       g_dbus_method_invocation_return_error (invocation,
706                                              UDISKS_ERROR,
707                                              UDISKS_ERROR_FAILED,
708                                              (is_remove) ? "Error remove %s from volume group: %s" : "Error emptying %s: %s",
709                                              data.pv_path,
710                                              error->message);
711       g_clear_error (&error);
712       goto out;
713     }
714 
715   if (do_wipe)
716     {
717       if (!udisks_daemon_launch_threaded_job_sync (daemon,
718                                                    UDISKS_OBJECT (object),
719                                                    "pv-format-erase",
720                                                    caller_uid,
721                                                    pvremove_job_func,
722                                                    &data,
723                                                    NULL, /* user_data_free_func */
724                                                    NULL, /* GCancellable */
725                                                    &error))
726         {
727           g_dbus_method_invocation_return_error (invocation,
728                                                  UDISKS_ERROR,
729                                                  UDISKS_ERROR_FAILED,
730                                                  "Error wiping %s after removal from volume group %s: %s",
731                                                  data.pv_path,
732                                                  udisks_linux_volume_group_object_get_name (object),
733                                                  error->message);
734           g_clear_error (&error);
735           goto out;
736         }
737     }
738 
739   udisks_volume_group_complete_remove_device (_group, invocation);
740 
741  out:
742   g_clear_object (&member_device_object);
743   g_clear_object (&member_device);
744   g_clear_object (&object);
745   return TRUE; /* returning TRUE means that we handled the method invocation */
746 }
747 
748 
749 static gboolean
handle_remove_device(UDisksVolumeGroup * _group,GDBusMethodInvocation * invocation,const gchar * member_device_objpath,gboolean arg_wipe,GVariant * options)750 handle_remove_device (UDisksVolumeGroup     *_group,
751                       GDBusMethodInvocation *invocation,
752                       const gchar           *member_device_objpath,
753                       gboolean               arg_wipe,
754                       GVariant              *options)
755 {
756   return handle_remove_common (_group, invocation, member_device_objpath,
757                                options, TRUE, arg_wipe);
758 }
759 
760 /* ---------------------------------------------------------------------------------------------------- */
761 
762 static gboolean
handle_empty_device(UDisksVolumeGroup * _group,GDBusMethodInvocation * invocation,const gchar * member_device_objpath,GVariant * options)763 handle_empty_device (UDisksVolumeGroup     *_group,
764                      GDBusMethodInvocation *invocation,
765                      const gchar           *member_device_objpath,
766                      GVariant              *options)
767 {
768  return handle_remove_common (_group, invocation, member_device_objpath,
769                               options, FALSE, FALSE);
770 }
771 
772 /* ---------------------------------------------------------------------------------------------------- */
773 
774 struct WaitData {
775   UDisksLinuxVolumeGroupObject *group_object;
776   const gchar *name;
777 };
778 
779 static UDisksObject *
wait_for_logical_volume_object(UDisksDaemon * daemon,gpointer user_data)780 wait_for_logical_volume_object (UDisksDaemon *daemon,
781                                 gpointer      user_data)
782 {
783   struct WaitData *data = user_data;
784   UDisksLinuxLogicalVolumeObject *object;
785 
786   object = udisks_linux_volume_group_object_find_logical_volume_object (data->group_object, data->name);
787   if (object == NULL)
788     return NULL;
789 
790   return g_object_ref (UDISKS_OBJECT (object));
791 }
792 
793 static const gchar *
wait_for_logical_volume_path(UDisksLinuxVolumeGroupObject * group_object,const gchar * name,GError ** error)794 wait_for_logical_volume_path (UDisksLinuxVolumeGroupObject  *group_object,
795                               const gchar                   *name,
796                               GError                       **error)
797 {
798   struct WaitData data;
799   UDisksLinuxModuleLVM2 *module;
800   UDisksDaemon *daemon;
801   UDisksObject *volume_object;
802   const gchar *object_path;
803 
804   data.group_object = group_object;
805   data.name = name;
806   module = udisks_linux_volume_group_object_get_module (group_object);
807   daemon = udisks_module_get_daemon (UDISKS_MODULE (module));
808   volume_object = udisks_daemon_wait_for_object_sync (daemon,
809                                                       wait_for_logical_volume_object,
810                                                       &data,
811                                                       NULL,
812                                                       UDISKS_DEFAULT_WAIT_TIMEOUT,
813                                                       error);
814   if (volume_object == NULL)
815     return NULL;
816 
817   object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (volume_object));
818   g_object_unref (volume_object);
819 
820   return object_path;
821 }
822 
823 /* ---------------------------------------------------------------------------------------------------- */
824 
825 enum VolumeType { VOL_PLAIN, VOL_THIN_POOL, VOL_THIN_VOLUME, VOL_VDO_VOLUME };
826 typedef void (*VolumeCompletionFunc) (UDisksVolumeGroup     *object,
827                                       GDBusMethodInvocation *invocation,
828                                       const gchar           *result);
829 
830 static gboolean
handle_create_volume(UDisksVolumeGroup * _group,GDBusMethodInvocation * invocation,const gchar * arg_name,guint64 arg_size,GVariant * options,enum VolumeType vol_creation_type,const gchar * arg_pool,guint64 arg_virtual_size,guint64 arg_index_memory,gboolean arg_compression,gboolean arg_deduplication,const gchar * arg_write_policy)831 handle_create_volume (UDisksVolumeGroup              *_group,
832                       GDBusMethodInvocation          *invocation,
833                       const gchar                    *arg_name,
834                       guint64                         arg_size,
835                       GVariant                       *options,
836                       enum VolumeType                 vol_creation_type,
837                       const gchar                    *arg_pool,
838                       guint64                         arg_virtual_size,
839                       guint64                         arg_index_memory,
840                       gboolean                        arg_compression,
841                       gboolean                        arg_deduplication,
842                       const gchar                    *arg_write_policy)
843 {
844   GError *error = NULL;
845   UDisksLinuxVolumeGroup *group = UDISKS_LINUX_VOLUME_GROUP (_group);
846   UDisksLinuxVolumeGroupObject *object = NULL;
847   UDisksLinuxModuleLVM2 *module;
848   UDisksDaemon *daemon;
849   uid_t caller_uid;
850   const gchar *lv_objpath;
851   LVJobData data = {0};
852   UDisksLinuxLogicalVolumeObject *pool_object = NULL;
853   const gchar *auth_error_msg = NULL;
854   UDisksThreadedJobFunc create_function = NULL;
855   VolumeCompletionFunc completion_function = NULL;
856 
857   if (VOL_PLAIN == vol_creation_type)
858     {
859       auth_error_msg = N_("Authentication is required to create a logical volume");
860       create_function = lvcreate_job_func;
861       completion_function = udisks_volume_group_complete_create_plain_volume;
862     }
863   else if (VOL_THIN_VOLUME == vol_creation_type)
864     {
865       auth_error_msg = N_("Authentication is required to create a thin volume");
866       create_function = lvcreate_thin_job_func;
867       completion_function = udisks_volume_group_complete_create_thin_volume;
868     }
869   else if (VOL_THIN_POOL == vol_creation_type)
870     {
871       auth_error_msg = N_("Authentication is required to create a thin pool volume");
872       create_function = lvcreate_thin_pool_job_func;
873       completion_function = udisks_volume_group_complete_create_thin_pool_volume;
874     }
875   else if (VOL_VDO_VOLUME == vol_creation_type)
876     {
877       auth_error_msg = N_("Authentication is required to create a VDO volume");
878       create_function = lvcreate_vdo_job_func;
879       completion_function = udisks_volume_group_complete_create_vdo_volume;
880     }
881 
882   object = udisks_daemon_util_dup_object (group, &error);
883   if (object == NULL)
884     {
885       g_dbus_method_invocation_take_error (invocation, error);
886       goto out;
887     }
888 
889   module = udisks_linux_volume_group_object_get_module (object);
890   daemon = udisks_module_get_daemon (UDISKS_MODULE (module));
891   if (!udisks_daemon_util_get_caller_uid_sync (daemon,
892                                                invocation,
893                                                NULL /* GCancellable */,
894                                                &caller_uid,
895                                                &error))
896     {
897       g_dbus_method_invocation_return_gerror (invocation, error);
898       g_clear_error (&error);
899       goto out;
900     }
901 
902   /* Policy check. */
903   UDISKS_DAEMON_CHECK_AUTHORIZATION (daemon,
904                                      UDISKS_OBJECT (object),
905                                      LVM2_POLICY_ACTION_ID,
906                                      options,
907                                      auth_error_msg,
908                                      invocation);
909 
910   data.vg_name = udisks_linux_volume_group_object_get_name (object);
911   data.new_lv_name = arg_name;
912   data.new_lv_size = arg_size;
913 
914   if (VOL_THIN_POOL == vol_creation_type)
915     data.extent_size = udisks_volume_group_get_extent_size (UDISKS_VOLUME_GROUP (group));
916 
917   if (VOL_THIN_VOLUME == vol_creation_type)
918     {
919       pool_object = UDISKS_LINUX_LOGICAL_VOLUME_OBJECT (udisks_daemon_find_object (daemon, arg_pool));
920       if (pool_object == NULL || !UDISKS_IS_LINUX_LOGICAL_VOLUME_OBJECT (pool_object))
921         {
922           g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
923                                                  "Not a logical volume");
924           goto out;
925         }
926       data.pool_name = udisks_linux_logical_volume_object_get_name (pool_object);
927     }
928 
929   if (VOL_VDO_VOLUME == vol_creation_type)
930     {
931       data.pool_name = arg_pool;
932       data.virtual_size = arg_virtual_size;
933       data.index_memory = arg_index_memory;
934       data.compression = arg_compression;
935       data.deduplication = arg_deduplication;
936       data.write_policy = arg_write_policy;
937     }
938 
939   if (!udisks_daemon_launch_threaded_job_sync (daemon,
940                                                UDISKS_OBJECT (object),
941                                                "lvm-vg-create-volume",
942                                                caller_uid,
943                                                create_function,
944                                                &data,
945                                                NULL, /* user_data_free_func */
946                                                NULL, /* GCancellable */
947                                                &error))
948     {
949       g_dbus_method_invocation_return_error (invocation,
950                                              UDISKS_ERROR,
951                                              UDISKS_ERROR_FAILED,
952                                              "Error creating volume: %s",
953                                              error->message);
954       g_clear_error (&error);
955       goto out;
956     }
957 
958   lv_objpath = wait_for_logical_volume_path (object, arg_name, &error);
959   if (lv_objpath == NULL)
960     {
961       g_prefix_error (&error,
962                       "Error waiting for logical volume object for '%s': ",
963                       arg_name);
964       g_dbus_method_invocation_take_error (invocation, error);
965       goto out;
966     }
967 
968   completion_function (_group, invocation, lv_objpath);
969 
970  out:
971   g_clear_object (&pool_object);
972   g_clear_object (&object);
973   return TRUE;
974 }
975 
976 static gboolean
handle_create_plain_volume(UDisksVolumeGroup * _group,GDBusMethodInvocation * invocation,const gchar * arg_name,guint64 arg_size,GVariant * options)977 handle_create_plain_volume (UDisksVolumeGroup     *_group,
978                             GDBusMethodInvocation *invocation,
979                             const gchar           *arg_name,
980                             guint64                arg_size,
981                             GVariant              *options)
982 {
983   return handle_create_volume(_group, invocation, arg_name, arg_size, options,
984                               VOL_PLAIN, NULL, 0, 0, FALSE, FALSE, NULL);
985 }
986 
987 /* ---------------------------------------------------------------------------------------------------- */
988 
989 static gboolean
handle_create_thin_pool_volume(UDisksVolumeGroup * _group,GDBusMethodInvocation * invocation,const gchar * arg_name,guint64 arg_size,GVariant * options)990 handle_create_thin_pool_volume (UDisksVolumeGroup     *_group,
991                                 GDBusMethodInvocation *invocation,
992                                 const gchar           *arg_name,
993                                 guint64                arg_size,
994                                 GVariant              *options)
995 {
996   return handle_create_volume(_group, invocation, arg_name, arg_size, options,
997                               VOL_THIN_POOL, NULL, 0, 0, FALSE, FALSE, NULL);
998 }
999 
1000 /* ---------------------------------------------------------------------------------------------------- */
1001 
1002 static gboolean
handle_create_thin_volume(UDisksVolumeGroup * _group,GDBusMethodInvocation * invocation,const gchar * arg_name,guint64 arg_size,const gchar * arg_pool,GVariant * options)1003 handle_create_thin_volume (UDisksVolumeGroup     *_group,
1004                            GDBusMethodInvocation *invocation,
1005                            const gchar           *arg_name,
1006                            guint64                arg_size,
1007                            const gchar           *arg_pool,
1008                            GVariant              *options)
1009 {
1010   return handle_create_volume(_group, invocation, arg_name, arg_size, options,
1011                               VOL_THIN_VOLUME, arg_pool, 0, 0, FALSE, FALSE, NULL);
1012 }
1013 
1014 static gboolean
handle_create_vdo_volume(UDisksVolumeGroup * _group,GDBusMethodInvocation * invocation,const gchar * arg_name,const gchar * arg_pool,guint64 arg_size,guint64 arg_virtual_size,guint64 arg_index_memory,gboolean arg_compression,gboolean arg_deduplication,const gchar * arg_write_policy,GVariant * options)1015 handle_create_vdo_volume (UDisksVolumeGroup     *_group,
1016                           GDBusMethodInvocation *invocation,
1017                           const gchar           *arg_name,
1018                           const gchar           *arg_pool,
1019                           guint64                arg_size,
1020                           guint64                arg_virtual_size,
1021                           guint64                arg_index_memory,
1022                           gboolean               arg_compression,
1023                           gboolean               arg_deduplication,
1024                           const gchar           *arg_write_policy,
1025                           GVariant              *options)
1026 {
1027   return handle_create_volume (_group, invocation, arg_name, arg_size, options,
1028                                VOL_VDO_VOLUME, arg_pool, arg_virtual_size, arg_index_memory,
1029                                arg_compression, arg_deduplication, arg_write_policy);
1030 }
1031 
1032 /* ---------------------------------------------------------------------------------------------------- */
1033 
1034 static void
volume_group_iface_init(UDisksVolumeGroupIface * iface)1035 volume_group_iface_init (UDisksVolumeGroupIface *iface)
1036 {
1037   iface->handle_poll = handle_poll;
1038 
1039   iface->handle_delete = handle_delete;
1040   iface->handle_rename = handle_rename;
1041 
1042   iface->handle_add_device = handle_add_device;
1043   iface->handle_remove_device = handle_remove_device;
1044   iface->handle_empty_device = handle_empty_device;
1045 
1046   iface->handle_create_plain_volume = handle_create_plain_volume;
1047   iface->handle_create_thin_pool_volume = handle_create_thin_pool_volume;
1048   iface->handle_create_thin_volume = handle_create_thin_volume;
1049 
1050   iface->handle_create_vdo_volume = handle_create_vdo_volume;
1051 }
1052