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.h>
23 #include <glib/gi18n-lib.h>
24 
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/ioctl.h>
28 #include <fcntl.h>
29 #include <inttypes.h>
30 #include <errno.h>
31 #include <linux/bsg.h>
32 #include <scsi/scsi.h>
33 #include <scsi/sg.h>
34 #include <scsi/scsi_ioctl.h>
35 
36 #include <pwd.h>
37 #include <grp.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <stdio.h>
41 #include <mntent.h>
42 
43 #include <glib/gstdio.h>
44 
45 #include "udiskslogging.h"
46 #include "udiskslinuxprovider.h"
47 #include "udiskslinuxdriveobject.h"
48 #include "udiskslinuxdrive.h"
49 #include "udiskslinuxblockobject.h"
50 #include "udisksdaemon.h"
51 #include "udisksdaemonutil.h"
52 #include "udiskslinuxdevice.h"
53 #include "udisksconfigmanager.h"
54 
55 /**
56  * SECTION:udiskslinuxdrive
57  * @title: UDisksLinuxDrive
58  * @short_description: Linux implementation of #UDisksDrive
59  *
60  * This type provides an implementation of the #UDisksDrive interface
61  * on Linux.
62  */
63 
64 typedef struct _UDisksLinuxDriveClass   UDisksLinuxDriveClass;
65 
66 /**
67  * UDisksLinuxDrive:
68  *
69  * The #UDisksLinuxDrive structure contains only private data and should
70  * only be accessed using the provided API.
71  */
72 struct _UDisksLinuxDrive
73 {
74   UDisksDriveSkeleton parent_instance;
75 
76   gint64 time_detected;
77   gint64 time_media_detected;
78   gchar *sort_key;
79 };
80 
81 struct _UDisksLinuxDriveClass
82 {
83   UDisksDriveSkeletonClass parent_class;
84 };
85 
86 static void drive_iface_init (UDisksDriveIface *iface);
87 
88 G_DEFINE_TYPE_WITH_CODE (UDisksLinuxDrive, udisks_linux_drive, UDISKS_TYPE_DRIVE_SKELETON,
89                          G_IMPLEMENT_INTERFACE (UDISKS_TYPE_DRIVE, drive_iface_init));
90 
91 /* ---------------------------------------------------------------------------------------------------- */
92 
93 static void
udisks_linux_drive_finalize(GObject * object)94 udisks_linux_drive_finalize (GObject *object)
95 {
96   UDisksLinuxDrive *drive = UDISKS_LINUX_DRIVE (object);
97 
98   g_free (drive->sort_key);
99 
100   if (G_OBJECT_CLASS (udisks_linux_drive_parent_class)->finalize != NULL)
101     G_OBJECT_CLASS (udisks_linux_drive_parent_class)->finalize (object);
102 }
103 
104 static void
udisks_linux_drive_init(UDisksLinuxDrive * drive)105 udisks_linux_drive_init (UDisksLinuxDrive *drive)
106 {
107   g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (drive),
108                                        G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD);
109 }
110 
111 static void
udisks_linux_drive_class_init(UDisksLinuxDriveClass * klass)112 udisks_linux_drive_class_init (UDisksLinuxDriveClass *klass)
113 {
114   GObjectClass *gobject_class;
115 
116   gobject_class = G_OBJECT_CLASS (klass);
117   gobject_class->finalize     = udisks_linux_drive_finalize;
118 }
119 
120 /**
121  * udisks_linux_drive_new:
122  *
123  * Creates a new #UDisksLinuxDrive instance.
124  *
125  * Returns: A new #UDisksLinuxDrive. Free with g_object_unref().
126  */
127 UDisksDrive *
udisks_linux_drive_new(void)128 udisks_linux_drive_new (void)
129 {
130   return UDISKS_DRIVE (g_object_new (UDISKS_TYPE_LINUX_DRIVE,
131                                           NULL));
132 }
133 
134 /* ---------------------------------------------------------------------------------------------------- */
135 
136 static void
set_id(UDisksDrive * iface)137 set_id (UDisksDrive *iface)
138 {
139   GString *id;
140   const gchar *vendor;
141   const gchar *model;
142   const gchar *serial;
143   const gchar *wwn;
144   guint n;
145 
146   vendor = udisks_drive_get_vendor (iface);
147   model = udisks_drive_get_model (iface);
148   serial = udisks_drive_get_serial (iface);
149   wwn = udisks_drive_get_wwn (iface);
150 
151   id = g_string_new (NULL);
152 
153   /* VENDOR-MODEL-[SERIAL|WWN] */
154 
155   if (vendor != NULL && strlen (vendor) > 0)
156     {
157       g_string_append (id, vendor);
158     }
159   if (model != NULL && strlen (model) > 0)
160     {
161       if (id->len > 0)
162         g_string_append_c (id, '-');
163       g_string_append (id, model);
164     }
165 
166   if (serial != NULL && strlen (serial) > 0)
167     {
168       if (id->len > 0)
169         g_string_append_c (id, '-');
170       g_string_append (id, serial);
171     }
172   else if (wwn != NULL && strlen (wwn) > 0)
173     {
174       if (id->len > 0)
175         g_string_append_c (id, '-');
176       g_string_append (id, wwn);
177     }
178   else
179     {
180       g_string_set_size (id, 0);
181     }
182 
183   for (n = 0; n < id->len; n++)
184     {
185       if (id->str[n] == '/' || id->str[n] == ' ')
186         id->str[n] = '-';
187     }
188 
189   udisks_drive_set_id (iface, id->str);
190 
191   g_string_free (id, TRUE);
192 }
193 
194 /* ---------------------------------------------------------------------------------------------------- */
195 
196 static gboolean
_g_variant_equal0(GVariant * a,GVariant * b)197 _g_variant_equal0 (GVariant *a, GVariant *b)
198 {
199   gboolean ret = FALSE;
200   if (a == NULL && b == NULL)
201     {
202       ret = TRUE;
203       goto out;
204     }
205   if (a == NULL || b == NULL)
206     goto out;
207   ret = g_variant_equal (a, b);
208 out:
209   return ret;
210 }
211 
212 /* ---------------------------------------------------------------------------------------------------- */
213 
214 typedef struct {
215   const gchar *asv_key;
216   const gchar *group;
217   const gchar *key;
218   const GVariantType *type;
219 } VariantKeyfileMapping;
220 
221 static const VariantKeyfileMapping drive_configuration_mapping[5] = {
222   {"ata-pm-standby",             "ATA", "StandbyTimeout",       G_VARIANT_TYPE_INT32},
223   {"ata-apm-level",              "ATA", "APMLevel",             G_VARIANT_TYPE_INT32},
224   {"ata-aam-level",              "ATA", "AAMLevel",             G_VARIANT_TYPE_INT32},
225   {"ata-write-cache-enabled",    "ATA", "WriteCacheEnabled",    G_VARIANT_TYPE_BOOLEAN},
226   {"ata-read-lookahead-enabled", "ATA", "ReadLookaheadEnabled", G_VARIANT_TYPE_BOOLEAN},
227 };
228 
229 /* ---------------------------------------------------------------------------------------------------- */
230 
231 static gchar *
configuration_get_path(UDisksLinuxDrive * drive,UDisksDaemon * daemon)232 configuration_get_path (UDisksLinuxDrive *drive,
233                         UDisksDaemon     *daemon)
234 {
235   UDisksConfigManager *config_manager;
236   const gchar *id;
237   gchar *id_config_file;
238   gchar *path;
239 
240   config_manager = udisks_daemon_get_config_manager (daemon);
241 
242   id = udisks_drive_get_id (UDISKS_DRIVE (drive));
243   if (id == NULL || strlen (id) == 0)
244     return NULL;
245 
246   id_config_file = g_strdup_printf ("%s.conf", id);
247   path = g_build_filename (udisks_config_manager_get_config_dir (config_manager),
248                            id_config_file,
249                            NULL);
250   g_free (id_config_file);
251 
252   return path;
253 }
254 
255 /* returns TRUE if configuration changed */
256 static gboolean
update_configuration(UDisksLinuxDrive * drive,UDisksLinuxDriveObject * object)257 update_configuration (UDisksLinuxDrive       *drive,
258                       UDisksLinuxDriveObject *object)
259 {
260   UDisksDaemon *daemon;
261   GKeyFile *key_file = NULL;
262   gboolean ret = FALSE;
263   gchar *path = NULL;
264   GError *error = NULL;
265   GVariant *value = NULL;
266   GVariantBuilder builder;
267   GVariant *old_value;
268   guint n;
269 
270   daemon = udisks_linux_drive_object_get_daemon (object);
271 
272   path = configuration_get_path (drive, daemon);
273   if (path == NULL)
274     goto out;
275 
276   key_file = g_key_file_new ();
277   if (!g_key_file_load_from_file (key_file,
278                                   path,
279                                   G_KEY_FILE_NONE,
280                                   &error))
281     {
282       if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
283         {
284           udisks_critical ("Error loading drive config file: %s (%s, %d)",
285                         error->message, g_quark_to_string (error->domain), error->code);
286         }
287       g_clear_error (&error);
288       goto out;
289     }
290 
291   g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
292   for (n = 0; n < G_N_ELEMENTS (drive_configuration_mapping); n++)
293     {
294       const VariantKeyfileMapping *mapping = &drive_configuration_mapping[n];
295 
296       if (!g_key_file_has_key (key_file, mapping->group, mapping->key, NULL))
297         continue;
298 
299       if (g_variant_type_equal (mapping->type, G_VARIANT_TYPE_INT32))
300         {
301           gint32 int_value = g_key_file_get_integer (key_file, mapping->group, mapping->key, &error);
302           if (error != NULL)
303             {
304               udisks_critical ("Error parsing int32 key %s in group %s in drive config file %s: %s (%s, %d)",
305                             mapping->key, mapping->group, path,
306                             error->message, g_quark_to_string (error->domain), error->code);
307               g_clear_error (&error);
308             }
309           else
310             {
311               g_variant_builder_add (&builder, "{sv}", mapping->asv_key, g_variant_new_int32 (int_value));
312             }
313         }
314       else if (g_variant_type_equal (mapping->type, G_VARIANT_TYPE_BOOLEAN))
315         {
316           gboolean bool_value = g_key_file_get_boolean (key_file, mapping->group, mapping->key, &error);
317           if (error != NULL)
318             {
319               udisks_critical ("Error parsing boolean key %s in group %s in drive config file %s: %s (%s, %d)",
320                             mapping->key, mapping->group, path,
321                             error->message, g_quark_to_string (error->domain), error->code);
322               g_clear_error (&error);
323             }
324           else
325             {
326               g_variant_builder_add (&builder, "{sv}", mapping->asv_key, g_variant_new_boolean (bool_value));
327             }
328         }
329       else
330         {
331           g_assert_not_reached ();
332         }
333     }
334 
335   value = g_variant_ref_sink (g_variant_builder_end (&builder));
336 
337  out:
338   g_free (path);
339 
340   old_value = udisks_drive_get_configuration (UDISKS_DRIVE (drive));
341   if (!_g_variant_equal0 (old_value, value))
342     ret = TRUE;
343   udisks_drive_set_configuration (UDISKS_DRIVE (drive), value);
344 
345   if (key_file != NULL)
346     g_key_file_free (key_file);
347   if (value != NULL)
348     g_variant_unref (value);
349 
350   return ret;
351 }
352 
353 /* ---------------------------------------------------------------------------------------------------- */
354 
355 static const struct
356 {
357   const gchar *udev_property;
358   const gchar *media_name;
359   gboolean force_non_removable;
360   gboolean force_removable;
361 } drive_media_mapping[] =
362 {
363   { "ID_DRIVE_THUMB", "thumb", TRUE, FALSE },
364   { "ID_DRIVE_FLASH", "flash", FALSE, TRUE },
365   { "ID_DRIVE_FLASH_CF", "flash_cf", FALSE, TRUE },
366   { "ID_DRIVE_FLASH_MS", "flash_ms", FALSE, TRUE },
367   { "ID_DRIVE_FLASH_SM", "flash_sm", FALSE, TRUE },
368   { "ID_DRIVE_FLASH_SD", "flash_sd", FALSE, TRUE },
369   { "ID_DRIVE_FLASH_SDHC", "flash_sdhc", FALSE, TRUE },
370   { "ID_DRIVE_FLASH_SDXC", "flash_sdxc", FALSE, TRUE },
371   { "ID_DRIVE_FLASH_SDIO", "flash_sdio", FALSE, TRUE },
372   { "ID_DRIVE_FLASH_SD_COMBO", "flash_sd_combo", FALSE, TRUE },
373   { "ID_DRIVE_FLASH_MMC", "flash_mmc", TRUE, FALSE },
374   { "ID_DRIVE_FLOPPY", "floppy", FALSE, TRUE },
375   { "ID_DRIVE_FLOPPY_ZIP", "floppy_zip", FALSE, TRUE },
376   { "ID_DRIVE_FLOPPY_JAZ", "floppy_jaz", FALSE, TRUE },
377   { "ID_CDROM", "optical_cd", FALSE, TRUE },
378   { "ID_CDROM_CD_R", "optical_cd_r", FALSE, TRUE },
379   { "ID_CDROM_CD_RW", "optical_cd_rw", FALSE, TRUE },
380   { "ID_CDROM_DVD", "optical_dvd", FALSE, TRUE },
381   { "ID_CDROM_DVD_R", "optical_dvd_r", FALSE, TRUE },
382   { "ID_CDROM_DVD_RW", "optical_dvd_rw", FALSE, TRUE },
383   { "ID_CDROM_DVD_RAM", "optical_dvd_ram", FALSE, TRUE },
384   { "ID_CDROM_DVD_PLUS_R", "optical_dvd_plus_r", FALSE, TRUE },
385   { "ID_CDROM_DVD_PLUS_RW", "optical_dvd_plus_rw", FALSE, TRUE },
386   { "ID_CDROM_DVD_PLUS_R_DL", "optical_dvd_plus_r_dl", FALSE, TRUE },
387   { "ID_CDROM_DVD_PLUS_RW_DL", "optical_dvd_plus_rw_dl", FALSE, TRUE },
388   { "ID_CDROM_BD", "optical_bd", FALSE, TRUE },
389   { "ID_CDROM_BD_R", "optical_bd_r", FALSE, TRUE },
390   { "ID_CDROM_BD_RE", "optical_bd_re", FALSE, TRUE },
391   { "ID_CDROM_HDDVD", "optical_hddvd", FALSE, TRUE },
392   { "ID_CDROM_HDDVD_R", "optical_hddvd_r", FALSE, TRUE },
393   { "ID_CDROM_HDDVD_RW", "optical_hddvd_rw", FALSE, TRUE },
394   { "ID_CDROM_MO", "optical_mo", FALSE, TRUE },
395   { "ID_CDROM_MRW", "optical_mrw", FALSE, TRUE },
396   { "ID_CDROM_MRW_W", "optical_mrw_w", FALSE, TRUE },
397   { NULL, NULL, FALSE, FALSE }
398 };
399 
400 static const struct
401 {
402   const gchar *udev_property;
403   const gchar *media_name;
404 } media_mapping[] =
405 {
406   { "ID_DRIVE_MEDIA_FLASH", "flash" },
407   { "ID_DRIVE_MEDIA_FLASH_CF", "flash_cf" },
408   { "ID_DRIVE_MEDIA_FLASH_MS", "flash_ms" },
409   { "ID_DRIVE_MEDIA_FLASH_SM", "flash_sm" },
410   { "ID_DRIVE_MEDIA_FLASH_SD", "flash_sd" },
411   { "ID_DRIVE_MEDIA_FLASH_SDHC", "flash_sdhc" },
412   { "ID_DRIVE_MEDIA_FLASH_SDXC", "flash_sdxc" },
413   { "ID_DRIVE_MEDIA_FLASH_SDIO", "flash_sdio" },
414   { "ID_DRIVE_MEDIA_FLASH_MMC", "flash_mmc" },
415   { "ID_DRIVE_MEDIA_FLOPPY", "floppy" },
416   { "ID_DRIVE_MEDIA_FLOPPY_ZIP", "floppy_zip" },
417   { "ID_DRIVE_MEDIA_FLOPPY_JAZ", "floppy_jaz" },
418   { "ID_CDROM_MEDIA_CD", "optical_cd" },
419   { "ID_CDROM_MEDIA_CD_R", "optical_cd_r" },
420   { "ID_CDROM_MEDIA_CD_RW", "optical_cd_rw" },
421   { "ID_CDROM_MEDIA_DVD", "optical_dvd" },
422   { "ID_CDROM_MEDIA_DVD_R", "optical_dvd_r" },
423   { "ID_CDROM_MEDIA_DVD_RW", "optical_dvd_rw" },
424   { "ID_CDROM_MEDIA_DVD_RAM", "optical_dvd_ram" },
425   { "ID_CDROM_MEDIA_DVD_PLUS_R", "optical_dvd_plus_r" },
426   { "ID_CDROM_MEDIA_DVD_PLUS_RW", "optical_dvd_plus_rw" },
427   { "ID_CDROM_MEDIA_DVD_PLUS_R_DL", "optical_dvd_plus_r_dl" },
428   { "ID_CDROM_MEDIA_DVD_PLUS_RW_DL", "optical_dvd_plus_rw_dl" },
429   { "ID_CDROM_MEDIA_BD", "optical_bd" },
430   { "ID_CDROM_MEDIA_BD_R", "optical_bd_r" },
431   { "ID_CDROM_MEDIA_BD_RE", "optical_bd_re" },
432   { "ID_CDROM_MEDIA_HDDVD", "optical_hddvd" },
433   { "ID_CDROM_MEDIA_HDDVD_R", "optical_hddvd_r" },
434   { "ID_CDROM_MEDIA_HDDVD_RW", "optical_hddvd_rw" },
435   { "ID_CDROM_MEDIA_MO", "optical_mo" },
436   { "ID_CDROM_MEDIA_MRW", "optical_mrw" },
437   { "ID_CDROM_MEDIA_MRW_W", "optical_mrw_w" },
438   { NULL, NULL }
439 };
440 
441 static gint
ptr_str_array_compare(const gchar ** a,const gchar ** b)442 ptr_str_array_compare (const gchar **a,
443                        const gchar **b)
444 {
445   return g_strcmp0 (*a, *b);
446 }
447 
448 static void
set_media(UDisksDrive * iface,UDisksLinuxDevice * device,gboolean is_pc_floppy_drive)449 set_media (UDisksDrive       *iface,
450            UDisksLinuxDevice *device,
451            gboolean           is_pc_floppy_drive)
452 {
453   guint n;
454   GPtrArray *media_compat_array;
455   const gchar *media_in_drive;
456   gboolean is_disc = FALSE;
457   gboolean disc_is_blank = FALSE;
458   guint disc_session_count = 0;
459   guint disc_track_count = 0;
460   guint disc_track_count_audio = 0;
461   guint disc_track_count_data = 0;
462   gboolean force_non_removable = FALSE;
463   gboolean force_removable = FALSE;
464   gboolean ejectable = FALSE;
465   gboolean removable = FALSE;
466 
467   media_compat_array = g_ptr_array_new ();
468   for (n = 0; drive_media_mapping[n].udev_property != NULL; n++)
469     {
470       if (g_udev_device_get_property_as_boolean (device->udev_device, drive_media_mapping[n].udev_property))
471         {
472           g_ptr_array_add (media_compat_array, (gpointer) drive_media_mapping[n].media_name);
473           if (drive_media_mapping[n].force_non_removable)
474             force_non_removable = TRUE;
475           if (drive_media_mapping[n].force_removable)
476             force_removable = TRUE;
477         }
478     }
479   g_ptr_array_sort (media_compat_array, (GCompareFunc) ptr_str_array_compare);
480   g_ptr_array_add (media_compat_array, NULL);
481 
482   removable = ejectable = g_udev_device_get_sysfs_attr_as_boolean (device->udev_device, "removable");
483   if (force_non_removable)
484     removable = FALSE;
485   if (force_removable)
486     removable = TRUE;
487   udisks_drive_set_media_removable (iface, removable);
488   if (is_pc_floppy_drive)
489     ejectable = FALSE;
490   /* MMC/SD: disrespect the sysfs 'removable' attr as it usually doesn't reflect reality */
491   if (g_str_has_prefix (g_udev_device_get_name (device->udev_device), "mmcblk"))
492     ejectable = removable;
493   udisks_drive_set_ejectable (iface, ejectable);
494 
495   media_in_drive = NULL;
496   if (udisks_drive_get_media_available (iface))
497     {
498       for (n = 0; media_mapping[n].udev_property != NULL; n++)
499         {
500           if (g_udev_device_get_property_as_boolean (device->udev_device, media_mapping[n].udev_property))
501             {
502               media_in_drive = media_mapping[n].media_name;
503               break;
504             }
505         }
506       /* If the media isn't set (from e.g. udev rules), just pick the first one in media_compat - note
507        * that this may be NULL (if we don't know what media is compatible with the drive) which is OK.
508        */
509       if (media_in_drive == NULL)
510         media_in_drive = ((const gchar **) media_compat_array->pdata)[0];
511     }
512   udisks_drive_set_media_compatibility (iface, (const gchar* const *) media_compat_array->pdata);
513   udisks_drive_set_media (iface, media_in_drive);
514   g_ptr_array_free (media_compat_array, TRUE);
515 
516   if (g_udev_device_get_property_as_boolean (device->udev_device, "ID_CDROM_MEDIA"))
517     {
518       const gchar *state;
519       is_disc = TRUE;
520       state = g_udev_device_get_property (device->udev_device, "ID_CDROM_MEDIA_STATE");
521       if (g_strcmp0 (state, "blank") == 0)
522         disc_is_blank = TRUE;
523       disc_session_count = g_udev_device_get_property_as_int (device->udev_device, "ID_CDROM_MEDIA_SESSION_COUNT");
524       disc_track_count = g_udev_device_get_property_as_int (device->udev_device, "ID_CDROM_MEDIA_TRACK_COUNT");
525       disc_track_count_audio = g_udev_device_get_property_as_int (device->udev_device, "ID_CDROM_MEDIA_TRACK_COUNT_AUDIO");
526       disc_track_count_data = g_udev_device_get_property_as_int (device->udev_device, "ID_CDROM_MEDIA_TRACK_COUNT_DATA");
527     }
528   udisks_drive_set_optical (iface, is_disc);
529   udisks_drive_set_optical_blank (iface, disc_is_blank);
530   udisks_drive_set_optical_num_sessions (iface, disc_session_count);
531   udisks_drive_set_optical_num_tracks (iface, disc_track_count);
532   udisks_drive_set_optical_num_audio_tracks (iface, disc_track_count_audio);
533   udisks_drive_set_optical_num_data_tracks (iface, disc_track_count_data);
534 }
535 
536 static void
set_rotation_rate(UDisksDrive * iface,UDisksLinuxDevice * device)537 set_rotation_rate (UDisksDrive       *iface,
538                    UDisksLinuxDevice *device)
539 {
540   gint rate;
541 
542   if (!g_udev_device_get_sysfs_attr_as_boolean (device->udev_device, "queue/rotational"))
543     {
544       rate = 0;
545     }
546   else
547     {
548       rate = -1;
549       if (device->ata_identify_device_data != NULL)
550         {
551           guint word_217 = 0;
552 
553           /* ATA8: 7.16 IDENTIFY DEVICE - ECh, PIO Data-In - Table 29 IDENTIFY DEVICE data
554            *
555            * Table 37 - Nominal Media Rotation Rate:
556            *
557            *  0000h        Rate not reported
558            *  0001h        Non-rotating media (e.g., solid state device)
559            *  0002h-0400h  Reserved
560            *  0401h-FFFEh  Nominal media rotation rate in rotations per minute (rpm) (e.g., 7200 rpm = 1C20h)
561            *  FFFFh        Reserved
562            */
563           word_217 = udisks_ata_identify_get_word (device->ata_identify_device_data, 217);
564           if (word_217 == 0x0001)
565             rate = 0;
566           else if (word_217 >= 0x0401 && word_217 <= 0xfffe)
567             rate = word_217;
568         }
569     }
570   udisks_drive_set_rotation_rate (iface, rate);
571 }
572 
573 static void
set_connection_bus(UDisksDrive * iface,UDisksLinuxDevice * device)574 set_connection_bus (UDisksDrive       *iface,
575                     UDisksLinuxDevice *device)
576 {
577   GUdevDevice *parent;
578   gboolean can_power_off = FALSE;
579   gchar *sibling_id = NULL;
580 
581   /* note: @device may vary - it can be any path for drive */
582 
583   udisks_drive_set_connection_bus (iface, "");
584   parent = g_udev_device_get_parent_with_subsystem (device->udev_device, "usb", "usb_interface");
585   if (parent != NULL)
586     {
587       /* TODO: should probably check that it's a storage interface */
588       udisks_drive_set_connection_bus (iface, "usb");
589       sibling_id = g_strdup (g_udev_device_get_sysfs_path (parent));
590       g_object_unref (parent);
591       can_power_off = TRUE;
592       goto out;
593     }
594 
595   parent = g_udev_device_get_parent_with_subsystem (device->udev_device, "firewire", NULL);
596   if (parent != NULL)
597     {
598       /* TODO: should probably check that it's a storage interface */
599       udisks_drive_set_connection_bus (iface, "ieee1394");
600       g_object_unref (parent);
601       goto out;
602     }
603 
604   if (g_str_has_prefix (g_udev_device_get_name (device->udev_device), "mmcblk"))
605     {
606       udisks_drive_set_connection_bus (iface, "sdio");
607       goto out;
608     }
609 
610  out:
611   /* Make it possible to override if a drive can be powered off */
612   if (g_udev_device_has_property (device->udev_device, "UDISKS_CAN_POWER_OFF"))
613     can_power_off = g_udev_device_get_property_as_boolean (device->udev_device, "UDISKS_CAN_POWER_OFF");
614   udisks_drive_set_can_power_off (iface, can_power_off);
615   udisks_drive_set_sibling_id (iface, sibling_id);
616   g_free (sibling_id);
617 }
618 
619 static void
set_media_time_detected(UDisksLinuxDrive * drive,UDisksLinuxDevice * device,gboolean is_pc_floppy_drive,gboolean coldplug)620 set_media_time_detected (UDisksLinuxDrive  *drive,
621                          UDisksLinuxDevice *device,
622                          gboolean           is_pc_floppy_drive,
623                          gboolean           coldplug)
624 {
625   UDisksDrive *iface = UDISKS_DRIVE (drive);
626   gint64 now;
627 
628   now = g_get_real_time ();
629 
630   /* First, initialize time_detected */
631   if (drive->time_detected == 0)
632     {
633       if (coldplug)
634         {
635           drive->time_detected = now - g_udev_device_get_usec_since_initialized (device->udev_device);
636         }
637       else
638         {
639           drive->time_detected = now;
640         }
641     }
642 
643   if (!g_udev_device_get_sysfs_attr_as_boolean (device->udev_device, "removable") || is_pc_floppy_drive)
644     {
645       drive->time_media_detected = drive->time_detected;
646     }
647   else
648     {
649       if (!udisks_drive_get_media_available (iface))
650         {
651           /* no media currently available */
652           drive->time_media_detected = 0;
653         }
654       else
655         {
656           /* media currently available */
657           if (drive->time_media_detected == 0)
658             {
659               if (coldplug)
660                 {
661                   drive->time_media_detected = drive->time_detected;
662                 }
663               else
664                 {
665                   drive->time_media_detected = now;
666                 }
667             }
668         }
669     }
670 
671   udisks_drive_set_time_detected (iface, drive->time_detected);
672   udisks_drive_set_time_media_detected (iface, drive->time_media_detected);
673 }
674 
675 static gchar *
append_fixedup_sd(const gchar * prefix,const gchar * device_name)676 append_fixedup_sd (const gchar *prefix,
677                    const gchar *device_name)
678 {
679   guint num_alphas, n;
680   GString *str;
681 
682   g_return_val_if_fail (g_str_has_prefix (device_name, "sd"), NULL);
683 
684   /* make sure sdaa comes after e.g. sdz by inserting up to 5 '_' characters
685    * between sd and a in sda...
686    */
687   for (num_alphas = 0; g_ascii_isalpha (device_name[num_alphas + 2]); num_alphas++)
688     ;
689   str = g_string_new (prefix);
690   g_string_append (str, "sd");
691   for (n = 0; n < 5 - num_alphas; n++)
692     g_string_append_c (str, '_');
693 
694   g_string_append (str, device_name + 2);
695 
696   return g_string_free (str, FALSE);
697 }
698 
699 /**
700  * udisks_linux_drive_update:
701  * @drive: A #UDisksLinuxDrive.
702  * @object: The enclosing #UDisksLinuxDriveObject instance.
703  *
704  * Updates the interface.
705  *
706  * Returns: %TRUE if configuration has changed, %FALSE otherwise.
707  */
708 gboolean
udisks_linux_drive_update(UDisksLinuxDrive * drive,UDisksLinuxDriveObject * object)709 udisks_linux_drive_update (UDisksLinuxDrive       *drive,
710                            UDisksLinuxDriveObject *object)
711 {
712   gboolean ret = FALSE;
713   UDisksDrive *iface = UDISKS_DRIVE (drive);
714   UDisksLinuxDevice *device = NULL;
715   const gchar *serial = NULL;
716   guint64 size;
717   gboolean media_available;
718   gboolean media_change_detected;
719   gboolean is_pc_floppy_drive = FALSE;
720   gboolean removable_hint = FALSE;
721   UDisksDaemon *daemon;
722   UDisksLinuxProvider *provider;
723   gboolean coldplug = FALSE;
724   const gchar *seat;
725 
726   if (object == NULL)
727       goto out;
728 
729   device = udisks_linux_drive_object_get_device (object, TRUE /* get_hw */);
730   if (device == NULL)
731     goto out;
732 
733   daemon = udisks_linux_drive_object_get_daemon (object);
734   provider = udisks_daemon_get_linux_provider (daemon);
735   coldplug = udisks_linux_provider_get_coldplug (provider);
736 
737   if (g_udev_device_get_property_as_boolean (device->udev_device, "ID_DRIVE_FLOPPY") ||
738       g_str_has_prefix (g_udev_device_get_name (device->udev_device), "fd"))
739     is_pc_floppy_drive = TRUE;
740 
741   /* this is the _almost_ the same for both ATA and SCSI devices (cf. udev's ata_id and scsi_id)
742    * but we special case since there are subtle differences...
743    */
744   if (g_udev_device_get_property_as_boolean (device->udev_device, "ID_ATA"))
745     {
746       const gchar *model;
747 
748       model = g_udev_device_get_property (device->udev_device, "ID_MODEL_ENC");
749       if (model != NULL)
750         {
751           gchar *s;
752           s = udisks_decode_udev_string (model,
753                                          g_udev_device_get_property (device->udev_device, "ID_MODEL"));
754           g_strstrip (s);
755           udisks_drive_set_model (iface, s);
756           g_free (s);
757         }
758 
759       udisks_drive_set_vendor (iface, "");
760       udisks_drive_set_revision (iface, g_udev_device_get_property (device->udev_device, "ID_REVISION"));
761       udisks_drive_set_wwn (iface, g_udev_device_get_property (device->udev_device, "ID_WWN_WITH_EXTENSION"));
762     }
763   else if (g_udev_device_get_property_as_boolean (device->udev_device, "ID_SCSI"))
764     {
765       const gchar *vendor;
766       const gchar *model;
767 
768       vendor = g_udev_device_get_property (device->udev_device, "ID_VENDOR_ENC");
769       if (vendor != NULL)
770         {
771           gchar *s;
772           s = udisks_decode_udev_string (vendor,
773                                          g_udev_device_get_property (device->udev_device, "ID_VENDOR"));
774           g_strstrip (s);
775           udisks_drive_set_vendor (iface, s);
776           g_free (s);
777         }
778 
779       model = g_udev_device_get_property (device->udev_device, "ID_MODEL_ENC");
780       if (model != NULL)
781         {
782           gchar *s;
783           s = udisks_decode_udev_string (model,
784                                          g_udev_device_get_property (device->udev_device, "ID_MODEL"));
785           g_strstrip (s);
786           udisks_drive_set_model (iface, s);
787           g_free (s);
788         }
789 
790       udisks_drive_set_revision (iface, g_udev_device_get_property (device->udev_device, "ID_REVISION"));
791       serial = g_udev_device_get_property (device->udev_device, "ID_SCSI_SERIAL");
792       udisks_drive_set_wwn (iface, g_udev_device_get_property (device->udev_device, "ID_WWN_WITH_EXTENSION"));
793     }
794   else if (g_str_has_prefix (g_udev_device_get_name (device->udev_device), "mmcblk"))
795     {
796       /* sigh, mmc is non-standard and using ID_NAME instead of ID_MODEL.. */
797       udisks_drive_set_model (iface, g_udev_device_get_property (device->udev_device, "ID_NAME"));
798       /* TODO:
799        *  - lookup Vendor from manfid and oemid in sysfs
800        *  - lookup Revision from fwrev and hwrev in sysfs
801        */
802     }
803   else
804     {
805       const gchar *vendor;
806       const gchar *model;
807       const gchar *name;
808 
809       name = g_udev_device_get_name (device->udev_device);
810 
811       /* generic fallback... */
812       vendor = g_udev_device_get_property (device->udev_device, "ID_VENDOR_ENC");
813       if (vendor != NULL)
814         {
815           gchar *s;
816           s = udisks_decode_udev_string (vendor,
817                                          g_udev_device_get_property (device->udev_device, "ID_VENDOR"));
818           g_strstrip (s);
819           udisks_drive_set_vendor (iface, s);
820           g_free (s);
821         }
822       else
823         {
824           vendor = g_udev_device_get_property (device->udev_device, "ID_VENDOR");
825           if (vendor != NULL)
826             {
827               udisks_drive_set_vendor (iface, vendor);
828             }
829           /* workaround for missing ID_VENDOR for floppy drives */
830           else if (is_pc_floppy_drive)
831             {
832               udisks_drive_set_vendor (iface, "");
833             }
834           /* workaround for missing ID_VENDOR on virtio-blk */
835           else if (g_str_has_prefix (name, "vd"))
836             {
837               /* TODO: could lookup the vendor sysfs attr on the virtio object */
838               udisks_drive_set_vendor (iface, "");
839             }
840         }
841 
842       model = g_udev_device_get_property (device->udev_device, "ID_MODEL_ENC");
843       if (model != NULL)
844         {
845           gchar *s;
846           s = udisks_decode_udev_string (model,
847                                          g_udev_device_get_property (device->udev_device, "ID_MODEL"));
848           g_strstrip (s);
849           udisks_drive_set_model (iface, s);
850           g_free (s);
851         }
852       else
853         {
854           model = g_udev_device_get_property (device->udev_device, "ID_MODEL");
855           if (model != NULL)
856             {
857               udisks_drive_set_model (iface, model);
858             }
859           /* workaround for missing ID_MODEL for floppy drives */
860           else if (g_str_has_prefix (name, "fd"))
861             {
862               udisks_drive_set_model (iface, "Floppy Drive");
863             }
864           /* workaround for missing ID_MODEL on virtio-blk */
865           else if (g_str_has_prefix (name, "vd"))
866             {
867               udisks_drive_set_model (iface, "VirtIO Disk");
868             }
869         }
870 
871       udisks_drive_set_revision (iface, g_udev_device_get_property (device->udev_device, "ID_REVISION"));
872       if (g_udev_device_has_property (device->udev_device, "ID_WWN_WITH_EXTENSION"))
873         udisks_drive_set_wwn (iface, g_udev_device_get_property (device->udev_device, "ID_WWN_WITH_EXTENSION"));
874       else
875         udisks_drive_set_wwn (iface, g_udev_device_get_property (device->udev_device, "ID_WWN"));
876     }
877 
878   /* try to find a serial number if we don't have one yet */
879   if (serial == NULL)
880     serial = g_udev_device_get_property (device->udev_device, "ID_SERIAL_SHORT");
881   if (serial == NULL)
882     serial = g_udev_device_get_property (device->udev_device, "ID_SERIAL");
883   udisks_drive_set_serial (iface, serial);
884 
885   /* common bits go here */
886   size = udisks_daemon_util_block_get_size (device->udev_device,
887                                             &media_available,
888                                             &media_change_detected);
889   udisks_drive_set_size (iface, size);
890   udisks_drive_set_media_available (iface, media_available);
891   udisks_drive_set_media_change_detected (iface, media_change_detected);
892   set_media (iface, device, is_pc_floppy_drive);
893   set_rotation_rate (iface, device);
894   set_connection_bus (iface, device);
895 
896   if (udisks_drive_get_media_removable (iface) ||
897       g_strcmp0 (udisks_drive_get_connection_bus (iface), "usb") == 0 ||
898       g_strcmp0 (udisks_drive_get_connection_bus (iface), "ieee1394") == 0)
899     removable_hint = TRUE;
900   udisks_drive_set_removable (iface, removable_hint);
901 
902   seat = g_udev_device_get_property (device->udev_device, "ID_SEAT");
903   /* assume seat0 if not set */
904   if (seat == NULL || strlen (seat) == 0)
905     seat = "seat0";
906   udisks_drive_set_seat (iface, seat);
907 
908   set_media_time_detected (drive, device, is_pc_floppy_drive, coldplug);
909 
910   /* calculate sort-key  */
911   if (drive->sort_key == NULL)
912     {
913       if (coldplug)
914         {
915           const gchar *device_name;
916           /* TODO: adjust device_name for better sort order (so e.g. sdaa comes after sdz) */
917           device_name = g_udev_device_get_name (device->udev_device);
918           if (udisks_drive_get_removable (iface))
919             {
920               /* make sure fd* BEFORE sr* BEFORE sd* */
921               if (g_str_has_prefix (device_name, "fd"))
922                 {
923                   drive->sort_key = g_strdup_printf ("00coldplug/10removable/%s", device_name);
924                 }
925               else if (g_str_has_prefix (device_name, "sr"))
926                 {
927                   drive->sort_key = g_strdup_printf ("00coldplug/11removable/%s", device_name);
928                 }
929               else if (g_str_has_prefix (device_name, "sd"))
930                 {
931                   drive->sort_key = append_fixedup_sd ("00coldplug/12removable/", device_name);
932                 }
933               else
934                 {
935                   drive->sort_key = g_strdup_printf ("00coldplug/12removable/%s", device_name);
936                 }
937             }
938           else
939             {
940               if (g_str_has_prefix (device_name, "sd"))
941                 drive->sort_key = append_fixedup_sd ("00coldplug/00fixed/", device_name);
942               else
943                 drive->sort_key = g_strdup_printf ("00coldplug/00fixed/%s", device_name);
944             }
945         }
946       else
947         {
948           drive->sort_key = g_strdup_printf ("01hotplug/%" G_GINT64_FORMAT, drive->time_detected);
949         }
950       udisks_drive_set_sort_key (iface, drive->sort_key);
951     }
952 
953   set_id (iface);
954 
955   ret = update_configuration (drive, object);
956 
957  out:
958   g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (drive));
959   if (device != NULL)
960     g_clear_object (&device);
961 
962   return ret;
963 }
964 
965 /* ---------------------------------------------------------------------------------------------------- */
966 
967 static gboolean
handle_eject(UDisksDrive * _drive,GDBusMethodInvocation * invocation,GVariant * options)968 handle_eject (UDisksDrive           *_drive,
969               GDBusMethodInvocation *invocation,
970               GVariant              *options)
971 {
972   UDisksLinuxDrive *drive = UDISKS_LINUX_DRIVE (_drive);
973   UDisksLinuxDriveObject *object;
974   UDisksLinuxBlockObject *block_object = NULL;
975   UDisksBlock *block = NULL;
976   UDisksDaemon *daemon = NULL;
977   const gchar *action_id;
978   const gchar *message;
979   gchar *error_message = NULL;
980   GError *error = NULL;
981   gchar *escaped_device = NULL;
982   uid_t caller_uid;
983 
984   object = udisks_daemon_util_dup_object (drive, &error);
985   if (object == NULL)
986     {
987       g_dbus_method_invocation_take_error (invocation, error);
988       goto out;
989     }
990 
991   daemon = udisks_linux_drive_object_get_daemon (object);
992   block_object = udisks_linux_drive_object_get_block (object, FALSE);
993   if (block_object == NULL)
994     {
995       g_dbus_method_invocation_return_error (invocation,
996                                              UDISKS_ERROR,
997                                              UDISKS_ERROR_FAILED,
998                                              "Unable to find block device for drive");
999       goto out;
1000     }
1001   block = udisks_object_peek_block (UDISKS_OBJECT (block_object));
1002 
1003   /* refuse to eject if drive appears to be in use */
1004   if (!udisks_linux_drive_object_is_not_in_use (object, NULL, &error))
1005     {
1006       g_prefix_error (&error, "Cannot eject drive in use: ");
1007       g_dbus_method_invocation_take_error (invocation, error);
1008       goto out;
1009     }
1010 
1011   error = NULL;
1012   if (!udisks_daemon_util_get_caller_uid_sync (daemon,
1013                                                invocation,
1014                                                NULL /* GCancellable */,
1015                                                &caller_uid,
1016                                                &error))
1017     {
1018       g_dbus_method_invocation_return_gerror (invocation, error);
1019       g_clear_error (&error);
1020       goto out;
1021     }
1022 
1023   /* Translators: Shown in authentication dialog when the user
1024    * requests ejecting media from a drive.
1025    *
1026    * Do not translate $(drive), it's a placeholder and
1027    * will be replaced by the name of the drive/device in question
1028    */
1029   message = N_("Authentication is required to eject $(drive)");
1030   action_id = "org.freedesktop.udisks2.eject-media";
1031   if (udisks_block_get_hint_system (block))
1032     {
1033       action_id = "org.freedesktop.udisks2.eject-media-system";
1034     }
1035   else if (!udisks_daemon_util_on_user_seat (daemon, UDISKS_OBJECT (object), caller_uid))
1036     {
1037       action_id = "org.freedesktop.udisks2.eject-media-other-seat";
1038     }
1039 
1040   /* Check that the user is actually authorized */
1041   if (!udisks_daemon_util_check_authorization_sync (daemon,
1042                                                     UDISKS_OBJECT (block_object),
1043                                                     action_id,
1044                                                     options,
1045                                                     message,
1046                                                     invocation))
1047     goto out;
1048 
1049   escaped_device = g_shell_quote (udisks_block_get_device (block));
1050 
1051   if (!udisks_daemon_launch_spawned_job_sync (daemon,
1052                                               UDISKS_OBJECT (object),
1053                                               "drive-eject", caller_uid,
1054                                               NULL, /* GCancellable */
1055                                               0,    /* uid_t run_as_uid */
1056                                               0,    /* uid_t run_as_euid */
1057                                               NULL, /* gint *out_status */
1058                                               &error_message,
1059                                               NULL,  /* input_string */
1060                                               "eject %s",
1061                                               escaped_device))
1062     {
1063       g_dbus_method_invocation_return_error (invocation,
1064                                              UDISKS_ERROR,
1065                                              UDISKS_ERROR_FAILED,
1066                                              "Error ejecting %s: %s",
1067                                              udisks_block_get_device (block),
1068                                              error_message);
1069       goto out;
1070     }
1071 
1072   udisks_drive_complete_eject (UDISKS_DRIVE (drive), invocation);
1073 
1074  out:
1075   g_free (escaped_device);
1076   g_clear_object (&block_object);
1077   g_free (error_message);
1078   g_clear_object (&object);
1079   return TRUE; /* returning TRUE means that we handled the method invocation */
1080 }
1081 
1082 /* ---------------------------------------------------------------------------------------------------- */
1083 
1084 static gboolean
handle_set_configuration(UDisksDrive * _drive,GDBusMethodInvocation * invocation,GVariant * configuration,GVariant * options)1085 handle_set_configuration (UDisksDrive           *_drive,
1086                           GDBusMethodInvocation *invocation,
1087                           GVariant              *configuration,
1088                           GVariant              *options)
1089 {
1090   UDisksLinuxDrive *drive = UDISKS_LINUX_DRIVE (_drive);
1091   UDisksDaemon *daemon;
1092   UDisksLinuxDriveObject *object;
1093   const gchar *action_id;
1094   const gchar *message;
1095   GKeyFile *key_file = NULL;
1096   GError *error = NULL;
1097   gchar *path = NULL;
1098   gchar *data = NULL;
1099   gsize data_len;
1100   guint n;
1101 
1102   object = udisks_daemon_util_dup_object (drive, &error);
1103   if (object == NULL)
1104     {
1105       g_dbus_method_invocation_take_error (invocation, error);
1106       goto out;
1107     }
1108 
1109   daemon = udisks_linux_drive_object_get_daemon (object);
1110 
1111   /* Translators: Shown in authentication dialog when the user
1112    * changes settings for a drive.
1113    *
1114    * Do not translate $(drive), it's a placeholder and will be
1115    * replaced by the name of the drive/device in question
1116    */
1117   message = N_("Authentication is required to configure settings for $(drive)");
1118   action_id = "org.freedesktop.udisks2.modify-drive-settings";
1119 
1120   /* Check that the user is actually authorized */
1121   if (!udisks_daemon_util_check_authorization_sync (daemon,
1122                                                     UDISKS_OBJECT (object),
1123                                                     action_id,
1124                                                     options,
1125                                                     message,
1126                                                     invocation))
1127     goto out;
1128 
1129   path = configuration_get_path (drive, daemon);
1130   if (path == NULL)
1131     {
1132       g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
1133                                              "Drive has no persistent unique id");
1134       goto out;
1135     }
1136 
1137   key_file = g_key_file_new ();
1138   if (!g_key_file_load_from_file (key_file,
1139                                   path,
1140                                   G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
1141                                   &error))
1142     {
1143       if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
1144         {
1145           g_dbus_method_invocation_take_error (invocation, error);
1146           goto out;
1147         }
1148       /* not a problem, just create a new file */
1149       g_key_file_set_comment (key_file,
1150                               NULL, /* group_name */
1151                               NULL, /* key */
1152                               " See udisks(8) for the format of this file.",
1153                               NULL);
1154       g_clear_error (&error);
1155     }
1156 
1157   for (n = 0; n < G_N_ELEMENTS (drive_configuration_mapping); n++)
1158     {
1159       const VariantKeyfileMapping *mapping = &drive_configuration_mapping[n];
1160       GVariant *value = NULL;
1161 
1162       value = g_variant_lookup_value (configuration, mapping->asv_key, mapping->type);
1163       if (value == NULL)
1164         {
1165           g_key_file_remove_key (key_file, mapping->group, mapping->key, NULL);
1166         }
1167       else
1168         {
1169           if (g_variant_type_equal (mapping->type, G_VARIANT_TYPE_INT32))
1170             {
1171               g_key_file_set_integer (key_file, mapping->group, mapping->key, g_variant_get_int32 (value));
1172             }
1173           else if (g_variant_type_equal (mapping->type, G_VARIANT_TYPE_BOOLEAN))
1174             {
1175               g_key_file_set_boolean (key_file, mapping->group, mapping->key, g_variant_get_boolean (value));
1176             }
1177           else
1178             {
1179               g_assert_not_reached ();
1180             }
1181         }
1182     }
1183 
1184   data = g_key_file_to_data (key_file, &data_len, NULL);
1185 
1186   if (!udisks_daemon_util_file_set_contents (path,
1187                                              data,
1188                                              data_len,
1189                                              0600, /* mode to use if non-existent */
1190                                              &error))
1191     {
1192       g_dbus_method_invocation_take_error (invocation, error);
1193       goto out;
1194     }
1195 
1196 
1197   udisks_drive_complete_set_configuration (UDISKS_DRIVE (drive), invocation);
1198 
1199  out:
1200   g_free (data);
1201   g_free (path);
1202   g_clear_object (&object);
1203   if (key_file != NULL)
1204     g_key_file_free (key_file);
1205   return TRUE; /* returning TRUE means that we handled the method invocation */
1206 }
1207 
1208 /* ---------------------------------------------------------------------------------------------------- */
1209 
1210 /* TODO: move to udisksscsi.[ch] similar what we do for ATA with udisksata.[ch] */
1211 
1212 static gboolean
send_scsi_command_sync(gint fd,guint8 * cdb,gsize cdb_len,GError ** error)1213 send_scsi_command_sync (gint      fd,
1214                         guint8   *cdb,
1215                         gsize     cdb_len,
1216                         GError  **error)
1217 {
1218   struct sg_io_v4 io_v4;
1219   uint8_t sense[32];
1220   gboolean ret = FALSE;
1221   gint rc;
1222   gint timeout_msec = 30000; /* 30 seconds */
1223 
1224   g_return_val_if_fail (fd != -1, FALSE);
1225   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1226 
1227   /* See http://sg.danny.cz/sg/sg_io.html and http://www.tldp.org/HOWTO/SCSI-Generic-HOWTO/index.html
1228    * for detailed information about how the SG_IO ioctl work
1229    */
1230 
1231   memset (sense, 0, sizeof (sense));
1232   memset (&io_v4, 0, sizeof (io_v4));
1233   io_v4.guard = 'Q';
1234   io_v4.protocol = BSG_PROTOCOL_SCSI;
1235   io_v4.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD;
1236   io_v4.request_len = cdb_len;
1237   io_v4.request = (uintptr_t) cdb;
1238   io_v4.max_response_len = sizeof (sense);
1239   io_v4.response = (uintptr_t) sense;
1240   io_v4.timeout = timeout_msec;
1241 
1242   rc = ioctl (fd, SG_IO, &io_v4);
1243   if (rc != 0)
1244     {
1245       /* could be that the driver doesn't do version 4, try version 3 */
1246       if (errno == EINVAL)
1247         {
1248           struct sg_io_hdr io_hdr;
1249           memset (&io_hdr, 0, sizeof (struct sg_io_hdr));
1250           io_hdr.interface_id = 'S';
1251           io_hdr.cmdp = (unsigned char*) cdb;
1252           io_hdr.cmd_len = cdb_len;
1253           io_hdr.dxfer_direction = SG_DXFER_NONE;
1254           io_hdr.sbp = sense;
1255           io_hdr.mx_sb_len = sizeof (sense);
1256           io_hdr.timeout = timeout_msec;
1257 
1258           rc = ioctl (fd, SG_IO, &io_hdr);
1259           if (rc != 0)
1260             {
1261               g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
1262                            "SGIO v3 ioctl failed (v4 not supported): %m");
1263               goto out;
1264             }
1265           else
1266             {
1267               if (!(io_hdr.status == 0 &&
1268                     io_hdr.host_status == 0 &&
1269                     io_hdr.driver_status == 0))
1270                 {
1271                   g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1272                                "Non-GOOD SCSI status from SGIO v3 ioctl: "
1273                                "status=%d host_status=%d driver_status=%d",
1274                                io_hdr.status,
1275                                io_hdr.host_status,
1276                                io_hdr.driver_status);
1277                   goto out;
1278                 }
1279             }
1280         }
1281       else
1282         {
1283           g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
1284                        "SGIO v4 ioctl failed: %m");
1285           goto out;
1286         }
1287     }
1288   else
1289     {
1290       if (!(io_v4.device_status == 0 &&
1291             io_v4.transport_status == 0 &&
1292             io_v4.driver_status == 0))
1293         {
1294           g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1295                        "Non-GOOD SCSI status from SGIO v4 ioctl: "
1296                        "device_status=%u transport_status=%u driver_status=%u",
1297                        io_v4.device_status,
1298                        io_v4.transport_status,
1299                        io_v4.driver_status);
1300           goto out;
1301         }
1302     }
1303 
1304   ret = TRUE;
1305 
1306  out:
1307   return ret;
1308 }
1309 
1310 static gboolean
send_scsi_synchronize_cache_command_sync(gint fd,GError ** error)1311 send_scsi_synchronize_cache_command_sync (gint      fd,
1312                                           GError  **error)
1313 {
1314   uint8_t cdb[10];
1315 
1316   /* SBC3 (SCSI Block Commands), 5.18 SYNCHRONIZE CACHE (10) command
1317    */
1318   memset (cdb, 0, sizeof cdb);
1319   cdb[0] = 0x35;                        /* OPERATION CODE: SYNCHRONIZE CACHE (10) */
1320 
1321   return send_scsi_command_sync (fd, cdb, sizeof cdb, error);
1322 }
1323 
1324 static gboolean
send_scsi_start_stop_unit_command_sync(gint fd,GError ** error)1325 send_scsi_start_stop_unit_command_sync (gint      fd,
1326                                         GError  **error)
1327 {
1328   uint8_t cdb[6];
1329 
1330   /* SBC3 (SCSI Block Commands), 5.20 START STOP UNIT command
1331    */
1332   memset (cdb, 0, sizeof cdb);
1333   cdb[0] = 0x1b;                        /* OPERATION CODE: START STOP UNIT */
1334 
1335   return send_scsi_command_sync (fd, cdb, sizeof cdb, error);
1336 }
1337 
1338 /* ---------------------------------------------------------------------------------------------------- */
1339 
1340 static gboolean
handle_power_off(UDisksDrive * _drive,GDBusMethodInvocation * invocation,GVariant * options)1341 handle_power_off (UDisksDrive           *_drive,
1342                   GDBusMethodInvocation *invocation,
1343                   GVariant              *options)
1344 {
1345   UDisksLinuxDrive *drive = UDISKS_LINUX_DRIVE (_drive);
1346   UDisksLinuxDevice *device = NULL;
1347   gchar *remove_path = NULL;
1348   FILE *f;
1349   GUdevDevice *usb_device = NULL;
1350   UDisksLinuxDriveObject *object;
1351   UDisksLinuxBlockObject *block_object = NULL;
1352   GList *blocks_to_sync = NULL;
1353   UDisksBlock *block = NULL;
1354   UDisksDaemon *daemon = NULL;
1355   const gchar *action_id;
1356   const gchar *message;
1357   gchar *error_message = NULL;
1358   GError *error = NULL;
1359   uid_t caller_uid;
1360   GList *sibling_objects = NULL, *l;
1361   gint fd = -1;
1362 
1363   object = udisks_daemon_util_dup_object (drive, &error);
1364   if (object == NULL)
1365     {
1366       g_dbus_method_invocation_take_error (invocation, error);
1367       goto out;
1368     }
1369 
1370   daemon = udisks_linux_drive_object_get_daemon (object);
1371   block_object = udisks_linux_drive_object_get_block (object, FALSE);
1372   if (block_object == NULL)
1373     {
1374       g_dbus_method_invocation_return_error (invocation,
1375                                              UDISKS_ERROR,
1376                                              UDISKS_ERROR_FAILED,
1377                                              "Unable to find block device for drive");
1378       goto out;
1379     }
1380   block = udisks_object_peek_block (UDISKS_OBJECT (block_object));
1381   blocks_to_sync = g_list_prepend (blocks_to_sync, g_object_ref (block));
1382 
1383   sibling_objects = udisks_linux_drive_object_get_siblings (object);
1384 
1385   /* refuse if drive - or one of its siblings - appears to be in use */
1386   if (!udisks_linux_drive_object_is_not_in_use (object, NULL, &error))
1387     {
1388       g_prefix_error (&error, "The drive in use: ");
1389       g_dbus_method_invocation_take_error (invocation, error);
1390       goto out;
1391     }
1392   for (l = sibling_objects; l != NULL; l = l->next)
1393     {
1394       UDisksLinuxDriveObject *sibling_object = UDISKS_LINUX_DRIVE_OBJECT (l->data);
1395       UDisksLinuxBlockObject *sibling_block_object;
1396 
1397       if (!udisks_linux_drive_object_is_not_in_use (sibling_object, NULL, &error))
1398         {
1399           g_prefix_error (&error, "A drive that is part of the same device is in use: ");
1400           g_dbus_method_invocation_take_error (invocation, error);
1401           goto out;
1402         }
1403 
1404       sibling_block_object = udisks_linux_drive_object_get_block (sibling_object, FALSE); /* get_hw */
1405       if (sibling_block_object != NULL)
1406         {
1407           UDisksBlock *sibling_block = udisks_object_get_block (UDISKS_OBJECT (sibling_block_object));
1408           if (sibling_block != NULL)
1409             blocks_to_sync = g_list_prepend (blocks_to_sync, sibling_block);
1410           g_object_unref (sibling_block_object);
1411         }
1412     }
1413 
1414   error = NULL;
1415   if (!udisks_daemon_util_get_caller_uid_sync (daemon,
1416                                                invocation,
1417                                                NULL /* GCancellable */,
1418                                                &caller_uid,
1419                                                &error))
1420     {
1421       g_dbus_method_invocation_return_gerror (invocation, error);
1422       g_clear_error (&error);
1423       goto out;
1424     }
1425 
1426   /* Translators: Shown in authentication dialog when the user
1427    * requests ejecting media from a drive.
1428    *
1429    * Do not translate $(drive), it's a placeholder and
1430    * will be replaced by the name of the drive/device in question
1431    */
1432   message = N_("Authentication is required to power off $(drive)");
1433   action_id = "org.freedesktop.udisks2.power-off-drive";
1434   if (udisks_block_get_hint_system (block))
1435     {
1436       action_id = "org.freedesktop.udisks2.power-off-drive-system";
1437     }
1438   else if (!udisks_daemon_util_on_user_seat (daemon, UDISKS_OBJECT (object), caller_uid))
1439     {
1440       action_id = "org.freedesktop.udisks2.power-off-drive-other-seat";
1441     }
1442 
1443   /* Check that the user is actually authorized */
1444   if (!udisks_daemon_util_check_authorization_sync (daemon,
1445                                                     UDISKS_OBJECT (block_object),
1446                                                     action_id,
1447                                                     options,
1448                                                     message,
1449                                                     invocation))
1450     goto out;
1451 
1452   /* sync all block devices */
1453   for (l = blocks_to_sync; l != NULL ; l = l->next)
1454     {
1455       UDisksBlock *block_to_sync = UDISKS_BLOCK (l->data);
1456       const gchar *device_file;
1457       gint device_fd;
1458       device_file = udisks_block_get_device (block_to_sync);
1459       device_fd = open (device_file, O_RDONLY|O_NONBLOCK|O_EXCL);
1460       if (device_fd == -1)
1461         {
1462           g_dbus_method_invocation_return_error (invocation,
1463                                                  UDISKS_ERROR,
1464                                                  UDISKS_ERROR_FAILED,
1465                                                  "Error opening %s for fsync: %m",
1466                                                  device_file);
1467           goto out;
1468         }
1469       if (fsync (device_fd) != 0)
1470         {
1471           g_dbus_method_invocation_return_error (invocation,
1472                                                  UDISKS_ERROR,
1473                                                  UDISKS_ERROR_FAILED,
1474                                                  "Error syncing  %s: %m",
1475                                                  device_file);
1476           close (device_fd);
1477           goto out;
1478         }
1479       if (close (device_fd) != 0)
1480         {
1481           g_dbus_method_invocation_return_error (invocation,
1482                                                  UDISKS_ERROR,
1483                                                  UDISKS_ERROR_FAILED,
1484                                                  "Error closing %s (after syncing): %m",
1485                                                  device_file);
1486           goto out;
1487         }
1488     }
1489 
1490   /* Send the "SCSI SYNCHRONIZE CACHE" and then the "SCSI START STOP
1491    * UNIT" command to request that the unit be stopped. Don't treat
1492    * failures as fatal. In fact some USB-attached hard-disks fails
1493    * with one or both of these commands, probably due to the SCSI/SATA
1494    * translation layer.
1495    */
1496   fd = open (udisks_block_get_device (block), O_RDONLY|O_NONBLOCK|O_EXCL);
1497   if (fd == -1)
1498     {
1499       g_dbus_method_invocation_return_error (invocation,
1500                                              UDISKS_ERROR,
1501                                              UDISKS_ERROR_FAILED,
1502                                              "Error opening %s for cache synchronize: %m",
1503                                              udisks_block_get_device (block));
1504       goto out;
1505     }
1506 
1507   if (!send_scsi_synchronize_cache_command_sync (fd, &error))
1508     {
1509       udisks_warning ("Ignoring SCSI command SYNCHRONIZE CACHE failure (%s) on %s",
1510                       error->message,
1511                       udisks_block_get_device (block));
1512       g_clear_error (&error);
1513     }
1514   else
1515     {
1516       udisks_notice ("Successfully sent SCSI command SYNCHRONIZE CACHE to %s",
1517                      udisks_block_get_device (block));
1518     }
1519 
1520   if (!send_scsi_start_stop_unit_command_sync (fd, &error))
1521     {
1522       udisks_warning ("Ignoring SCSI command START STOP UNIT failure (%s) on %s",
1523                       error->message,
1524                       udisks_block_get_device (block));
1525       g_clear_error (&error);
1526     }
1527   else
1528     {
1529       udisks_notice ("Successfully sent SCSI command START STOP UNIT to %s",
1530                      udisks_block_get_device (block));
1531     }
1532 
1533   if (close (fd) != 0)
1534     {
1535       g_dbus_method_invocation_return_error (invocation,
1536                                              UDISKS_ERROR,
1537                                              UDISKS_ERROR_FAILED,
1538                                              "Error closing %s: %m",
1539                                              udisks_block_get_device (block));
1540       goto out;
1541     }
1542   fd = -1;
1543 
1544   device = udisks_linux_drive_object_get_device (object, TRUE /* get_hw */);
1545   if (device == NULL)
1546     {
1547       g_dbus_method_invocation_return_error (invocation,
1548                                              UDISKS_ERROR,
1549                                              UDISKS_ERROR_FAILED,
1550                                              "No device");
1551       goto out;
1552     }
1553   usb_device = g_udev_device_get_parent_with_subsystem (device->udev_device, "usb", "usb_device");
1554   if (usb_device == NULL)
1555     {
1556       g_dbus_method_invocation_return_error (invocation,
1557                                              UDISKS_ERROR,
1558                                              UDISKS_ERROR_FAILED,
1559                                              "No usb device");
1560       goto out;
1561     }
1562 
1563   /* http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=commit;h=253e05724f9230910344357b1142ad8642ff9f5a */
1564   remove_path = g_strdup_printf ("%s/remove", g_udev_device_get_sysfs_path (usb_device));
1565   f = fopen (remove_path, "w");
1566   if (f == NULL)
1567     {
1568       g_dbus_method_invocation_return_error (invocation,
1569                                              UDISKS_ERROR,
1570                                              UDISKS_ERROR_FAILED,
1571                                              "Error opening %s for device removal: %m",
1572                                              remove_path);
1573       goto out;
1574     }
1575   else
1576     {
1577       gchar contents[1] = {'1'};
1578       if (fwrite (contents, 1, 1, f) != 1)
1579         {
1580           g_dbus_method_invocation_return_error (invocation,
1581                                                  UDISKS_ERROR,
1582                                                  UDISKS_ERROR_FAILED,
1583                                                  "Error writing to sysfs file %s: %m",
1584                                                  remove_path);
1585           fclose (f);
1586           goto out;
1587         }
1588     }
1589   fclose (f);
1590   udisks_notice ("Powered off %s - successfully wrote to sysfs path %s",
1591                  udisks_block_get_device (block),
1592                  remove_path);
1593 
1594   udisks_drive_complete_power_off (UDISKS_DRIVE (drive), invocation);
1595 
1596  out:
1597   if (fd != -1)
1598     {
1599       if (close (fd) != 0)
1600         {
1601           udisks_warning ("Error closing device: %m");
1602         }
1603     }
1604   g_list_free_full (blocks_to_sync, g_object_unref);
1605   g_list_free_full (sibling_objects, g_object_unref);
1606   g_free (remove_path);
1607   g_clear_object (&usb_device);
1608   g_clear_object (&device);
1609   g_clear_object (&block_object);
1610   g_free (error_message);
1611   g_clear_object (&object);
1612   return TRUE; /* returning TRUE means that we handled the method invocation */
1613 }
1614 
1615 /* ---------------------------------------------------------------------------------------------------- */
1616 
1617 static void
drive_iface_init(UDisksDriveIface * iface)1618 drive_iface_init (UDisksDriveIface *iface)
1619 {
1620   iface->handle_eject = handle_eject;
1621   iface->handle_set_configuration = handle_set_configuration;
1622   iface->handle_power_off = handle_power_off;
1623 }
1624