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