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