1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 *
3 * Copyright (C) 2007-2010 David Zeuthen <zeuthen@gmail.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
21 #include "config.h"
22 #include <glib/gi18n-lib.h>
23
24 #include <string.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27
28 #include "udiskslogging.h"
29 #include "udisksdaemon.h"
30 #include "udisksdaemonutil.h"
31 #include "udiskslinuxprovider.h"
32 #include "udiskslinuxdriveobject.h"
33 #include "udiskslinuxdrive.h"
34 #include "udiskslinuxdriveata.h"
35 #include "udiskslinuxblockobject.h"
36 #include "udiskslinuxdevice.h"
37 #include "udisksmodulemanager.h"
38 #include "udisksmodule.h"
39 #include "udisksmoduleobject.h"
40
41 /**
42 * SECTION:udiskslinuxdriveobject
43 * @title: UDisksLinuxDriveObject
44 * @short_description: Object representing a drive on Linux
45 *
46 * Object corresponding to a drive on Linux.
47 */
48
49 typedef struct _UDisksLinuxDriveObjectClass UDisksLinuxDriveObjectClass;
50
51 /**
52 * UDisksLinuxDriveObject:
53 *
54 * The #UDisksLinuxDriveObject structure contains only private data and
55 * should only be accessed using the provided API.
56 */
57 struct _UDisksLinuxDriveObject
58 {
59 UDisksObjectSkeleton parent_instance;
60
61 UDisksDaemon *daemon;
62
63 /* list of UDisksLinuxDevice objects for block objects */
64 GList *devices;
65
66 /* interfaces */
67 UDisksDrive *iface_drive;
68 UDisksDriveAta *iface_drive_ata;
69 GHashTable *module_ifaces;
70 };
71
72 struct _UDisksLinuxDriveObjectClass
73 {
74 UDisksObjectSkeletonClass parent_class;
75 };
76
77 enum
78 {
79 PROP_0,
80 PROP_DAEMON,
81 PROP_DEVICE
82 };
83
84 G_DEFINE_TYPE (UDisksLinuxDriveObject, udisks_linux_drive_object, UDISKS_TYPE_OBJECT_SKELETON);
85
86 static void
udisks_linux_drive_object_finalize(GObject * _object)87 udisks_linux_drive_object_finalize (GObject *_object)
88 {
89 UDisksLinuxDriveObject *object = UDISKS_LINUX_DRIVE_OBJECT (_object);
90
91 /* note: we don't hold a ref to drive_object->daemon or drive_object->mount_monitor */
92 g_list_free_full (object->devices, g_object_unref);
93
94 if (object->iface_drive != NULL)
95 g_object_unref (object->iface_drive);
96 if (object->iface_drive_ata != NULL)
97 g_object_unref (object->iface_drive_ata);
98 if (object->module_ifaces != NULL)
99 g_hash_table_destroy (object->module_ifaces);
100
101 if (G_OBJECT_CLASS (udisks_linux_drive_object_parent_class)->finalize != NULL)
102 G_OBJECT_CLASS (udisks_linux_drive_object_parent_class)->finalize (_object);
103 }
104
105 static void
udisks_linux_drive_object_get_property(GObject * __object,guint prop_id,GValue * value,GParamSpec * pspec)106 udisks_linux_drive_object_get_property (GObject *__object,
107 guint prop_id,
108 GValue *value,
109 GParamSpec *pspec)
110 {
111 UDisksLinuxDriveObject *object = UDISKS_LINUX_DRIVE_OBJECT (__object);
112
113 switch (prop_id)
114 {
115 case PROP_DAEMON:
116 g_value_set_object (value, udisks_linux_drive_object_get_daemon (object));
117 break;
118
119 default:
120 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
121 break;
122 }
123 }
124
125 static void
udisks_linux_drive_object_set_property(GObject * __object,guint prop_id,const GValue * value,GParamSpec * pspec)126 udisks_linux_drive_object_set_property (GObject *__object,
127 guint prop_id,
128 const GValue *value,
129 GParamSpec *pspec)
130 {
131 UDisksLinuxDriveObject *object = UDISKS_LINUX_DRIVE_OBJECT (__object);
132
133 switch (prop_id)
134 {
135 case PROP_DAEMON:
136 g_assert (object->daemon == NULL);
137 /* we don't take a reference to the daemon */
138 object->daemon = g_value_get_object (value);
139 break;
140
141 case PROP_DEVICE:
142 g_assert (object->devices == NULL);
143 object->devices = g_list_prepend (NULL, g_value_dup_object (value));
144 break;
145
146 default:
147 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
148 break;
149 }
150 }
151
152
153 static void
udisks_linux_drive_object_init(UDisksLinuxDriveObject * object)154 udisks_linux_drive_object_init (UDisksLinuxDriveObject *object)
155 {
156 }
157
158 static GObjectConstructParam *
find_construct_property(guint n_construct_properties,GObjectConstructParam * construct_properties,const gchar * name)159 find_construct_property (guint n_construct_properties,
160 GObjectConstructParam *construct_properties,
161 const gchar *name)
162 {
163 guint n;
164 for (n = 0; n < n_construct_properties; n++)
165 if (g_strcmp0 (g_param_spec_get_name (construct_properties[n].pspec), name) == 0)
166 return &construct_properties[n];
167 return NULL;
168 }
169
170 /* unless given, compute object path from sysfs path */
171 static GObject *
udisks_linux_drive_object_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)172 udisks_linux_drive_object_constructor (GType type,
173 guint n_construct_properties,
174 GObjectConstructParam *construct_properties)
175 {
176 GObjectConstructParam *cp;
177 UDisksDaemon *daemon;
178 GUdevClient *client;
179 UDisksLinuxDevice *device;
180
181 cp = find_construct_property (n_construct_properties, construct_properties, "daemon");
182 g_assert (cp != NULL);
183 daemon = UDISKS_DAEMON (g_value_get_object (cp->value));
184 g_assert (daemon != NULL);
185
186 client = udisks_linux_provider_get_udev_client (udisks_daemon_get_linux_provider (daemon));
187
188 cp = find_construct_property (n_construct_properties, construct_properties, "device");
189 g_assert (cp != NULL);
190 device = g_value_get_object (cp->value);
191 g_assert (device != NULL);
192
193 if (!udisks_linux_drive_object_should_include_device (client, device, NULL))
194 {
195 return NULL;
196 }
197 else
198 {
199 return G_OBJECT_CLASS (udisks_linux_drive_object_parent_class)->constructor (type,
200 n_construct_properties,
201 construct_properties);
202 }
203 }
204
205 static void
strip_and_replace_with_uscore(gchar * s)206 strip_and_replace_with_uscore (gchar *s)
207 {
208 guint n;
209
210 if (s == NULL)
211 goto out;
212
213 g_strstrip (s);
214
215 for (n = 0; s != NULL && s[n] != '\0'; n++)
216 {
217 if (s[n] == ' ' || s[n] == '-')
218 s[n] = '_';
219 }
220
221 out:
222 ;
223 }
224
225 static gboolean
is_dm_multipath(UDisksLinuxDevice * device)226 is_dm_multipath (UDisksLinuxDevice *device)
227 {
228 const gchar *dm_uuid;
229
230 dm_uuid = g_udev_device_get_sysfs_attr (device->udev_device, "dm/uuid");
231 return dm_uuid != NULL && g_str_has_prefix (dm_uuid, "mpath-");
232 }
233
234 static void
udisks_linux_drive_object_constructed(GObject * _object)235 udisks_linux_drive_object_constructed (GObject *_object)
236 {
237 UDisksLinuxDriveObject *object = UDISKS_LINUX_DRIVE_OBJECT (_object);
238 gchar *vendor;
239 gchar *model;
240 gchar *serial;
241 GString *str;
242
243 object->module_ifaces = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
244
245 /* initial coldplug */
246 udisks_linux_drive_object_uevent (object, "add", object->devices->data);
247
248 /* compute the object path */
249 vendor = g_strdup (udisks_drive_get_vendor (object->iface_drive));
250 model = g_strdup (udisks_drive_get_model (object->iface_drive));
251 serial = g_strdup (udisks_drive_get_serial (object->iface_drive));
252 strip_and_replace_with_uscore (vendor);
253 strip_and_replace_with_uscore (model);
254 strip_and_replace_with_uscore (serial);
255 str = g_string_new ("/org/freedesktop/UDisks2/drives/");
256 if (vendor == NULL && model == NULL && serial == NULL)
257 {
258 g_string_append (str, "drive");
259 }
260 else
261 {
262 /* <VENDOR>_<MODEL>_<SERIAL> */
263 if (vendor != NULL && strlen (vendor) > 0)
264 {
265 udisks_safe_append_to_object_path (str, vendor);
266 }
267 if (model != NULL && strlen (model) > 0)
268 {
269 if (str->str[str->len - 1] != '/')
270 g_string_append_c (str, '_');
271 udisks_safe_append_to_object_path (str, model);
272 }
273 if (serial != NULL && strlen (serial) > 0)
274 {
275 if (str->str[str->len - 1] != '/')
276 g_string_append_c (str, '_');
277 udisks_safe_append_to_object_path (str, serial);
278 }
279 }
280 g_free (vendor);
281 g_free (model);
282 g_free (serial);
283 g_dbus_object_skeleton_set_object_path (G_DBUS_OBJECT_SKELETON (object), str->str);
284 g_string_free (str, TRUE);
285
286 if (G_OBJECT_CLASS (udisks_linux_drive_object_parent_class)->constructed != NULL)
287 G_OBJECT_CLASS (udisks_linux_drive_object_parent_class)->constructed (_object);
288 }
289
290 static void
udisks_linux_drive_object_class_init(UDisksLinuxDriveObjectClass * klass)291 udisks_linux_drive_object_class_init (UDisksLinuxDriveObjectClass *klass)
292 {
293 GObjectClass *gobject_class;
294
295 gobject_class = G_OBJECT_CLASS (klass);
296 gobject_class->constructor = udisks_linux_drive_object_constructor;
297 gobject_class->finalize = udisks_linux_drive_object_finalize;
298 gobject_class->constructed = udisks_linux_drive_object_constructed;
299 gobject_class->set_property = udisks_linux_drive_object_set_property;
300 gobject_class->get_property = udisks_linux_drive_object_get_property;
301
302 /**
303 * UDisksLinuxDriveObject:daemon:
304 *
305 * The #UDisksDaemon the object is for.
306 */
307 g_object_class_install_property (gobject_class,
308 PROP_DAEMON,
309 g_param_spec_object ("daemon",
310 "Daemon",
311 "The daemon the object is for",
312 UDISKS_TYPE_DAEMON,
313 G_PARAM_READABLE |
314 G_PARAM_WRITABLE |
315 G_PARAM_CONSTRUCT_ONLY |
316 G_PARAM_STATIC_STRINGS));
317
318 /**
319 * UDisksLinuxDriveObject:device:
320 *
321 * The #UDisksLinuxDevice for the object. Connect to the #GObject::notify
322 * signal to get notified whenever this is updated.
323 */
324 g_object_class_install_property (gobject_class,
325 PROP_DEVICE,
326 g_param_spec_object ("device",
327 "Device",
328 "The device for the object",
329 UDISKS_TYPE_LINUX_DEVICE,
330 G_PARAM_WRITABLE |
331 G_PARAM_CONSTRUCT_ONLY |
332 G_PARAM_STATIC_STRINGS));
333
334 }
335
336 /**
337 * udisks_linux_drive_object_new:
338 * @daemon: A #UDisksDaemon.
339 * @device: The #UDisksLinuxDevice for the sysfs block device.
340 *
341 * Create a new drive object.
342 *
343 * Returns: A #UDisksLinuxDriveObject object or %NULL if @device does not represent a drive. Free with g_object_unref().
344 */
345 UDisksLinuxDriveObject *
udisks_linux_drive_object_new(UDisksDaemon * daemon,UDisksLinuxDevice * device)346 udisks_linux_drive_object_new (UDisksDaemon *daemon,
347 UDisksLinuxDevice *device)
348 {
349 GObject *object;
350
351 g_return_val_if_fail (UDISKS_IS_DAEMON (daemon), NULL);
352 g_return_val_if_fail (UDISKS_IS_LINUX_DEVICE (device), NULL);
353
354 object = g_object_new (UDISKS_TYPE_LINUX_DRIVE_OBJECT,
355 "daemon", daemon,
356 "device", device,
357 NULL);
358
359 if (object != NULL)
360 return UDISKS_LINUX_DRIVE_OBJECT (object);
361 else
362 return NULL;
363 }
364
365 /**
366 * udisks_linux_drive_object_get_daemon:
367 * @object: A #UDisksLinuxDriveObject.
368 *
369 * Gets the daemon used by @object.
370 *
371 * Returns: A #UDisksDaemon. Do not free, the object is owned by @object.
372 */
373 UDisksDaemon *
udisks_linux_drive_object_get_daemon(UDisksLinuxDriveObject * object)374 udisks_linux_drive_object_get_daemon (UDisksLinuxDriveObject *object)
375 {
376 g_return_val_if_fail (UDISKS_IS_LINUX_DRIVE_OBJECT (object), NULL);
377 return object->daemon;
378 }
379
380 /**
381 * udisks_linux_drive_object_get_devices:
382 * @object: A #UDisksLinuxDriveObject.
383 *
384 * Gets the current #UDisksLinuxDevice objects associated with @object.
385 *
386 * Returns: A list of #UDisksLinuxDevice objects. Free each element with
387 * g_object_unref(), then free the list with g_list_free().
388 */
389 GList *
udisks_linux_drive_object_get_devices(UDisksLinuxDriveObject * object)390 udisks_linux_drive_object_get_devices (UDisksLinuxDriveObject *object)
391 {
392 GList *ret;
393 g_return_val_if_fail (UDISKS_IS_LINUX_DRIVE_OBJECT (object), NULL);
394 ret = g_list_copy_deep (object->devices, (GCopyFunc) udisks_g_object_ref_copy, NULL);
395 return ret;
396 }
397
398 /**
399 * udisks_linux_drive_object_get_device:
400 * @object: A #UDisksLinuxDriveObject.
401 * @get_hw: If the drive is multipath, set to %TRUE to get a path device instead of the multipath device.
402 *
403 * Gets one of the #UDisksLinuxDevice object associated with @object.
404 *
405 * If @get_hw is %TRUE and @object represents a multipath device then
406 * one of the paths is returned rather than the multipath device. This
407 * is useful if you e.g. need to configure the physical hardware.
408 *
409 * Returns: A #UDisksLinuxDevice or %NULL. The returned object must be freed
410 * with g_object_unref().
411 */
412 UDisksLinuxDevice *
udisks_linux_drive_object_get_device(UDisksLinuxDriveObject * object,gboolean get_hw)413 udisks_linux_drive_object_get_device (UDisksLinuxDriveObject *object,
414 gboolean get_hw)
415 {
416 UDisksLinuxDevice *ret = NULL;
417 GList *devices;
418
419 for (devices = object->devices; devices; devices = devices->next)
420 {
421 if (!get_hw || !is_dm_multipath (UDISKS_LINUX_DEVICE (devices->data)))
422 {
423 ret = devices->data;
424 break;
425 }
426 }
427
428 if (ret != NULL)
429 g_object_ref (ret);
430 return ret;
431 }
432
433 /**
434 * udisks_linux_drive_object_get_block:
435 * @object: A #UDisksLinuxDriveObject.
436 * @get_hw: If the drive is multipath, set to %TRUE to get a path device instead of the multipath device.
437 *
438 * Gets a #UDisksLinuxBlockObject representing a block device associated with @object.
439 *
440 * Returns: A #UDisksLinuxBlockObject or %NULL. The returned object
441 * must be freed with g_object_unref().
442 */
443 UDisksLinuxBlockObject *
udisks_linux_drive_object_get_block(UDisksLinuxDriveObject * object,gboolean get_hw)444 udisks_linux_drive_object_get_block (UDisksLinuxDriveObject *object,
445 gboolean get_hw)
446 {
447 GDBusObjectManagerServer *object_manager;
448 UDisksLinuxBlockObject *ret;
449 GList *objects;
450 GList *l;
451
452 ret = NULL;
453
454 object_manager = udisks_daemon_get_object_manager (object->daemon);
455 objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (object_manager));
456 for (l = objects; l != NULL; l = l->next)
457 {
458 GDBusObjectSkeleton *iter_object = G_DBUS_OBJECT_SKELETON (l->data);
459 UDisksBlock *block;
460 UDisksLinuxDevice *device;
461 gboolean skip;
462
463 if (!UDISKS_IS_LINUX_BLOCK_OBJECT (iter_object))
464 continue;
465
466 device = udisks_linux_block_object_get_device (UDISKS_LINUX_BLOCK_OBJECT (iter_object));
467 skip = (g_strcmp0 (g_udev_device_get_devtype (device->udev_device), "disk") != 0
468 || (get_hw && is_dm_multipath (device)));
469 g_object_unref (device);
470
471 if (skip)
472 continue;
473
474 block = udisks_object_peek_block (UDISKS_OBJECT (iter_object));
475 if (g_strcmp0 (udisks_block_get_drive (block),
476 g_dbus_object_get_object_path (G_DBUS_OBJECT (object))) == 0)
477 {
478 ret = UDISKS_LINUX_BLOCK_OBJECT (g_object_ref (iter_object));
479 goto out;
480 }
481 }
482
483 out:
484 g_list_free_full (objects, g_object_unref);
485 return ret;
486 }
487
488 /* ---------------------------------------------------------------------------------------------------- */
489
490 static gboolean
update_iface(UDisksObject * object,const gchar * uevent_action,UDisksObjectHasInterfaceFunc has_func,UDisksObjectConnectInterfaceFunc connect_func,UDisksObjectUpdateInterfaceFunc update_func,GType skeleton_type,gpointer _interface_pointer)491 update_iface (UDisksObject *object,
492 const gchar *uevent_action,
493 UDisksObjectHasInterfaceFunc has_func,
494 UDisksObjectConnectInterfaceFunc connect_func,
495 UDisksObjectUpdateInterfaceFunc update_func,
496 GType skeleton_type,
497 gpointer _interface_pointer)
498 {
499 gboolean ret = FALSE;
500 gboolean has;
501 gboolean add;
502 GDBusInterface **interface_pointer = _interface_pointer;
503 GDBusInterfaceInfo *interface_info = NULL;
504 GDBusInterface *tmp_iface = NULL;
505
506 g_return_val_if_fail (object != NULL, FALSE);
507 g_return_val_if_fail (has_func != NULL, FALSE);
508 g_return_val_if_fail (update_func != NULL, FALSE);
509 g_return_val_if_fail (g_type_is_a (skeleton_type, G_TYPE_OBJECT), FALSE);
510 g_return_val_if_fail (g_type_is_a (skeleton_type, G_TYPE_DBUS_INTERFACE), FALSE);
511 g_return_val_if_fail (interface_pointer != NULL, FALSE);
512 g_return_val_if_fail (*interface_pointer == NULL || G_IS_DBUS_INTERFACE (*interface_pointer), FALSE);
513
514 add = FALSE;
515 has = has_func (object);
516 if (*interface_pointer == NULL)
517 {
518 if (has)
519 {
520 *interface_pointer = g_object_new (skeleton_type, NULL);
521 if (connect_func != NULL)
522 connect_func (object);
523 add = TRUE;
524 }
525 }
526 else
527 {
528 if (!has)
529 {
530 /* Check before we remove interface from object */
531 interface_info = g_dbus_interface_get_info (*interface_pointer);
532 tmp_iface = g_dbus_object_get_interface ((GDBusObject *) object,
533 interface_info->name);
534
535 if (tmp_iface)
536 {
537 g_dbus_object_skeleton_remove_interface
538 (G_DBUS_OBJECT_SKELETON (object),
539 G_DBUS_INTERFACE_SKELETON (*interface_pointer));
540 g_object_unref(tmp_iface);
541 }
542
543 g_object_unref (*interface_pointer);
544 *interface_pointer = NULL;
545 }
546 }
547
548 if (*interface_pointer != NULL)
549 {
550 if (update_func (object, uevent_action, G_DBUS_INTERFACE (*interface_pointer)))
551 ret = TRUE;
552 if (add)
553 g_dbus_object_skeleton_add_interface (G_DBUS_OBJECT_SKELETON (object),
554 G_DBUS_INTERFACE_SKELETON (*interface_pointer));
555 }
556
557 return ret;
558 }
559
560 /* ---------------------------------------------------------------------------------------------------- */
561
562 static gboolean
drive_check(UDisksObject * object)563 drive_check (UDisksObject *object)
564 {
565 return TRUE;
566 }
567
568 static void
drive_connect(UDisksObject * object)569 drive_connect (UDisksObject *object)
570 {
571 }
572
573 static gboolean
drive_update(UDisksObject * object,const gchar * uevent_action,GDBusInterface * _iface)574 drive_update (UDisksObject *object,
575 const gchar *uevent_action,
576 GDBusInterface *_iface)
577 {
578 UDisksLinuxDriveObject *drive_object = UDISKS_LINUX_DRIVE_OBJECT (object);
579
580 return udisks_linux_drive_update (UDISKS_LINUX_DRIVE (drive_object->iface_drive), drive_object);
581 }
582
583 /* ---------------------------------------------------------------------------------------------------- */
584
585 static gboolean
drive_ata_check(UDisksObject * object)586 drive_ata_check (UDisksObject *object)
587 {
588 UDisksLinuxDriveObject *drive_object = UDISKS_LINUX_DRIVE_OBJECT (object);
589 gboolean ret;
590 UDisksLinuxDevice *device;
591
592 ret = FALSE;
593 if (drive_object->devices == NULL)
594 goto out;
595
596 device = drive_object->devices->data;
597 if (device->ata_identify_device_data != NULL || device->ata_identify_packet_device_data != NULL)
598 ret = TRUE;
599
600 out:
601 return ret;
602 }
603
604 static void
drive_ata_connect(UDisksObject * object)605 drive_ata_connect (UDisksObject *object)
606 {
607
608 }
609
610 static gboolean
drive_ata_update(UDisksObject * object,const gchar * uevent_action,GDBusInterface * _iface)611 drive_ata_update (UDisksObject *object,
612 const gchar *uevent_action,
613 GDBusInterface *_iface)
614 {
615 UDisksLinuxDriveObject *drive_object = UDISKS_LINUX_DRIVE_OBJECT (object);
616
617 return udisks_linux_drive_ata_update (UDISKS_LINUX_DRIVE_ATA (drive_object->iface_drive_ata), drive_object);
618 }
619
620 /* ---------------------------------------------------------------------------------------------------- */
621
622 static void apply_configuration (UDisksLinuxDriveObject *object);
623
624 static GList *
find_link_for_sysfs_path(UDisksLinuxDriveObject * object,const gchar * sysfs_path)625 find_link_for_sysfs_path (UDisksLinuxDriveObject *object,
626 const gchar *sysfs_path)
627 {
628 GList *l;
629 GList *ret;
630 ret = NULL;
631 for (l = object->devices; l != NULL; l = l->next)
632 {
633 UDisksLinuxDevice *device = l->data;
634 if (g_strcmp0 (g_udev_device_get_sysfs_path (device->udev_device), sysfs_path) == 0)
635 {
636 ret = l;
637 goto out;
638 }
639 }
640 out:
641 return ret;
642 }
643
644 /**
645 * udisks_linux_drive_object_uevent:
646 * @object: A #UDisksLinuxDriveObject.
647 * @action: Uevent action or %NULL
648 * @device: A #UDisksLinuxDevice device object or %NULL if the device hasn't changed.
649 *
650 * Updates all information on interfaces on @drive.
651 */
652 void
udisks_linux_drive_object_uevent(UDisksLinuxDriveObject * object,const gchar * action,UDisksLinuxDevice * device)653 udisks_linux_drive_object_uevent (UDisksLinuxDriveObject *object,
654 const gchar *action,
655 UDisksLinuxDevice *device)
656 {
657 GList *link;
658 gboolean conf_changed;
659 UDisksModuleManager *module_manager;
660 GList *modules;
661 GList *l;
662
663 g_return_if_fail (UDISKS_IS_LINUX_DRIVE_OBJECT (object));
664 g_return_if_fail (device == NULL || UDISKS_IS_LINUX_DEVICE (device));
665
666 link = NULL;
667 if (device != NULL)
668 link = find_link_for_sysfs_path (object, g_udev_device_get_sysfs_path (device->udev_device));
669 if (g_strcmp0 (action, "remove") == 0)
670 {
671 if (link != NULL)
672 {
673 g_object_unref (UDISKS_LINUX_DEVICE (link->data));
674 object->devices = g_list_delete_link (object->devices, link);
675 }
676 else
677 {
678 udisks_warning ("Drive doesn't have device with sysfs path %s on remove event",
679 device ? g_udev_device_get_sysfs_path (device->udev_device) : "(null device)");
680 }
681 }
682 else
683 {
684 if (link != NULL)
685 {
686 g_object_unref (UDISKS_LINUX_DEVICE (link->data));
687 link->data = g_object_ref (device);
688 }
689 else
690 {
691 if (device != NULL)
692 object->devices = g_list_append (object->devices, g_object_ref (device));
693 }
694 }
695
696 conf_changed = FALSE;
697 conf_changed |= update_iface (UDISKS_OBJECT (object), action, drive_check, drive_connect, drive_update,
698 UDISKS_TYPE_LINUX_DRIVE, &object->iface_drive);
699 conf_changed |= update_iface (UDISKS_OBJECT (object), action, drive_ata_check, drive_ata_connect, drive_ata_update,
700 UDISKS_TYPE_LINUX_DRIVE_ATA, &object->iface_drive_ata);
701
702 /* Attach interfaces from modules */
703 module_manager = udisks_daemon_get_module_manager (object->daemon);
704 modules = udisks_module_manager_get_modules (module_manager);
705 for (l = modules; l; l = g_list_next (l))
706 {
707 UDisksModule *module = l->data;
708 GType *types;
709
710 types = udisks_module_get_drive_object_interface_types (module);
711 for (; types && *types; types++)
712 {
713 GDBusInterfaceSkeleton *interface;
714 gboolean keep = TRUE;
715
716 interface = g_hash_table_lookup (object->module_ifaces, GSIZE_TO_POINTER (*types));
717 if (interface != NULL)
718 {
719 /* ask the existing instance to process the uevent */
720 if (udisks_module_object_process_uevent (UDISKS_MODULE_OBJECT (interface),
721 action, device, &keep))
722 {
723 conf_changed = TRUE;
724 if (! keep)
725 {
726 g_dbus_object_skeleton_remove_interface (G_DBUS_OBJECT_SKELETON (object), interface);
727 g_hash_table_remove (object->module_ifaces, GSIZE_TO_POINTER (*types));
728 }
729 }
730 }
731 else
732 {
733 /* try create new interface and see if the module is interested in this object */
734 interface = udisks_module_new_drive_object_interface (module, object, *types);
735 if (interface)
736 {
737 /* do coldplug after creation */
738 udisks_module_object_process_uevent (UDISKS_MODULE_OBJECT (interface), action, device, &keep);
739 g_dbus_object_skeleton_add_interface (G_DBUS_OBJECT_SKELETON (object), interface);
740 g_warn_if_fail (g_hash_table_replace (object->module_ifaces, GSIZE_TO_POINTER (*types), interface));
741 conf_changed = TRUE;
742 }
743 }
744 }
745 }
746 g_list_free_full (modules, g_object_unref);
747
748 if (g_strcmp0 (action, "reconfigure") == 0)
749 conf_changed = TRUE;
750
751 if (conf_changed)
752 apply_configuration (object);
753 }
754
755 /* ---------------------------------------------------------------------------------------------------- */
756
757 static void
apply_configuration(UDisksLinuxDriveObject * object)758 apply_configuration (UDisksLinuxDriveObject *object)
759 {
760 GVariant *configuration = NULL;
761 UDisksLinuxDevice *device = NULL;
762
763 if (object->iface_drive == NULL)
764 goto out;
765
766 configuration = udisks_drive_dup_configuration (object->iface_drive);
767 if (configuration == NULL)
768 goto out;
769
770 device = udisks_linux_drive_object_get_device (object, TRUE /* get_hw */);
771 if (device == NULL)
772 goto out;
773
774 if (object->iface_drive_ata != NULL)
775 {
776 udisks_linux_drive_ata_apply_configuration (UDISKS_LINUX_DRIVE_ATA (object->iface_drive_ata),
777 device,
778 configuration);
779 }
780
781 out:
782 g_clear_object (&device);
783 if (configuration != NULL)
784 g_variant_unref (configuration);
785 }
786
787 /* ---------------------------------------------------------------------------------------------------- */
788
789 /* utility routine to blacklist WWNs that are not suitable to use
790 * for identification purposes
791 */
792 static gboolean
is_wwn_black_listed(const gchar * wwn)793 is_wwn_black_listed (const gchar *wwn)
794 {
795 g_return_val_if_fail (wwn != NULL, FALSE);
796
797 if (g_str_has_prefix (wwn, "0x") || g_str_has_prefix (wwn, "0X"))
798 wwn += 2;
799
800 if (g_ascii_strcasecmp (wwn, "50f0000000000000") == 0)
801 {
802 /* SAMSUNG SP1604N (PATA), see https://bugzilla.redhat.com/show_bug.cgi?id=838691#c4 */
803 return TRUE;
804 }
805 else
806 {
807 return FALSE;
808 }
809 }
810
811 static gchar *
check_for_vpd(GUdevDevice * device)812 check_for_vpd (GUdevDevice *device)
813 {
814 gchar *ret = NULL;
815 const gchar *serial;
816 const gchar *wwn;
817 const gchar *path;
818 const gchar *model;
819
820 g_return_val_if_fail (G_UDEV_IS_DEVICE (device), FALSE);
821
822 /* order of preference: WWN_serial, WWN, Model_serial, serial, path */
823 serial = g_udev_device_get_property (device, "ID_SERIAL");
824 wwn = g_udev_device_get_property (device, "ID_WWN_WITH_EXTENSION");
825 path = g_udev_device_get_property (device, "ID_PATH");
826 model = g_udev_device_get_property (device, "ID_MODEL");
827 if (wwn != NULL && strlen (wwn) > 0 && !is_wwn_black_listed (wwn))
828 {
829 if (serial != NULL && strlen (serial) > 0)
830 ret = g_strdup_printf ("%s_%s", wwn, serial);
831 else
832 ret = g_strdup (wwn);
833 }
834 else if (serial != NULL && strlen (serial) > 0)
835 {
836 if (model != NULL && strlen (model) > 0)
837 ret = g_strdup_printf ("%s_%s", model, serial);
838 else
839 ret = g_strdup (serial);
840 }
841 else if (path != NULL && strlen (path) > 0)
842 {
843 ret = g_strdup (path);
844 }
845 return ret;
846 }
847
848 /* <internal>
849 * udisks_linux_drive_object_should_include_device:
850 * @client: A #GUdevClient.
851 * @device: A #UDisksLinuxDevice.
852 * @out_vpd: Return location for unique ID or %NULL.
853 *
854 * Checks if we should even construct a #UDisksLinuxDriveObject for @device.
855 *
856 * Returns: %TRUE if we should construct an object, %FALSE otherwise.
857 */
858 gboolean
udisks_linux_drive_object_should_include_device(GUdevClient * client,UDisksLinuxDevice * device,gchar ** out_vpd)859 udisks_linux_drive_object_should_include_device (GUdevClient *client,
860 UDisksLinuxDevice *device,
861 gchar **out_vpd)
862 {
863 gboolean ret;
864 gchar *vpd;
865
866 ret = FALSE;
867 vpd = NULL;
868
869 /* The 'block' subsystem encompasses several objects with varying
870 * DEVTYPE including
871 *
872 * - disk
873 * - partition
874 *
875 * and we are only interested in the first.
876 */
877 if (g_strcmp0 (g_udev_device_get_devtype (device->udev_device), "disk") != 0)
878 goto out;
879
880 vpd = check_for_vpd (device->udev_device);
881
882 if (vpd == NULL)
883 {
884 const gchar *name;
885 const gchar *vendor;
886 const gchar *model;
887 GUdevDevice *parent;
888
889 name = g_udev_device_get_name (device->udev_device);
890
891 /* workaround for floppy devices */
892 if (g_str_has_prefix (name, "fd"))
893 {
894 vpd = g_strdup_printf ("pcfloppy_%s", name);
895 goto found;
896 }
897
898 /* workaround for missing serial/wwn on virtio-blk */
899 if (g_str_has_prefix (name, "vd"))
900 {
901 vpd = g_strdup (name);
902 goto found;
903 }
904
905 /* workaround for missing serial/wwn on VMware */
906 vendor = g_udev_device_get_property (device->udev_device, "ID_VENDOR");
907 model = g_udev_device_get_property (device->udev_device, "ID_MODEL");
908 if (g_str_has_prefix (name, "sd") &&
909 vendor != NULL && g_strcmp0 (vendor, "VMware") == 0 &&
910 model != NULL && g_str_has_prefix (model, "Virtual"))
911 {
912 vpd = g_strdup (name);
913 goto found;
914 }
915
916 /* workaround for missing serial/wwn on firewire devices */
917 parent = g_udev_device_get_parent_with_subsystem (device->udev_device, "firewire", NULL);
918 if (parent != NULL)
919 {
920 vpd = g_strdup (name);
921 g_object_unref (parent);
922 goto found;
923 }
924
925 /* dm-multipath */
926 if (is_dm_multipath (device))
927 {
928 gchar **slaves;
929 guint n;
930 slaves = udisks_daemon_util_resolve_links (g_udev_device_get_sysfs_path (device->udev_device), "slaves");
931 for (n = 0; slaves[n] != NULL; n++)
932 {
933 GUdevDevice *slave;
934 slave = g_udev_client_query_by_sysfs_path (client, slaves[n]);
935 if (slave != NULL)
936 {
937 vpd = check_for_vpd (slave);
938 if (vpd != NULL)
939 {
940 g_object_unref (slave);
941 g_strfreev (slaves);
942 goto found;
943 }
944 g_object_unref (slave);
945 }
946 }
947 g_strfreev (slaves);
948 }
949 }
950
951 found:
952 if (vpd != NULL)
953 {
954 if (out_vpd != NULL)
955 {
956 *out_vpd = vpd;
957 vpd = NULL;
958 }
959 ret = TRUE;
960 }
961
962 out:
963 g_free (vpd);
964 return ret;
965 }
966
967 /* ---------------------------------------------------------------------------------------------------- */
968
969 /**
970 * udisks_linux_drive_object_housekeeping:
971 * @object: A #UDisksLinuxDriveObject.
972 * @secs_since_last: Number of seconds sincex the last housekeeping or 0 if the first housekeeping ever.
973 * @cancellable: A %GCancellable or %NULL.
974 * @error: Return location for error or %NULL.
975 *
976 * Called periodically (every ten minutes or so) to perform
977 * housekeeping tasks such as refreshing ATA SMART data.
978 *
979 * The function runs in a dedicated thread and is allowed to perform
980 * blocking I/O.
981 *
982 * Long-running tasks should periodically check @cancellable to see if
983 * they have been cancelled.
984 *
985 * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
986 */
987 gboolean
udisks_linux_drive_object_housekeeping(UDisksLinuxDriveObject * object,guint secs_since_last,GCancellable * cancellable,GError ** error)988 udisks_linux_drive_object_housekeeping (UDisksLinuxDriveObject *object,
989 guint secs_since_last,
990 GCancellable *cancellable,
991 GError **error)
992 {
993 gboolean ret;
994
995 ret = FALSE;
996
997 if (object->iface_drive_ata != NULL &&
998 udisks_drive_ata_get_smart_supported (object->iface_drive_ata) &&
999 udisks_drive_ata_get_smart_enabled (object->iface_drive_ata))
1000 {
1001 GError *local_error;
1002 gboolean nowakeup;
1003
1004 /* Wake-up only on start-up */
1005 nowakeup = TRUE;
1006 if (secs_since_last == 0)
1007 nowakeup = FALSE;
1008
1009 udisks_info ("Refreshing SMART data on %s (nowakeup=%d)",
1010 g_dbus_object_get_object_path (G_DBUS_OBJECT (object)),
1011 nowakeup);
1012
1013 local_error = NULL;
1014 if (!udisks_linux_drive_ata_refresh_smart_sync (UDISKS_LINUX_DRIVE_ATA (object->iface_drive_ata),
1015 nowakeup,
1016 NULL, /* simulate_path */
1017 cancellable,
1018 &local_error))
1019 {
1020 if (nowakeup && (local_error->domain == UDISKS_ERROR &&
1021 local_error->code == UDISKS_ERROR_WOULD_WAKEUP))
1022 {
1023 udisks_info ("Drive %s is in a sleep state",
1024 g_dbus_object_get_object_path (G_DBUS_OBJECT (object)));
1025 g_clear_error (&local_error);
1026 }
1027 else if (nowakeup && (local_error->domain == UDISKS_ERROR &&
1028 local_error->code == UDISKS_ERROR_DEVICE_BUSY))
1029 {
1030 /* typically because a "secure erase" operation is pending */
1031 udisks_info ("Drive %s is busy",
1032 g_dbus_object_get_object_path (G_DBUS_OBJECT (object)));
1033 g_clear_error (&local_error);
1034 }
1035 else
1036 {
1037 g_propagate_prefixed_error (error, local_error, "Error updating SMART data: ");
1038 goto out;
1039 }
1040 }
1041 }
1042
1043 ret = TRUE;
1044
1045 out:
1046 return ret;
1047 }
1048
1049 static gboolean
is_block_unlocked(GList * objects,const gchar * crypto_object_path)1050 is_block_unlocked (GList *objects, const gchar *crypto_object_path)
1051 {
1052 gboolean ret = FALSE;
1053 GList *l;
1054 for (l = objects; l != NULL; l = l->next)
1055 {
1056 UDisksObject *object = UDISKS_OBJECT (l->data);
1057 UDisksBlock *block;
1058 block = udisks_object_peek_block (object);
1059 if (block != NULL)
1060 {
1061 if (g_strcmp0 (udisks_block_get_crypto_backing_device (block), crypto_object_path) == 0)
1062 {
1063 ret = TRUE;
1064 goto out;
1065 }
1066 }
1067 }
1068 out:
1069 return ret;
1070 }
1071
1072 /**
1073 * udisks_linux_drive_object_is_not_in_use:
1074 * @object: A #UDisksLinuxDriveObject.
1075 * @cancellable: (allow-none): A #GCancellable or %NULL.
1076 * @error: A #GError or %NULL.
1077 *
1078 * Checks if the drive represented by @object is in use and sets
1079 * @error if so.
1080 *
1081 * Returns: %TRUE if @object is not is use, %FALSE if @error is set.
1082 */
1083 gboolean
udisks_linux_drive_object_is_not_in_use(UDisksLinuxDriveObject * object,GCancellable * cancellable,GError ** error)1084 udisks_linux_drive_object_is_not_in_use (UDisksLinuxDriveObject *object,
1085 GCancellable *cancellable,
1086 GError **error)
1087 {
1088 GDBusObjectManagerServer *object_manager;
1089 const gchar *drive_object_path;
1090 gboolean ret = TRUE;
1091 GList *objects = NULL;
1092 GList *l;
1093
1094 g_return_val_if_fail (UDISKS_IS_LINUX_DRIVE_OBJECT (object), FALSE);
1095 g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
1096 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1097
1098 drive_object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (object));
1099
1100 object_manager = udisks_daemon_get_object_manager (object->daemon);
1101 objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (object_manager));
1102
1103 /* Visit all block devices related to the drive... */
1104 for (l = objects; l != NULL; l = l->next)
1105 {
1106 GDBusObjectSkeleton *iter_object = G_DBUS_OBJECT_SKELETON (l->data);
1107 UDisksBlock *block;
1108 UDisksFilesystem *filesystem;
1109
1110 if (!UDISKS_IS_LINUX_BLOCK_OBJECT (iter_object))
1111 continue;
1112
1113 block = udisks_object_peek_block (UDISKS_OBJECT (iter_object));
1114 filesystem = udisks_object_peek_filesystem (UDISKS_OBJECT (iter_object));
1115
1116 if (g_strcmp0 (udisks_block_get_drive (block), drive_object_path) != 0)
1117 continue;
1118
1119 /* bail if block device is mounted */
1120 if (filesystem != NULL)
1121 {
1122 if (g_strv_length ((gchar **) udisks_filesystem_get_mount_points (filesystem)) > 0)
1123 {
1124 g_set_error (error,
1125 UDISKS_ERROR,
1126 UDISKS_ERROR_DEVICE_BUSY,
1127 "Device %s is mounted",
1128 udisks_block_get_preferred_device (block));
1129 ret = FALSE;
1130 goto out;
1131 }
1132 }
1133
1134 /* bail if block device is unlocked (LUKS) */
1135 if (is_block_unlocked (objects, g_dbus_object_get_object_path (G_DBUS_OBJECT (iter_object))))
1136 {
1137 g_set_error (error,
1138 UDISKS_ERROR,
1139 UDISKS_ERROR_DEVICE_BUSY,
1140 "Encrypted device %s is unlocked",
1141 udisks_block_get_preferred_device (block));
1142 ret = FALSE;
1143 goto out;
1144 }
1145 }
1146
1147 out:
1148 g_list_free_full (objects, g_object_unref);
1149 return ret;
1150 }
1151
1152 /* ---------------------------------------------------------------------------------------------------- */
1153
1154 /**
1155 * udisks_linux_drive_object_get_siblings:
1156 * @object: A #UDisksLinuxDriveObject.
1157 *
1158 * Gets the siblings for @object, if any.
1159 *
1160 * Returns: (transfer full) (element-type UDisksLinuxDriveObject): A list of #UDisksLinuxDriveObject
1161 * instances. The returned list should be freed with g_list_free() after each element has been
1162 * freed with g_object_unref().
1163 */
1164 GList *
udisks_linux_drive_object_get_siblings(UDisksLinuxDriveObject * object)1165 udisks_linux_drive_object_get_siblings (UDisksLinuxDriveObject *object)
1166 {
1167 GDBusObjectManagerServer *object_manager;
1168 GList *ret = NULL;
1169 GList *objects = NULL;
1170 GList *l;
1171 gchar *sibling_id = NULL;
1172
1173 if (object->iface_drive == NULL)
1174 goto out;
1175
1176 sibling_id = udisks_drive_dup_sibling_id (object->iface_drive);
1177 if (sibling_id == NULL || strlen (sibling_id) == 0)
1178 goto out;
1179
1180 object_manager = udisks_daemon_get_object_manager (object->daemon);
1181 objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (object_manager));
1182 for (l = objects; l != NULL; l = l->next)
1183 {
1184 GDBusObjectSkeleton *iter_object = G_DBUS_OBJECT_SKELETON (l->data);
1185 UDisksLinuxDriveObject *iter_linux_drive_object;
1186
1187 if (!UDISKS_IS_LINUX_DRIVE_OBJECT (iter_object))
1188 continue;
1189
1190 iter_linux_drive_object = UDISKS_LINUX_DRIVE_OBJECT (iter_object);
1191 if (iter_linux_drive_object->iface_drive != NULL &&
1192 g_strcmp0 (udisks_drive_get_sibling_id (iter_linux_drive_object->iface_drive), sibling_id) == 0)
1193 {
1194 ret = g_list_prepend (ret, g_object_ref (iter_object));
1195 }
1196 }
1197
1198 out:
1199 ret = g_list_reverse (ret);
1200 g_list_free_full (objects, g_object_unref);
1201 g_free (sibling_id);
1202 return ret;
1203 }
1204