Merge lp:~psusi/ubuntu/utopic/udisks2/fix-standby into lp:ubuntu/utopic/udisks2

Proposed by Phillip Susi
Status: Merged
Merge reported by: Martin Pitt
Merged at revision: not available
Proposed branch: lp:~psusi/ubuntu/utopic/udisks2/fix-standby
Merge into: lp:ubuntu/utopic/udisks2
Diff against target: 5388 lines (+5238/-50)
8 files modified
.pc/0001-Use-internal-pm-check-for-smart-poll.patch/src/udiskslinuxdriveata.c (+2458/-0)
.pc/0002-Fix-standby-timers.patch/src/udiskslinuxdriveata.c (+2461/-0)
.pc/applied-patches (+2/-0)
debian/changelog (+11/-0)
debian/patches/0001-Use-internal-pm-check-for-smart-poll.patch (+162/-0)
debian/patches/0002-Fix-standby-timers.patch (+67/-0)
debian/patches/series (+2/-0)
src/udiskslinuxdriveata.c (+75/-50)
To merge this branch: bzr merge lp:~psusi/ubuntu/utopic/udisks2/fix-standby
Reviewer Review Type Date Requested Status
Martin Pitt Approve
Review via email: mp+223170@code.launchpad.net

Description of the change

Fix disk standby timers. Requesting review by Martin Pitt, as I'm not sure if he wants to upload this to debian first and keep ubuntu in sync.

To post a comment you must log in.
Revision history for this message
Martin Pitt (pitti) wrote :

Thanks Phillip! I pushed the 0001-Use-internal-pm-check-for-smart-poll.patch upstream with a tiny code formatting fix: http://cgit.freedesktop.org/udisks/commit/?id=10fb36a8d6

The second patch required some more cleanup:
 - The format string was wrong, %lud will parse an unsigned followed by a literal 'd'. Fixed.
 - fscanf() ought to verify that it could actually read the two values (this detected the error above)
 - Changed variable names to underscore separation
 - Moved variable declarations to the top of the block.
 - I used a statically allocated stat_path like in the rest of the code.

Pushed upstream now: http://cgit.freedesktop.org/udisks/commit/?id=65c901f1

I'd like to get this in via Debian and packaging latest git upstream, so I won't directly merge this MP. But I'll close it anyway, as in some sense it is merged now :-)

