1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2  *
3  * Copyright (C) 2015 Dominika Hodovska <dhodovsk@redhat.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 
20 #include "config.h"
21 
22 #include <glib/gi18n.h>
23 #include <blockdev/kbd.h>
24 
25 #include <src/udisksdaemon.h>
26 #include <src/udisksdaemonutil.h>
27 #include <src/udiskslogging.h>
28 #include <src/udiskslinuxblock.h>
29 #include <src/udiskslinuxblockobject.h>
30 #include "udiskslinuxblockbcache.h"
31 #include "udiskslinuxmanagerbcache.h"
32 #include "udiskslinuxmodulebcache.h"
33 
34 /**
35  * SECTION: udiskslinuxmanagerbcache
36  * @title: UDisksLinuxManagerBcache
37  * @short_description: Linux implementation  of #UDisksLinuxManagerBcache
38  *
39  * This type provides an implementation of the #UDisksLinuxManagerBcache
40  * interface on Linux.
41  */
42 
43 /**
44  * UDisksLinuxManagerBcache:
45  *
46  * The #UDisksLinuxManagerBcache structure contains only private data and
47  * should only be accessed using the provided API.
48  */
49 
50 struct _UDisksLinuxManagerBcache {
51   UDisksManagerBcacheSkeleton parent_instance;
52 
53   UDisksLinuxModuleBcache *module;
54 };
55 
56 struct _UDisksLinuxManagerBcacheClass {
57   UDisksManagerBcacheSkeletonClass parent_class;
58 };
59 
60 static void udisks_linux_manager_bcache_iface_init (UDisksManagerBcacheIface *iface);
61 
62 G_DEFINE_TYPE_WITH_CODE (UDisksLinuxManagerBcache, udisks_linux_manager_bcache, UDISKS_TYPE_MANAGER_BCACHE_SKELETON,
63                          G_IMPLEMENT_INTERFACE (UDISKS_TYPE_MANAGER_BCACHE, udisks_linux_manager_bcache_iface_init));
64 
65 enum
66 {
67   PROP_0,
68   PROP_MODULE,
69   N_PROPERTIES
70 };
71 
72 static void
udisks_linux_manager_bcache_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)73 udisks_linux_manager_bcache_get_property (GObject     *object,
74                                           guint        property_id,
75                                           GValue      *value,
76                                           GParamSpec  *pspec)
77 {
78   UDisksLinuxManagerBcache *manager = UDISKS_LINUX_MANAGER_BCACHE (object);
79 
80   switch (property_id)
81     {
82     case PROP_MODULE:
83       g_value_set_object (value, udisks_linux_manager_bcache_get_module (manager));
84       break;
85 
86     default:
87       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
88       break;
89     }
90 }
91 
92 static void
udisks_linux_manager_bcache_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)93 udisks_linux_manager_bcache_set_property (GObject       *object,
94                                           guint          property_id,
95                                           const GValue  *value,
96                                           GParamSpec    *pspec)
97 {
98   UDisksLinuxManagerBcache *manager = UDISKS_LINUX_MANAGER_BCACHE (object);
99 
100   switch (property_id)
101     {
102     case PROP_MODULE:
103       g_assert (manager->module == NULL);
104       manager->module = g_value_dup_object (value);
105       break;
106 
107     default:
108       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
109       break;
110     }
111 }
112 
113 static void
udisks_linux_manager_bcache_finalize(GObject * object)114 udisks_linux_manager_bcache_finalize (GObject *object)
115 {
116   UDisksLinuxManagerBcache *manager = UDISKS_LINUX_MANAGER_BCACHE (object);
117 
118   g_object_unref (manager->module);
119 
120   if (G_OBJECT_CLASS (udisks_linux_manager_bcache_parent_class))
121     G_OBJECT_CLASS (udisks_linux_manager_bcache_parent_class)->finalize (object);
122 }
123 
124 static void
udisks_linux_manager_bcache_class_init(UDisksLinuxManagerBcacheClass * klass)125 udisks_linux_manager_bcache_class_init (UDisksLinuxManagerBcacheClass *klass)
126 {
127   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
128 
129   gobject_class->get_property = udisks_linux_manager_bcache_get_property;
130   gobject_class->set_property = udisks_linux_manager_bcache_set_property;
131   gobject_class->finalize = udisks_linux_manager_bcache_finalize;
132 
133   /**
134    * UDisksLinuxManagerBcache:module:
135    *
136    * The #UDisksModule for the object.
137    */
138   g_object_class_install_property (gobject_class,
139                                    PROP_MODULE,
140                                    g_param_spec_object ("module",
141                                                         "Module",
142                                                         "The module for the object",
143                                                         UDISKS_TYPE_LINUX_MODULE_BCACHE,
144                                                         G_PARAM_READABLE |
145                                                         G_PARAM_WRITABLE |
146                                                         G_PARAM_CONSTRUCT_ONLY |
147                                                         G_PARAM_STATIC_STRINGS));
148 }
149 
150 static void
udisks_linux_manager_bcache_init(UDisksLinuxManagerBcache * self)151 udisks_linux_manager_bcache_init (UDisksLinuxManagerBcache *self)
152 {
153   g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (self),
154                                        G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD);
155 }
156 
157 /**
158  * udisks_linux_manager_bcache_new:
159  * @module: A #UDisksLinuxModuleBcache.
160  *
161  * Creates a new #UDisksLinuxManagerBcache instance.
162  *
163  * Returns: A new #UDisksLinuxManagerBcache. Free with g_object_unref ().
164  */
165 UDisksLinuxManagerBcache *
udisks_linux_manager_bcache_new(UDisksLinuxModuleBcache * module)166 udisks_linux_manager_bcache_new (UDisksLinuxModuleBcache *module)
167 {
168   g_return_val_if_fail (UDISKS_IS_LINUX_MODULE_BCACHE (module), NULL);
169   return UDISKS_LINUX_MANAGER_BCACHE (g_object_new (UDISKS_TYPE_LINUX_MANAGER_BCACHE,
170                                                     "module", module,
171                                                     NULL));
172 }
173 
174 /**
175  * udisks_linux_manager_bcache_get_module:
176  * @manager: A #UDisksLinuxManagerBcache.
177  *
178  * Gets the module used by @manager.
179  *
180  * Returns: A #UDisksLinuxModuleBcache. Do not free, the object is owned by @manager.
181  */
182 UDisksLinuxModuleBcache *
udisks_linux_manager_bcache_get_module(UDisksLinuxManagerBcache * manager)183 udisks_linux_manager_bcache_get_module (UDisksLinuxManagerBcache *manager)
184 {
185   g_return_val_if_fail (UDISKS_IS_LINUX_MANAGER_BCACHE (manager), NULL);
186   return manager->module;
187 }
188 
189 static UDisksObject *
wait_for_bcache_object(UDisksDaemon * daemon,gpointer user_data)190 wait_for_bcache_object (UDisksDaemon *daemon,
191                         gpointer      user_data)
192 {
193   UDisksObject *object = NULL;
194   UDisksBlock *block = NULL;
195 
196   object = udisks_daemon_find_block_by_device_file (daemon, (const gchar *) user_data);
197   if (object == NULL)
198       goto out;
199 
200   block = udisks_object_peek_block (object);
201   if (block == NULL)
202     {
203       g_clear_object (&object);
204       goto out;
205     }
206 
207 out:
208   return object;
209 }
210 
211 static gboolean
handle_bcache_create(UDisksManagerBcache * object,GDBusMethodInvocation * invocation,const gchar * arg_backing_dev,const gchar * arg_cache_dev,GVariant * options)212 handle_bcache_create (UDisksManagerBcache    *object,
213                       GDBusMethodInvocation  *invocation,
214                       const gchar            *arg_backing_dev,
215                       const gchar            *arg_cache_dev,
216                       GVariant               *options)
217 {
218   UDisksLinuxManagerBcache *manager = UDISKS_LINUX_MANAGER_BCACHE (object);
219   UDisksDaemon *daemon;
220   UDisksObject *backing_dev_object = NULL;
221   UDisksBlock *backing_dev_block = NULL;
222   gchar *backing_dev_path = NULL;
223   UDisksObject *cache_dev_object = NULL;
224   UDisksBlock *cache_dev_block = NULL;
225   gchar *cache_dev_path = NULL;
226   gchar *bcache_name = NULL;
227   gchar *bcache_file = NULL;
228   UDisksObject *bcache_object = NULL;
229   GError *error = NULL;
230 
231   daemon = udisks_module_get_daemon (UDISKS_MODULE (manager->module));
232 
233   /* Policy check */
234   UDISKS_DAEMON_CHECK_AUTHORIZATION (daemon,
235                                      NULL,
236                                      BCACHE_POLICY_ACTION_ID,
237                                      options,
238                                      N_("Authentication is required to create bcache device."),
239                                      invocation);
240 
241   /* get path for the backing device */
242   backing_dev_object = udisks_daemon_find_object (daemon, arg_backing_dev);
243   if (backing_dev_object == NULL)
244     {
245       g_dbus_method_invocation_return_error (invocation,
246                                              UDISKS_ERROR,
247                                              UDISKS_ERROR_FAILED,
248                                              "Invalid object path %s",
249                                              arg_backing_dev);
250       goto out;
251     }
252 
253   backing_dev_block = udisks_object_get_block (backing_dev_object);
254   if (backing_dev_block == NULL)
255     {
256       g_dbus_method_invocation_return_error (invocation,
257                                              UDISKS_ERROR,
258                                              UDISKS_ERROR_FAILED,
259                                              "Object path %s is not a block device",
260                                              arg_backing_dev);
261       goto out;
262     }
263 
264   backing_dev_path = udisks_block_dup_device (backing_dev_block);
265 
266   /* get path for the cache device */
267   cache_dev_object = udisks_daemon_find_object (daemon, arg_cache_dev);
268   if (cache_dev_object == NULL)
269     {
270       g_dbus_method_invocation_return_error (invocation,
271                                              UDISKS_ERROR,
272                                              UDISKS_ERROR_FAILED,
273                                              "Invalid object path %s",
274                                              arg_cache_dev);
275       goto out;
276     }
277 
278   cache_dev_block = udisks_object_get_block (cache_dev_object);
279   if (cache_dev_block == NULL)
280     {
281       g_dbus_method_invocation_return_error (invocation,
282                                              UDISKS_ERROR,
283                                              UDISKS_ERROR_FAILED,
284                                              "Object path %s is not a block device",
285                                              arg_cache_dev);
286       goto out;
287     }
288 
289   cache_dev_path = udisks_block_dup_device (cache_dev_block);
290 
291   /* XXX: the type casting below looks like a bug in libblockdev */
292   if (! bd_kbd_bcache_create (backing_dev_path, cache_dev_path, NULL, (const gchar **) &bcache_name, &error))
293     {
294       g_dbus_method_invocation_take_error (invocation, error);
295       goto out;
296     }
297 
298   bcache_file = g_strdup_printf ("/dev/%s", bcache_name);
299   /* sit and wait for the bcache object to show up */
300   bcache_object = udisks_daemon_wait_for_object_sync (daemon,
301                                                       wait_for_bcache_object,
302                                                       bcache_file,
303                                                       NULL,
304                                                       UDISKS_DEFAULT_WAIT_TIMEOUT,
305                                                       &error);
306 
307   if (bcache_object == NULL)
308     {
309       g_prefix_error (&error,
310                       "Error waiting for bcache object after creating '%s': ",
311                       bcache_name);
312       g_dbus_method_invocation_take_error (invocation, error);
313       goto out;
314     }
315 
316   udisks_manager_bcache_complete_bcache_create (object,
317                                                 invocation,
318                                                 g_dbus_object_get_object_path (G_DBUS_OBJECT (bcache_object)));
319 out:
320   g_free (backing_dev_path);
321   g_free (cache_dev_path);
322   g_free (bcache_name);
323   g_free (bcache_file);
324   g_clear_object (&bcache_object);
325   g_clear_object (&backing_dev_object);
326   g_clear_object (&backing_dev_block);
327   g_clear_object (&cache_dev_object);
328   g_clear_object (&cache_dev_block);
329 
330   return TRUE;
331 }
332 
333 static void
udisks_linux_manager_bcache_iface_init(UDisksManagerBcacheIface * iface)334 udisks_linux_manager_bcache_iface_init (UDisksManagerBcacheIface *iface)
335 {
336   iface->handle_bcache_create = handle_bcache_create;
337 }
338