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