Thanks!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory '.pc/0001-Use-internal-pm-check-for-smart-poll.patch'
2=== added file '.pc/0001-Use-internal-pm-check-for-smart-poll.patch/.timestamp'
3=== added directory '.pc/0001-Use-internal-pm-check-for-smart-poll.patch/src'
4=== added file '.pc/0001-Use-internal-pm-check-for-smart-poll.patch/src/udiskslinuxdriveata.c'
5--- .pc/0001-Use-internal-pm-check-for-smart-poll.patch/src/udiskslinuxdriveata.c 1970-01-01 00:00:00 +0000
6+++ .pc/0001-Use-internal-pm-check-for-smart-poll.patch/src/udiskslinuxdriveata.c 2014-06-15 14:18:36 +0000
7@@ -0,0 +1,2458 @@
8+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
9+ *
10+ * Copyright (C) 2007-2010 David Zeuthen <zeuthen@gmail.com>
11+ *
12+ * This program is free software; you can redistribute it and/or modify
13+ * it under the terms of the GNU General Public License as published by
14+ * the Free Software Foundation; either version 2 of the License, or
15+ * (at your option) any later version.
16+ *
17+ * This program is distributed in the hope that it will be useful,
18+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
19+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+ * GNU General Public License for more details.
21+ *
22+ * You should have received a copy of the GNU General Public License
23+ * along with this program; if not, write to the Free Software
24+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25+ *
26+ */
27+
28+#include "config.h"
29+#include <glib/gi18n-lib.h>
30+
31+#include <sys/types.h>
32+#include <sys/stat.h>
33+#include <sys/ioctl.h>
34+#include <fcntl.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+#include <errno.h>
45+
46+#include <atasmart.h>
47+
48+#include "udiskslogging.h"
49+#include "udiskslinuxprovider.h"
50+#include "udiskslinuxdriveobject.h"
51+#include "udiskslinuxdriveata.h"
52+#include "udiskslinuxblockobject.h"
53+#include "udisksdaemon.h"
54+#include "udisksdaemonutil.h"
55+#include "udisksbasejob.h"
56+#include "udiskssimplejob.h"
57+#include "udisksthreadedjob.h"
58+#include "udisksata.h"
59+#include "udiskslinuxdevice.h"
60+
61+/**
62+ * SECTION:udiskslinuxdriveata
63+ * @title: UDisksLinuxDriveAta
64+ * @short_description: Linux implementation of #UDisksDriveAta
65+ *
66+ * This type provides an implementation of the #UDisksDriveAta
67+ * interface on Linux.
68+ */
69+
70+typedef struct _UDisksLinuxDriveAtaClass UDisksLinuxDriveAtaClass;
71+
72+/**
73+ * UDisksLinuxDriveAta:
74+ *
75+ * The #UDisksLinuxDriveAta structure contains only private data and should
76+ * only be accessed using the provided API.
77+ */
78+struct _UDisksLinuxDriveAta
79+{
80+ UDisksDriveAtaSkeleton parent_instance;
81+
82+ gboolean smart_is_from_blob;
83+ guint64 smart_updated;
84+ gboolean smart_failing;
85+ gdouble smart_temperature;
86+ guint64 smart_power_on_seconds;
87+ gint smart_num_attributes_failing;
88+ gint smart_num_attributes_failed_in_the_past;
89+ gint64 smart_num_bad_sectors;
90+ const gchar *smart_selftest_status;
91+ gint smart_selftest_percent_remaining;
92+
93+ GVariant *smart_attributes;
94+
95+ UDisksThreadedJob *selftest_job;
96+
97+ gboolean secure_erase_in_progress;
98+};
99+
100+struct _UDisksLinuxDriveAtaClass
101+{
102+ UDisksDriveAtaSkeletonClass parent_class;
103+};
104+
105+static void drive_ata_iface_init (UDisksDriveAtaIface *iface);
106+
107+G_DEFINE_TYPE_WITH_CODE (UDisksLinuxDriveAta, udisks_linux_drive_ata, UDISKS_TYPE_DRIVE_ATA_SKELETON,
108+ G_IMPLEMENT_INTERFACE (UDISKS_TYPE_DRIVE_ATA, drive_ata_iface_init));
109+
110+G_LOCK_DEFINE_STATIC (object_lock);
111+
112+/* ---------------------------------------------------------------------------------------------------- */
113+
114+static void
115+udisks_linux_drive_ata_finalize (GObject *object)
116+{
117+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (object);
118+
119+ if (drive->smart_attributes != NULL)
120+ g_variant_unref (drive->smart_attributes);
121+
122+ if (G_OBJECT_CLASS (udisks_linux_drive_ata_parent_class)->finalize != NULL)
123+ G_OBJECT_CLASS (udisks_linux_drive_ata_parent_class)->finalize (object);
124+}
125+
126+
127+static void
128+udisks_linux_drive_ata_init (UDisksLinuxDriveAta *drive)
129+{
130+ g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (drive),
131+ G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD);
132+}
133+
134+static void
135+udisks_linux_drive_ata_class_init (UDisksLinuxDriveAtaClass *klass)
136+{
137+ GObjectClass *gobject_class;
138+
139+ gobject_class = G_OBJECT_CLASS (klass);
140+ gobject_class->finalize = udisks_linux_drive_ata_finalize;
141+}
142+
143+/**
144+ * udisks_linux_drive_ata_new:
145+ *
146+ * Creates a new #UDisksLinuxDriveAta instance.
147+ *
148+ * Returns: A new #UDisksLinuxDriveAta. Free with g_object_unref().
149+ */
150+UDisksDriveAta *
151+udisks_linux_drive_ata_new (void)
152+{
153+ return UDISKS_DRIVE_ATA (g_object_new (UDISKS_TYPE_LINUX_DRIVE_ATA,
154+ NULL));
155+}
156+
157+/* ---------------------------------------------------------------------------------------------------- */
158+
159+/* may be called from *any* thread when the SMART data has been updated */
160+static void
161+update_smart (UDisksLinuxDriveAta *drive,
162+ UDisksLinuxDevice *device)
163+{
164+ gboolean supported = FALSE;
165+ gboolean enabled = FALSE;
166+ guint64 updated = 0;
167+ gboolean failing = FALSE;
168+ gdouble temperature = 0.0;
169+ guint64 power_on_seconds = 0;
170+ const gchar *selftest_status = NULL;
171+ gint selftest_percent_remaining = -1;
172+ gint num_attributes_failing = -1;
173+ gint num_attributes_failed_in_the_past = -1;
174+ gint64 num_bad_sectors = 1;
175+ guint16 word_82 = 0;
176+ guint16 word_85 = 0;
177+
178+ /* ATA8: 7.16 IDENTIFY DEVICE - ECh, PIO Data-In - Table 29 IDENTIFY DEVICE data */
179+ word_82 = udisks_ata_identify_get_word (device->ata_identify_device_data, 82);
180+ word_85 = udisks_ata_identify_get_word (device->ata_identify_device_data, 85);
181+ supported = word_82 & (1<<0);
182+ enabled = word_85 & (1<<0);
183+
184+ G_LOCK (object_lock);
185+ if ((drive->smart_is_from_blob || enabled) && drive->smart_updated > 0)
186+ {
187+ if (drive->smart_is_from_blob)
188+ supported = enabled = TRUE;
189+ updated = drive->smart_updated;
190+ failing = drive->smart_failing;
191+ temperature = drive->smart_temperature;
192+ power_on_seconds = drive->smart_power_on_seconds;
193+ num_attributes_failing = drive->smart_num_attributes_failing;
194+ num_attributes_failed_in_the_past = drive->smart_num_attributes_failed_in_the_past;
195+ num_bad_sectors = drive->smart_num_bad_sectors;
196+ selftest_status = drive->smart_selftest_status;
197+ selftest_percent_remaining = drive->smart_selftest_percent_remaining;
198+ }
199+ G_UNLOCK (object_lock);
200+
201+ if (selftest_status == NULL)
202+ selftest_status = "";
203+
204+ g_object_freeze_notify (G_OBJECT (drive));
205+ udisks_drive_ata_set_smart_supported (UDISKS_DRIVE_ATA (drive), supported);
206+ udisks_drive_ata_set_smart_enabled (UDISKS_DRIVE_ATA (drive), enabled);
207+ udisks_drive_ata_set_smart_updated (UDISKS_DRIVE_ATA (drive), updated);
208+ udisks_drive_ata_set_smart_failing (UDISKS_DRIVE_ATA (drive), failing);
209+ udisks_drive_ata_set_smart_temperature (UDISKS_DRIVE_ATA (drive), temperature);
210+ udisks_drive_ata_set_smart_power_on_seconds (UDISKS_DRIVE_ATA (drive), power_on_seconds);
211+ udisks_drive_ata_set_smart_num_attributes_failing (UDISKS_DRIVE_ATA (drive), num_attributes_failing);
212+ udisks_drive_ata_set_smart_num_attributes_failed_in_the_past (UDISKS_DRIVE_ATA (drive), num_attributes_failed_in_the_past);
213+ udisks_drive_ata_set_smart_num_bad_sectors (UDISKS_DRIVE_ATA (drive), num_bad_sectors);
214+ udisks_drive_ata_set_smart_selftest_status (UDISKS_DRIVE_ATA (drive), selftest_status);
215+ udisks_drive_ata_set_smart_selftest_percent_remaining (UDISKS_DRIVE_ATA (drive), selftest_percent_remaining);
216+ g_object_thaw_notify (G_OBJECT (drive));
217+}
218+
219+/* ---------------------------------------------------------------------------------------------------- */
220+
221+static void
222+update_pm (UDisksLinuxDriveAta *drive,
223+ UDisksLinuxDevice *device)
224+{
225+ gboolean pm_supported = FALSE;
226+ gboolean pm_enabled = FALSE;
227+ gboolean apm_supported = FALSE;
228+ gboolean apm_enabled = FALSE;
229+ gboolean aam_supported = FALSE;
230+ gboolean aam_enabled = FALSE;
231+ gboolean write_cache_supported = FALSE;
232+ gboolean write_cache_enabled = FALSE;
233+ gint aam_vendor_recommended_value = 0;
234+ guint16 word_82 = 0;
235+ guint16 word_83 = 0;
236+ guint16 word_85 = 0;
237+ guint16 word_86 = 0;
238+ guint16 word_94 = 0;
239+
240+ /* ATA8: 7.16 IDENTIFY DEVICE - ECh, PIO Data-In - Table 29 IDENTIFY DEVICE data */
241+ word_82 = udisks_ata_identify_get_word (device->ata_identify_device_data, 82);
242+ word_83 = udisks_ata_identify_get_word (device->ata_identify_device_data, 83);
243+ word_85 = udisks_ata_identify_get_word (device->ata_identify_device_data, 85);
244+ word_86 = udisks_ata_identify_get_word (device->ata_identify_device_data, 86);
245+ word_94 = udisks_ata_identify_get_word (device->ata_identify_device_data, 94);
246+
247+ pm_supported = word_82 & (1<<3);
248+ pm_enabled = word_85 & (1<<3);
249+ apm_supported = word_83 & (1<<3);
250+ apm_enabled = word_86 & (1<<3);
251+ aam_supported = word_83 & (1<<9);
252+ aam_enabled = word_86 & (1<<9);
253+ if (aam_supported)
254+ aam_vendor_recommended_value = (word_94 >> 8);
255+ write_cache_supported = word_82 & (1<<5);
256+ write_cache_enabled = word_85 & (1<<5);
257+
258+ g_object_freeze_notify (G_OBJECT (drive));
259+ udisks_drive_ata_set_pm_supported (UDISKS_DRIVE_ATA (drive), !!pm_supported);
260+ udisks_drive_ata_set_pm_enabled (UDISKS_DRIVE_ATA (drive), !!pm_enabled);
261+ udisks_drive_ata_set_apm_supported (UDISKS_DRIVE_ATA (drive), !!apm_supported);
262+ udisks_drive_ata_set_apm_enabled (UDISKS_DRIVE_ATA (drive), !!apm_enabled);
263+ udisks_drive_ata_set_aam_supported (UDISKS_DRIVE_ATA (drive), !!aam_supported);
264+ udisks_drive_ata_set_aam_enabled (UDISKS_DRIVE_ATA (drive), !!aam_enabled);
265+ udisks_drive_ata_set_aam_vendor_recommended_value (UDISKS_DRIVE_ATA (drive), aam_vendor_recommended_value);
266+ udisks_drive_ata_set_write_cache_supported (UDISKS_DRIVE_ATA (drive), !!write_cache_supported);
267+ udisks_drive_ata_set_write_cache_enabled (UDISKS_DRIVE_ATA (drive), !!write_cache_enabled);
268+ g_object_thaw_notify (G_OBJECT (drive));
269+}
270+
271+/* ---------------------------------------------------------------------------------------------------- */
272+
273+static void
274+update_security (UDisksLinuxDriveAta *drive,
275+ UDisksLinuxDevice *device)
276+{
277+ gint erase_unit = 0;
278+ gint enhanced_erase_unit = 0;
279+ gboolean frozen = FALSE;
280+ gboolean security_supported = FALSE;
281+ G_GNUC_UNUSED gboolean security_enabled = FALSE;
282+ guint16 word_82 = 0;
283+ guint16 word_85 = 0;
284+ guint16 word_89 = 0;
285+ guint16 word_90 = 0;
286+ guint16 word_128 = 0;
287+
288+ /* ATA8: 7.16 IDENTIFY DEVICE - ECh, PIO Data-In - Table 29 IDENTIFY DEVICE data */
289+ word_82 = udisks_ata_identify_get_word (device->ata_identify_device_data, 82);
290+ word_85 = udisks_ata_identify_get_word (device->ata_identify_device_data, 85);
291+ word_89 = udisks_ata_identify_get_word (device->ata_identify_device_data, 89);
292+ word_90 = udisks_ata_identify_get_word (device->ata_identify_device_data, 90);
293+ word_128 = udisks_ata_identify_get_word (device->ata_identify_device_data, 128);
294+
295+ security_supported = word_82 & (1<<1);
296+ security_enabled = word_85 & (1<<1);
297+ if (security_supported)
298+ {
299+ erase_unit = (word_89 & 0xff) * 2;
300+ enhanced_erase_unit = (word_90 & 0xff) * 2;
301+ }
302+ frozen = word_128 & (1<<3);
303+
304+ g_object_freeze_notify (G_OBJECT (drive));
305+ /* TODO: export Security{Supported,Enabled} properties
306+ udisks_drive_ata_set_security_supported (UDISKS_DRIVE_ATA (drive), !!security_supported);
307+ udisks_drive_ata_set_security_enabled (UDISKS_DRIVE_ATA (drive), !!security_enabled);
308+ */
309+ udisks_drive_ata_set_security_erase_unit_minutes (UDISKS_DRIVE_ATA (drive), erase_unit);
310+ udisks_drive_ata_set_security_enhanced_erase_unit_minutes (UDISKS_DRIVE_ATA (drive), enhanced_erase_unit);
311+ udisks_drive_ata_set_security_frozen (UDISKS_DRIVE_ATA (drive), !!frozen);
312+ g_object_thaw_notify (G_OBJECT (drive));
313+}
314+
315+/* ---------------------------------------------------------------------------------------------------- */
316+
317+/**
318+ * udisks_linux_drive_ata_update:
319+ * @drive: A #UDisksLinuxDriveAta.
320+ * @object: The enclosing #UDisksLinuxDriveObject instance.
321+ *
322+ * Updates the interface.
323+ *
324+ * Returns: %TRUE if configuration has changed, %FALSE otherwise.
325+ */
326+gboolean
327+udisks_linux_drive_ata_update (UDisksLinuxDriveAta *drive,
328+ UDisksLinuxDriveObject *object)
329+{
330+ UDisksLinuxDevice *device
331+;
332+ device = udisks_linux_drive_object_get_device (object, TRUE /* get_hw */);
333+ if (device == NULL)
334+ goto out;
335+
336+ update_smart (drive, device);
337+ update_pm (drive, device);
338+ update_security (drive, device);
339+
340+ out:
341+ if (device != NULL)
342+ g_object_unref (device);
343+
344+ return FALSE;
345+}
346+
347+/* ---------------------------------------------------------------------------------------------------- */
348+
349+typedef struct
350+{
351+ GVariantBuilder builder;
352+ gint num_attributes_failing;
353+ gint num_attributes_failed_in_the_past;
354+} ParseData;
355+
356+static void
357+parse_attr_cb (SkDisk *d,
358+ const SkSmartAttributeParsedData *a,
359+ void *user_data)
360+{
361+ ParseData *data = user_data;
362+ gboolean failed = FALSE;
363+ gboolean failed_in_the_past = FALSE;
364+ gint current, worst, threshold;
365+
366+ current = a->current_value_valid ? a->current_value : -1;
367+ worst = a->worst_value_valid ? a->worst_value : -1;
368+ threshold = a->threshold_valid ? a->threshold : -1;
369+
370+ g_variant_builder_add (&data->builder,
371+ "(ysqiiixia{sv})",
372+ a->id,
373+ a->name,
374+ a->flags,
375+ current,
376+ worst,
377+ threshold,
378+ a->pretty_value, a->pretty_unit,
379+ NULL); /* expansion unused for now */
380+
381+ if (current > 0 && threshold > 0 && current <= threshold)
382+ failed = TRUE;
383+
384+ if (worst > 0 && threshold > 0 && worst <= threshold)
385+ failed_in_the_past = TRUE;
386+
387+ if (failed)
388+ data->num_attributes_failing += 1;
389+
390+ if (failed_in_the_past)
391+ data->num_attributes_failed_in_the_past += 1;
392+}
393+
394+static const gchar *
395+selftest_status_to_string (SkSmartSelfTestExecutionStatus status)
396+{
397+ const gchar *ret;
398+ switch (status)
399+ {
400+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_SUCCESS_OR_NEVER:
401+ ret = "success";
402+ break;
403+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_ABORTED:
404+ ret = "aborted";
405+ break;
406+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED:
407+ ret = "interrupted";
408+ break;
409+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_FATAL:
410+ ret = "fatal";
411+ break;
412+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_UNKNOWN:
413+ ret = "error_unknown";
414+ break;
415+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL:
416+ ret = "error_electrical";
417+ break;
418+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_SERVO:
419+ ret = "error_servo";
420+ break;
421+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ:
422+ ret = "error_read";
423+ break;
424+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_HANDLING:
425+ ret = "error_handling";
426+ break;
427+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS:
428+ ret = "inprogress";
429+ break;
430+ default:
431+ ret = "";
432+ break;
433+ }
434+ return ret;
435+}
436+
437+/**
438+ * udisks_linux_drive_ata_refresh_smart_sync:
439+ * @drive: The #UDisksLinuxDriveAta to refresh.
440+ * @nowakeup: If %TRUE, will not wake up the disk if asleep.
441+ * @simulate_path: If not %NULL, the path of a file with a libatasmart blob to use.
442+ * @cancellable: A #GCancellable or %NULL.
443+ * @error: Return location for error.
444+ *
445+ * Synchronously refreshes ATA S.M.A.R.T. data on @drive using one of
446+ * the physical drives associated with it. The calling thread is
447+ * blocked until the data has been obtained.
448+ *
449+ * If @nowake is %TRUE and the disk is in a sleep state this fails
450+ * with %UDISKS_ERROR_WOULD_WAKEUP.
451+ *
452+ * This may only be called if @drive has been associated with a
453+ * #UDisksLinuxDriveObject instance.
454+ *
455+ * This method may be called from any thread.
456+ *
457+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
458+ */
459+gboolean
460+udisks_linux_drive_ata_refresh_smart_sync (UDisksLinuxDriveAta *drive,
461+ gboolean nowakeup,
462+ const gchar *simulate_path,
463+ GCancellable *cancellable,
464+ GError **error)
465+{
466+ UDisksLinuxDriveObject *object;
467+ UDisksLinuxDevice *device = NULL;
468+ gboolean ret = FALSE;
469+ SkDisk *d = NULL;
470+ SkBool awake;
471+ SkBool good;
472+ uint64_t temp_mkelvin = 0;
473+ uint64_t power_on_msec = 0;
474+ uint64_t num_bad_sectors = 0;
475+ const SkSmartParsedData *data;
476+ ParseData parse_data;
477+
478+ object = udisks_daemon_util_dup_object (drive, error);
479+ if (object == NULL)
480+ goto out;
481+
482+ if (drive->secure_erase_in_progress)
483+ {
484+ g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_DEVICE_BUSY,
485+ "Secure erase in progress");
486+ goto out;
487+ }
488+
489+ device = udisks_linux_drive_object_get_device (object, TRUE /* get_hw */);
490+ g_assert (device != NULL);
491+
492+ /* TODO: use cancellable */
493+
494+ if (simulate_path != NULL)
495+ {
496+ gchar *blob;
497+ gsize blob_len;
498+
499+ if (!g_file_get_contents (simulate_path,
500+ &blob,
501+ &blob_len,
502+ error))
503+ {
504+ goto out;
505+ }
506+
507+ if (sk_disk_open (NULL, &d) != 0)
508+ {
509+ g_set_error (error,
510+ UDISKS_ERROR,
511+ UDISKS_ERROR_FAILED,
512+ "sk_disk_open: %m");
513+ goto out;
514+ }
515+
516+ if (sk_disk_set_blob (d, blob, blob_len) != 0)
517+ {
518+ g_set_error (error,
519+ UDISKS_ERROR,
520+ UDISKS_ERROR_FAILED,
521+ "sk_disk_set_blob: %m");
522+ g_free (blob);
523+ goto out;
524+ }
525+ g_free (blob);
526+ }
527+ else
528+ {
529+ if (sk_disk_open (g_udev_device_get_device_file (device->udev_device), &d) != 0)
530+ {
531+ g_set_error (error,
532+ UDISKS_ERROR,
533+ UDISKS_ERROR_FAILED,
534+ "sk_disk_open: %m");
535+ goto out;
536+ }
537+
538+ if (sk_disk_check_sleep_mode (d, &awake) != 0)
539+ {
540+ g_set_error (error,
541+ UDISKS_ERROR,
542+ UDISKS_ERROR_FAILED,
543+ "sk_disk_check_sleep_mode: %m");
544+ goto out;
545+ }
546+
547+ /* don't wake up disk unless specically asked to */
548+ if (nowakeup && !awake)
549+ {
550+ g_set_error (error,
551+ UDISKS_ERROR,
552+ UDISKS_ERROR_WOULD_WAKEUP,
553+ "Disk is in sleep mode and the nowakeup option was passed");
554+ goto out;
555+ }
556+ }
557+
558+ if (sk_disk_smart_read_data (d) != 0)
559+ {
560+ g_set_error (error,
561+ UDISKS_ERROR,
562+ UDISKS_ERROR_FAILED,
563+ "sk_disk_smart_read_data: %m");
564+ goto out;
565+ }
566+
567+ if (sk_disk_smart_status (d, &good) != 0)
568+ {
569+ g_set_error (error,
570+ UDISKS_ERROR,
571+ UDISKS_ERROR_FAILED,
572+ "sk_disk_smart_status: %m");
573+ goto out;
574+ }
575+
576+ if (sk_disk_smart_parse (d, &data) != 0)
577+ {
578+ g_set_error (error,
579+ UDISKS_ERROR,
580+ UDISKS_ERROR_FAILED,
581+ "sk_disk_smart_parse: %m");
582+ goto out;
583+ }
584+
585+ /* don't care if these are failing or not */
586+ sk_disk_smart_get_temperature (d, &temp_mkelvin);
587+ sk_disk_smart_get_power_on (d, &power_on_msec);
588+ sk_disk_smart_get_bad (d, &num_bad_sectors);
589+
590+ memset (&parse_data, 0, sizeof (ParseData));
591+ g_variant_builder_init (&parse_data.builder, G_VARIANT_TYPE ("a(ysqiiixia{sv})"));
592+ sk_disk_smart_parse_attributes (d, parse_attr_cb, &parse_data);
593+
594+ G_LOCK (object_lock);
595+ drive->smart_is_from_blob = (simulate_path != NULL);
596+ drive->smart_updated = time (NULL);
597+ drive->smart_failing = !good;
598+ drive->smart_temperature = temp_mkelvin / 1000.0;
599+ drive->smart_power_on_seconds = power_on_msec / 1000.0;
600+ drive->smart_num_attributes_failing = parse_data.num_attributes_failing;
601+ drive->smart_num_attributes_failed_in_the_past = parse_data.num_attributes_failed_in_the_past;
602+ drive->smart_num_bad_sectors = num_bad_sectors;
603+ drive->smart_selftest_status = selftest_status_to_string (data->self_test_execution_status);
604+ drive->smart_selftest_percent_remaining = data->self_test_execution_percent_remaining;
605+ if (drive->smart_attributes != NULL)
606+ g_variant_unref (drive->smart_attributes);
607+ drive->smart_attributes = g_variant_ref_sink (g_variant_builder_end (&parse_data.builder));
608+ G_UNLOCK (object_lock);
609+
610+ update_smart (drive, device);
611+
612+ ret = TRUE;
613+
614+ out:
615+ g_clear_object (&device);
616+ if (d != NULL)
617+ sk_disk_free (d);
618+ g_clear_object (&object);
619+ return ret;
620+}
621+
622+/* ---------------------------------------------------------------------------------------------------- */
623+
624+/**
625+ * udisks_linux_drive_ata_smart_selftest_sync:
626+ * @drive: A #UDisksLinuxDriveAta.
627+ * @type: The type of selftest to run.
628+ * @cancellable: (allow-none): A #GCancellable that can be used to cancel the operation or %NULL.
629+ * @error: Return location for error or %NULL.
630+ *
631+ * Starts (or aborts) a SMART self-test on @drive. Valid values for
632+ * @type includes 'short', 'extended', 'conveyance' and 'abort'.
633+ *
634+ * The calling thread is blocked while sending the command to the
635+ * drive but will return immediately after the drive acknowledges the
636+ * command.
637+ *
638+ * Returns: %TRUE if the operation succeed, %FALSE if @error is set.
639+ */
640+gboolean
641+udisks_linux_drive_ata_smart_selftest_sync (UDisksLinuxDriveAta *drive,
642+ const gchar *type,
643+ GCancellable *cancellable,
644+ GError **error)
645+{
646+ UDisksLinuxDriveObject *object;
647+ UDisksLinuxDevice *device;
648+ SkDisk *d = NULL;
649+ gboolean ret = FALSE;
650+ SkSmartSelfTest test;
651+
652+ object = udisks_daemon_util_dup_object (drive, error);
653+ if (object == NULL)
654+ goto out;
655+
656+ device = udisks_linux_drive_object_get_device (object, TRUE /* get_hw */);
657+ g_assert (device != NULL);
658+
659+ if (g_strcmp0 (type, "short") == 0)
660+ test = SK_SMART_SELF_TEST_SHORT;
661+ else if (g_strcmp0 (type, "extended") == 0)
662+ test = SK_SMART_SELF_TEST_EXTENDED;
663+ else if (g_strcmp0 (type, "conveyance") == 0)
664+ test = SK_SMART_SELF_TEST_CONVEYANCE;
665+ else if (g_strcmp0 (type, "abort") == 0)
666+ test = SK_SMART_SELF_TEST_ABORT;
667+ else
668+ {
669+ g_set_error (error,
670+ UDISKS_ERROR,
671+ UDISKS_ERROR_FAILED,
672+ "unknown type %s", type);
673+ goto out;
674+ }
675+
676+ if (sk_disk_open (g_udev_device_get_device_file (device->udev_device), &d) != 0)
677+ {
678+ g_set_error (error,
679+ UDISKS_ERROR,
680+ UDISKS_ERROR_FAILED,
681+ "sk_disk_open: %m");
682+ goto out;
683+ }
684+
685+ if (sk_disk_smart_self_test (d, test) != 0)
686+ {
687+ g_set_error (error,
688+ UDISKS_ERROR,
689+ UDISKS_ERROR_FAILED,
690+ "sk_disk_smart_self_test: %m");
691+ goto out;
692+ }
693+
694+ ret = TRUE;
695+
696+ out:
697+ g_clear_object (&device);
698+ if (d != NULL)
699+ sk_disk_free (d);
700+ g_clear_object (&object);
701+ return ret;
702+}
703+
704+/* ---------------------------------------------------------------------------------------------------- */
705+
706+static gboolean
707+handle_smart_update (UDisksDriveAta *_drive,
708+ GDBusMethodInvocation *invocation,
709+ GVariant *options)
710+{
711+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (_drive);
712+ UDisksLinuxDriveObject *object;
713+ UDisksLinuxBlockObject *block_object = NULL;
714+ UDisksDaemon *daemon;
715+ gboolean nowakeup = FALSE;
716+ const gchar *atasmart_blob = NULL;
717+ GError *error;
718+ const gchar *message;
719+ const gchar *action_id;
720+
721+ daemon = NULL;
722+
723+ error = NULL;
724+ object = udisks_daemon_util_dup_object (drive, &error);
725+ if (object == NULL)
726+ {
727+ g_dbus_method_invocation_take_error (invocation, error);
728+ goto out;
729+ }
730+
731+ daemon = udisks_linux_drive_object_get_daemon (object);
732+ block_object = udisks_linux_drive_object_get_block (object, TRUE);
733+ if (block_object == NULL)
734+ {
735+ g_dbus_method_invocation_return_error (invocation,
736+ UDISKS_ERROR,
737+ UDISKS_ERROR_FAILED,
738+ "Unable to find physical block device for drive");
739+ goto out;
740+ }
741+
742+ g_variant_lookup (options, "nowakeup", "b", &nowakeup);
743+ g_variant_lookup (options, "atasmart_blob", "s", &atasmart_blob);
744+
745+ /* Translators: Shown in authentication dialog when the user
746+ * refreshes SMART data from a disk.
747+ *
748+ * Do not translate $(drive), it's a placeholder and
749+ * will be replaced by the name of the drive/device in question
750+ */
751+ message = N_("Authentication is required to update SMART data from $(drive)");
752+ action_id = "org.freedesktop.udisks2.ata-smart-update";
753+
754+ if (atasmart_blob != NULL)
755+ {
756+ /* Translators: Shown in authentication dialog when the user
757+ * tries to simulate SMART data from a libatasmart blob.
758+ *
759+ * Do not translate $(drive), it's a placeholder and
760+ * will be replaced by the name of the drive/device in question
761+ */
762+ message = N_("Authentication is required to set SMART data from a blob on $(drive)");
763+ action_id = "org.freedesktop.udisks2.ata-smart-simulate";
764+ }
765+ else
766+ {
767+ if (!udisks_drive_ata_get_smart_supported (UDISKS_DRIVE_ATA (drive)))
768+ {
769+ g_dbus_method_invocation_return_error (invocation,
770+ UDISKS_ERROR,
771+ UDISKS_ERROR_FAILED,
772+ "SMART is not supported");
773+ goto out;
774+ }
775+
776+ if (!udisks_drive_ata_get_smart_enabled (UDISKS_DRIVE_ATA (drive)))
777+ {
778+ g_dbus_method_invocation_return_error (invocation,
779+ UDISKS_ERROR,
780+ UDISKS_ERROR_FAILED,
781+ "SMART is not enabled");
782+ goto out;
783+ }
784+ }
785+
786+ /* Check that the user is authorized */
787+ if (!udisks_daemon_util_check_authorization_sync (daemon,
788+ UDISKS_OBJECT (block_object),
789+ action_id,
790+ options,
791+ message,
792+ invocation))
793+ goto out;
794+
795+ error = NULL;
796+ if (!udisks_linux_drive_ata_refresh_smart_sync (drive,
797+ nowakeup,
798+ atasmart_blob,
799+ NULL, /* cancellable */
800+ &error))
801+ {
802+ udisks_warning ("Error updating ATA smart for %s: %s (%s, %d)",
803+ g_dbus_object_get_object_path (G_DBUS_OBJECT (object)),
804+ error->message, g_quark_to_string (error->domain), error->code);
805+ g_dbus_method_invocation_take_error (invocation, error);
806+ goto out;
807+ }
808+
809+ udisks_drive_ata_complete_smart_update (UDISKS_DRIVE_ATA (drive), invocation);
810+
811+ out:
812+ g_clear_object (&block_object);
813+ g_clear_object (&object);
814+ return TRUE; /* returning TRUE means that we handled the method invocation */
815+}
816+
817+/* ---------------------------------------------------------------------------------------------------- */
818+
819+static gboolean
820+handle_smart_get_attributes (UDisksDriveAta *_drive,
821+ GDBusMethodInvocation *invocation,
822+ GVariant *options)
823+{
824+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (_drive);
825+
826+ G_LOCK (object_lock);
827+ if (drive->smart_attributes == NULL)
828+ {
829+ g_dbus_method_invocation_return_error (invocation,
830+ UDISKS_ERROR,
831+ UDISKS_ERROR_FAILED,
832+ "SMART data not collected");
833+ }
834+ else
835+ {
836+ udisks_drive_ata_complete_smart_get_attributes (UDISKS_DRIVE_ATA (drive), invocation,
837+ drive->smart_attributes);
838+ }
839+ G_UNLOCK (object_lock);
840+
841+ return TRUE; /* returning TRUE means that we handled the method invocation */
842+}
843+
844+/* ---------------------------------------------------------------------------------------------------- */
845+
846+static gboolean
847+handle_smart_selftest_abort (UDisksDriveAta *_drive,
848+ GDBusMethodInvocation *invocation,
849+ GVariant *options)
850+{
851+ UDisksLinuxDriveObject *object;
852+ UDisksLinuxBlockObject *block_object;
853+ UDisksDaemon *daemon;
854+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (_drive);
855+ GError *error;
856+
857+ error = NULL;
858+ object = udisks_daemon_util_dup_object (drive, &error);
859+ if (object == NULL)
860+ {
861+ g_dbus_method_invocation_take_error (invocation, error);
862+ goto out;
863+ }
864+
865+ daemon = udisks_linux_drive_object_get_daemon (object);
866+ block_object = udisks_linux_drive_object_get_block (object, TRUE);
867+ if (block_object == NULL)
868+ {
869+ g_dbus_method_invocation_return_error (invocation,
870+ UDISKS_ERROR,
871+ UDISKS_ERROR_FAILED,
872+ "Unable to find physical block device for drive");
873+ goto out;
874+ }
875+
876+ if (!udisks_drive_ata_get_smart_supported (UDISKS_DRIVE_ATA (drive)) ||
877+ !udisks_drive_ata_get_smart_enabled (UDISKS_DRIVE_ATA (drive)))
878+ {
879+ g_dbus_method_invocation_return_error (invocation,
880+ UDISKS_ERROR,
881+ UDISKS_ERROR_FAILED,
882+ "SMART is not supported or enabled");
883+ goto out;
884+ }
885+
886+ if (!udisks_daemon_util_check_authorization_sync (daemon,
887+ UDISKS_OBJECT (block_object),
888+ "org.freedesktop.udisks2.ata-smart-selftest",
889+ options,
890+ /* Translators: Shown in authentication dialog when the user
891+ * aborts a running SMART self-test.
892+ *
893+ * Do not translate $(drive), it's a placeholder and
894+ * will be replaced by the name of the drive/device in question
895+ */
896+ N_("Authentication is required to abort a SMART self-test on $(drive)"),
897+ invocation))
898+ goto out;
899+
900+ error = NULL;
901+ if (!udisks_linux_drive_ata_smart_selftest_sync (drive,
902+ "abort",
903+ NULL, /* cancellable */
904+ &error))
905+ {
906+ udisks_warning ("Error aborting SMART selftest for %s: %s (%s, %d)",
907+ g_dbus_object_get_object_path (G_DBUS_OBJECT (object)),
908+ error->message, g_quark_to_string (error->domain), error->code);
909+ g_dbus_method_invocation_take_error (invocation, error);
910+ goto out;
911+ }
912+
913+ /* This wakes up the selftest thread */
914+ G_LOCK (object_lock);
915+ if (drive->selftest_job != NULL)
916+ {
917+ g_cancellable_cancel (udisks_base_job_get_cancellable (UDISKS_BASE_JOB (drive->selftest_job)));
918+ }
919+ G_UNLOCK (object_lock);
920+ /* TODO: wait for the selftest thread to terminate */
921+
922+ error = NULL;
923+ if (!udisks_linux_drive_ata_refresh_smart_sync (drive,
924+ FALSE, /* nowakeup */
925+ NULL, /* blob */
926+ NULL, /* cancellable */
927+ &error))
928+ {
929+ udisks_warning ("Error updating ATA smart for %s: %s (%s, %d)",
930+ g_dbus_object_get_object_path (G_DBUS_OBJECT (object)),
931+ error->message, g_quark_to_string (error->domain), error->code);
932+ g_dbus_method_invocation_take_error (invocation, error);
933+ goto out;
934+ }
935+
936+ udisks_drive_ata_complete_smart_selftest_abort (UDISKS_DRIVE_ATA (drive), invocation);
937+
938+ out:
939+ g_clear_object (&object);
940+ return TRUE; /* returning TRUE means that we handled the method invocation */
941+}
942+
943+/* ---------------------------------------------------------------------------------------------------- */
944+
945+static gboolean
946+selftest_job_func (UDisksThreadedJob *job,
947+ GCancellable *cancellable,
948+ gpointer user_data,
949+ GError **error)
950+{
951+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (user_data);
952+ UDisksLinuxDriveObject *object;
953+ gboolean ret = FALSE;
954+
955+ object = udisks_daemon_util_dup_object (drive, error);
956+ if (object == NULL)
957+ goto out;
958+
959+ udisks_job_set_progress_valid (UDISKS_JOB (job), TRUE);
960+ udisks_job_set_progress (UDISKS_JOB (job), 0.0);
961+
962+ while (TRUE)
963+ {
964+ gboolean still_in_progress;
965+ GPollFD poll_fd;
966+ gdouble progress;
967+
968+ if (!udisks_linux_drive_ata_refresh_smart_sync (drive,
969+ FALSE, /* nowakeup */
970+ NULL, /* blob */
971+ NULL, /* cancellable */
972+ error))
973+ {
974+ udisks_warning ("Error updating ATA smart for %s while polling during self-test: %s (%s, %d)",
975+ g_dbus_object_get_object_path (G_DBUS_OBJECT (object)),
976+ (*error)->message, g_quark_to_string ((*error)->domain), (*error)->code);
977+ goto out;
978+ }
979+
980+ /* TODO: set estimation properties etc. on the Job object */
981+
982+ G_LOCK (object_lock);
983+ still_in_progress = (g_strcmp0 (drive->smart_selftest_status, "inprogress") == 0);
984+ progress = (100.0 - drive->smart_selftest_percent_remaining) / 100.0;
985+ G_UNLOCK (object_lock);
986+ if (!still_in_progress)
987+ {
988+ ret = TRUE;
989+ goto out;
990+ }
991+
992+ if (progress < 0.0)
993+ progress = 0.0;
994+ if (progress > 1.0)
995+ progress = 1.0;
996+ udisks_job_set_progress (UDISKS_JOB (job), progress);
997+
998+ /* Sleep for 30 seconds or until we're cancelled */
999+ if (g_cancellable_make_pollfd (cancellable, &poll_fd))
1000+ {
1001+ gint poll_ret;
1002+ do
1003+ {
1004+ poll_ret = g_poll (&poll_fd, 1, 30 * 1000);
1005+ }
1006+ while (poll_ret == -1 && errno == EINTR);
1007+ g_cancellable_release_fd (cancellable);
1008+ }
1009+ else
1010+ {
1011+ g_set_error (error,
1012+ UDISKS_ERROR,
1013+ UDISKS_ERROR_FAILED,
1014+ "Error creating pollfd for cancellable");
1015+ goto out;
1016+ }
1017+
1018+ /* Check if we're cancelled */
1019+ if (g_cancellable_is_cancelled (cancellable))
1020+ {
1021+ GError *c_error;
1022+
1023+ g_set_error (error,
1024+ UDISKS_ERROR,
1025+ UDISKS_ERROR_CANCELLED,
1026+ "Self-test was cancelled");
1027+
1028+ /* OK, cancelled ... still need to a) abort the test; and b) update the status */
1029+ c_error = NULL;
1030+ if (!udisks_linux_drive_ata_smart_selftest_sync (drive,
1031+ "abort",
1032+ NULL, /* cancellable */
1033+ &c_error))
1034+ {
1035+ udisks_warning ("Error aborting SMART selftest for %s on cancel path: %s (%s, %d)",
1036+ g_dbus_object_get_object_path (G_DBUS_OBJECT (object)),
1037+ c_error->message, g_quark_to_string (c_error->domain), c_error->code);
1038+ g_clear_error (&c_error);
1039+ }
1040+ if (!udisks_linux_drive_ata_refresh_smart_sync (drive,
1041+ FALSE, /* nowakeup */
1042+ NULL, /* blob */
1043+ NULL, /* cancellable */
1044+ &c_error))
1045+ {
1046+ udisks_warning ("Error updating ATA smart for %s on cancel path: %s (%s, %d)",
1047+ g_dbus_object_get_object_path (G_DBUS_OBJECT (object)),
1048+ c_error->message, g_quark_to_string (c_error->domain), c_error->code);
1049+ g_clear_error (&c_error);
1050+ }
1051+ goto out;
1052+ }
1053+ }
1054+
1055+ ret = TRUE;
1056+
1057+ out:
1058+ /* terminate the job */
1059+ G_LOCK (object_lock);
1060+ drive->selftest_job = NULL;
1061+ G_UNLOCK (object_lock);
1062+ g_clear_object (&object);
1063+ return ret;
1064+}
1065+
1066+
1067+static gboolean
1068+handle_smart_selftest_start (UDisksDriveAta *_drive,
1069+ GDBusMethodInvocation *invocation,
1070+ const gchar *type,
1071+ GVariant *options)
1072+{
1073+ UDisksLinuxDriveObject *object;
1074+ UDisksLinuxBlockObject *block_object;
1075+ UDisksDaemon *daemon;
1076+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (_drive);
1077+ uid_t caller_uid;
1078+ gid_t caller_gid;
1079+ GError *error;
1080+
1081+ error = NULL;
1082+ object = udisks_daemon_util_dup_object (drive, &error);
1083+ if (object == NULL)
1084+ {
1085+ g_dbus_method_invocation_take_error (invocation, error);
1086+ goto out;
1087+ }
1088+
1089+ daemon = udisks_linux_drive_object_get_daemon (object);
1090+ block_object = udisks_linux_drive_object_get_block (object, TRUE);
1091+ if (block_object == NULL)
1092+ {
1093+ g_dbus_method_invocation_return_error (invocation,
1094+ UDISKS_ERROR,
1095+ UDISKS_ERROR_FAILED,
1096+ "Unable to find physical block device for drive");
1097+ goto out;
1098+ }
1099+
1100+ if (!udisks_drive_ata_get_smart_supported (UDISKS_DRIVE_ATA (drive)) ||
1101+ !udisks_drive_ata_get_smart_enabled (UDISKS_DRIVE_ATA (drive)))
1102+ {
1103+ g_dbus_method_invocation_return_error (invocation,
1104+ UDISKS_ERROR,
1105+ UDISKS_ERROR_FAILED,
1106+ "SMART is not supported or enabled");
1107+ goto out;
1108+ }
1109+
1110+ error = NULL;
1111+ if (!udisks_daemon_util_get_caller_uid_sync (daemon,
1112+ invocation,
1113+ NULL /* GCancellable */,
1114+ &caller_uid,
1115+ &caller_gid,
1116+ NULL,
1117+ &error))
1118+ {
1119+ g_dbus_method_invocation_return_gerror (invocation, error);
1120+ g_error_free (error);
1121+ goto out;
1122+ }
1123+
1124+ G_LOCK (object_lock);
1125+ if (drive->selftest_job != NULL)
1126+ {
1127+ g_dbus_method_invocation_return_error (invocation,
1128+ UDISKS_ERROR,
1129+ UDISKS_ERROR_FAILED,
1130+ "There is already SMART self-test running");
1131+ G_UNLOCK (object_lock);
1132+ goto out;
1133+ }
1134+ G_UNLOCK (object_lock);
1135+
1136+ if (!udisks_daemon_util_check_authorization_sync (daemon,
1137+ UDISKS_OBJECT (block_object),
1138+ "org.freedesktop.udisks2.ata-smart-selftest",
1139+ options,
1140+ /* Translators: Shown in authentication dialog when the user
1141+ * initiates a SMART self-test.
1142+ *
1143+ * Do not translate $(drive), it's a placeholder and
1144+ * will be replaced by the name of the drive/device in question
1145+ */
1146+ N_("Authentication is required to start a SMART self-test on $(drive)"),
1147+ invocation))
1148+ goto out;
1149+
1150+ error = NULL;
1151+ if (!udisks_linux_drive_ata_smart_selftest_sync (drive,
1152+ type,
1153+ NULL, /* cancellable */
1154+ &error))
1155+ {
1156+ udisks_warning ("Error starting SMART selftest for %s: %s (%s, %d)",
1157+ g_dbus_object_get_object_path (G_DBUS_OBJECT (object)),
1158+ error->message, g_quark_to_string (error->domain), error->code);
1159+ g_dbus_method_invocation_take_error (invocation, error);
1160+ goto out;
1161+ }
1162+
1163+ G_LOCK (object_lock);
1164+ if (drive->selftest_job == NULL)
1165+ {
1166+ drive->selftest_job = UDISKS_THREADED_JOB (udisks_daemon_launch_threaded_job (daemon,
1167+ UDISKS_OBJECT (object),
1168+ "ata-smart-selftest", caller_uid,
1169+ selftest_job_func,
1170+ g_object_ref (drive),
1171+ g_object_unref,
1172+ NULL)); /* GCancellable */
1173+ }
1174+ G_UNLOCK (object_lock);
1175+
1176+ udisks_drive_ata_complete_smart_selftest_start (UDISKS_DRIVE_ATA (drive), invocation);
1177+
1178+ out:
1179+ g_clear_object (&object);
1180+ return TRUE; /* returning TRUE means that we handled the method invocation */
1181+}
1182+
1183+/* ---------------------------------------------------------------------------------------------------- */
1184+
1185+static gboolean
1186+handle_pm_get_state (UDisksDriveAta *_drive,
1187+ GDBusMethodInvocation *invocation,
1188+ GVariant *options)
1189+{
1190+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (_drive);
1191+ UDisksLinuxDriveObject *object = NULL;
1192+ UDisksDaemon *daemon;
1193+ UDisksLinuxDevice *device = NULL;
1194+ gint fd = -1;
1195+ GError *error = NULL;
1196+ const gchar *message;
1197+ const gchar *action_id;
1198+
1199+ object = udisks_daemon_util_dup_object (drive, &error);
1200+ if (object == NULL)
1201+ {
1202+ g_dbus_method_invocation_take_error (invocation, error);
1203+ goto out;
1204+ }
1205+
1206+ daemon = udisks_linux_drive_object_get_daemon (object);
1207+
1208+ if (!udisks_drive_ata_get_pm_supported (UDISKS_DRIVE_ATA (drive)) ||
1209+ !udisks_drive_ata_get_pm_enabled (UDISKS_DRIVE_ATA (drive)))
1210+ {
1211+ g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
1212+ "PM is not supported or enabled");
1213+ goto out;
1214+ }
1215+
1216+ /* If a secure erase is in progress, the CHECK POWER command would be queued
1217+ * until the erase has been completed (can easily take hours). So just return
1218+ * 0xff which is active/idle...
1219+ */
1220+ if (drive->secure_erase_in_progress)
1221+ {
1222+ g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_DEVICE_BUSY,
1223+ "A secure erase is in progress");
1224+ goto out;
1225+ }
1226+
1227+ /* Translators: Shown in authentication dialog when the user
1228+ * requests the power state of a drive.
1229+ *
1230+ * Do not translate $(drive), it's a placeholder and
1231+ * will be replaced by the name of the drive/device in question
1232+ */
1233+ message = N_("Authentication is required to check power state for $(drive)");
1234+ action_id = "org.freedesktop.udisks2.ata-check-power";
1235+
1236+ /* TODO: maybe not check with polkit if this is OK (consider gnome-disks(1) polling all drives every few seconds) */
1237+
1238+ /* Check that the user is authorized */
1239+ if (!udisks_daemon_util_check_authorization_sync (daemon,
1240+ UDISKS_OBJECT (object),
1241+ action_id,
1242+ options,
1243+ message,
1244+ invocation))
1245+ goto out;
1246+
1247+ device = udisks_linux_drive_object_get_device (object, TRUE /* get_hw */);
1248+ if (device == NULL)
1249+ {
1250+ g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
1251+ "No udev device");
1252+ goto out;
1253+ }
1254+
1255+ fd = open (g_udev_device_get_device_file (device->udev_device), O_RDONLY|O_NONBLOCK);
1256+ if (fd == -1)
1257+ {
1258+ g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
1259+ "Error opening device file %s: %m",
1260+ g_udev_device_get_device_file (device->udev_device));
1261+ goto out;
1262+ }
1263+
1264+ {
1265+ /* ATA8: 7.8 CHECK POWER MODE - E5h, Non-Data */
1266+ UDisksAtaCommandInput input = {.command = 0xe5};
1267+ UDisksAtaCommandOutput output = {0};
1268+ if (!udisks_ata_send_command_sync (fd,
1269+ -1,
1270+ UDISKS_ATA_COMMAND_PROTOCOL_NONE,
1271+ &input,
1272+ &output,
1273+ &error))
1274+ {
1275+ g_prefix_error (&error, "Error sending ATA command CHECK POWER MODE: ");
1276+ g_dbus_method_invocation_take_error (invocation, error);
1277+ goto out;
1278+ }
1279+ /* count field is used for the state, see ATA8: table 102 */
1280+ udisks_drive_ata_complete_pm_get_state (_drive, invocation, output.count);
1281+ }
1282+
1283+ out:
1284+ if (fd != -1)
1285+ close (fd);
1286+ g_clear_object (&device);
1287+ g_clear_object (&object);
1288+ return TRUE; /* returning TRUE means that we handled the method invocation */
1289+}
1290+
1291+/* ---------------------------------------------------------------------------------------------------- */
1292+
1293+static gboolean
1294+handle_pm_standby (UDisksDriveAta *_drive,
1295+ GDBusMethodInvocation *invocation,
1296+ GVariant *options)
1297+{
1298+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (_drive);
1299+ UDisksLinuxDriveObject *object = NULL;
1300+ UDisksLinuxBlockObject *block_object = NULL;
1301+ UDisksBlock *block = NULL;
1302+ UDisksDaemon *daemon;
1303+ UDisksLinuxDevice *device = NULL;
1304+ gint fd = -1;
1305+ GError *error = NULL;
1306+ const gchar *message;
1307+ const gchar *action_id;
1308+ pid_t caller_pid;
1309+
1310+ object = udisks_daemon_util_dup_object (drive, &error);
1311+ if (object == NULL)
1312+ {
1313+ g_dbus_method_invocation_take_error (invocation, error);
1314+ goto out;
1315+ }
1316+
1317+ block_object = udisks_linux_drive_object_get_block (object, FALSE);
1318+ if (block_object == NULL)
1319+ {
1320+ g_dbus_method_invocation_return_error (invocation,
1321+ UDISKS_ERROR,
1322+ UDISKS_ERROR_FAILED,
1323+ "Unable to find block device for drive");
1324+ goto out;
1325+ }
1326+ block = udisks_object_peek_block (UDISKS_OBJECT (block_object));
1327+
1328+ daemon = udisks_linux_drive_object_get_daemon (object);
1329+
1330+ if (!udisks_drive_ata_get_pm_supported (UDISKS_DRIVE_ATA (drive)) ||
1331+ !udisks_drive_ata_get_pm_enabled (UDISKS_DRIVE_ATA (drive)))
1332+ {
1333+ g_dbus_method_invocation_return_error (invocation,
1334+ UDISKS_ERROR,
1335+ UDISKS_ERROR_FAILED,
1336+ "PM is not supported or enabled");
1337+ goto out;
1338+ }
1339+
1340+ error = NULL;
1341+ if (!udisks_daemon_util_get_caller_pid_sync (daemon,
1342+ invocation,
1343+ NULL /* GCancellable */,
1344+ &caller_pid,
1345+ &error))
1346+ {
1347+ g_dbus_method_invocation_return_gerror (invocation, error);
1348+ g_error_free (error);
1349+ goto out;
1350+ }
1351+
1352+ /* Translators: Shown in authentication dialog when the user
1353+ * tries to put a drive into standby mode.
1354+ *
1355+ * Do not translate $(drive), it's a placeholder and
1356+ * will be replaced by the name of the drive/device in question
1357+ */
1358+ message = N_("Authentication is required to put $(drive) in standby mode");
1359+ action_id = "org.freedesktop.udisks2.ata-standby";
1360+ if (udisks_block_get_hint_system (block))
1361+ {
1362+ action_id = "org.freedesktop.udisks2.ata-standby-system";
1363+ }
1364+ else if (!udisks_daemon_util_on_same_seat (daemon, UDISKS_OBJECT (object), caller_pid))
1365+ {
1366+ action_id = "org.freedesktop.udisks2.ata-standby-other-seat";
1367+ }
1368+
1369+ /* Check that the user is authorized */
1370+ if (!udisks_daemon_util_check_authorization_sync (daemon,
1371+ UDISKS_OBJECT (object),
1372+ action_id,
1373+ options,
1374+ message,
1375+ invocation))
1376+ goto out;
1377+
1378+ device = udisks_linux_drive_object_get_device (object, TRUE /* get_hw */);
1379+ if (device == NULL)
1380+ {
1381+ g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
1382+ "No udev device");
1383+ goto out;
1384+ }
1385+
1386+ fd = open (g_udev_device_get_device_file (device->udev_device), O_RDONLY|O_NONBLOCK);
1387+ if (fd == -1)
1388+ {
1389+ g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
1390+ "Error opening device file %s: %m",
1391+ g_udev_device_get_device_file (device->udev_device));
1392+ goto out;
1393+ }
1394+
1395+ {
1396+ /* ATA8: 7.55 STANDBY IMMEDIATE - E0h, Non-Data */
1397+ UDisksAtaCommandInput input = {.command = 0xe0};
1398+ UDisksAtaCommandOutput output = {0};
1399+ if (!udisks_ata_send_command_sync (fd,
1400+ -1,
1401+ UDISKS_ATA_COMMAND_PROTOCOL_NONE,
1402+ &input,
1403+ &output,
1404+ &error))
1405+ {
1406+ g_prefix_error (&error, "Error sending ATA command STANDBY IMMEDIATE: ");
1407+ g_dbus_method_invocation_take_error (invocation, error);
1408+ goto out;
1409+ }
1410+ udisks_drive_ata_complete_pm_standby (_drive, invocation);
1411+ }
1412+
1413+ out:
1414+ if (fd != -1)
1415+ close (fd);
1416+ g_clear_object (&device);
1417+ g_clear_object (&block_object);
1418+ g_clear_object (&object);
1419+ return TRUE; /* returning TRUE means that we handled the method invocation */
1420+}
1421+
1422+/* ---------------------------------------------------------------------------------------------------- */
1423+
1424+static gboolean
1425+handle_pm_wakeup (UDisksDriveAta *_drive,
1426+ GDBusMethodInvocation *invocation,
1427+ GVariant *options)
1428+{
1429+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (_drive);
1430+ UDisksLinuxDriveObject *object = NULL;
1431+ UDisksLinuxBlockObject *block_object = NULL;
1432+ UDisksBlock *block = NULL;
1433+ UDisksDaemon *daemon;
1434+ UDisksLinuxDevice *device = NULL;
1435+ gint fd = -1;
1436+ GError *error = NULL;
1437+ const gchar *message;
1438+ const gchar *action_id;
1439+ pid_t caller_pid;
1440+ guchar buf[4096];
1441+
1442+ object = udisks_daemon_util_dup_object (drive, &error);
1443+ if (object == NULL)
1444+ {
1445+ g_dbus_method_invocation_take_error (invocation, error);
1446+ goto out;
1447+ }
1448+
1449+ block_object = udisks_linux_drive_object_get_block (object, FALSE);
1450+ if (block_object == NULL)
1451+ {
1452+ g_dbus_method_invocation_return_error (invocation,
1453+ UDISKS_ERROR,
1454+ UDISKS_ERROR_FAILED,
1455+ "Unable to find block device for drive");
1456+ goto out;
1457+ }
1458+ block = udisks_object_peek_block (UDISKS_OBJECT (block_object));
1459+
1460+ daemon = udisks_linux_drive_object_get_daemon (object);
1461+
1462+ if (!udisks_drive_ata_get_pm_supported (UDISKS_DRIVE_ATA (drive)) ||
1463+ !udisks_drive_ata_get_pm_enabled (UDISKS_DRIVE_ATA (drive)))
1464+ {
1465+ g_dbus_method_invocation_return_error (invocation,
1466+ UDISKS_ERROR,
1467+ UDISKS_ERROR_FAILED,
1468+ "PM is not supported or enabled");
1469+ goto out;
1470+ }
1471+
1472+ error = NULL;
1473+ if (!udisks_daemon_util_get_caller_pid_sync (daemon,
1474+ invocation,
1475+ NULL /* GCancellable */,
1476+ &caller_pid,
1477+ &error))
1478+ {
1479+ g_dbus_method_invocation_return_gerror (invocation, error);
1480+ g_error_free (error);
1481+ goto out;
1482+ }
1483+
1484+ /* Translators: Shown in authentication dialog when the user
1485+ * tries to wake up a drive from standby mode.
1486+ *
1487+ * Do not translate $(drive), it's a placeholder and
1488+ * will be replaced by the name of the drive/device in question
1489+ */
1490+ message = N_("Authentication is required to wake up $(drive) from standby mode");
1491+ action_id = "org.freedesktop.udisks2.ata-standby";
1492+ if (udisks_block_get_hint_system (block))
1493+ {
1494+ action_id = "org.freedesktop.udisks2.ata-standby-system";
1495+ }
1496+ else if (!udisks_daemon_util_on_same_seat (daemon, UDISKS_OBJECT (object), caller_pid))
1497+ {
1498+ action_id = "org.freedesktop.udisks2.ata-standby-other-seat";
1499+ }
1500+
1501+ /* Check that the user is authorized */
1502+ if (!udisks_daemon_util_check_authorization_sync (daemon,
1503+ UDISKS_OBJECT (object),
1504+ action_id,
1505+ options,
1506+ message,
1507+ invocation))
1508+ goto out;
1509+
1510+ device = udisks_linux_drive_object_get_device (object, TRUE /* get_hw */);
1511+ if (device == NULL)
1512+ {
1513+ g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
1514+ "No udev device");
1515+ goto out;
1516+ }
1517+
1518+ fd = open (g_udev_device_get_device_file (device->udev_device), O_RDONLY);
1519+ if (fd == -1)
1520+ {
1521+ g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
1522+ "Error opening device file %s: %m",
1523+ g_udev_device_get_device_file (device->udev_device));
1524+ goto out;
1525+ }
1526+
1527+ if (read (fd, buf, sizeof (buf)) != sizeof (buf))
1528+ {
1529+ g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
1530+ "Error reading %d bytes from %s: %m",
1531+ (gint) sizeof (buf),
1532+ g_udev_device_get_device_file (device->udev_device));
1533+ goto out;
1534+ }
1535+
1536+ udisks_drive_ata_complete_pm_wakeup (_drive, invocation);
1537+
1538+ out:
1539+ if (fd != -1)
1540+ close (fd);
1541+ g_clear_object (&device);
1542+ g_clear_object (&block_object);
1543+ g_clear_object (&object);
1544+ return TRUE; /* returning TRUE means that we handled the method invocation */
1545+}
1546+
1547+/* ---------------------------------------------------------------------------------------------------- */
1548+
1549+static gchar *
1550+ata_pm_standby_to_string (guint value)
1551+{
1552+ gchar *ret;
1553+ gint seconds = -1;
1554+
1555+ if (value == 0)
1556+ {
1557+ ret = g_strdup ("disabled");
1558+ goto out;
1559+ }
1560+ else if (value == 253)
1561+ {
1562+ ret = g_strdup ("vendor-defined");
1563+ goto out;
1564+ }
1565+ else if (value == 254)
1566+ {
1567+ ret = g_strdup ("reserved");
1568+ goto out;
1569+ }
1570+ else if (value < 241)
1571+ {
1572+ seconds = value * 5;
1573+ }
1574+ else if (value < 252)
1575+ {
1576+ seconds = (value - 240) * 30 * 60;
1577+ }
1578+ else if (value == 252)
1579+ {
1580+ seconds = 21 * 60;
1581+ }
1582+ else if (value == 255)
1583+ {
1584+ seconds = 21 * 60 + 15;
1585+ }
1586+
1587+ ret = g_strdup_printf ("%d seconds", seconds);
1588+
1589+ out:
1590+ return ret;
1591+}
1592+
1593+/* ---------------------------------------------------------------------------------------------------- */
1594+
1595+typedef struct
1596+{
1597+ gint ata_pm_standby;
1598+ gint ata_apm_level;
1599+ gint ata_aam_level;
1600+ gboolean ata_write_cache_enabled;
1601+ gboolean ata_write_cache_enabled_set;
1602+ UDisksLinuxDriveAta *ata;
1603+ UDisksLinuxDevice *device;
1604+ GVariant *configuration;
1605+ UDisksDrive *drive;
1606+ UDisksLinuxDriveObject *object;
1607+} ApplyConfData;
1608+
1609+static void
1610+apply_conf_data_free (ApplyConfData *data)
1611+{
1612+ g_clear_object (&data->ata);
1613+ g_clear_object (&data->device);
1614+ g_variant_unref (data->configuration);
1615+ g_clear_object (&data->drive);
1616+ g_clear_object (&data->object);
1617+ g_free (data);
1618+}
1619+
1620+static gpointer
1621+apply_configuration_thread_func (gpointer user_data)
1622+{
1623+ ApplyConfData *data = user_data;
1624+ const gchar *device_file = NULL;
1625+ gint fd = -1;
1626+ GError *error = NULL;
1627+
1628+ device_file = g_udev_device_get_device_file (data->device->udev_device);
1629+
1630+ udisks_notice ("Applying configuration from %s/udisks2/%s.conf to %s",
1631+ PACKAGE_SYSCONF_DIR, udisks_drive_get_id (data->drive), device_file);
1632+
1633+
1634+ /* Use O_RDRW instead of O_RDONLY to force a 'change' uevent so properties are updated */
1635+ fd = open (device_file, O_RDWR|O_NONBLOCK);
1636+ if (fd == -1)
1637+ {
1638+ udisks_error ("Error opening device file %s: %m", device_file);
1639+ goto out;
1640+ }
1641+
1642+ if (data->ata_pm_standby != -1)
1643+ {
1644+ /* ATA8: 7.18 IDLE - E3h, Non-Data */
1645+ UDisksAtaCommandInput input = {.command = 0xe3, .count = data->ata_pm_standby};
1646+ UDisksAtaCommandOutput output = {0};
1647+ if (!udisks_ata_send_command_sync (fd,
1648+ -1,
1649+ UDISKS_ATA_COMMAND_PROTOCOL_NONE,
1650+ &input,
1651+ &output,
1652+ &error))
1653+ {
1654+ udisks_error ("Error sending ATA command IDLE (timeout=%d) to %s: %s (%s, %d)",
1655+ data->ata_pm_standby, device_file,
1656+ error->message, g_quark_to_string (error->domain), error->code);
1657+ g_clear_error (&error);
1658+ }
1659+ else
1660+ {
1661+ gchar *pretty = ata_pm_standby_to_string (data->ata_pm_standby);
1662+ udisks_notice ("Set standby timer to %s (value %d) on %s [%s]",
1663+ pretty, data->ata_pm_standby, device_file, udisks_drive_get_id (data->drive));
1664+ g_free (pretty);
1665+ }
1666+ }
1667+
1668+ if (data->ata_apm_level != -1)
1669+ {
1670+ /* ATA8: 7.48 SET FEATURES - EFh, Non-Data
1671+ * 7.48.6 Enable/disable the APM feature set
1672+ */
1673+ UDisksAtaCommandInput input = {.command = 0xef, .feature = 0x05, .count = data->ata_apm_level};
1674+ UDisksAtaCommandOutput output = {0};
1675+ if (data->ata_apm_level == 0xff)
1676+ {
1677+ input.feature = 0x85;
1678+ input.count = 0x00;
1679+ }
1680+ if (!udisks_ata_send_command_sync (fd,
1681+ -1,
1682+ UDISKS_ATA_COMMAND_PROTOCOL_NONE,
1683+ &input,
1684+ &output,
1685+ &error))
1686+ {
1687+ udisks_error ("Error sending ATA command SET FEATURES, sub-command 0x%02x (ata_apm_level=%d) to %s: %s (%s, %d)",
1688+ input.feature, data->ata_apm_level, device_file,
1689+ error->message, g_quark_to_string (error->domain), error->code);
1690+ g_clear_error (&error);
1691+ }
1692+ else
1693+ {
1694+ udisks_notice ("Set APM level to %d on %s [%s]",
1695+ data->ata_apm_level, device_file, udisks_drive_get_id (data->drive));
1696+ }
1697+ }
1698+
1699+ if (data->ata_aam_level != -1)
1700+ {
1701+ /* ATA8: 7.48 SET FEATURES - EFh, Non-Data
1702+ * 7.48.11 Enable/disable the AAM feature set
1703+ */
1704+ UDisksAtaCommandInput input = {.command = 0xef, .feature = 0x42, .count = data->ata_aam_level};
1705+ UDisksAtaCommandOutput output = {0};
1706+ if (data->ata_apm_level == 0xff)
1707+ {
1708+ input.feature = 0xc2;
1709+ input.count = 0x00;
1710+ }
1711+ if (!udisks_ata_send_command_sync (fd,
1712+ -1,
1713+ UDISKS_ATA_COMMAND_PROTOCOL_NONE,
1714+ &input,
1715+ &output,
1716+ &error))
1717+ {
1718+ udisks_error ("Error sending ATA command SET FEATURES, sub-command 0x%02x (ata_aam_level=%d) to %s: %s (%s, %d)",
1719+ input.feature, data->ata_aam_level, device_file,
1720+ error->message, g_quark_to_string (error->domain), error->code);
1721+ g_clear_error (&error);
1722+ }
1723+ else
1724+ {
1725+ udisks_notice ("Set AAM value to %d on %s [%s]",
1726+ data->ata_aam_level, device_file, udisks_drive_get_id (data->drive));
1727+ }
1728+ }
1729+
1730+ if (data->ata_write_cache_enabled_set)
1731+ {
1732+ /* ATA8: 7.48 SET FEATURES - EFh, Non-Data
1733+ * 7.48.4 Enable/disable volatile write cache
1734+ */
1735+ UDisksAtaCommandInput input = {.command = 0xef, .feature = 0x82};
1736+ UDisksAtaCommandOutput output = {0};
1737+ if (data->ata_write_cache_enabled)
1738+ input.feature = 0x02;
1739+ if (!udisks_ata_send_command_sync (fd,
1740+ -1,
1741+ UDISKS_ATA_COMMAND_PROTOCOL_NONE,
1742+ &input,
1743+ &output,
1744+ &error))
1745+ {
1746+ udisks_error ("Error sending ATA command SET FEATURES, sub-command 0x%02x to %s: %s (%s, %d)",
1747+ input.feature, device_file,
1748+ error->message, g_quark_to_string (error->domain), error->code);
1749+ g_clear_error (&error);
1750+ }
1751+ else
1752+ {
1753+ udisks_notice ("%s Write-Cache on %s [%s]",
1754+ data->ata_write_cache_enabled ? "Enabled" : "Disabled",
1755+ device_file, udisks_drive_get_id (data->drive));
1756+ }
1757+ }
1758+
1759+ out:
1760+ if (fd != -1)
1761+ close (fd);
1762+ apply_conf_data_free (data);
1763+ return NULL;
1764+}
1765+
1766+/**
1767+ * udisks_linux_drive_ata_apply_configuration:
1768+ * @drive: A #UDisksLinuxDriveAta.
1769+ * @device: A #UDisksLinuxDevice
1770+ * @configuration: The configuration to apply.
1771+ *
1772+ * Spawns a thread to apply @configuration to @drive, if any. Does not
1773+ * wait for the thread to terminate.
1774+ */
1775+void
1776+udisks_linux_drive_ata_apply_configuration (UDisksLinuxDriveAta *drive,
1777+ UDisksLinuxDevice *device,
1778+ GVariant *configuration)
1779+{
1780+ gboolean has_conf = FALSE;
1781+ ApplyConfData *data = NULL;
1782+
1783+ data = g_new0 (ApplyConfData, 1);
1784+ data->ata_pm_standby = -1;
1785+ data->ata_apm_level = -1;
1786+ data->ata_aam_level = -1;
1787+ data->ata_write_cache_enabled = FALSE;
1788+ data->ata_write_cache_enabled_set = FALSE;
1789+ data->ata = g_object_ref (drive);
1790+ data->device = g_object_ref (device);
1791+ data->configuration = g_variant_ref (configuration);
1792+
1793+ data->object = udisks_daemon_util_dup_object (drive, NULL);
1794+ if (data->object == NULL)
1795+ goto out;
1796+
1797+ data->drive = udisks_object_get_drive (UDISKS_OBJECT (data->object));
1798+ if (data->drive == NULL)
1799+ goto out;
1800+
1801+
1802+ has_conf |= g_variant_lookup (configuration, "ata-pm-standby", "i", &data->ata_pm_standby);
1803+ has_conf |= g_variant_lookup (configuration, "ata-apm-level", "i", &data->ata_apm_level);
1804+ has_conf |= g_variant_lookup (configuration, "ata-aam-level", "i", &data->ata_aam_level);
1805+ if (g_variant_lookup (configuration, "ata-write-cache-enabled", "b", &data->ata_write_cache_enabled))
1806+ {
1807+ data->ata_write_cache_enabled_set = TRUE;
1808+ has_conf = TRUE;
1809+ }
1810+
1811+ /* don't do anything if none of the configuration is set */
1812+ if (!has_conf)
1813+ goto out;
1814+
1815+ /* this can easily take a long time and thus block (the drive may be in standby mode
1816+ * and needs to spin up) - so run it in a thread
1817+ */
1818+ g_thread_new ("apply-conf-thread",
1819+ apply_configuration_thread_func,
1820+ data);
1821+
1822+ data = NULL; /* don't free data below */
1823+
1824+ out:
1825+ if (data != NULL)
1826+ apply_conf_data_free (data);
1827+}
1828+
1829+/* ---------------------------------------------------------------------------------------------------- */
1830+
1831+static gboolean
1832+on_secure_erase_update_progress_timeout (gpointer user_data)
1833+{
1834+ UDisksJob *job = UDISKS_JOB (user_data);
1835+ gint64 now;
1836+ gint64 start;
1837+ gint64 end;
1838+ gdouble progress;
1839+
1840+ now = g_get_real_time ();
1841+ start = udisks_job_get_start_time (job);
1842+ end = udisks_job_get_expected_end_time (job);
1843+
1844+ progress = ((gdouble) (now - start)) / (end - start);
1845+ if (progress < 0)
1846+ progress = 0;
1847+ if (progress > 1)
1848+ progress = 1;
1849+
1850+ /* TODO: if we've exceeded the expected end time, we could add
1851+ * another couple of minutes or so... that'd be kinda cheating
1852+ * though, wouldn't it?
1853+ */
1854+
1855+ udisks_job_set_progress (job, progress);
1856+
1857+ return TRUE; /* keep source around */
1858+}
1859+
1860+/**
1861+ * udisks_linux_drive_ata_secure_erase_sync:
1862+ * @drive: A #UDisksLinuxDriveAta.
1863+ * @caller_uid: The unix user if of the caller requesting the operation.
1864+ * @enhanced: %TRUE to use the enhanced version of the ATA secure erase command.
1865+ * @error: Return location for error or %NULL.
1866+ *
1867+ * Performs an ATA Secure Erase opeartion. Blocks the calling thread until the operation completes.
1868+ *
1869+ * This operation may take a very long time (hours) to complete.
1870+ *
1871+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
1872+ */
1873+gboolean
1874+udisks_linux_drive_ata_secure_erase_sync (UDisksLinuxDriveAta *drive,
1875+ uid_t caller_uid,
1876+ gboolean enhanced,
1877+ GError **error)
1878+{
1879+ gboolean ret = FALSE;
1880+ UDisksDrive *_drive = NULL;
1881+ UDisksLinuxDriveObject *object = NULL;
1882+ UDisksLinuxBlockObject *block_object = NULL;
1883+ UDisksDaemon *daemon;
1884+ UDisksLinuxDevice *device = NULL;
1885+ const gchar *device_file = NULL;
1886+ gint fd = -1;
1887+ union
1888+ {
1889+ guchar buf[512];
1890+ guint16 words[256];
1891+ } identify;
1892+ guint16 word_82;
1893+ guint16 word_128;
1894+ UDisksBaseJob *job = NULL;
1895+ gint num_minutes = 0;
1896+ guint timeout_id = 0;
1897+ gboolean claimed = FALSE;
1898+ GError *local_error = NULL;
1899+ const gchar *pass = "xxxx";
1900+ gboolean clear_passwd_on_failure = FALSE;
1901+
1902+ g_return_val_if_fail (UDISKS_IS_LINUX_DRIVE_ATA (drive), FALSE);
1903+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1904+
1905+ object = udisks_daemon_util_dup_object (drive, &local_error);
1906+ if (object == NULL)
1907+ goto out;
1908+ _drive = udisks_object_peek_drive (UDISKS_OBJECT (object));
1909+
1910+ block_object = udisks_linux_drive_object_get_block (object, FALSE);
1911+ if (block_object == NULL)
1912+ {
1913+ g_set_error (&local_error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
1914+ "Unable to find block device for drive");
1915+ goto out;
1916+ }
1917+
1918+ daemon = udisks_linux_drive_object_get_daemon (object);
1919+
1920+ device = udisks_linux_drive_object_get_device (object, TRUE /* get_hw */);
1921+ if (device == NULL)
1922+ {
1923+ g_set_error (&local_error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
1924+ "No udev device");
1925+ goto out;
1926+ }
1927+
1928+ if (drive->secure_erase_in_progress)
1929+ {
1930+ g_set_error (&local_error, UDISKS_ERROR, UDISKS_ERROR_DEVICE_BUSY,
1931+ "Secure erase in progress");
1932+ goto out;
1933+ }
1934+
1935+ /* Use O_EXCL so it fails if mounted or in use */
1936+ device_file = g_udev_device_get_device_file (device->udev_device);
1937+ fd = open (g_udev_device_get_device_file (device->udev_device), O_RDONLY | O_EXCL);
1938+ if (fd == -1)
1939+ {
1940+ g_set_error (&local_error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
1941+ "Error opening device file %s: %m",
1942+ device_file);
1943+ goto out;
1944+ }
1945+
1946+ drive->secure_erase_in_progress = TRUE;
1947+
1948+ claimed = TRUE;
1949+
1950+ /* First get the IDENTIFY data directly from the drive, for sanity checks */
1951+ {
1952+ /* ATA8: 7.16 IDENTIFY DEVICE - ECh, PIO Data-In */
1953+ UDisksAtaCommandInput input = {.command = 0xec};
1954+ UDisksAtaCommandOutput output = {.buffer = identify.buf, .buffer_size = sizeof (identify.buf)};
1955+ if (!udisks_ata_send_command_sync (fd,
1956+ -1,
1957+ UDISKS_ATA_COMMAND_PROTOCOL_DRIVE_TO_HOST,
1958+ &input,
1959+ &output,
1960+ &local_error))
1961+ {
1962+ g_prefix_error (&local_error, "Error sending ATA command IDENTIFY DEVICE: ");
1963+ goto out;
1964+ }
1965+ }
1966+
1967+ /* Support of the Security feature set is indicated in IDENTIFY
1968+ * DEVICE and IDENTIFY PACKET DEVICE data word 82 and data word 128.
1969+ * Security information in words 82, 89 and 90 is fixed until the
1970+ * next power-on reset and shall not change unless DEVICE
1971+ * CONFIGURATION OVERLAY removes support for the Security feature
1972+ * set. Security information in words 85, 92 and 128 are variable
1973+ * and may change. If the Security feature set is not supported,
1974+ * then words 89, 90, 92 and 128 are N/A.
1975+ *
1976+ * word 82: ...
1977+ * 1 The Security feature set is supported
1978+ *
1979+ * word 128: 15:9 Reserved
1980+ * 8 Master Password Capability: 0 = High, 1 = Maximum
1981+ * 7:6 Reserved
1982+ * 5 Enhanced security erase supported
1983+ * 4 Security count expired
1984+ * 3 Security frozen
1985+ * 2 Security locked
1986+ * 1 Security enabled
1987+ * 0 Security supported
1988+ */
1989+ word_82 = GUINT16_FROM_LE (identify.words[82]);
1990+ word_128 = GUINT16_FROM_LE (identify.words[128]);
1991+
1992+ if (!(
1993+ (word_82 & (1<<1)) && (word_128 & (1<<0))
1994+ ))
1995+ {
1996+ g_set_error (&local_error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
1997+ "Drive does not support the ATA security feature");
1998+ goto out;
1999+ }
2000+
2001+ if (word_128 & (1<<3))
2002+ {
2003+ g_set_error (&local_error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
2004+ "Drive is frozen, cannot perform a secure erase");
2005+ goto out;
2006+ }
2007+
2008+ if (enhanced && !(word_128 & (1<<5)))
2009+ {
2010+ g_set_error (&local_error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
2011+ "Enhanced erase requested but not supported");
2012+ goto out;
2013+ }
2014+
2015+ /* OK, all checks done, let's do this thing! */
2016+
2017+ /* First, set up a Job object to track progress */
2018+ num_minutes = enhanced ? 2 * GUINT16_FROM_LE (identify.words[90]) : 2 * GUINT16_FROM_LE (identify.words[89]);
2019+ job = udisks_daemon_launch_simple_job (daemon,
2020+ UDISKS_OBJECT (object),
2021+ enhanced ? "ata-enhanced-secure-erase" : "ata-secure-erase",
2022+ caller_uid, NULL);
2023+ udisks_job_set_cancelable (UDISKS_JOB (job), FALSE);
2024+
2025+ /* A value of 510 (255 in the IDENTIFY DATA register) means "erase
2026+ * is expected to take _at least_ 508 minutes" ... so don't attempt
2027+ * to predict when the job is going to end and don't report progress
2028+ */
2029+ if (num_minutes != 510)
2030+ {
2031+ udisks_job_set_expected_end_time (UDISKS_JOB (job),
2032+ g_get_real_time () + num_minutes * 60LL * G_USEC_PER_SEC);
2033+ udisks_job_set_progress_valid (UDISKS_JOB (job), TRUE);
2034+ timeout_id = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT,
2035+ 1,
2036+ on_secure_erase_update_progress_timeout,
2037+ g_object_ref (job),
2038+ g_object_unref);
2039+ }
2040+
2041+ /* Second, set the user password to 'xxxx' */
2042+ {
2043+ /* ATA8: 7.45 SECURITY SET PASSWORD - F1h, PIO Data-Out */
2044+ guchar buf[512];
2045+ UDisksAtaCommandInput input = {.command = 0xf1, .buffer = buf, .buffer_size = sizeof (buf)};
2046+ UDisksAtaCommandOutput output = {0};
2047+ memset (buf, 0, sizeof (buf));
2048+ memcpy (buf + 2, pass, strlen (pass));
2049+ if (!udisks_ata_send_command_sync (fd,
2050+ -1,
2051+ UDISKS_ATA_COMMAND_PROTOCOL_HOST_TO_DRIVE,
2052+ &input,
2053+ &output,
2054+ &local_error))
2055+ {
2056+ g_prefix_error (&local_error, "Error sending ATA command SECURITY SET PASSWORD: ");
2057+ goto out;
2058+ }
2059+ }
2060+
2061+ clear_passwd_on_failure = TRUE;
2062+
2063+ udisks_notice ("Commencing ATA%s secure erase of %s (%s). This operation is expected to take at least %d minutes to complete",
2064+ enhanced ? " enhanced" : "",
2065+ device_file,
2066+ udisks_drive_get_id (_drive),
2067+ num_minutes);
2068+
2069+ /* Third... do SECURITY ERASE PREPARE */
2070+ {
2071+ /* ATA8: 7.42 SECURITY ERASE PREPARE - F3h, Non-Data */
2072+ UDisksAtaCommandInput input = {.command = 0xf3};
2073+ UDisksAtaCommandOutput output = {0};
2074+ if (!udisks_ata_send_command_sync (fd,
2075+ -1,
2076+ UDISKS_ATA_COMMAND_PROTOCOL_NONE,
2077+ &input,
2078+ &output,
2079+ &local_error))
2080+ {
2081+ g_prefix_error (&local_error, "Error sending ATA command SECURITY ERASE PREPARE: ");
2082+ goto out;
2083+ }
2084+ }
2085+
2086+ /* Fourth... do SECURITY ERASE UNIT */
2087+ {
2088+ /* ATA8: 7.43 SECURITY ERASE UNIT - F4h, PIO Data-Out */
2089+ guchar buf[512];
2090+ UDisksAtaCommandInput input = {.command = 0xf4, .buffer = buf, .buffer_size = sizeof (buf)};
2091+ UDisksAtaCommandOutput output = {0};
2092+ memset (buf, 0, sizeof (buf));
2093+ if (enhanced)
2094+ buf[0] |= 0x02;
2095+ memcpy (buf + 2, pass, strlen (pass));
2096+ if (!udisks_ata_send_command_sync (fd,
2097+ G_MAXINT, /* disable timeout */
2098+ UDISKS_ATA_COMMAND_PROTOCOL_HOST_TO_DRIVE,
2099+ &input,
2100+ &output,
2101+ &local_error))
2102+ {
2103+ g_prefix_error (&local_error, "Error sending ATA command SECURITY ERASE UNIT (enhanced=%d): ",
2104+ enhanced ? 1 : 0);
2105+ goto out;
2106+ }
2107+ }
2108+
2109+ clear_passwd_on_failure = FALSE;
2110+
2111+ udisks_linux_block_object_reread_partition_table (UDISKS_LINUX_BLOCK_OBJECT (block_object));
2112+
2113+ ret = TRUE;
2114+
2115+ out:
2116+ /* Clear the password if something went wrong */
2117+ if (clear_passwd_on_failure)
2118+ {
2119+ /* ATA8: 7.41 SECURITY DISABLE PASSWORD - F6h, PIO Data-Out */
2120+ guchar buf[512];
2121+ UDisksAtaCommandInput input = {.command = 0xf6, .buffer = buf, .buffer_size = sizeof (buf)};
2122+ UDisksAtaCommandOutput output = {0};
2123+ GError *cleanup_error = NULL;
2124+ memset (buf, 0, sizeof (buf));
2125+ memcpy (buf + 2, pass, strlen (pass));
2126+ if (!udisks_ata_send_command_sync (fd,
2127+ -1,
2128+ UDISKS_ATA_COMMAND_PROTOCOL_HOST_TO_DRIVE,
2129+ &input,
2130+ &output,
2131+ &cleanup_error))
2132+ {
2133+ udisks_error ("Failed to clear user password '%s' on %s (%s) while attemping clean-up after a failed secure erase operation. You may need to manually unlock the drive. The error was: %s (%s, %d)",
2134+ pass,
2135+ device_file,
2136+ udisks_drive_get_id (_drive),
2137+ cleanup_error->message, g_quark_to_string (cleanup_error->domain), cleanup_error->code);
2138+ g_clear_error (&cleanup_error);
2139+ }
2140+ else
2141+ {
2142+ udisks_info ("Successfully removed user password '%s' from %s (%s) during clean-up after a failed secure erase operation",
2143+ pass,
2144+ device_file,
2145+ udisks_drive_get_id (_drive));
2146+ }
2147+ }
2148+
2149+ if (ret)
2150+ {
2151+ udisks_notice ("Finished securely erasing %s (%s)",
2152+ device_file,
2153+ udisks_drive_get_id (_drive));
2154+ }
2155+ else
2156+ {
2157+ udisks_notice ("Error securely erasing %s (%s): %s (%s, %d)",
2158+ device_file,
2159+ udisks_drive_get_id (_drive),
2160+ local_error->message, g_quark_to_string (local_error->domain), local_error->code);
2161+ }
2162+
2163+ if (claimed)
2164+ drive->secure_erase_in_progress = FALSE;
2165+
2166+ if (timeout_id > 0)
2167+ g_source_remove (timeout_id);
2168+ if (job != NULL)
2169+ {
2170+ /* propagate error, if any */
2171+ if (local_error == NULL)
2172+ {
2173+ udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), TRUE, "");
2174+ }
2175+ else
2176+ {
2177+ gchar *s = g_strdup_printf ("Secure Erase failed: %s (%s, %d)",
2178+ local_error->message, g_quark_to_string (local_error->domain), local_error->code);
2179+ udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), FALSE, s);
2180+ g_free (s);
2181+ }
2182+ }
2183+ if (local_error != NULL)
2184+ g_propagate_error (error, local_error);
2185+ if (fd != -1)
2186+ close (fd);
2187+ g_clear_object (&device);
2188+ g_clear_object (&block_object);
2189+ g_clear_object (&object);
2190+ return ret;
2191+}
2192+
2193+static gboolean
2194+handle_security_erase_unit (UDisksDriveAta *_drive,
2195+ GDBusMethodInvocation *invocation,
2196+ GVariant *options)
2197+{
2198+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (_drive);
2199+ UDisksLinuxDriveObject *object = NULL;
2200+ UDisksLinuxBlockObject *block_object = NULL;
2201+ UDisksDaemon *daemon;
2202+ GError *error = NULL;
2203+ const gchar *message;
2204+ const gchar *action_id;
2205+ uid_t caller_uid;
2206+ gid_t caller_gid;
2207+ gboolean enhanced = FALSE;
2208+ UDisksInhibitCookie *inhibit_cookie = NULL;
2209+
2210+ object = udisks_daemon_util_dup_object (drive, &error);
2211+ if (object == NULL)
2212+ {
2213+ g_dbus_method_invocation_take_error (invocation, error);
2214+ goto out;
2215+ }
2216+
2217+ block_object = udisks_linux_drive_object_get_block (object, FALSE);
2218+ if (block_object == NULL)
2219+ {
2220+ g_dbus_method_invocation_return_error (invocation,
2221+ UDISKS_ERROR,
2222+ UDISKS_ERROR_FAILED,
2223+ "Unable to find block device for drive");
2224+ goto out;
2225+ }
2226+
2227+ daemon = udisks_linux_drive_object_get_daemon (object);
2228+
2229+ error = NULL;
2230+ if (!udisks_daemon_util_get_caller_uid_sync (daemon,
2231+ invocation,
2232+ NULL /* GCancellable */,
2233+ &caller_uid,
2234+ &caller_gid,
2235+ NULL,
2236+ &error))
2237+ {
2238+ g_dbus_method_invocation_return_gerror (invocation, error);
2239+ g_error_free (error);
2240+ goto out;
2241+ }
2242+
2243+ g_variant_lookup (options, "enhanced", "b", &enhanced);
2244+
2245+ /* Translators: Shown in authentication dialog when the user
2246+ * requests erasing a hard disk using the SECURE ERASE UNIT command.
2247+ *
2248+ * Do not translate $(drive), it's a placeholder and
2249+ * will be replaced by the name of the drive/device in question
2250+ */
2251+ message = N_("Authentication is required to perform a secure erase of $(drive)");
2252+ action_id = "org.freedesktop.udisks2.ata-secure-erase";
2253+
2254+ /* Check that the user is authorized */
2255+ if (!udisks_daemon_util_check_authorization_sync (daemon,
2256+ UDISKS_OBJECT (object),
2257+ action_id,
2258+ options,
2259+ message,
2260+ invocation))
2261+ goto out;
2262+
2263+ inhibit_cookie = udisks_daemon_util_inhibit_system_sync (N_("Formatting Device"));
2264+
2265+ if (!udisks_linux_drive_ata_secure_erase_sync (drive, caller_uid, enhanced, &error))
2266+ {
2267+ g_dbus_method_invocation_return_gerror (invocation, error);
2268+ g_error_free (error);
2269+ goto out;
2270+ }
2271+
2272+ udisks_linux_block_object_reread_partition_table (UDISKS_LINUX_BLOCK_OBJECT (block_object));
2273+
2274+ udisks_drive_ata_complete_security_erase_unit (_drive, invocation);
2275+
2276+ out:
2277+ udisks_daemon_util_uninhibit_system_sync (inhibit_cookie);
2278+ g_clear_object (&block_object);
2279+ g_clear_object (&object);
2280+ return TRUE; /* returning TRUE means that we handled the method invocation */
2281+}
2282+
2283+/* ---------------------------------------------------------------------------------------------------- */
2284+
2285+static gboolean
2286+handle_smart_set_enabled (UDisksDriveAta *_drive,
2287+ GDBusMethodInvocation *invocation,
2288+ gboolean value,
2289+ GVariant *options)
2290+{
2291+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (_drive);
2292+ UDisksLinuxDriveObject *object = NULL;
2293+ UDisksLinuxBlockObject *block_object = NULL;
2294+ UDisksDaemon *daemon;
2295+ GError *error = NULL;
2296+ UDisksLinuxDevice *device = NULL;
2297+ gint fd = -1;
2298+ const gchar *message;
2299+ const gchar *action_id;
2300+ uid_t caller_uid;
2301+ gid_t caller_gid;
2302+
2303+ object = udisks_daemon_util_dup_object (drive, &error);
2304+ if (object == NULL)
2305+ {
2306+ g_dbus_method_invocation_take_error (invocation, error);
2307+ goto out;
2308+ }
2309+
2310+ block_object = udisks_linux_drive_object_get_block (object, FALSE);
2311+ if (block_object == NULL)
2312+ {
2313+ g_dbus_method_invocation_return_error (invocation,
2314+ UDISKS_ERROR,
2315+ UDISKS_ERROR_FAILED,
2316+ "Unable to find block device for drive");
2317+ goto out;
2318+ }
2319+
2320+ daemon = udisks_linux_drive_object_get_daemon (object);
2321+
2322+ error = NULL;
2323+ if (!udisks_daemon_util_get_caller_uid_sync (daemon,
2324+ invocation,
2325+ NULL /* GCancellable */,
2326+ &caller_uid,
2327+ &caller_gid,
2328+ NULL,
2329+ &error))
2330+ {
2331+ g_dbus_method_invocation_return_gerror (invocation, error);
2332+ g_error_free (error);
2333+ goto out;
2334+ }
2335+
2336+ if (value)
2337+ {
2338+ /* Translators: Shown in authentication dialog when the user
2339+ * requests enabling SMART on a disk.
2340+ *
2341+ * Do not translate $(drive), it's a placeholder and
2342+ * will be replaced by the name of the drive/device in question
2343+ */
2344+ message = N_("Authentication is required to enable SMART on $(drive)");
2345+ }
2346+ else
2347+ {
2348+ /* Translators: Shown in authentication dialog when the user
2349+ * requests enabling SMART on a disk.
2350+ *
2351+ * Do not translate $(drive), it's a placeholder and
2352+ * will be replaced by the name of the drive/device in question
2353+ */
2354+ message = N_("Authentication is required to disable SMART on $(drive)");
2355+ }
2356+ action_id = "org.freedesktop.udisks2.ata-smart-enable-disable";
2357+
2358+ /* Check that the user is authorized */
2359+ if (!udisks_daemon_util_check_authorization_sync (daemon,
2360+ UDISKS_OBJECT (object),
2361+ action_id,
2362+ options,
2363+ message,
2364+ invocation))
2365+ goto out;
2366+
2367+ device = udisks_linux_drive_object_get_device (object, TRUE /* get_hw */);
2368+ if (device == NULL)
2369+ {
2370+ g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
2371+ "No udev device");
2372+ goto out;
2373+ }
2374+
2375+ fd = open (g_udev_device_get_device_file (device->udev_device), O_RDONLY|O_NONBLOCK);
2376+ if (fd == -1)
2377+ {
2378+ g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
2379+ "Error opening device file %s: %m",
2380+ g_udev_device_get_device_file (device->udev_device));
2381+ goto out;
2382+ }
2383+
2384+ {
2385+ /* ATA8: 7.53.4 SMART ENABLE OPERATIONS - B0h/D8h, Non-Data
2386+ * 7.53.2 SMART DISABLE OPERATIONS - B0h/D9h, Non-Data
2387+ */
2388+ UDisksAtaCommandInput input = {.command = 0xb0};
2389+ UDisksAtaCommandOutput output = {0};
2390+ if (value)
2391+ input.feature = 0xd8;
2392+ else
2393+ input.feature = 0xd9;
2394+ input.lba = 0x004fc2; /* will be encoded as 0xc2 0x4f 0x00 as per the ATA spec */
2395+ if (!udisks_ata_send_command_sync (fd,
2396+ -1,
2397+ UDISKS_ATA_COMMAND_PROTOCOL_NONE,
2398+ &input,
2399+ &output,
2400+ &error))
2401+ {
2402+ g_prefix_error (&error, "Error sending ATA command SMART, sub-command %s OPERATIONS: ",
2403+ value ? "ENABLE" : "DISABLE");
2404+ g_dbus_method_invocation_take_error (invocation, error);
2405+ goto out;
2406+ }
2407+ }
2408+
2409+ /* Reread new IDENTIFY data */
2410+ if (!udisks_linux_device_reprobe_sync (device, NULL, &error))
2411+ {
2412+ g_prefix_error (&error, "Error reprobing device: ");
2413+ g_dbus_method_invocation_take_error (invocation, error);
2414+ goto out;
2415+ }
2416+
2417+ /* if we just enabled SMART, re-read SMART data before returning */
2418+ if (value)
2419+ {
2420+ if (!udisks_linux_drive_ata_refresh_smart_sync (drive,
2421+ FALSE, /* nowakeup */
2422+ NULL, /* simulate_path */
2423+ NULL, /* cancellable */
2424+ &error))
2425+ {
2426+ g_prefix_error (&error, "Error updating SMART data: ");
2427+ g_dbus_method_invocation_take_error (invocation, error);
2428+ goto out;
2429+ }
2430+ }
2431+ else
2432+ {
2433+ update_smart (drive, device);
2434+ }
2435+ /* ensure property changes are sent before the method return */
2436+ g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (drive));
2437+
2438+ udisks_drive_ata_complete_smart_set_enabled (_drive, invocation);
2439+
2440+ out:
2441+ if (fd != -1)
2442+ close (fd);
2443+ g_clear_object (&device);
2444+ g_clear_object (&block_object);
2445+ g_clear_object (&object);
2446+ return TRUE; /* returning TRUE means that we handled the method invocation */
2447+}
2448+
2449+/* ---------------------------------------------------------------------------------------------------- */
2450+
2451+static void
2452+drive_ata_iface_init (UDisksDriveAtaIface *iface)
2453+{
2454+ iface->handle_smart_update = handle_smart_update;
2455+ iface->handle_smart_get_attributes = handle_smart_get_attributes;
2456+ iface->handle_smart_selftest_abort = handle_smart_selftest_abort;
2457+ iface->handle_smart_selftest_start = handle_smart_selftest_start;
2458+ iface->handle_smart_set_enabled = handle_smart_set_enabled;
2459+
2460+ iface->handle_pm_get_state = handle_pm_get_state;
2461+ iface->handle_pm_standby = handle_pm_standby;
2462+ iface->handle_pm_wakeup = handle_pm_wakeup;
2463+
2464+ iface->handle_security_erase_unit = handle_security_erase_unit;
2465+}
2466
2467=== added directory '.pc/0002-Fix-standby-timers.patch'
2468=== added file '.pc/0002-Fix-standby-timers.patch/.timestamp'
2469=== added directory '.pc/0002-Fix-standby-timers.patch/src'
2470=== added file '.pc/0002-Fix-standby-timers.patch/src/udiskslinuxdriveata.c'
2471--- .pc/0002-Fix-standby-timers.patch/src/udiskslinuxdriveata.c 1970-01-01 00:00:00 +0000
2472+++ .pc/0002-Fix-standby-timers.patch/src/udiskslinuxdriveata.c 2014-06-15 14:18:36 +0000
2473@@ -0,0 +1,2461 @@
2474+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2475+ *
2476+ * Copyright (C) 2007-2010 David Zeuthen <zeuthen@gmail.com>
2477+ *
2478+ * This program is free software; you can redistribute it and/or modify
2479+ * it under the terms of the GNU General Public License as published by
2480+ * the Free Software Foundation; either version 2 of the License, or
2481+ * (at your option) any later version.
2482+ *
2483+ * This program is distributed in the hope that it will be useful,
2484+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2485+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2486+ * GNU General Public License for more details.
2487+ *
2488+ * You should have received a copy of the GNU General Public License
2489+ * along with this program; if not, write to the Free Software
2490+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2491+ *
2492+ */
2493+
2494+#include "config.h"
2495+#include <glib/gi18n-lib.h>
2496+
2497+#include <sys/types.h>
2498+#include <sys/stat.h>
2499+#include <sys/ioctl.h>
2500+#include <fcntl.h>
2501+
2502+#include <pwd.h>
2503+#include <grp.h>
2504+#include <string.h>
2505+#include <stdlib.h>
2506+#include <stdio.h>
2507+#include <mntent.h>
2508+
2509+#include <glib/gstdio.h>
2510+#include <errno.h>
2511+
2512+#include <atasmart.h>
2513+
2514+#include "udiskslogging.h"
2515+#include "udiskslinuxprovider.h"
2516+#include "udiskslinuxdriveobject.h"
2517+#include "udiskslinuxdriveata.h"
2518+#include "udiskslinuxblockobject.h"
2519+#include "udisksdaemon.h"
2520+#include "udisksdaemonutil.h"
2521+#include "udisksbasejob.h"
2522+#include "udiskssimplejob.h"
2523+#include "udisksthreadedjob.h"
2524+#include "udisksata.h"
2525+#include "udiskslinuxdevice.h"
2526+
2527+/**
2528+ * SECTION:udiskslinuxdriveata
2529+ * @title: UDisksLinuxDriveAta
2530+ * @short_description: Linux implementation of #UDisksDriveAta
2531+ *
2532+ * This type provides an implementation of the #UDisksDriveAta
2533+ * interface on Linux.
2534+ */
2535+
2536+typedef struct _UDisksLinuxDriveAtaClass UDisksLinuxDriveAtaClass;
2537+
2538+/**
2539+ * UDisksLinuxDriveAta:
2540+ *
2541+ * The #UDisksLinuxDriveAta structure contains only private data and should
2542+ * only be accessed using the provided API.
2543+ */
2544+struct _UDisksLinuxDriveAta
2545+{
2546+ UDisksDriveAtaSkeleton parent_instance;
2547+
2548+ gboolean smart_is_from_blob;
2549+ guint64 smart_updated;
2550+ gboolean smart_failing;
2551+ gdouble smart_temperature;
2552+ guint64 smart_power_on_seconds;
2553+ gint smart_num_attributes_failing;
2554+ gint smart_num_attributes_failed_in_the_past;
2555+ gint64 smart_num_bad_sectors;
2556+ const gchar *smart_selftest_status;
2557+ gint smart_selftest_percent_remaining;
2558+
2559+ GVariant *smart_attributes;
2560+
2561+ UDisksThreadedJob *selftest_job;
2562+
2563+ gboolean secure_erase_in_progress;
2564+};
2565+
2566+struct _UDisksLinuxDriveAtaClass
2567+{
2568+ UDisksDriveAtaSkeletonClass parent_class;
2569+};
2570+
2571+static void drive_ata_iface_init (UDisksDriveAtaIface *iface);
2572+
2573+G_DEFINE_TYPE_WITH_CODE (UDisksLinuxDriveAta, udisks_linux_drive_ata, UDISKS_TYPE_DRIVE_ATA_SKELETON,
2574+ G_IMPLEMENT_INTERFACE (UDISKS_TYPE_DRIVE_ATA, drive_ata_iface_init));
2575+
2576+G_LOCK_DEFINE_STATIC (object_lock);
2577+
2578+/* ---------------------------------------------------------------------------------------------------- */
2579+
2580+static void
2581+udisks_linux_drive_ata_finalize (GObject *object)
2582+{
2583+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (object);
2584+
2585+ if (drive->smart_attributes != NULL)
2586+ g_variant_unref (drive->smart_attributes);
2587+
2588+ if (G_OBJECT_CLASS (udisks_linux_drive_ata_parent_class)->finalize != NULL)
2589+ G_OBJECT_CLASS (udisks_linux_drive_ata_parent_class)->finalize (object);
2590+}
2591+
2592+
2593+static void
2594+udisks_linux_drive_ata_init (UDisksLinuxDriveAta *drive)
2595+{
2596+ g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (drive),
2597+ G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD);
2598+}
2599+
2600+static void
2601+udisks_linux_drive_ata_class_init (UDisksLinuxDriveAtaClass *klass)
2602+{
2603+ GObjectClass *gobject_class;
2604+
2605+ gobject_class = G_OBJECT_CLASS (klass);
2606+ gobject_class->finalize = udisks_linux_drive_ata_finalize;
2607+}
2608+
2609+/**
2610+ * udisks_linux_drive_ata_new:
2611+ *
2612+ * Creates a new #UDisksLinuxDriveAta instance.
2613+ *
2614+ * Returns: A new #UDisksLinuxDriveAta. Free with g_object_unref().
2615+ */
2616+UDisksDriveAta *
2617+udisks_linux_drive_ata_new (void)
2618+{
2619+ return UDISKS_DRIVE_ATA (g_object_new (UDISKS_TYPE_LINUX_DRIVE_ATA,
2620+ NULL));
2621+}
2622+
2623+/* ---------------------------------------------------------------------------------------------------- */
2624+
2625+/* may be called from *any* thread when the SMART data has been updated */
2626+static void
2627+update_smart (UDisksLinuxDriveAta *drive,
2628+ UDisksLinuxDevice *device)
2629+{
2630+ gboolean supported = FALSE;
2631+ gboolean enabled = FALSE;
2632+ guint64 updated = 0;
2633+ gboolean failing = FALSE;
2634+ gdouble temperature = 0.0;
2635+ guint64 power_on_seconds = 0;
2636+ const gchar *selftest_status = NULL;
2637+ gint selftest_percent_remaining = -1;
2638+ gint num_attributes_failing = -1;
2639+ gint num_attributes_failed_in_the_past = -1;
2640+ gint64 num_bad_sectors = 1;
2641+ guint16 word_82 = 0;
2642+ guint16 word_85 = 0;
2643+
2644+ /* ATA8: 7.16 IDENTIFY DEVICE - ECh, PIO Data-In - Table 29 IDENTIFY DEVICE data */
2645+ word_82 = udisks_ata_identify_get_word (device->ata_identify_device_data, 82);
2646+ word_85 = udisks_ata_identify_get_word (device->ata_identify_device_data, 85);
2647+ supported = word_82 & (1<<0);
2648+ enabled = word_85 & (1<<0);
2649+
2650+ G_LOCK (object_lock);
2651+ if ((drive->smart_is_from_blob || enabled) && drive->smart_updated > 0)
2652+ {
2653+ if (drive->smart_is_from_blob)
2654+ supported = enabled = TRUE;
2655+ updated = drive->smart_updated;
2656+ failing = drive->smart_failing;
2657+ temperature = drive->smart_temperature;
2658+ power_on_seconds = drive->smart_power_on_seconds;
2659+ num_attributes_failing = drive->smart_num_attributes_failing;
2660+ num_attributes_failed_in_the_past = drive->smart_num_attributes_failed_in_the_past;
2661+ num_bad_sectors = drive->smart_num_bad_sectors;
2662+ selftest_status = drive->smart_selftest_status;
2663+ selftest_percent_remaining = drive->smart_selftest_percent_remaining;
2664+ }
2665+ G_UNLOCK (object_lock);
2666+
2667+ if (selftest_status == NULL)
2668+ selftest_status = "";
2669+
2670+ g_object_freeze_notify (G_OBJECT (drive));
2671+ udisks_drive_ata_set_smart_supported (UDISKS_DRIVE_ATA (drive), supported);
2672+ udisks_drive_ata_set_smart_enabled (UDISKS_DRIVE_ATA (drive), enabled);
2673+ udisks_drive_ata_set_smart_updated (UDISKS_DRIVE_ATA (drive), updated);
2674+ udisks_drive_ata_set_smart_failing (UDISKS_DRIVE_ATA (drive), failing);
2675+ udisks_drive_ata_set_smart_temperature (UDISKS_DRIVE_ATA (drive), temperature);
2676+ udisks_drive_ata_set_smart_power_on_seconds (UDISKS_DRIVE_ATA (drive), power_on_seconds);
2677+ udisks_drive_ata_set_smart_num_attributes_failing (UDISKS_DRIVE_ATA (drive), num_attributes_failing);
2678+ udisks_drive_ata_set_smart_num_attributes_failed_in_the_past (UDISKS_DRIVE_ATA (drive), num_attributes_failed_in_the_past);
2679+ udisks_drive_ata_set_smart_num_bad_sectors (UDISKS_DRIVE_ATA (drive), num_bad_sectors);
2680+ udisks_drive_ata_set_smart_selftest_status (UDISKS_DRIVE_ATA (drive), selftest_status);
2681+ udisks_drive_ata_set_smart_selftest_percent_remaining (UDISKS_DRIVE_ATA (drive), selftest_percent_remaining);
2682+ g_object_thaw_notify (G_OBJECT (drive));
2683+}
2684+
2685+/* ---------------------------------------------------------------------------------------------------- */
2686+
2687+static void
2688+update_pm (UDisksLinuxDriveAta *drive,
2689+ UDisksLinuxDevice *device)
2690+{
2691+ gboolean pm_supported = FALSE;
2692+ gboolean pm_enabled = FALSE;
2693+ gboolean apm_supported = FALSE;
2694+ gboolean apm_enabled = FALSE;
2695+ gboolean aam_supported = FALSE;
2696+ gboolean aam_enabled = FALSE;
2697+ gboolean write_cache_supported = FALSE;
2698+ gboolean write_cache_enabled = FALSE;
2699+ gint aam_vendor_recommended_value = 0;
2700+ guint16 word_82 = 0;
2701+ guint16 word_83 = 0;
2702+ guint16 word_85 = 0;
2703+ guint16 word_86 = 0;
2704+ guint16 word_94 = 0;
2705+
2706+ /* ATA8: 7.16 IDENTIFY DEVICE - ECh, PIO Data-In - Table 29 IDENTIFY DEVICE data */
2707+ word_82 = udisks_ata_identify_get_word (device->ata_identify_device_data, 82);
2708+ word_83 = udisks_ata_identify_get_word (device->ata_identify_device_data, 83);
2709+ word_85 = udisks_ata_identify_get_word (device->ata_identify_device_data, 85);
2710+ word_86 = udisks_ata_identify_get_word (device->ata_identify_device_data, 86);
2711+ word_94 = udisks_ata_identify_get_word (device->ata_identify_device_data, 94);
2712+
2713+ pm_supported = word_82 & (1<<3);
2714+ pm_enabled = word_85 & (1<<3);
2715+ apm_supported = word_83 & (1<<3);
2716+ apm_enabled = word_86 & (1<<3);
2717+ aam_supported = word_83 & (1<<9);
2718+ aam_enabled = word_86 & (1<<9);
2719+ if (aam_supported)
2720+ aam_vendor_recommended_value = (word_94 >> 8);
2721+ write_cache_supported = word_82 & (1<<5);
2722+ write_cache_enabled = word_85 & (1<<5);
2723+
2724+ g_object_freeze_notify (G_OBJECT (drive));
2725+ udisks_drive_ata_set_pm_supported (UDISKS_DRIVE_ATA (drive), !!pm_supported);
2726+ udisks_drive_ata_set_pm_enabled (UDISKS_DRIVE_ATA (drive), !!pm_enabled);
2727+ udisks_drive_ata_set_apm_supported (UDISKS_DRIVE_ATA (drive), !!apm_supported);
2728+ udisks_drive_ata_set_apm_enabled (UDISKS_DRIVE_ATA (drive), !!apm_enabled);
2729+ udisks_drive_ata_set_aam_supported (UDISKS_DRIVE_ATA (drive), !!aam_supported);
2730+ udisks_drive_ata_set_aam_enabled (UDISKS_DRIVE_ATA (drive), !!aam_enabled);
2731+ udisks_drive_ata_set_aam_vendor_recommended_value (UDISKS_DRIVE_ATA (drive), aam_vendor_recommended_value);
2732+ udisks_drive_ata_set_write_cache_supported (UDISKS_DRIVE_ATA (drive), !!write_cache_supported);
2733+ udisks_drive_ata_set_write_cache_enabled (UDISKS_DRIVE_ATA (drive), !!write_cache_enabled);
2734+ g_object_thaw_notify (G_OBJECT (drive));
2735+}
2736+
2737+/* ---------------------------------------------------------------------------------------------------- */
2738+
2739+static void
2740+update_security (UDisksLinuxDriveAta *drive,
2741+ UDisksLinuxDevice *device)
2742+{
2743+ gint erase_unit = 0;
2744+ gint enhanced_erase_unit = 0;
2745+ gboolean frozen = FALSE;
2746+ gboolean security_supported = FALSE;
2747+ G_GNUC_UNUSED gboolean security_enabled = FALSE;
2748+ guint16 word_82 = 0;
2749+ guint16 word_85 = 0;
2750+ guint16 word_89 = 0;
2751+ guint16 word_90 = 0;
2752+ guint16 word_128 = 0;
2753+
2754+ /* ATA8: 7.16 IDENTIFY DEVICE - ECh, PIO Data-In - Table 29 IDENTIFY DEVICE data */
2755+ word_82 = udisks_ata_identify_get_word (device->ata_identify_device_data, 82);
2756+ word_85 = udisks_ata_identify_get_word (device->ata_identify_device_data, 85);
2757+ word_89 = udisks_ata_identify_get_word (device->ata_identify_device_data, 89);
2758+ word_90 = udisks_ata_identify_get_word (device->ata_identify_device_data, 90);
2759+ word_128 = udisks_ata_identify_get_word (device->ata_identify_device_data, 128);
2760+
2761+ security_supported = word_82 & (1<<1);
2762+ security_enabled = word_85 & (1<<1);
2763+ if (security_supported)
2764+ {
2765+ erase_unit = (word_89 & 0xff) * 2;
2766+ enhanced_erase_unit = (word_90 & 0xff) * 2;
2767+ }
2768+ frozen = word_128 & (1<<3);
2769+
2770+ g_object_freeze_notify (G_OBJECT (drive));
2771+ /* TODO: export Security{Supported,Enabled} properties
2772+ udisks_drive_ata_set_security_supported (UDISKS_DRIVE_ATA (drive), !!security_supported);
2773+ udisks_drive_ata_set_security_enabled (UDISKS_DRIVE_ATA (drive), !!security_enabled);
2774+ */
2775+ udisks_drive_ata_set_security_erase_unit_minutes (UDISKS_DRIVE_ATA (drive), erase_unit);
2776+ udisks_drive_ata_set_security_enhanced_erase_unit_minutes (UDISKS_DRIVE_ATA (drive), enhanced_erase_unit);
2777+ udisks_drive_ata_set_security_frozen (UDISKS_DRIVE_ATA (drive), !!frozen);
2778+ g_object_thaw_notify (G_OBJECT (drive));
2779+}
2780+
2781+/* ---------------------------------------------------------------------------------------------------- */
2782+
2783+/**
2784+ * udisks_linux_drive_ata_update:
2785+ * @drive: A #UDisksLinuxDriveAta.
2786+ * @object: The enclosing #UDisksLinuxDriveObject instance.
2787+ *
2788+ * Updates the interface.
2789+ *
2790+ * Returns: %TRUE if configuration has changed, %FALSE otherwise.
2791+ */
2792+gboolean
2793+udisks_linux_drive_ata_update (UDisksLinuxDriveAta *drive,
2794+ UDisksLinuxDriveObject *object)
2795+{
2796+ UDisksLinuxDevice *device
2797+;
2798+ device = udisks_linux_drive_object_get_device (object, TRUE /* get_hw */);
2799+ if (device == NULL)
2800+ goto out;
2801+
2802+ update_smart (drive, device);
2803+ update_pm (drive, device);
2804+ update_security (drive, device);
2805+
2806+ out:
2807+ if (device != NULL)
2808+ g_object_unref (device);
2809+
2810+ return FALSE;
2811+}
2812+
2813+/* ---------------------------------------------------------------------------------------------------- */
2814+
2815+typedef struct
2816+{
2817+ GVariantBuilder builder;
2818+ gint num_attributes_failing;
2819+ gint num_attributes_failed_in_the_past;
2820+} ParseData;
2821+
2822+static void
2823+parse_attr_cb (SkDisk *d,
2824+ const SkSmartAttributeParsedData *a,
2825+ void *user_data)
2826+{
2827+ ParseData *data = user_data;
2828+ gboolean failed = FALSE;
2829+ gboolean failed_in_the_past = FALSE;
2830+ gint current, worst, threshold;
2831+
2832+ current = a->current_value_valid ? a->current_value : -1;
2833+ worst = a->worst_value_valid ? a->worst_value : -1;
2834+ threshold = a->threshold_valid ? a->threshold : -1;
2835+
2836+ g_variant_builder_add (&data->builder,
2837+ "(ysqiiixia{sv})",
2838+ a->id,
2839+ a->name,
2840+ a->flags,
2841+ current,
2842+ worst,
2843+ threshold,
2844+ a->pretty_value, a->pretty_unit,
2845+ NULL); /* expansion unused for now */
2846+
2847+ if (current > 0 && threshold > 0 && current <= threshold)
2848+ failed = TRUE;
2849+
2850+ if (worst > 0 && threshold > 0 && worst <= threshold)
2851+ failed_in_the_past = TRUE;
2852+
2853+ if (failed)
2854+ data->num_attributes_failing += 1;
2855+
2856+ if (failed_in_the_past)
2857+ data->num_attributes_failed_in_the_past += 1;
2858+}
2859+
2860+static const gchar *
2861+selftest_status_to_string (SkSmartSelfTestExecutionStatus status)
2862+{
2863+ const gchar *ret;
2864+ switch (status)
2865+ {
2866+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_SUCCESS_OR_NEVER:
2867+ ret = "success";
2868+ break;
2869+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_ABORTED:
2870+ ret = "aborted";
2871+ break;
2872+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED:
2873+ ret = "interrupted";
2874+ break;
2875+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_FATAL:
2876+ ret = "fatal";
2877+ break;
2878+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_UNKNOWN:
2879+ ret = "error_unknown";
2880+ break;
2881+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL:
2882+ ret = "error_electrical";
2883+ break;
2884+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_SERVO:
2885+ ret = "error_servo";
2886+ break;
2887+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ:
2888+ ret = "error_read";
2889+ break;
2890+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_HANDLING:
2891+ ret = "error_handling";
2892+ break;
2893+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS:
2894+ ret = "inprogress";
2895+ break;
2896+ default:
2897+ ret = "";
2898+ break;
2899+ }
2900+ return ret;
2901+}
2902+
2903+static gboolean get_pm_state (UDisksLinuxDevice *device, GError **error, guchar *count)
2904+{
2905+ int fd = open (g_udev_device_get_device_file (device->udev_device), O_RDONLY|O_NONBLOCK);
2906+ gboolean rc = FALSE;
2907+ /* ATA8: 7.8 CHECK POWER MODE - E5h, Non-Data */
2908+ UDisksAtaCommandInput input = {.command = 0xe5};
2909+ UDisksAtaCommandOutput output = {0};
2910+
2911+ if (fd == -1)
2912+ {
2913+ g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
2914+ "Error opening device file %s: %m",
2915+ g_udev_device_get_device_file (device->udev_device));
2916+ goto out;
2917+ }
2918+
2919+ if (!udisks_ata_send_command_sync (fd,
2920+ -1,
2921+ UDISKS_ATA_COMMAND_PROTOCOL_NONE,
2922+ &input,
2923+ &output,
2924+ error))
2925+ {
2926+ g_prefix_error (error, "Error sending ATA command CHECK POWER MODE: ");
2927+ goto out;
2928+ }
2929+ /* count field is used for the state, see ATA8: table 102 */
2930+ *count = output.count;
2931+ rc = TRUE;
2932+ out:
2933+ if (fd != -1)
2934+ close(fd);
2935+ return rc;
2936+}
2937+
2938+/**
2939+ * udisks_linux_drive_ata_refresh_smart_sync:
2940+ * @drive: The #UDisksLinuxDriveAta to refresh.
2941+ * @nowakeup: If %TRUE, will not wake up the disk if asleep.
2942+ * @simulate_path: If not %NULL, the path of a file with a libatasmart blob to use.
2943+ * @cancellable: A #GCancellable or %NULL.
2944+ * @error: Return location for error.
2945+ *
2946+ * Synchronously refreshes ATA S.M.A.R.T. data on @drive using one of
2947+ * the physical drives associated with it. The calling thread is
2948+ * blocked until the data has been obtained.
2949+ *
2950+ * If @nowake is %TRUE and the disk is in a sleep state this fails
2951+ * with %UDISKS_ERROR_WOULD_WAKEUP.
2952+ *
2953+ * This may only be called if @drive has been associated with a
2954+ * #UDisksLinuxDriveObject instance.
2955+ *
2956+ * This method may be called from any thread.
2957+ *
2958+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
2959+ */
2960+gboolean
2961+udisks_linux_drive_ata_refresh_smart_sync (UDisksLinuxDriveAta *drive,
2962+ gboolean nowakeup,
2963+ const gchar *simulate_path,
2964+ GCancellable *cancellable,
2965+ GError **error)
2966+{
2967+ UDisksLinuxDriveObject *object;
2968+ UDisksLinuxDevice *device = NULL;
2969+ gboolean ret = FALSE;
2970+ SkDisk *d = NULL;
2971+ SkBool awake;
2972+ SkBool good;
2973+ uint64_t temp_mkelvin = 0;
2974+ uint64_t power_on_msec = 0;
2975+ uint64_t num_bad_sectors = 0;
2976+ const SkSmartParsedData *data;
2977+ ParseData parse_data;
2978+
2979+ object = udisks_daemon_util_dup_object (drive, error);
2980+ if (object == NULL)
2981+ goto out;
2982+
2983+ if (drive->secure_erase_in_progress)
2984+ {
2985+ g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_DEVICE_BUSY,
2986+ "Secure erase in progress");
2987+ goto out;
2988+ }
2989+
2990+ device = udisks_linux_drive_object_get_device (object, TRUE /* get_hw */);
2991+ g_assert (device != NULL);
2992+
2993+ /* TODO: use cancellable */
2994+
2995+ if (simulate_path != NULL)
2996+ {
2997+ gchar *blob;
2998+ gsize blob_len;
2999+
3000+ if (!g_file_get_contents (simulate_path,
3001+ &blob,
3002+ &blob_len,
3003+ error))
3004+ {
3005+ goto out;
3006+ }
3007+
3008+ if (sk_disk_open (NULL, &d) != 0)
3009+ {
3010+ g_set_error (error,
3011+ UDISKS_ERROR,
3012+ UDISKS_ERROR_FAILED,
3013+ "sk_disk_open: %m");
3014+ goto out;
3015+ }
3016+
3017+ if (sk_disk_set_blob (d, blob, blob_len) != 0)
3018+ {
3019+ g_set_error (error,
3020+ UDISKS_ERROR,
3021+ UDISKS_ERROR_FAILED,
3022+ "sk_disk_set_blob: %m");
3023+ g_free (blob);
3024+ goto out;
3025+ }
3026+ g_free (blob);
3027+ }
3028+ else
3029+ {
3030+ guchar count;
3031+ if (!get_pm_state(device, error, &count))
3032+ goto out;
3033+ awake = count == 0xFF || count == 0x80;
3034+ /* don't wake up disk unless specically asked to */
3035+ if (nowakeup && !awake)
3036+ {
3037+ g_set_error (error,
3038+ UDISKS_ERROR,
3039+ UDISKS_ERROR_WOULD_WAKEUP,
3040+ "Disk is in sleep mode and the nowakeup option was passed");
3041+ goto out;
3042+ }
3043+ }
3044+
3045+ if (sk_disk_open (g_udev_device_get_device_file (device->udev_device), &d) != 0)
3046+ {
3047+ g_set_error (error,
3048+ UDISKS_ERROR,
3049+ UDISKS_ERROR_FAILED,
3050+ "sk_disk_open: %m");
3051+ goto out;
3052+ }
3053+
3054+ if (sk_disk_smart_read_data (d) != 0)
3055+ {
3056+ g_set_error (error,
3057+ UDISKS_ERROR,
3058+ UDISKS_ERROR_FAILED,
3059+ "sk_disk_smart_read_data: %m");
3060+ goto out;
3061+ }
3062+
3063+ if (sk_disk_smart_status (d, &good) != 0)
3064+ {
3065+ g_set_error (error,
3066+ UDISKS_ERROR,
3067+ UDISKS_ERROR_FAILED,
3068+ "sk_disk_smart_status: %m");
3069+ goto out;
3070+ }
3071+
3072+ if (sk_disk_smart_parse (d, &data) != 0)
3073+ {
3074+ g_set_error (error,
3075+ UDISKS_ERROR,
3076+ UDISKS_ERROR_FAILED,
3077+ "sk_disk_smart_parse: %m");
3078+ goto out;
3079+ }
3080+
3081+ /* don't care if these are failing or not */
3082+ sk_disk_smart_get_temperature (d, &temp_mkelvin);
3083+ sk_disk_smart_get_power_on (d, &power_on_msec);
3084+ sk_disk_smart_get_bad (d, &num_bad_sectors);
3085+
3086+ memset (&parse_data, 0, sizeof (ParseData));
3087+ g_variant_builder_init (&parse_data.builder, G_VARIANT_TYPE ("a(ysqiiixia{sv})"));
3088+ sk_disk_smart_parse_attributes (d, parse_attr_cb, &parse_data);
3089+
3090+ G_LOCK (object_lock);
3091+ drive->smart_is_from_blob = (simulate_path != NULL);
3092+ drive->smart_updated = time (NULL);
3093+ drive->smart_failing = !good;
3094+ drive->smart_temperature = temp_mkelvin / 1000.0;
3095+ drive->smart_power_on_seconds = power_on_msec / 1000.0;
3096+ drive->smart_num_attributes_failing = parse_data.num_attributes_failing;
3097+ drive->smart_num_attributes_failed_in_the_past = parse_data.num_attributes_failed_in_the_past;
3098+ drive->smart_num_bad_sectors = num_bad_sectors;
3099+ drive->smart_selftest_status = selftest_status_to_string (data->self_test_execution_status);
3100+ drive->smart_selftest_percent_remaining = data->self_test_execution_percent_remaining;
3101+ if (drive->smart_attributes != NULL)
3102+ g_variant_unref (drive->smart_attributes);
3103+ drive->smart_attributes = g_variant_ref_sink (g_variant_builder_end (&parse_data.builder));
3104+ G_UNLOCK (object_lock);
3105+
3106+ update_smart (drive, device);
3107+
3108+ ret = TRUE;
3109+
3110+ out:
3111+ g_clear_object (&device);
3112+ if (d != NULL)
3113+ sk_disk_free (d);
3114+ g_clear_object (&object);
3115+ return ret;
3116+}
3117+
3118+/* ---------------------------------------------------------------------------------------------------- */
3119+
3120+/**
3121+ * udisks_linux_drive_ata_smart_selftest_sync:
3122+ * @drive: A #UDisksLinuxDriveAta.
3123+ * @type: The type of selftest to run.
3124+ * @cancellable: (allow-none): A #GCancellable that can be used to cancel the operation or %NULL.
3125+ * @error: Return location for error or %NULL.
3126+ *
3127+ * Starts (or aborts) a SMART self-test on @drive. Valid values for
3128+ * @type includes 'short', 'extended', 'conveyance' and 'abort'.
3129+ *
3130+ * The calling thread is blocked while sending the command to the
3131+ * drive but will return immediately after the drive acknowledges the
3132+ * command.
3133+ *
3134+ * Returns: %TRUE if the operation succeed, %FALSE if @error is set.
3135+ */
3136+gboolean
3137+udisks_linux_drive_ata_smart_selftest_sync (UDisksLinuxDriveAta *drive,
3138+ const gchar *type,
3139+ GCancellable *cancellable,
3140+ GError **error)
3141+{
3142+ UDisksLinuxDriveObject *object;
3143+ UDisksLinuxDevice *device;
3144+ SkDisk *d = NULL;
3145+ gboolean ret = FALSE;
3146+ SkSmartSelfTest test;
3147+
3148+ object = udisks_daemon_util_dup_object (drive, error);
3149+ if (object == NULL)
3150+ goto out;
3151+
3152+ device = udisks_linux_drive_object_get_device (object, TRUE /* get_hw */);
3153+ g_assert (device != NULL);
3154+
3155+ if (g_strcmp0 (type, "short") == 0)
3156+ test = SK_SMART_SELF_TEST_SHORT;
3157+ else if (g_strcmp0 (type, "extended") == 0)
3158+ test = SK_SMART_SELF_TEST_EXTENDED;
3159+ else if (g_strcmp0 (type, "conveyance") == 0)
3160+ test = SK_SMART_SELF_TEST_CONVEYANCE;
3161+ else if (g_strcmp0 (type, "abort") == 0)
3162+ test = SK_SMART_SELF_TEST_ABORT;
3163+ else
3164+ {
3165+ g_set_error (error,
3166+ UDISKS_ERROR,
3167+ UDISKS_ERROR_FAILED,
3168+ "unknown type %s", type);
3169+ goto out;
3170+ }
3171+
3172+ if (sk_disk_open (g_udev_device_get_device_file (device->udev_device), &d) != 0)
3173+ {
3174+ g_set_error (error,
3175+ UDISKS_ERROR,
3176+ UDISKS_ERROR_FAILED,
3177+ "sk_disk_open: %m");
3178+ goto out;
3179+ }
3180+
3181+ if (sk_disk_smart_self_test (d, test) != 0)
3182+ {
3183+ g_set_error (error,
3184+ UDISKS_ERROR,
3185+ UDISKS_ERROR_FAILED,
3186+ "sk_disk_smart_self_test: %m");
3187+ goto out;
3188+ }
3189+
3190+ ret = TRUE;
3191+
3192+ out:
3193+ g_clear_object (&device);
3194+ if (d != NULL)
3195+ sk_disk_free (d);
3196+ g_clear_object (&object);
3197+ return ret;
3198+}
3199+
3200+/* ---------------------------------------------------------------------------------------------------- */
3201+
3202+static gboolean
3203+handle_smart_update (UDisksDriveAta *_drive,
3204+ GDBusMethodInvocation *invocation,
3205+ GVariant *options)
3206+{
3207+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (_drive);
3208+ UDisksLinuxDriveObject *object;
3209+ UDisksLinuxBlockObject *block_object = NULL;
3210+ UDisksDaemon *daemon;
3211+ gboolean nowakeup = FALSE;
3212+ const gchar *atasmart_blob = NULL;
3213+ GError *error;
3214+ const gchar *message;
3215+ const gchar *action_id;
3216+
3217+ daemon = NULL;
3218+
3219+ error = NULL;
3220+ object = udisks_daemon_util_dup_object (drive, &error);
3221+ if (object == NULL)
3222+ {
3223+ g_dbus_method_invocation_take_error (invocation, error);
3224+ goto out;
3225+ }
3226+
3227+ daemon = udisks_linux_drive_object_get_daemon (object);
3228+ block_object = udisks_linux_drive_object_get_block (object, TRUE);
3229+ if (block_object == NULL)
3230+ {
3231+ g_dbus_method_invocation_return_error (invocation,
3232+ UDISKS_ERROR,
3233+ UDISKS_ERROR_FAILED,
3234+ "Unable to find physical block device for drive");
3235+ goto out;
3236+ }
3237+
3238+ g_variant_lookup (options, "nowakeup", "b", &nowakeup);
3239+ g_variant_lookup (options, "atasmart_blob", "s", &atasmart_blob);
3240+
3241+ /* Translators: Shown in authentication dialog when the user
3242+ * refreshes SMART data from a disk.
3243+ *
3244+ * Do not translate $(drive), it's a placeholder and
3245+ * will be replaced by the name of the drive/device in question
3246+ */
3247+ message = N_("Authentication is required to update SMART data from $(drive)");
3248+ action_id = "org.freedesktop.udisks2.ata-smart-update";
3249+
3250+ if (atasmart_blob != NULL)
3251+ {
3252+ /* Translators: Shown in authentication dialog when the user
3253+ * tries to simulate SMART data from a libatasmart blob.
3254+ *
3255+ * Do not translate $(drive), it's a placeholder and
3256+ * will be replaced by the name of the drive/device in question
3257+ */
3258+ message = N_("Authentication is required to set SMART data from a blob on $(drive)");
3259+ action_id = "org.freedesktop.udisks2.ata-smart-simulate";
3260+ }
3261+ else
3262+ {
3263+ if (!udisks_drive_ata_get_smart_supported (UDISKS_DRIVE_ATA (drive)))
3264+ {
3265+ g_dbus_method_invocation_return_error (invocation,
3266+ UDISKS_ERROR,
3267+ UDISKS_ERROR_FAILED,
3268+ "SMART is not supported");
3269+ goto out;
3270+ }
3271+
3272+ if (!udisks_drive_ata_get_smart_enabled (UDISKS_DRIVE_ATA (drive)))
3273+ {
3274+ g_dbus_method_invocation_return_error (invocation,
3275+ UDISKS_ERROR,
3276+ UDISKS_ERROR_FAILED,
3277+ "SMART is not enabled");
3278+ goto out;
3279+ }
3280+ }
3281+
3282+ /* Check that the user is authorized */
3283+ if (!udisks_daemon_util_check_authorization_sync (daemon,
3284+ UDISKS_OBJECT (block_object),
3285+ action_id,
3286+ options,
3287+ message,
3288+ invocation))
3289+ goto out;
3290+
3291+ error = NULL;
3292+ if (!udisks_linux_drive_ata_refresh_smart_sync (drive,
3293+ nowakeup,
3294+ atasmart_blob,
3295+ NULL, /* cancellable */
3296+ &error))
3297+ {
3298+ udisks_warning ("Error updating ATA smart for %s: %s (%s, %d)",
3299+ g_dbus_object_get_object_path (G_DBUS_OBJECT (object)),
3300+ error->message, g_quark_to_string (error->domain), error->code);
3301+ g_dbus_method_invocation_take_error (invocation, error);
3302+ goto out;
3303+ }
3304+
3305+ udisks_drive_ata_complete_smart_update (UDISKS_DRIVE_ATA (drive), invocation);
3306+
3307+ out:
3308+ g_clear_object (&block_object);
3309+ g_clear_object (&object);
3310+ return TRUE; /* returning TRUE means that we handled the method invocation */
3311+}
3312+
3313+/* ---------------------------------------------------------------------------------------------------- */
3314+
3315+static gboolean
3316+handle_smart_get_attributes (UDisksDriveAta *_drive,
3317+ GDBusMethodInvocation *invocation,
3318+ GVariant *options)
3319+{
3320+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (_drive);
3321+
3322+ G_LOCK (object_lock);
3323+ if (drive->smart_attributes == NULL)
3324+ {
3325+ g_dbus_method_invocation_return_error (invocation,
3326+ UDISKS_ERROR,
3327+ UDISKS_ERROR_FAILED,
3328+ "SMART data not collected");
3329+ }
3330+ else
3331+ {
3332+ udisks_drive_ata_complete_smart_get_attributes (UDISKS_DRIVE_ATA (drive), invocation,
3333+ drive->smart_attributes);
3334+ }
3335+ G_UNLOCK (object_lock);
3336+
3337+ return TRUE; /* returning TRUE means that we handled the method invocation */
3338+}
3339+
3340+/* ---------------------------------------------------------------------------------------------------- */
3341+
3342+static gboolean
3343+handle_smart_selftest_abort (UDisksDriveAta *_drive,
3344+ GDBusMethodInvocation *invocation,
3345+ GVariant *options)
3346+{
3347+ UDisksLinuxDriveObject *object;
3348+ UDisksLinuxBlockObject *block_object;
3349+ UDisksDaemon *daemon;
3350+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (_drive);
3351+ GError *error;
3352+
3353+ error = NULL;
3354+ object = udisks_daemon_util_dup_object (drive, &error);
3355+ if (object == NULL)
3356+ {
3357+ g_dbus_method_invocation_take_error (invocation, error);
3358+ goto out;
3359+ }
3360+
3361+ daemon = udisks_linux_drive_object_get_daemon (object);
3362+ block_object = udisks_linux_drive_object_get_block (object, TRUE);
3363+ if (block_object == NULL)
3364+ {
3365+ g_dbus_method_invocation_return_error (invocation,
3366+ UDISKS_ERROR,
3367+ UDISKS_ERROR_FAILED,
3368+ "Unable to find physical block device for drive");
3369+ goto out;
3370+ }
3371+
3372+ if (!udisks_drive_ata_get_smart_supported (UDISKS_DRIVE_ATA (drive)) ||
3373+ !udisks_drive_ata_get_smart_enabled (UDISKS_DRIVE_ATA (drive)))
3374+ {
3375+ g_dbus_method_invocation_return_error (invocation,
3376+ UDISKS_ERROR,
3377+ UDISKS_ERROR_FAILED,
3378+ "SMART is not supported or enabled");
3379+ goto out;
3380+ }
3381+
3382+ if (!udisks_daemon_util_check_authorization_sync (daemon,
3383+ UDISKS_OBJECT (block_object),
3384+ "org.freedesktop.udisks2.ata-smart-selftest",
3385+ options,
3386+ /* Translators: Shown in authentication dialog when the user
3387+ * aborts a running SMART self-test.
3388+ *
3389+ * Do not translate $(drive), it's a placeholder and
3390+ * will be replaced by the name of the drive/device in question
3391+ */
3392+ N_("Authentication is required to abort a SMART self-test on $(drive)"),
3393+ invocation))
3394+ goto out;
3395+
3396+ error = NULL;
3397+ if (!udisks_linux_drive_ata_smart_selftest_sync (drive,
3398+ "abort",
3399+ NULL, /* cancellable */
3400+ &error))
3401+ {
3402+ udisks_warning ("Error aborting SMART selftest for %s: %s (%s, %d)",
3403+ g_dbus_object_get_object_path (G_DBUS_OBJECT (object)),
3404+ error->message, g_quark_to_string (error->domain), error->code);
3405+ g_dbus_method_invocation_take_error (invocation, error);
3406+ goto out;
3407+ }
3408+
3409+ /* This wakes up the selftest thread */
3410+ G_LOCK (object_lock);
3411+ if (drive->selftest_job != NULL)
3412+ {
3413+ g_cancellable_cancel (udisks_base_job_get_cancellable (UDISKS_BASE_JOB (drive->selftest_job)));
3414+ }
3415+ G_UNLOCK (object_lock);
3416+ /* TODO: wait for the selftest thread to terminate */
3417+
3418+ error = NULL;
3419+ if (!udisks_linux_drive_ata_refresh_smart_sync (drive,
3420+ FALSE, /* nowakeup */
3421+ NULL, /* blob */
3422+ NULL, /* cancellable */
3423+ &error))
3424+ {
3425+ udisks_warning ("Error updating ATA smart for %s: %s (%s, %d)",
3426+ g_dbus_object_get_object_path (G_DBUS_OBJECT (object)),
3427+ error->message, g_quark_to_string (error->domain), error->code);
3428+ g_dbus_method_invocation_take_error (invocation, error);
3429+ goto out;
3430+ }
3431+
3432+ udisks_drive_ata_complete_smart_selftest_abort (UDISKS_DRIVE_ATA (drive), invocation);
3433+
3434+ out:
3435+ g_clear_object (&object);
3436+ return TRUE; /* returning TRUE means that we handled the method invocation */
3437+}
3438+
3439+/* ---------------------------------------------------------------------------------------------------- */
3440+
3441+static gboolean
3442+selftest_job_func (UDisksThreadedJob *job,
3443+ GCancellable *cancellable,
3444+ gpointer user_data,
3445+ GError **error)
3446+{
3447+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (user_data);
3448+ UDisksLinuxDriveObject *object;
3449+ gboolean ret = FALSE;
3450+
3451+ object = udisks_daemon_util_dup_object (drive, error);
3452+ if (object == NULL)
3453+ goto out;
3454+
3455+ udisks_job_set_progress_valid (UDISKS_JOB (job), TRUE);
3456+ udisks_job_set_progress (UDISKS_JOB (job), 0.0);
3457+
3458+ while (TRUE)
3459+ {
3460+ gboolean still_in_progress;
3461+ GPollFD poll_fd;
3462+ gdouble progress;
3463+
3464+ if (!udisks_linux_drive_ata_refresh_smart_sync (drive,
3465+ FALSE, /* nowakeup */
3466+ NULL, /* blob */
3467+ NULL, /* cancellable */
3468+ error))
3469+ {
3470+ udisks_warning ("Error updating ATA smart for %s while polling during self-test: %s (%s, %d)",
3471+ g_dbus_object_get_object_path (G_DBUS_OBJECT (object)),
3472+ (*error)->message, g_quark_to_string ((*error)->domain), (*error)->code);
3473+ goto out;
3474+ }
3475+
3476+ /* TODO: set estimation properties etc. on the Job object */
3477+
3478+ G_LOCK (object_lock);
3479+ still_in_progress = (g_strcmp0 (drive->smart_selftest_status, "inprogress") == 0);
3480+ progress = (100.0 - drive->smart_selftest_percent_remaining) / 100.0;
3481+ G_UNLOCK (object_lock);
3482+ if (!still_in_progress)
3483+ {
3484+ ret = TRUE;
3485+ goto out;
3486+ }
3487+
3488+ if (progress < 0.0)
3489+ progress = 0.0;
3490+ if (progress > 1.0)
3491+ progress = 1.0;
3492+ udisks_job_set_progress (UDISKS_JOB (job), progress);
3493+
3494+ /* Sleep for 30 seconds or until we're cancelled */
3495+ if (g_cancellable_make_pollfd (cancellable, &poll_fd))
3496+ {
3497+ gint poll_ret;
3498+ do
3499+ {
3500+ poll_ret = g_poll (&poll_fd, 1, 30 * 1000);
3501+ }
3502+ while (poll_ret == -1 && errno == EINTR);
3503+ g_cancellable_release_fd (cancellable);
3504+ }
3505+ else
3506+ {
3507+ g_set_error (error,
3508+ UDISKS_ERROR,
3509+ UDISKS_ERROR_FAILED,
3510+ "Error creating pollfd for cancellable");
3511+ goto out;
3512+ }
3513+
3514+ /* Check if we're cancelled */
3515+ if (g_cancellable_is_cancelled (cancellable))
3516+ {
3517+ GError *c_error;
3518+
3519+ g_set_error (error,
3520+ UDISKS_ERROR,
3521+ UDISKS_ERROR_CANCELLED,
3522+ "Self-test was cancelled");
3523+
3524+ /* OK, cancelled ... still need to a) abort the test; and b) update the status */
3525+ c_error = NULL;
3526+ if (!udisks_linux_drive_ata_smart_selftest_sync (drive,
3527+ "abort",
3528+ NULL, /* cancellable */
3529+ &c_error))
3530+ {
3531+ udisks_warning ("Error aborting SMART selftest for %s on cancel path: %s (%s, %d)",
3532+ g_dbus_object_get_object_path (G_DBUS_OBJECT (object)),
3533+ c_error->message, g_quark_to_string (c_error->domain), c_error->code);
3534+ g_clear_error (&c_error);
3535+ }
3536+ if (!udisks_linux_drive_ata_refresh_smart_sync (drive,
3537+ FALSE, /* nowakeup */
3538+ NULL, /* blob */
3539+ NULL, /* cancellable */
3540+ &c_error))
3541+ {
3542+ udisks_warning ("Error updating ATA smart for %s on cancel path: %s (%s, %d)",
3543+ g_dbus_object_get_object_path (G_DBUS_OBJECT (object)),
3544+ c_error->message, g_quark_to_string (c_error->domain), c_error->code);
3545+ g_clear_error (&c_error);
3546+ }
3547+ goto out;
3548+ }
3549+ }
3550+
3551+ ret = TRUE;
3552+
3553+ out:
3554+ /* terminate the job */
3555+ G_LOCK (object_lock);
3556+ drive->selftest_job = NULL;
3557+ G_UNLOCK (object_lock);
3558+ g_clear_object (&object);
3559+ return ret;
3560+}
3561+
3562+
3563+static gboolean
3564+handle_smart_selftest_start (UDisksDriveAta *_drive,
3565+ GDBusMethodInvocation *invocation,
3566+ const gchar *type,
3567+ GVariant *options)
3568+{
3569+ UDisksLinuxDriveObject *object;
3570+ UDisksLinuxBlockObject *block_object;
3571+ UDisksDaemon *daemon;
3572+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (_drive);
3573+ uid_t caller_uid;
3574+ gid_t caller_gid;
3575+ GError *error;
3576+
3577+ error = NULL;
3578+ object = udisks_daemon_util_dup_object (drive, &error);
3579+ if (object == NULL)
3580+ {
3581+ g_dbus_method_invocation_take_error (invocation, error);
3582+ goto out;
3583+ }
3584+
3585+ daemon = udisks_linux_drive_object_get_daemon (object);
3586+ block_object = udisks_linux_drive_object_get_block (object, TRUE);
3587+ if (block_object == NULL)
3588+ {
3589+ g_dbus_method_invocation_return_error (invocation,
3590+ UDISKS_ERROR,
3591+ UDISKS_ERROR_FAILED,
3592+ "Unable to find physical block device for drive");
3593+ goto out;
3594+ }
3595+
3596+ if (!udisks_drive_ata_get_smart_supported (UDISKS_DRIVE_ATA (drive)) ||
3597+ !udisks_drive_ata_get_smart_enabled (UDISKS_DRIVE_ATA (drive)))
3598+ {
3599+ g_dbus_method_invocation_return_error (invocation,
3600+ UDISKS_ERROR,
3601+ UDISKS_ERROR_FAILED,
3602+ "SMART is not supported or enabled");
3603+ goto out;
3604+ }
3605+
3606+ error = NULL;
3607+ if (!udisks_daemon_util_get_caller_uid_sync (daemon,
3608+ invocation,
3609+ NULL /* GCancellable */,
3610+ &caller_uid,
3611+ &caller_gid,
3612+ NULL,
3613+ &error))
3614+ {
3615+ g_dbus_method_invocation_return_gerror (invocation, error);
3616+ g_error_free (error);
3617+ goto out;
3618+ }
3619+
3620+ G_LOCK (object_lock);
3621+ if (drive->selftest_job != NULL)
3622+ {
3623+ g_dbus_method_invocation_return_error (invocation,
3624+ UDISKS_ERROR,
3625+ UDISKS_ERROR_FAILED,
3626+ "There is already SMART self-test running");
3627+ G_UNLOCK (object_lock);
3628+ goto out;
3629+ }
3630+ G_UNLOCK (object_lock);
3631+
3632+ if (!udisks_daemon_util_check_authorization_sync (daemon,
3633+ UDISKS_OBJECT (block_object),
3634+ "org.freedesktop.udisks2.ata-smart-selftest",
3635+ options,
3636+ /* Translators: Shown in authentication dialog when the user
3637+ * initiates a SMART self-test.
3638+ *
3639+ * Do not translate $(drive), it's a placeholder and
3640+ * will be replaced by the name of the drive/device in question
3641+ */
3642+ N_("Authentication is required to start a SMART self-test on $(drive)"),
3643+ invocation))
3644+ goto out;
3645+
3646+ error = NULL;
3647+ if (!udisks_linux_drive_ata_smart_selftest_sync (drive,
3648+ type,
3649+ NULL, /* cancellable */
3650+ &error))
3651+ {
3652+ udisks_warning ("Error starting SMART selftest for %s: %s (%s, %d)",
3653+ g_dbus_object_get_object_path (G_DBUS_OBJECT (object)),
3654+ error->message, g_quark_to_string (error->domain), error->code);
3655+ g_dbus_method_invocation_take_error (invocation, error);
3656+ goto out;
3657+ }
3658+
3659+ G_LOCK (object_lock);
3660+ if (drive->selftest_job == NULL)
3661+ {
3662+ drive->selftest_job = UDISKS_THREADED_JOB (udisks_daemon_launch_threaded_job (daemon,
3663+ UDISKS_OBJECT (object),
3664+ "ata-smart-selftest", caller_uid,
3665+ selftest_job_func,
3666+ g_object_ref (drive),
3667+ g_object_unref,
3668+ NULL)); /* GCancellable */
3669+ }
3670+ G_UNLOCK (object_lock);
3671+
3672+ udisks_drive_ata_complete_smart_selftest_start (UDISKS_DRIVE_ATA (drive), invocation);
3673+
3674+ out:
3675+ g_clear_object (&object);
3676+ return TRUE; /* returning TRUE means that we handled the method invocation */
3677+}
3678+
3679+/* ---------------------------------------------------------------------------------------------------- */
3680+
3681+static gboolean
3682+handle_pm_get_state (UDisksDriveAta *_drive,
3683+ GDBusMethodInvocation *invocation,
3684+ GVariant *options)
3685+{
3686+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (_drive);
3687+ UDisksLinuxDriveObject *object = NULL;
3688+ UDisksDaemon *daemon;
3689+ UDisksLinuxDevice *device = NULL;
3690+ GError *error = NULL;
3691+ const gchar *message;
3692+ const gchar *action_id;
3693+ guchar count;
3694+
3695+ object = udisks_daemon_util_dup_object (drive, &error);
3696+ if (object == NULL)
3697+ {
3698+ g_dbus_method_invocation_take_error (invocation, error);
3699+ goto out;
3700+ }
3701+
3702+ daemon = udisks_linux_drive_object_get_daemon (object);
3703+
3704+ if (!udisks_drive_ata_get_pm_supported (UDISKS_DRIVE_ATA (drive)) ||
3705+ !udisks_drive_ata_get_pm_enabled (UDISKS_DRIVE_ATA (drive)))
3706+ {
3707+ g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
3708+ "PM is not supported or enabled");
3709+ goto out;
3710+ }
3711+
3712+ /* If a secure erase is in progress, the CHECK POWER command would be queued
3713+ * until the erase has been completed (can easily take hours). So just return
3714+ * 0xff which is active/idle...
3715+ */
3716+ if (drive->secure_erase_in_progress)
3717+ {
3718+ g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_DEVICE_BUSY,
3719+ "A secure erase is in progress");
3720+ goto out;
3721+ }
3722+
3723+ /* Translators: Shown in authentication dialog when the user
3724+ * requests the power state of a drive.
3725+ *
3726+ * Do not translate $(drive), it's a placeholder and
3727+ * will be replaced by the name of the drive/device in question
3728+ */
3729+ message = N_("Authentication is required to check power state for $(drive)");
3730+ action_id = "org.freedesktop.udisks2.ata-check-power";
3731+
3732+ /* TODO: maybe not check with polkit if this is OK (consider gnome-disks(1) polling all drives every few seconds) */
3733+
3734+ /* Check that the user is authorized */
3735+ if (!udisks_daemon_util_check_authorization_sync (daemon,
3736+ UDISKS_OBJECT (object),
3737+ action_id,
3738+ options,
3739+ message,
3740+ invocation))
3741+ goto out;
3742+
3743+ device = udisks_linux_drive_object_get_device (object, TRUE /* get_hw */);
3744+ if (device == NULL)
3745+ {
3746+ g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
3747+ "No udev device");
3748+ goto out;
3749+ }
3750+ if (get_pm_state (device, &error, &count))
3751+ udisks_drive_ata_complete_pm_get_state (_drive, invocation, count);
3752+ else g_dbus_method_invocation_take_error (invocation, error);
3753+
3754+ out:
3755+ g_clear_object (&device);
3756+ g_clear_object (&object);
3757+ return TRUE; /* returning TRUE means that we handled the method invocation */
3758+}
3759+
3760+/* ---------------------------------------------------------------------------------------------------- */
3761+
3762+static gboolean
3763+handle_pm_standby (UDisksDriveAta *_drive,
3764+ GDBusMethodInvocation *invocation,
3765+ GVariant *options)
3766+{
3767+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (_drive);
3768+ UDisksLinuxDriveObject *object = NULL;
3769+ UDisksLinuxBlockObject *block_object = NULL;
3770+ UDisksBlock *block = NULL;
3771+ UDisksDaemon *daemon;
3772+ UDisksLinuxDevice *device = NULL;
3773+ gint fd = -1;
3774+ GError *error = NULL;
3775+ const gchar *message;
3776+ const gchar *action_id;
3777+ pid_t caller_pid;
3778+
3779+ object = udisks_daemon_util_dup_object (drive, &error);
3780+ if (object == NULL)
3781+ {
3782+ g_dbus_method_invocation_take_error (invocation, error);
3783+ goto out;
3784+ }
3785+
3786+ block_object = udisks_linux_drive_object_get_block (object, FALSE);
3787+ if (block_object == NULL)
3788+ {
3789+ g_dbus_method_invocation_return_error (invocation,
3790+ UDISKS_ERROR,
3791+ UDISKS_ERROR_FAILED,
3792+ "Unable to find block device for drive");
3793+ goto out;
3794+ }
3795+ block = udisks_object_peek_block (UDISKS_OBJECT (block_object));
3796+
3797+ daemon = udisks_linux_drive_object_get_daemon (object);
3798+
3799+ if (!udisks_drive_ata_get_pm_supported (UDISKS_DRIVE_ATA (drive)) ||
3800+ !udisks_drive_ata_get_pm_enabled (UDISKS_DRIVE_ATA (drive)))
3801+ {
3802+ g_dbus_method_invocation_return_error (invocation,
3803+ UDISKS_ERROR,
3804+ UDISKS_ERROR_FAILED,
3805+ "PM is not supported or enabled");
3806+ goto out;
3807+ }
3808+
3809+ error = NULL;
3810+ if (!udisks_daemon_util_get_caller_pid_sync (daemon,
3811+ invocation,
3812+ NULL /* GCancellable */,
3813+ &caller_pid,
3814+ &error))
3815+ {
3816+ g_dbus_method_invocation_return_gerror (invocation, error);
3817+ g_error_free (error);
3818+ goto out;
3819+ }
3820+
3821+ /* Translators: Shown in authentication dialog when the user
3822+ * tries to put a drive into standby mode.
3823+ *
3824+ * Do not translate $(drive), it's a placeholder and
3825+ * will be replaced by the name of the drive/device in question
3826+ */
3827+ message = N_("Authentication is required to put $(drive) in standby mode");
3828+ action_id = "org.freedesktop.udisks2.ata-standby";
3829+ if (udisks_block_get_hint_system (block))
3830+ {
3831+ action_id = "org.freedesktop.udisks2.ata-standby-system";
3832+ }
3833+ else if (!udisks_daemon_util_on_same_seat (daemon, UDISKS_OBJECT (object), caller_pid))
3834+ {
3835+ action_id = "org.freedesktop.udisks2.ata-standby-other-seat";
3836+ }
3837+
3838+ /* Check that the user is authorized */
3839+ if (!udisks_daemon_util_check_authorization_sync (daemon,
3840+ UDISKS_OBJECT (object),
3841+ action_id,
3842+ options,
3843+ message,
3844+ invocation))
3845+ goto out;
3846+
3847+ device = udisks_linux_drive_object_get_device (object, TRUE /* get_hw */);
3848+ if (device == NULL)
3849+ {
3850+ g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
3851+ "No udev device");
3852+ goto out;
3853+ }
3854+
3855+ fd = open (g_udev_device_get_device_file (device->udev_device), O_RDONLY|O_NONBLOCK);
3856+ if (fd == -1)
3857+ {
3858+ g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
3859+ "Error opening device file %s: %m",
3860+ g_udev_device_get_device_file (device->udev_device));
3861+ goto out;
3862+ }
3863+
3864+ {
3865+ /* ATA8: 7.55 STANDBY IMMEDIATE - E0h, Non-Data */
3866+ UDisksAtaCommandInput input = {.command = 0xe0};
3867+ UDisksAtaCommandOutput output = {0};
3868+ if (!udisks_ata_send_command_sync (fd,
3869+ -1,
3870+ UDISKS_ATA_COMMAND_PROTOCOL_NONE,
3871+ &input,
3872+ &output,
3873+ &error))
3874+ {
3875+ g_prefix_error (&error, "Error sending ATA command STANDBY IMMEDIATE: ");
3876+ g_dbus_method_invocation_take_error (invocation, error);
3877+ goto out;
3878+ }
3879+ udisks_drive_ata_complete_pm_standby (_drive, invocation);
3880+ }
3881+
3882+ out:
3883+ if (fd != -1)
3884+ close (fd);
3885+ g_clear_object (&device);
3886+ g_clear_object (&block_object);
3887+ g_clear_object (&object);
3888+ return TRUE; /* returning TRUE means that we handled the method invocation */
3889+}
3890+
3891+/* ---------------------------------------------------------------------------------------------------- */
3892+
3893+static gboolean
3894+handle_pm_wakeup (UDisksDriveAta *_drive,
3895+ GDBusMethodInvocation *invocation,
3896+ GVariant *options)
3897+{
3898+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (_drive);
3899+ UDisksLinuxDriveObject *object = NULL;
3900+ UDisksLinuxBlockObject *block_object = NULL;
3901+ UDisksBlock *block = NULL;
3902+ UDisksDaemon *daemon;
3903+ UDisksLinuxDevice *device = NULL;
3904+ gint fd = -1;
3905+ GError *error = NULL;
3906+ const gchar *message;
3907+ const gchar *action_id;
3908+ pid_t caller_pid;
3909+ guchar buf[4096];
3910+
3911+ object = udisks_daemon_util_dup_object (drive, &error);
3912+ if (object == NULL)
3913+ {
3914+ g_dbus_method_invocation_take_error (invocation, error);
3915+ goto out;
3916+ }
3917+
3918+ block_object = udisks_linux_drive_object_get_block (object, FALSE);
3919+ if (block_object == NULL)
3920+ {
3921+ g_dbus_method_invocation_return_error (invocation,
3922+ UDISKS_ERROR,
3923+ UDISKS_ERROR_FAILED,
3924+ "Unable to find block device for drive");
3925+ goto out;
3926+ }
3927+ block = udisks_object_peek_block (UDISKS_OBJECT (block_object));
3928+
3929+ daemon = udisks_linux_drive_object_get_daemon (object);
3930+
3931+ if (!udisks_drive_ata_get_pm_supported (UDISKS_DRIVE_ATA (drive)) ||
3932+ !udisks_drive_ata_get_pm_enabled (UDISKS_DRIVE_ATA (drive)))
3933+ {
3934+ g_dbus_method_invocation_return_error (invocation,
3935+ UDISKS_ERROR,
3936+ UDISKS_ERROR_FAILED,
3937+ "PM is not supported or enabled");
3938+ goto out;
3939+ }
3940+
3941+ error = NULL;
3942+ if (!udisks_daemon_util_get_caller_pid_sync (daemon,
3943+ invocation,
3944+ NULL /* GCancellable */,
3945+ &caller_pid,
3946+ &error))
3947+ {
3948+ g_dbus_method_invocation_return_gerror (invocation, error);
3949+ g_error_free (error);
3950+ goto out;
3951+ }
3952+
3953+ /* Translators: Shown in authentication dialog when the user
3954+ * tries to wake up a drive from standby mode.
3955+ *
3956+ * Do not translate $(drive), it's a placeholder and
3957+ * will be replaced by the name of the drive/device in question
3958+ */
3959+ message = N_("Authentication is required to wake up $(drive) from standby mode");
3960+ action_id = "org.freedesktop.udisks2.ata-standby";
3961+ if (udisks_block_get_hint_system (block))
3962+ {
3963+ action_id = "org.freedesktop.udisks2.ata-standby-system";
3964+ }
3965+ else if (!udisks_daemon_util_on_same_seat (daemon, UDISKS_OBJECT (object), caller_pid))
3966+ {
3967+ action_id = "org.freedesktop.udisks2.ata-standby-other-seat";
3968+ }
3969+
3970+ /* Check that the user is authorized */
3971+ if (!udisks_daemon_util_check_authorization_sync (daemon,
3972+ UDISKS_OBJECT (object),
3973+ action_id,
3974+ options,
3975+ message,
3976+ invocation))
3977+ goto out;
3978+
3979+ device = udisks_linux_drive_object_get_device (object, TRUE /* get_hw */);
3980+ if (device == NULL)
3981+ {
3982+ g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
3983+ "No udev device");
3984+ goto out;
3985+ }
3986+
3987+ fd = open (g_udev_device_get_device_file (device->udev_device), O_RDONLY);
3988+ if (fd == -1)
3989+ {
3990+ g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
3991+ "Error opening device file %s: %m",
3992+ g_udev_device_get_device_file (device->udev_device));
3993+ goto out;
3994+ }
3995+
3996+ if (read (fd, buf, sizeof (buf)) != sizeof (buf))
3997+ {
3998+ g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
3999+ "Error reading %d bytes from %s: %m",
4000+ (gint) sizeof (buf),
4001+ g_udev_device_get_device_file (device->udev_device));
4002+ goto out;
4003+ }
4004+
4005+ udisks_drive_ata_complete_pm_wakeup (_drive, invocation);
4006+
4007+ out:
4008+ if (fd != -1)
4009+ close (fd);
4010+ g_clear_object (&device);
4011+ g_clear_object (&block_object);
4012+ g_clear_object (&object);
4013+ return TRUE; /* returning TRUE means that we handled the method invocation */
4014+}
4015+
4016+/* ---------------------------------------------------------------------------------------------------- */
4017+
4018+static gchar *
4019+ata_pm_standby_to_string (guint value)
4020+{
4021+ gchar *ret;
4022+ gint seconds = -1;
4023+
4024+ if (value == 0)
4025+ {
4026+ ret = g_strdup ("disabled");
4027+ goto out;
4028+ }
4029+ else if (value == 253)
4030+ {
4031+ ret = g_strdup ("vendor-defined");
4032+ goto out;
4033+ }
4034+ else if (value == 254)
4035+ {
4036+ ret = g_strdup ("reserved");
4037+ goto out;
4038+ }
4039+ else if (value < 241)
4040+ {
4041+ seconds = value * 5;
4042+ }
4043+ else if (value < 252)
4044+ {
4045+ seconds = (value - 240) * 30 * 60;
4046+ }
4047+ else if (value == 252)
4048+ {
4049+ seconds = 21 * 60;
4050+ }
4051+ else if (value == 255)
4052+ {
4053+ seconds = 21 * 60 + 15;
4054+ }
4055+
4056+ ret = g_strdup_printf ("%d seconds", seconds);
4057+
4058+ out:
4059+ return ret;
4060+}
4061+
4062+/* ---------------------------------------------------------------------------------------------------- */
4063+
4064+typedef struct
4065+{
4066+ gint ata_pm_standby;
4067+ gint ata_apm_level;
4068+ gint ata_aam_level;
4069+ gboolean ata_write_cache_enabled;
4070+ gboolean ata_write_cache_enabled_set;
4071+ UDisksLinuxDriveAta *ata;
4072+ UDisksLinuxDevice *device;
4073+ GVariant *configuration;
4074+ UDisksDrive *drive;
4075+ UDisksLinuxDriveObject *object;
4076+} ApplyConfData;
4077+
4078+static void
4079+apply_conf_data_free (ApplyConfData *data)
4080+{
4081+ g_clear_object (&data->ata);
4082+ g_clear_object (&data->device);
4083+ g_variant_unref (data->configuration);
4084+ g_clear_object (&data->drive);
4085+ g_clear_object (&data->object);
4086+ g_free (data);
4087+}
4088+
4089+static gpointer
4090+apply_configuration_thread_func (gpointer user_data)
4091+{
4092+ ApplyConfData *data = user_data;
4093+ const gchar *device_file = NULL;
4094+ gint fd = -1;
4095+ GError *error = NULL;
4096+
4097+ device_file = g_udev_device_get_device_file (data->device->udev_device);
4098+
4099+ udisks_notice ("Applying configuration from %s/udisks2/%s.conf to %s",
4100+ PACKAGE_SYSCONF_DIR, udisks_drive_get_id (data->drive), device_file);
4101+
4102+
4103+ /* Use O_RDRW instead of O_RDONLY to force a 'change' uevent so properties are updated */
4104+ fd = open (device_file, O_RDWR|O_NONBLOCK);
4105+ if (fd == -1)
4106+ {
4107+ udisks_error ("Error opening device file %s: %m", device_file);
4108+ goto out;
4109+ }
4110+
4111+ if (data->ata_pm_standby != -1)
4112+ {
4113+ /* ATA8: 7.18 IDLE - E3h, Non-Data */
4114+ UDisksAtaCommandInput input = {.command = 0xe3, .count = data->ata_pm_standby};
4115+ UDisksAtaCommandOutput output = {0};
4116+ if (!udisks_ata_send_command_sync (fd,
4117+ -1,
4118+ UDISKS_ATA_COMMAND_PROTOCOL_NONE,
4119+ &input,
4120+ &output,
4121+ &error))
4122+ {
4123+ udisks_error ("Error sending ATA command IDLE (timeout=%d) to %s: %s (%s, %d)",
4124+ data->ata_pm_standby, device_file,
4125+ error->message, g_quark_to_string (error->domain), error->code);
4126+ g_clear_error (&error);
4127+ }
4128+ else
4129+ {
4130+ gchar *pretty = ata_pm_standby_to_string (data->ata_pm_standby);
4131+ udisks_notice ("Set standby timer to %s (value %d) on %s [%s]",
4132+ pretty, data->ata_pm_standby, device_file, udisks_drive_get_id (data->drive));
4133+ g_free (pretty);
4134+ }
4135+ }
4136+
4137+ if (data->ata_apm_level != -1)
4138+ {
4139+ /* ATA8: 7.48 SET FEATURES - EFh, Non-Data
4140+ * 7.48.6 Enable/disable the APM feature set
4141+ */
4142+ UDisksAtaCommandInput input = {.command = 0xef, .feature = 0x05, .count = data->ata_apm_level};
4143+ UDisksAtaCommandOutput output = {0};
4144+ if (data->ata_apm_level == 0xff)
4145+ {
4146+ input.feature = 0x85;
4147+ input.count = 0x00;
4148+ }
4149+ if (!udisks_ata_send_command_sync (fd,
4150+ -1,
4151+ UDISKS_ATA_COMMAND_PROTOCOL_NONE,
4152+ &input,
4153+ &output,
4154+ &error))
4155+ {
4156+ udisks_error ("Error sending ATA command SET FEATURES, sub-command 0x%02x (ata_apm_level=%d) to %s: %s (%s, %d)",
4157+ input.feature, data->ata_apm_level, device_file,
4158+ error->message, g_quark_to_string (error->domain), error->code);
4159+ g_clear_error (&error);
4160+ }
4161+ else
4162+ {
4163+ udisks_notice ("Set APM level to %d on %s [%s]",
4164+ data->ata_apm_level, device_file, udisks_drive_get_id (data->drive));
4165+ }
4166+ }
4167+
4168+ if (data->ata_aam_level != -1)
4169+ {
4170+ /* ATA8: 7.48 SET FEATURES - EFh, Non-Data
4171+ * 7.48.11 Enable/disable the AAM feature set
4172+ */
4173+ UDisksAtaCommandInput input = {.command = 0xef, .feature = 0x42, .count = data->ata_aam_level};
4174+ UDisksAtaCommandOutput output = {0};
4175+ if (data->ata_apm_level == 0xff)
4176+ {
4177+ input.feature = 0xc2;
4178+ input.count = 0x00;
4179+ }
4180+ if (!udisks_ata_send_command_sync (fd,
4181+ -1,
4182+ UDISKS_ATA_COMMAND_PROTOCOL_NONE,
4183+ &input,
4184+ &output,
4185+ &error))
4186+ {
4187+ udisks_error ("Error sending ATA command SET FEATURES, sub-command 0x%02x (ata_aam_level=%d) to %s: %s (%s, %d)",
4188+ input.feature, data->ata_aam_level, device_file,
4189+ error->message, g_quark_to_string (error->domain), error->code);
4190+ g_clear_error (&error);
4191+ }
4192+ else
4193+ {
4194+ udisks_notice ("Set AAM value to %d on %s [%s]",
4195+ data->ata_aam_level, device_file, udisks_drive_get_id (data->drive));
4196+ }
4197+ }
4198+
4199+ if (data->ata_write_cache_enabled_set)
4200+ {
4201+ /* ATA8: 7.48 SET FEATURES - EFh, Non-Data
4202+ * 7.48.4 Enable/disable volatile write cache
4203+ */
4204+ UDisksAtaCommandInput input = {.command = 0xef, .feature = 0x82};
4205+ UDisksAtaCommandOutput output = {0};
4206+ if (data->ata_write_cache_enabled)
4207+ input.feature = 0x02;
4208+ if (!udisks_ata_send_command_sync (fd,
4209+ -1,
4210+ UDISKS_ATA_COMMAND_PROTOCOL_NONE,
4211+ &input,
4212+ &output,
4213+ &error))
4214+ {
4215+ udisks_error ("Error sending ATA command SET FEATURES, sub-command 0x%02x to %s: %s (%s, %d)",
4216+ input.feature, device_file,
4217+ error->message, g_quark_to_string (error->domain), error->code);
4218+ g_clear_error (&error);
4219+ }
4220+ else
4221+ {
4222+ udisks_notice ("%s Write-Cache on %s [%s]",
4223+ data->ata_write_cache_enabled ? "Enabled" : "Disabled",
4224+ device_file, udisks_drive_get_id (data->drive));
4225+ }
4226+ }
4227+
4228+ out:
4229+ if (fd != -1)
4230+ close (fd);
4231+ apply_conf_data_free (data);
4232+ return NULL;
4233+}
4234+
4235+/**
4236+ * udisks_linux_drive_ata_apply_configuration:
4237+ * @drive: A #UDisksLinuxDriveAta.
4238+ * @device: A #UDisksLinuxDevice
4239+ * @configuration: The configuration to apply.
4240+ *
4241+ * Spawns a thread to apply @configuration to @drive, if any. Does not
4242+ * wait for the thread to terminate.
4243+ */
4244+void
4245+udisks_linux_drive_ata_apply_configuration (UDisksLinuxDriveAta *drive,
4246+ UDisksLinuxDevice *device,
4247+ GVariant *configuration)
4248+{
4249+ gboolean has_conf = FALSE;
4250+ ApplyConfData *data = NULL;
4251+
4252+ data = g_new0 (ApplyConfData, 1);
4253+ data->ata_pm_standby = -1;
4254+ data->ata_apm_level = -1;
4255+ data->ata_aam_level = -1;
4256+ data->ata_write_cache_enabled = FALSE;
4257+ data->ata_write_cache_enabled_set = FALSE;
4258+ data->ata = g_object_ref (drive);
4259+ data->device = g_object_ref (device);
4260+ data->configuration = g_variant_ref (configuration);
4261+
4262+ data->object = udisks_daemon_util_dup_object (drive, NULL);
4263+ if (data->object == NULL)
4264+ goto out;
4265+
4266+ data->drive = udisks_object_get_drive (UDISKS_OBJECT (data->object));
4267+ if (data->drive == NULL)
4268+ goto out;
4269+
4270+
4271+ has_conf |= g_variant_lookup (configuration, "ata-pm-standby", "i", &data->ata_pm_standby);
4272+ has_conf |= g_variant_lookup (configuration, "ata-apm-level", "i", &data->ata_apm_level);
4273+ has_conf |= g_variant_lookup (configuration, "ata-aam-level", "i", &data->ata_aam_level);
4274+ if (g_variant_lookup (configuration, "ata-write-cache-enabled", "b", &data->ata_write_cache_enabled))
4275+ {
4276+ data->ata_write_cache_enabled_set = TRUE;
4277+ has_conf = TRUE;
4278+ }
4279+
4280+ /* don't do anything if none of the configuration is set */
4281+ if (!has_conf)
4282+ goto out;
4283+
4284+ /* this can easily take a long time and thus block (the drive may be in standby mode
4285+ * and needs to spin up) - so run it in a thread
4286+ */
4287+ g_thread_new ("apply-conf-thread",
4288+ apply_configuration_thread_func,
4289+ data);
4290+
4291+ data = NULL; /* don't free data below */
4292+
4293+ out:
4294+ if (data != NULL)
4295+ apply_conf_data_free (data);
4296+}
4297+
4298+/* ---------------------------------------------------------------------------------------------------- */
4299+
4300+static gboolean
4301+on_secure_erase_update_progress_timeout (gpointer user_data)
4302+{
4303+ UDisksJob *job = UDISKS_JOB (user_data);
4304+ gint64 now;
4305+ gint64 start;
4306+ gint64 end;
4307+ gdouble progress;
4308+
4309+ now = g_get_real_time ();
4310+ start = udisks_job_get_start_time (job);
4311+ end = udisks_job_get_expected_end_time (job);
4312+
4313+ progress = ((gdouble) (now - start)) / (end - start);
4314+ if (progress < 0)
4315+ progress = 0;
4316+ if (progress > 1)
4317+ progress = 1;
4318+
4319+ /* TODO: if we've exceeded the expected end time, we could add
4320+ * another couple of minutes or so... that'd be kinda cheating
4321+ * though, wouldn't it?
4322+ */
4323+
4324+ udisks_job_set_progress (job, progress);
4325+
4326+ return TRUE; /* keep source around */
4327+}
4328+
4329+/**
4330+ * udisks_linux_drive_ata_secure_erase_sync:
4331+ * @drive: A #UDisksLinuxDriveAta.
4332+ * @caller_uid: The unix user if of the caller requesting the operation.
4333+ * @enhanced: %TRUE to use the enhanced version of the ATA secure erase command.
4334+ * @error: Return location for error or %NULL.
4335+ *
4336+ * Performs an ATA Secure Erase opeartion. Blocks the calling thread until the operation completes.
4337+ *
4338+ * This operation may take a very long time (hours) to complete.
4339+ *
4340+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
4341+ */
4342+gboolean
4343+udisks_linux_drive_ata_secure_erase_sync (UDisksLinuxDriveAta *drive,
4344+ uid_t caller_uid,
4345+ gboolean enhanced,
4346+ GError **error)
4347+{
4348+ gboolean ret = FALSE;
4349+ UDisksDrive *_drive = NULL;
4350+ UDisksLinuxDriveObject *object = NULL;
4351+ UDisksLinuxBlockObject *block_object = NULL;
4352+ UDisksDaemon *daemon;
4353+ UDisksLinuxDevice *device = NULL;
4354+ const gchar *device_file = NULL;
4355+ gint fd = -1;
4356+ union
4357+ {
4358+ guchar buf[512];
4359+ guint16 words[256];
4360+ } identify;
4361+ guint16 word_82;
4362+ guint16 word_128;
4363+ UDisksBaseJob *job = NULL;
4364+ gint num_minutes = 0;
4365+ guint timeout_id = 0;
4366+ gboolean claimed = FALSE;
4367+ GError *local_error = NULL;
4368+ const gchar *pass = "xxxx";
4369+ gboolean clear_passwd_on_failure = FALSE;
4370+
4371+ g_return_val_if_fail (UDISKS_IS_LINUX_DRIVE_ATA (drive), FALSE);
4372+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
4373+
4374+ object = udisks_daemon_util_dup_object (drive, &local_error);
4375+ if (object == NULL)
4376+ goto out;
4377+ _drive = udisks_object_peek_drive (UDISKS_OBJECT (object));
4378+
4379+ block_object = udisks_linux_drive_object_get_block (object, FALSE);
4380+ if (block_object == NULL)
4381+ {
4382+ g_set_error (&local_error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
4383+ "Unable to find block device for drive");
4384+ goto out;
4385+ }
4386+
4387+ daemon = udisks_linux_drive_object_get_daemon (object);
4388+
4389+ device = udisks_linux_drive_object_get_device (object, TRUE /* get_hw */);
4390+ if (device == NULL)
4391+ {
4392+ g_set_error (&local_error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
4393+ "No udev device");
4394+ goto out;
4395+ }
4396+
4397+ if (drive->secure_erase_in_progress)
4398+ {
4399+ g_set_error (&local_error, UDISKS_ERROR, UDISKS_ERROR_DEVICE_BUSY,
4400+ "Secure erase in progress");
4401+ goto out;
4402+ }
4403+
4404+ /* Use O_EXCL so it fails if mounted or in use */
4405+ device_file = g_udev_device_get_device_file (device->udev_device);
4406+ fd = open (g_udev_device_get_device_file (device->udev_device), O_RDONLY | O_EXCL);
4407+ if (fd == -1)
4408+ {
4409+ g_set_error (&local_error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
4410+ "Error opening device file %s: %m",
4411+ device_file);
4412+ goto out;
4413+ }
4414+
4415+ drive->secure_erase_in_progress = TRUE;
4416+
4417+ claimed = TRUE;
4418+
4419+ /* First get the IDENTIFY data directly from the drive, for sanity checks */
4420+ {
4421+ /* ATA8: 7.16 IDENTIFY DEVICE - ECh, PIO Data-In */
4422+ UDisksAtaCommandInput input = {.command = 0xec};
4423+ UDisksAtaCommandOutput output = {.buffer = identify.buf, .buffer_size = sizeof (identify.buf)};
4424+ if (!udisks_ata_send_command_sync (fd,
4425+ -1,
4426+ UDISKS_ATA_COMMAND_PROTOCOL_DRIVE_TO_HOST,
4427+ &input,
4428+ &output,
4429+ &local_error))
4430+ {
4431+ g_prefix_error (&local_error, "Error sending ATA command IDENTIFY DEVICE: ");
4432+ goto out;
4433+ }
4434+ }
4435+
4436+ /* Support of the Security feature set is indicated in IDENTIFY
4437+ * DEVICE and IDENTIFY PACKET DEVICE data word 82 and data word 128.
4438+ * Security information in words 82, 89 and 90 is fixed until the
4439+ * next power-on reset and shall not change unless DEVICE
4440+ * CONFIGURATION OVERLAY removes support for the Security feature
4441+ * set. Security information in words 85, 92 and 128 are variable
4442+ * and may change. If the Security feature set is not supported,
4443+ * then words 89, 90, 92 and 128 are N/A.
4444+ *
4445+ * word 82: ...
4446+ * 1 The Security feature set is supported
4447+ *
4448+ * word 128: 15:9 Reserved
4449+ * 8 Master Password Capability: 0 = High, 1 = Maximum
4450+ * 7:6 Reserved
4451+ * 5 Enhanced security erase supported
4452+ * 4 Security count expired
4453+ * 3 Security frozen
4454+ * 2 Security locked
4455+ * 1 Security enabled
4456+ * 0 Security supported
4457+ */
4458+ word_82 = GUINT16_FROM_LE (identify.words[82]);
4459+ word_128 = GUINT16_FROM_LE (identify.words[128]);
4460+
4461+ if (!(
4462+ (word_82 & (1<<1)) && (word_128 & (1<<0))
4463+ ))
4464+ {
4465+ g_set_error (&local_error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
4466+ "Drive does not support the ATA security feature");
4467+ goto out;
4468+ }
4469+
4470+ if (word_128 & (1<<3))
4471+ {
4472+ g_set_error (&local_error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
4473+ "Drive is frozen, cannot perform a secure erase");
4474+ goto out;
4475+ }
4476+
4477+ if (enhanced && !(word_128 & (1<<5)))
4478+ {
4479+ g_set_error (&local_error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
4480+ "Enhanced erase requested but not supported");
4481+ goto out;
4482+ }
4483+
4484+ /* OK, all checks done, let's do this thing! */
4485+
4486+ /* First, set up a Job object to track progress */
4487+ num_minutes = enhanced ? 2 * GUINT16_FROM_LE (identify.words[90]) : 2 * GUINT16_FROM_LE (identify.words[89]);
4488+ job = udisks_daemon_launch_simple_job (daemon,
4489+ UDISKS_OBJECT (object),
4490+ enhanced ? "ata-enhanced-secure-erase" : "ata-secure-erase",
4491+ caller_uid, NULL);
4492+ udisks_job_set_cancelable (UDISKS_JOB (job), FALSE);
4493+
4494+ /* A value of 510 (255 in the IDENTIFY DATA register) means "erase
4495+ * is expected to take _at least_ 508 minutes" ... so don't attempt
4496+ * to predict when the job is going to end and don't report progress
4497+ */
4498+ if (num_minutes != 510)
4499+ {
4500+ udisks_job_set_expected_end_time (UDISKS_JOB (job),
4501+ g_get_real_time () + num_minutes * 60LL * G_USEC_PER_SEC);
4502+ udisks_job_set_progress_valid (UDISKS_JOB (job), TRUE);
4503+ timeout_id = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT,
4504+ 1,
4505+ on_secure_erase_update_progress_timeout,
4506+ g_object_ref (job),
4507+ g_object_unref);
4508+ }
4509+
4510+ /* Second, set the user password to 'xxxx' */
4511+ {
4512+ /* ATA8: 7.45 SECURITY SET PASSWORD - F1h, PIO Data-Out */
4513+ guchar buf[512];
4514+ UDisksAtaCommandInput input = {.command = 0xf1, .buffer = buf, .buffer_size = sizeof (buf)};
4515+ UDisksAtaCommandOutput output = {0};
4516+ memset (buf, 0, sizeof (buf));
4517+ memcpy (buf + 2, pass, strlen (pass));
4518+ if (!udisks_ata_send_command_sync (fd,
4519+ -1,
4520+ UDISKS_ATA_COMMAND_PROTOCOL_HOST_TO_DRIVE,
4521+ &input,
4522+ &output,
4523+ &local_error))
4524+ {
4525+ g_prefix_error (&local_error, "Error sending ATA command SECURITY SET PASSWORD: ");
4526+ goto out;
4527+ }
4528+ }
4529+
4530+ clear_passwd_on_failure = TRUE;
4531+
4532+ udisks_notice ("Commencing ATA%s secure erase of %s (%s). This operation is expected to take at least %d minutes to complete",
4533+ enhanced ? " enhanced" : "",
4534+ device_file,
4535+ udisks_drive_get_id (_drive),
4536+ num_minutes);
4537+
4538+ /* Third... do SECURITY ERASE PREPARE */
4539+ {
4540+ /* ATA8: 7.42 SECURITY ERASE PREPARE - F3h, Non-Data */
4541+ UDisksAtaCommandInput input = {.command = 0xf3};
4542+ UDisksAtaCommandOutput output = {0};
4543+ if (!udisks_ata_send_command_sync (fd,
4544+ -1,
4545+ UDISKS_ATA_COMMAND_PROTOCOL_NONE,
4546+ &input,
4547+ &output,
4548+ &local_error))
4549+ {
4550+ g_prefix_error (&local_error, "Error sending ATA command SECURITY ERASE PREPARE: ");
4551+ goto out;
4552+ }
4553+ }
4554+
4555+ /* Fourth... do SECURITY ERASE UNIT */
4556+ {
4557+ /* ATA8: 7.43 SECURITY ERASE UNIT - F4h, PIO Data-Out */
4558+ guchar buf[512];
4559+ UDisksAtaCommandInput input = {.command = 0xf4, .buffer = buf, .buffer_size = sizeof (buf)};
4560+ UDisksAtaCommandOutput output = {0};
4561+ memset (buf, 0, sizeof (buf));
4562+ if (enhanced)
4563+ buf[0] |= 0x02;
4564+ memcpy (buf + 2, pass, strlen (pass));
4565+ if (!udisks_ata_send_command_sync (fd,
4566+ G_MAXINT, /* disable timeout */
4567+ UDISKS_ATA_COMMAND_PROTOCOL_HOST_TO_DRIVE,
4568+ &input,
4569+ &output,
4570+ &local_error))
4571+ {
4572+ g_prefix_error (&local_error, "Error sending ATA command SECURITY ERASE UNIT (enhanced=%d): ",
4573+ enhanced ? 1 : 0);
4574+ goto out;
4575+ }
4576+ }
4577+
4578+ clear_passwd_on_failure = FALSE;
4579+
4580+ udisks_linux_block_object_reread_partition_table (UDISKS_LINUX_BLOCK_OBJECT (block_object));
4581+
4582+ ret = TRUE;
4583+
4584+ out:
4585+ /* Clear the password if something went wrong */
4586+ if (clear_passwd_on_failure)
4587+ {
4588+ /* ATA8: 7.41 SECURITY DISABLE PASSWORD - F6h, PIO Data-Out */
4589+ guchar buf[512];
4590+ UDisksAtaCommandInput input = {.command = 0xf6, .buffer = buf, .buffer_size = sizeof (buf)};
4591+ UDisksAtaCommandOutput output = {0};
4592+ GError *cleanup_error = NULL;
4593+ memset (buf, 0, sizeof (buf));
4594+ memcpy (buf + 2, pass, strlen (pass));
4595+ if (!udisks_ata_send_command_sync (fd,
4596+ -1,
4597+ UDISKS_ATA_COMMAND_PROTOCOL_HOST_TO_DRIVE,
4598+ &input,
4599+ &output,
4600+ &cleanup_error))
4601+ {
4602+ udisks_error ("Failed to clear user password '%s' on %s (%s) while attemping clean-up after a failed secure erase operation. You may need to manually unlock the drive. The error was: %s (%s, %d)",
4603+ pass,
4604+ device_file,
4605+ udisks_drive_get_id (_drive),
4606+ cleanup_error->message, g_quark_to_string (cleanup_error->domain), cleanup_error->code);
4607+ g_clear_error (&cleanup_error);
4608+ }
4609+ else
4610+ {
4611+ udisks_info ("Successfully removed user password '%s' from %s (%s) during clean-up after a failed secure erase operation",
4612+ pass,
4613+ device_file,
4614+ udisks_drive_get_id (_drive));
4615+ }
4616+ }
4617+
4618+ if (ret)
4619+ {
4620+ udisks_notice ("Finished securely erasing %s (%s)",
4621+ device_file,
4622+ udisks_drive_get_id (_drive));
4623+ }
4624+ else
4625+ {
4626+ udisks_notice ("Error securely erasing %s (%s): %s (%s, %d)",
4627+ device_file,
4628+ udisks_drive_get_id (_drive),
4629+ local_error->message, g_quark_to_string (local_error->domain), local_error->code);
4630+ }
4631+
4632+ if (claimed)
4633+ drive->secure_erase_in_progress = FALSE;
4634+
4635+ if (timeout_id > 0)
4636+ g_source_remove (timeout_id);
4637+ if (job != NULL)
4638+ {
4639+ /* propagate error, if any */
4640+ if (local_error == NULL)
4641+ {
4642+ udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), TRUE, "");
4643+ }
4644+ else
4645+ {
4646+ gchar *s = g_strdup_printf ("Secure Erase failed: %s (%s, %d)",
4647+ local_error->message, g_quark_to_string (local_error->domain), local_error->code);
4648+ udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), FALSE, s);
4649+ g_free (s);
4650+ }
4651+ }
4652+ if (local_error != NULL)
4653+ g_propagate_error (error, local_error);
4654+ if (fd != -1)
4655+ close (fd);
4656+ g_clear_object (&device);
4657+ g_clear_object (&block_object);
4658+ g_clear_object (&object);
4659+ return ret;
4660+}
4661+
4662+static gboolean
4663+handle_security_erase_unit (UDisksDriveAta *_drive,
4664+ GDBusMethodInvocation *invocation,
4665+ GVariant *options)
4666+{
4667+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (_drive);
4668+ UDisksLinuxDriveObject *object = NULL;
4669+ UDisksLinuxBlockObject *block_object = NULL;
4670+ UDisksDaemon *daemon;
4671+ GError *error = NULL;
4672+ const gchar *message;
4673+ const gchar *action_id;
4674+ uid_t caller_uid;
4675+ gid_t caller_gid;
4676+ gboolean enhanced = FALSE;
4677+ UDisksInhibitCookie *inhibit_cookie = NULL;
4678+
4679+ object = udisks_daemon_util_dup_object (drive, &error);
4680+ if (object == NULL)
4681+ {
4682+ g_dbus_method_invocation_take_error (invocation, error);
4683+ goto out;
4684+ }
4685+
4686+ block_object = udisks_linux_drive_object_get_block (object, FALSE);
4687+ if (block_object == NULL)
4688+ {
4689+ g_dbus_method_invocation_return_error (invocation,
4690+ UDISKS_ERROR,
4691+ UDISKS_ERROR_FAILED,
4692+ "Unable to find block device for drive");
4693+ goto out;
4694+ }
4695+
4696+ daemon = udisks_linux_drive_object_get_daemon (object);
4697+
4698+ error = NULL;
4699+ if (!udisks_daemon_util_get_caller_uid_sync (daemon,
4700+ invocation,
4701+ NULL /* GCancellable */,
4702+ &caller_uid,
4703+ &caller_gid,
4704+ NULL,
4705+ &error))
4706+ {
4707+ g_dbus_method_invocation_return_gerror (invocation, error);
4708+ g_error_free (error);
4709+ goto out;
4710+ }
4711+
4712+ g_variant_lookup (options, "enhanced", "b", &enhanced);
4713+
4714+ /* Translators: Shown in authentication dialog when the user
4715+ * requests erasing a hard disk using the SECURE ERASE UNIT command.
4716+ *
4717+ * Do not translate $(drive), it's a placeholder and
4718+ * will be replaced by the name of the drive/device in question
4719+ */
4720+ message = N_("Authentication is required to perform a secure erase of $(drive)");
4721+ action_id = "org.freedesktop.udisks2.ata-secure-erase";
4722+
4723+ /* Check that the user is authorized */
4724+ if (!udisks_daemon_util_check_authorization_sync (daemon,
4725+ UDISKS_OBJECT (object),
4726+ action_id,
4727+ options,
4728+ message,
4729+ invocation))
4730+ goto out;
4731+
4732+ inhibit_cookie = udisks_daemon_util_inhibit_system_sync (N_("Formatting Device"));
4733+
4734+ if (!udisks_linux_drive_ata_secure_erase_sync (drive, caller_uid, enhanced, &error))
4735+ {
4736+ g_dbus_method_invocation_return_gerror (invocation, error);
4737+ g_error_free (error);
4738+ goto out;
4739+ }
4740+
4741+ udisks_linux_block_object_reread_partition_table (UDISKS_LINUX_BLOCK_OBJECT (block_object));
4742+
4743+ udisks_drive_ata_complete_security_erase_unit (_drive, invocation);
4744+
4745+ out:
4746+ udisks_daemon_util_uninhibit_system_sync (inhibit_cookie);
4747+ g_clear_object (&block_object);
4748+ g_clear_object (&object);
4749+ return TRUE; /* returning TRUE means that we handled the method invocation */
4750+}
4751+
4752+/* ---------------------------------------------------------------------------------------------------- */
4753+
4754+static gboolean
4755+handle_smart_set_enabled (UDisksDriveAta *_drive,
4756+ GDBusMethodInvocation *invocation,
4757+ gboolean value,
4758+ GVariant *options)
4759+{
4760+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (_drive);
4761+ UDisksLinuxDriveObject *object = NULL;
4762+ UDisksLinuxBlockObject *block_object = NULL;
4763+ UDisksDaemon *daemon;
4764+ GError *error = NULL;
4765+ UDisksLinuxDevice *device = NULL;
4766+ gint fd = -1;
4767+ const gchar *message;
4768+ const gchar *action_id;
4769+ uid_t caller_uid;
4770+ gid_t caller_gid;
4771+
4772+ object = udisks_daemon_util_dup_object (drive, &error);
4773+ if (object == NULL)
4774+ {
4775+ g_dbus_method_invocation_take_error (invocation, error);
4776+ goto out;
4777+ }
4778+
4779+ block_object = udisks_linux_drive_object_get_block (object, FALSE);
4780+ if (block_object == NULL)
4781+ {
4782+ g_dbus_method_invocation_return_error (invocation,
4783+ UDISKS_ERROR,
4784+ UDISKS_ERROR_FAILED,
4785+ "Unable to find block device for drive");
4786+ goto out;
4787+ }
4788+
4789+ daemon = udisks_linux_drive_object_get_daemon (object);
4790+
4791+ error = NULL;
4792+ if (!udisks_daemon_util_get_caller_uid_sync (daemon,
4793+ invocation,
4794+ NULL /* GCancellable */,
4795+ &caller_uid,
4796+ &caller_gid,
4797+ NULL,
4798+ &error))
4799+ {
4800+ g_dbus_method_invocation_return_gerror (invocation, error);
4801+ g_error_free (error);
4802+ goto out;
4803+ }
4804+
4805+ if (value)
4806+ {
4807+ /* Translators: Shown in authentication dialog when the user
4808+ * requests enabling SMART on a disk.
4809+ *
4810+ * Do not translate $(drive), it's a placeholder and
4811+ * will be replaced by the name of the drive/device in question
4812+ */
4813+ message = N_("Authentication is required to enable SMART on $(drive)");
4814+ }
4815+ else
4816+ {
4817+ /* Translators: Shown in authentication dialog when the user
4818+ * requests enabling SMART on a disk.
4819+ *
4820+ * Do not translate $(drive), it's a placeholder and
4821+ * will be replaced by the name of the drive/device in question
4822+ */
4823+ message = N_("Authentication is required to disable SMART on $(drive)");
4824+ }
4825+ action_id = "org.freedesktop.udisks2.ata-smart-enable-disable";
4826+
4827+ /* Check that the user is authorized */
4828+ if (!udisks_daemon_util_check_authorization_sync (daemon,
4829+ UDISKS_OBJECT (object),
4830+ action_id,
4831+ options,
4832+ message,
4833+ invocation))
4834+ goto out;
4835+
4836+ device = udisks_linux_drive_object_get_device (object, TRUE /* get_hw */);
4837+ if (device == NULL)
4838+ {
4839+ g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
4840+ "No udev device");
4841+ goto out;
4842+ }
4843+
4844+ fd = open (g_udev_device_get_device_file (device->udev_device), O_RDONLY|O_NONBLOCK);
4845+ if (fd == -1)
4846+ {
4847+ g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
4848+ "Error opening device file %s: %m",
4849+ g_udev_device_get_device_file (device->udev_device));
4850+ goto out;
4851+ }
4852+
4853+ {
4854+ /* ATA8: 7.53.4 SMART ENABLE OPERATIONS - B0h/D8h, Non-Data
4855+ * 7.53.2 SMART DISABLE OPERATIONS - B0h/D9h, Non-Data
4856+ */
4857+ UDisksAtaCommandInput input = {.command = 0xb0};
4858+ UDisksAtaCommandOutput output = {0};
4859+ if (value)
4860+ input.feature = 0xd8;
4861+ else
4862+ input.feature = 0xd9;
4863+ input.lba = 0x004fc2; /* will be encoded as 0xc2 0x4f 0x00 as per the ATA spec */
4864+ if (!udisks_ata_send_command_sync (fd,
4865+ -1,
4866+ UDISKS_ATA_COMMAND_PROTOCOL_NONE,
4867+ &input,
4868+ &output,
4869+ &error))
4870+ {
4871+ g_prefix_error (&error, "Error sending ATA command SMART, sub-command %s OPERATIONS: ",
4872+ value ? "ENABLE" : "DISABLE");
4873+ g_dbus_method_invocation_take_error (invocation, error);
4874+ goto out;
4875+ }
4876+ }
4877+
4878+ /* Reread new IDENTIFY data */
4879+ if (!udisks_linux_device_reprobe_sync (device, NULL, &error))
4880+ {
4881+ g_prefix_error (&error, "Error reprobing device: ");
4882+ g_dbus_method_invocation_take_error (invocation, error);
4883+ goto out;
4884+ }
4885+
4886+ /* if we just enabled SMART, re-read SMART data before returning */
4887+ if (value)
4888+ {
4889+ if (!udisks_linux_drive_ata_refresh_smart_sync (drive,
4890+ FALSE, /* nowakeup */
4891+ NULL, /* simulate_path */
4892+ NULL, /* cancellable */
4893+ &error))
4894+ {
4895+ g_prefix_error (&error, "Error updating SMART data: ");
4896+ g_dbus_method_invocation_take_error (invocation, error);
4897+ goto out;
4898+ }
4899+ }
4900+ else
4901+ {
4902+ update_smart (drive, device);
4903+ }
4904+ /* ensure property changes are sent before the method return */
4905+ g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (drive));
4906+
4907+ udisks_drive_ata_complete_smart_set_enabled (_drive, invocation);
4908+
4909+ out:
4910+ if (fd != -1)
4911+ close (fd);
4912+ g_clear_object (&device);
4913+ g_clear_object (&block_object);
4914+ g_clear_object (&object);
4915+ return TRUE; /* returning TRUE means that we handled the method invocation */
4916+}
4917+
4918+/* ---------------------------------------------------------------------------------------------------- */
4919+
4920+static void
4921+drive_ata_iface_init (UDisksDriveAtaIface *iface)
4922+{
4923+ iface->handle_smart_update = handle_smart_update;
4924+ iface->handle_smart_get_attributes = handle_smart_get_attributes;
4925+ iface->handle_smart_selftest_abort = handle_smart_selftest_abort;
4926+ iface->handle_smart_selftest_start = handle_smart_selftest_start;
4927+ iface->handle_smart_set_enabled = handle_smart_set_enabled;
4928+
4929+ iface->handle_pm_get_state = handle_pm_get_state;
4930+ iface->handle_pm_standby = handle_pm_standby;
4931+ iface->handle_pm_wakeup = handle_pm_wakeup;
4932+
4933+ iface->handle_security_erase_unit = handle_security_erase_unit;
4934+}
4935
4936=== modified file '.pc/applied-patches'
4937--- .pc/applied-patches 2014-01-14 10:04:52 +0000
4938+++ .pc/applied-patches 2014-06-15 14:18:36 +0000
4939@@ -1,2 +1,4 @@
4940 mount_in_media.patch
4941 unsupported_acls.patch
4942+0001-Use-internal-pm-check-for-smart-poll.patch
4943+0002-Fix-standby-timers.patch
4944
4945=== modified file 'debian/changelog'
4946--- debian/changelog 2014-01-14 10:04:52 +0000
4947+++ debian/changelog 2014-06-15 14:18:36 +0000
4948@@ -1,3 +1,14 @@
4949+udisks2 (2.1.2-1ubuntu1) utopic; urgency=medium
4950+
4951+ * 0001-Use-internal-pm-check-for-smart-poll.patch: libatasmart issues
4952+ an IDENTIFY DEVICE command before CHECK POWER, which resets the
4953+ standby timer, so use our own check instead of libatasmart.
4954+ * 0002-Fix-standby-timers.patch: Check disk IO counters to see if
4955+ there has been any activity since the last SMART update, and skip
4956+ it if there hasn't been. This avoids resetting the standby timer.
4957+
4958+ -- Phillip Susi <psusi@ubuntu.com> Sun, 15 Jun 2014 09:31:49 -0400
4959+
4960 udisks2 (2.1.2-1) unstable; urgency=low
4961
4962 [ Martin Pitt ]
4963
4964=== added file 'debian/patches/0001-Use-internal-pm-check-for-smart-poll.patch'
4965--- debian/patches/0001-Use-internal-pm-check-for-smart-poll.patch 1970-01-01 00:00:00 +0000
4966+++ debian/patches/0001-Use-internal-pm-check-for-smart-poll.patch 2014-06-15 14:18:36 +0000
4967@@ -0,0 +1,162 @@
4968+From 0eec49018b25992ca9d32a8a22a5f31d98dfcbc0 Mon Sep 17 00:00:00 2001
4969+From: Phillip Susi <psusi@ubuntu.com>
4970+Date: Mon, 4 Nov 2013 18:41:25 -0500
4971+Subject: [PATCH 1/2] Use internal pm check for smart poll
4972+
4973+libatasmart apparently has a bug where it will wake up a disk from
4974+sleep just trying to open it, so use our own code to check the pm
4975+state to avoid this.
4976+
4977+Signed-off-by: Phillip Susi <psusi@ubuntu.com>
4978+---
4979+ src/udiskslinuxdriveata.c | 101 ++++++++++++++++++++++++----------------------
4980+ 1 file changed, 52 insertions(+), 49 deletions(-)
4981+
4982+diff --git a/src/udiskslinuxdriveata.c b/src/udiskslinuxdriveata.c
4983+index 534ef4d..23d0216 100644
4984+--- a/src/udiskslinuxdriveata.c
4985++++ b/src/udiskslinuxdriveata.c
4986+@@ -427,6 +427,41 @@ selftest_status_to_string (SkSmartSelfTestExecutionStatus status)
4987+ return ret;
4988+ }
4989+
4990++static gboolean get_pm_state (UDisksLinuxDevice *device, GError **error, guchar *count)
4991++{
4992++ int fd = open (g_udev_device_get_device_file (device->udev_device), O_RDONLY|O_NONBLOCK);
4993++ gboolean rc = FALSE;
4994++ /* ATA8: 7.8 CHECK POWER MODE - E5h, Non-Data */
4995++ UDisksAtaCommandInput input = {.command = 0xe5};
4996++ UDisksAtaCommandOutput output = {0};
4997++
4998++ if (fd == -1)
4999++ {
5000++ g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: