Merge lp:~psusi/ubuntu/utopic/udisks2/fix-standby into lp:ubuntu/utopic/udisks2
- Utopic (14.10)
- fix-standby
- Merge into utopic
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 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Martin Pitt | Approve | ||
Review via email: mp+223170@code.launchpad.net |
Commit message
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.
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.
Thanks Phillip! I pushed the 0001-Use- internal- pm-check- for-smart- poll.patch upstream with a tiny code formatting fix: http:// cgit.freedeskto p.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.freedeskto p.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!