Merge lp:~psusi/ubuntu/raring/libatasmart/fix-status into lp:ubuntu/raring/libatasmart

Proposed by Phillip Susi
Status: Merged
Merge reported by: Martin Pitt
Merged at revision: not available
Proposed branch: lp:~psusi/ubuntu/raring/libatasmart/fix-status
Merge into: lp:ubuntu/raring/libatasmart
Diff against target: 3141 lines (+3094/-2)
6 files modified
.pc/applied-patches (+1/-0)
.pc/fix-status-io-error.patch/atasmart.c (+3056/-0)
atasmart.c (+2/-2)
debian/changelog (+8/-0)
debian/patches/fix-status-io-error.patch (+26/-0)
debian/patches/series (+1/-0)
To merge this branch: bzr merge lp:~psusi/ubuntu/raring/libatasmart/fix-status
Reviewer Review Type Date Requested Status
Martin Pitt Approve
Marc Deslauriers Needs Fixing
Review via email: mp+153954@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Marc Deslauriers (mdeslaur) wrote :

Could you please update the patch tagging to add "Bug" and "Forwarded" tags so we can trace where this patch has been submitted upstream?

Thanks.

review: Needs Fixing
Revision history for this message
Martin Pitt (pitti) wrote :

Thanks for this! I just noticed the bug before, and applied the patch you sent upstream to Debian. I'll sync the Debian package once it's imported, so this MP is obsolete. Thanks!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file '.pc/applied-patches'
--- .pc/applied-patches 2012-05-23 08:44:25 +0000
+++ .pc/applied-patches 2013-03-18 23:14:22 +0000
@@ -1,1 +1,2 @@
10002-Drop-our-own-many-bad-sectors-heuristic.patch10002-Drop-our-own-many-bad-sectors-heuristic.patch
2fix-status-io-error.patch
23
=== added directory '.pc/fix-status-io-error.patch'
=== added file '.pc/fix-status-io-error.patch/.timestamp'
=== added file '.pc/fix-status-io-error.patch/atasmart.c'
--- .pc/fix-status-io-error.patch/atasmart.c 1970-01-01 00:00:00 +0000
+++ .pc/fix-status-io-error.patch/atasmart.c 2013-03-18 23:14:22 +0000
@@ -0,0 +1,3056 @@
1/*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3/***
4 This file is part of libatasmart.
5
6 Copyright 2008 Lennart Poettering
7
8 libatasmart is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as
10 published by the Free Software Foundation, either version 2.1 of the
11 License, or (at your option) any later version.
12
13 libatasmart is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public
19 License along with libatasmart. If not, If not, see
20 <http://www.gnu.org/licenses/>.
21***/
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <arpa/inet.h>
28#include <stdlib.h>
29#include <alloca.h>
30#include <assert.h>
31#include <fcntl.h>
32#include <unistd.h>
33#include <errno.h>
34#include <string.h>
35#include <stdio.h>
36#include <sys/stat.h>
37#include <sys/ioctl.h>
38#include <scsi/scsi.h>
39#include <scsi/sg.h>
40#include <scsi/scsi_ioctl.h>
41#include <linux/hdreg.h>
42#include <linux/fs.h>
43#include <sys/types.h>
44#include <regex.h>
45#include <sys/param.h>
46#include <libudev.h>
47
48#include "atasmart.h"
49
50#ifndef STRPOOL
51#define _P(x) x
52#endif
53
54#define SK_TIMEOUT 2000
55
56typedef enum SkDirection {
57 SK_DIRECTION_NONE,
58 SK_DIRECTION_IN,
59 SK_DIRECTION_OUT,
60 _SK_DIRECTION_MAX
61} SkDirection;
62
63typedef enum SkDiskType {
64 /* These three will be autotested for: */
65 SK_DISK_TYPE_ATA_PASSTHROUGH_12, /* ATA passthrough over SCSI transport, 12-byte version */
66 SK_DISK_TYPE_ATA_PASSTHROUGH_16, /* ATA passthrough over SCSI transport, 16-byte version */
67 SK_DISK_TYPE_LINUX_IDE, /* Classic Linux /dev/hda ioctls */
68
69 /* These three will not be autotested for */
70 SK_DISK_TYPE_SUNPLUS, /* SunPlus USB/ATA bridges */
71 SK_DISK_TYPE_JMICRON, /* JMicron USB/ATA bridges */
72 SK_DISK_TYPE_BLOB, /* From a file */
73 SK_DISK_TYPE_NONE, /* No access method */
74 SK_DISK_TYPE_AUTO, /* We don't know yet */
75 _SK_DISK_TYPE_MAX,
76 _SK_DISK_TYPE_TEST_MAX = SK_DISK_TYPE_SUNPLUS /* only auto test until here */
77} SkDiskType;
78
79#if __BYTE_ORDER == __LITTLE_ENDIAN
80#define MAKE_TAG(a,b,c,d) \
81 (((uint32_t) d << 24) | \
82 ((uint32_t) c << 16) | \
83 ((uint32_t) b << 8) | \
84 ((uint32_t) a))
85#else
86#define MAKE_TAG(a,b,c,d) \
87 (((uint32_t) a << 24) | \
88 ((uint32_t) b << 16) | \
89 ((uint32_t) c << 8) | \
90 ((uint32_t) d))
91#endif
92
93typedef enum SkBlobTag {
94 SK_BLOB_TAG_IDENTIFY = MAKE_TAG('I', 'D', 'F', 'Y'),
95 SK_BLOB_TAG_SMART_STATUS = MAKE_TAG('S', 'M', 'S', 'T'),
96 SK_BLOB_TAG_SMART_DATA = MAKE_TAG('S', 'M', 'D', 'T'),
97 SK_BLOB_TAG_SMART_THRESHOLDS = MAKE_TAG('S', 'M', 'T', 'H')
98} SkBlobTag;
99
100struct SkDisk {
101 char *name;
102 int fd;
103 SkDiskType type;
104
105 uint64_t size;
106
107 uint8_t identify[512];
108 uint8_t smart_data[512];
109 uint8_t smart_thresholds[512];
110
111 SkBool smart_initialized:1;
112
113 SkBool identify_valid:1;
114 SkBool smart_data_valid:1;
115 SkBool smart_thresholds_valid:1;
116
117 SkBool blob_smart_status:1;
118 SkBool blob_smart_status_valid:1;
119
120 SkBool attribute_verification_bad:1;
121
122 SkIdentifyParsedData identify_parsed_data;
123 SkSmartParsedData smart_parsed_data;
124
125 /* cache for commonly used attributes */
126 SkBool attribute_cache_valid:1;
127 SkBool bad_attribute_now:1;
128 SkBool bad_attribute_in_the_past:1;
129 SkBool reallocated_sector_count_found:1;
130 SkBool current_pending_sector_found:1;
131 uint64_t reallocated_sector_count;
132 uint64_t current_pending_sector;
133 SkBool reallocated_sector_count_bad:1;
134 SkBool current_pending_sector_bad:1;
135
136 void *blob;
137};
138
139/* ATA commands */
140typedef enum SkAtaCommand {
141 SK_ATA_COMMAND_IDENTIFY_DEVICE = 0xEC,
142 SK_ATA_COMMAND_IDENTIFY_PACKET_DEVICE = 0xA1,
143 SK_ATA_COMMAND_SMART = 0xB0,
144 SK_ATA_COMMAND_CHECK_POWER_MODE = 0xE5
145} SkAtaCommand;
146
147/* ATA SMART subcommands (ATA8 7.52.1) */
148typedef enum SkSmartCommand {
149 SK_SMART_COMMAND_READ_DATA = 0xD0,
150 SK_SMART_COMMAND_READ_THRESHOLDS = 0xD1,
151 SK_SMART_COMMAND_EXECUTE_OFFLINE_IMMEDIATE = 0xD4,
152 SK_SMART_COMMAND_ENABLE_OPERATIONS = 0xD8,
153 SK_SMART_COMMAND_DISABLE_OPERATIONS = 0xD9,
154 SK_SMART_COMMAND_RETURN_STATUS = 0xDA
155} SkSmartCommand;
156
157/* Hmm, if the data we parse is out of a certain range just consider it misparsed */
158#define SK_MKELVIN_VALID_MIN ((uint64_t) ((-15LL*1000LL) + 273150LL))
159#define SK_MKELVIN_VALID_MAX ((uint64_t) ((100LL*1000LL) + 273150LL))
160
161#define SK_MSECOND_VALID_MIN 1ULL
162#define SK_MSECOND_VALID_SHORT_MAX (60ULL * 60ULL * 1000ULL)
163#define SK_MSECOND_VALID_LONG_MAX (30ULL * 365ULL * 24ULL * 60ULL * 60ULL * 1000ULL)
164
165static int init_smart(SkDisk *d);
166
167static const char *disk_type_to_human_string(SkDiskType type) {
168
169 /* %STRINGPOOLSTART% */
170 static const char* const map[_SK_DISK_TYPE_MAX] = {
171 [SK_DISK_TYPE_ATA_PASSTHROUGH_16] = "16 Byte SCSI ATA SAT Passthru",
172 [SK_DISK_TYPE_ATA_PASSTHROUGH_12] = "12 Byte SCSI ATA SAT Passthru",
173 [SK_DISK_TYPE_LINUX_IDE] = "Native Linux IDE",
174 [SK_DISK_TYPE_SUNPLUS] = "Sunplus SCSI ATA Passthru",
175 [SK_DISK_TYPE_JMICRON] = "JMicron SCSI ATA Passthru",
176 [SK_DISK_TYPE_BLOB] = "Blob",
177 [SK_DISK_TYPE_AUTO] = "Automatic",
178 [SK_DISK_TYPE_NONE] = "None"
179 };
180 /* %STRINGPOOLSTOP% */
181
182 if (type >= _SK_DISK_TYPE_MAX)
183 return NULL;
184
185 return _P(map[type]);
186}
187
188static const char *disk_type_to_prefix_string(SkDiskType type) {
189
190 /* %STRINGPOOLSTART% */
191 static const char* const map[_SK_DISK_TYPE_MAX] = {
192 [SK_DISK_TYPE_ATA_PASSTHROUGH_16] = "sat16",
193 [SK_DISK_TYPE_ATA_PASSTHROUGH_12] = "sat12",
194 [SK_DISK_TYPE_LINUX_IDE] = "linux-ide",
195 [SK_DISK_TYPE_SUNPLUS] = "sunplus",
196 [SK_DISK_TYPE_JMICRON] = "jmicron",
197 [SK_DISK_TYPE_NONE] = "none",
198 [SK_DISK_TYPE_AUTO] = "auto",
199 };
200 /* %STRINGPOOLSTOP% */
201
202 if (type >= _SK_DISK_TYPE_MAX)
203 return NULL;
204
205 return _P(map[type]);
206}
207
208static const char *disk_type_from_string(const char *s, SkDiskType *type) {
209 unsigned u;
210
211 assert(s);
212 assert(type);
213
214 for (u = 0; u < _SK_DISK_TYPE_MAX; u++) {
215 const char *t;
216 size_t l;
217
218 if (!(t = disk_type_to_prefix_string(u)))
219 continue;
220
221 l = strlen(t);
222
223 if (strncmp(s, t, l))
224 continue;
225
226 if (s[l] != ':')
227 continue;
228
229 *type = u;
230
231 return s + l + 1;
232 }
233
234 return NULL;
235}
236
237static SkBool disk_smart_is_available(SkDisk *d) {
238 return d->identify_valid && !!(d->identify[164] & 1);
239}
240
241static SkBool disk_smart_is_enabled(SkDisk *d) {
242 return d->identify_valid && !!(d->identify[170] & 1);
243}
244
245static SkBool disk_smart_is_conveyance_test_available(SkDisk *d) {
246 assert(d->smart_data_valid);
247
248 return !!(d->smart_data[367] & 32);
249}
250static SkBool disk_smart_is_short_and_extended_test_available(SkDisk *d) {
251 assert(d->smart_data_valid);
252
253 return !!(d->smart_data[367] & 16);
254}
255
256static SkBool disk_smart_is_start_test_available(SkDisk *d) {
257 assert(d->smart_data_valid);
258
259 return !!(d->smart_data[367] & 1);
260}
261
262static SkBool disk_smart_is_abort_test_available(SkDisk *d) {
263 assert(d->smart_data_valid);
264
265 return !!(d->smart_data[367] & 41);
266}
267
268static int disk_linux_ide_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
269 uint8_t *bytes = cmd_data;
270 int ret;
271
272 assert(d->type == SK_DISK_TYPE_LINUX_IDE);
273
274 switch (direction) {
275
276 case SK_DIRECTION_OUT:
277
278 /* We could use HDIO_DRIVE_TASKFILE here, but
279 * that's a deprecated ioctl(), hence we don't
280 * do it. And we don't need writing anyway. */
281
282 errno = ENOTSUP;
283 return -1;
284
285 case SK_DIRECTION_IN: {
286 uint8_t *ioctl_data;
287
288 /* We have HDIO_DRIVE_CMD which can only read, but not write,
289 * and cannot do LBA. We use it for all read commands. */
290
291 ioctl_data = alloca(4 + *len);
292 memset(ioctl_data, 0, 4 + *len);
293
294 ioctl_data[0] = (uint8_t) command; /* COMMAND */
295 ioctl_data[1] = ioctl_data[0] == WIN_SMART ? bytes[9] : bytes[3]; /* SECTOR/NSECTOR */
296 ioctl_data[2] = bytes[1]; /* FEATURE */
297 ioctl_data[3] = bytes[3]; /* NSECTOR */
298
299 if ((ret = ioctl(d->fd, HDIO_DRIVE_CMD, ioctl_data)) < 0)
300 return ret;
301
302 memset(bytes, 0, 12);
303 bytes[11] = ioctl_data[0];
304 bytes[1] = ioctl_data[1];
305 bytes[3] = ioctl_data[2];
306
307 memcpy(data, ioctl_data+4, *len);
308
309 return ret;
310 }
311
312 case SK_DIRECTION_NONE: {
313 uint8_t ioctl_data[7];
314
315 /* We have HDIO_DRIVE_TASK which can neither read nor
316 * write, but can do LBA. We use it for all commands that
317 * do neither read nor write */
318
319 memset(ioctl_data, 0, sizeof(ioctl_data));
320
321 ioctl_data[0] = (uint8_t) command; /* COMMAND */
322 ioctl_data[1] = bytes[1]; /* FEATURE */
323 ioctl_data[2] = bytes[3]; /* NSECTOR */
324
325 ioctl_data[3] = bytes[9]; /* LBA LOW */
326 ioctl_data[4] = bytes[8]; /* LBA MID */
327 ioctl_data[5] = bytes[7]; /* LBA HIGH */
328 ioctl_data[6] = bytes[10]; /* SELECT */
329
330 if ((ret = ioctl(d->fd, HDIO_DRIVE_TASK, ioctl_data)))
331 return ret;
332
333 memset(bytes, 0, 12);
334 bytes[11] = ioctl_data[0];
335 bytes[1] = ioctl_data[1];
336 bytes[3] = ioctl_data[2];
337
338 bytes[9] = ioctl_data[3];
339 bytes[8] = ioctl_data[4];
340 bytes[7] = ioctl_data[5];
341
342 bytes[10] = ioctl_data[6];
343
344 return ret;
345 }
346
347 default:
348 assert(FALSE);
349 return -1;
350 }
351}
352
353/* Sends a SCSI command block */
354static int sg_io(int fd, int direction,
355 const void *cdb, size_t cdb_len,
356 void *data, size_t data_len,
357 void *sense, size_t sense_len) {
358
359 struct sg_io_hdr io_hdr;
360
361 memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
362
363 io_hdr.interface_id = 'S';
364 io_hdr.cmdp = (unsigned char*) cdb;
365 io_hdr.cmd_len = cdb_len;
366 io_hdr.dxferp = data;
367 io_hdr.dxfer_len = data_len;
368 io_hdr.sbp = sense;
369 io_hdr.mx_sb_len = sense_len;
370 io_hdr.dxfer_direction = direction;
371 io_hdr.timeout = SK_TIMEOUT;
372
373 return ioctl(fd, SG_IO, &io_hdr);
374}
375
376static int disk_passthrough_16_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
377 uint8_t *bytes = cmd_data;
378 uint8_t cdb[16];
379 uint8_t sense[32];
380 uint8_t *desc = sense+8;
381 int ret;
382
383 static const int direction_map[] = {
384 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
385 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
386 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
387 };
388
389 assert(d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_16);
390
391 /* ATA Pass-Through 16 byte command, as described in "T10 04-262r8
392 * ATA Command Pass-Through":
393 * http://www.t10.org/ftp/t10/document.04/04-262r8.pdf */
394
395 memset(cdb, 0, sizeof(cdb));
396
397 cdb[0] = 0x85; /* OPERATION CODE: 16 byte pass through */
398
399 if (direction == SK_DIRECTION_NONE) {
400 cdb[1] = 3 << 1; /* PROTOCOL: Non-Data */
401 cdb[2] = 0x20; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=0, T_LENGTH=0 */
402
403 } else if (direction == SK_DIRECTION_IN) {
404 cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */
405 cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
406
407 } else if (direction == SK_DIRECTION_OUT) {
408 cdb[1] = 5 << 1; /* PROTOCOL: PIO Data-Out */
409 cdb[2] = 0x26; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=1, T_LENGTH=2 */
410 }
411
412 cdb[3] = bytes[0]; /* FEATURES */
413 cdb[4] = bytes[1];
414
415 cdb[5] = bytes[2]; /* SECTORS */
416 cdb[6] = bytes[3];
417
418 cdb[8] = bytes[9]; /* LBA LOW */
419 cdb[10] = bytes[8]; /* LBA MID */
420 cdb[12] = bytes[7]; /* LBA HIGH */
421
422 cdb[13] = bytes[10] & 0x4F; /* SELECT */
423 cdb[14] = (uint8_t) command;
424
425 memset(sense, 0, sizeof(sense));
426
427 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len ? *len : 0, sense, sizeof(sense))) < 0)
428 return ret;
429
430 if (sense[0] != 0x72 || desc[0] != 0x9 || desc[1] != 0x0c) {
431 errno = EIO;
432 return -1;
433 }
434
435 memset(bytes, 0, 12);
436
437 bytes[1] = desc[3];
438 bytes[2] = desc[4];
439 bytes[3] = desc[5];
440 bytes[9] = desc[7];
441 bytes[8] = desc[9];
442 bytes[7] = desc[11];
443 bytes[10] = desc[12];
444 bytes[11] = desc[13];
445
446 return ret;
447}
448
449static int disk_passthrough_12_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
450 uint8_t *bytes = cmd_data;
451 uint8_t cdb[12];
452 uint8_t sense[32];
453 uint8_t *desc = sense+8;
454 int ret;
455
456 static const int direction_map[] = {
457 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
458 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
459 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
460 };
461
462 assert(d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12);
463
464 /* ATA Pass-Through 12 byte command, as described in "T10 04-262r8
465 * ATA Command Pass-Through":
466 * http://www.t10.org/ftp/t10/document.04/04-262r8.pdf */
467
468 memset(cdb, 0, sizeof(cdb));
469
470 cdb[0] = 0xa1; /* OPERATION CODE: 12 byte pass through */
471
472 if (direction == SK_DIRECTION_NONE) {
473 cdb[1] = 3 << 1; /* PROTOCOL: Non-Data */
474 cdb[2] = 0x20; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=0, T_LENGTH=0 */
475
476 } else if (direction == SK_DIRECTION_IN) {
477 cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */
478 cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
479
480 } else if (direction == SK_DIRECTION_OUT) {
481 cdb[1] = 5 << 1; /* PROTOCOL: PIO Data-Out */
482 cdb[2] = 0x26; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=1, T_LENGTH=2 */
483 }
484
485 cdb[3] = bytes[1]; /* FEATURES */
486 cdb[4] = bytes[3]; /* SECTORS */
487
488 cdb[5] = bytes[9]; /* LBA LOW */
489 cdb[6] = bytes[8]; /* LBA MID */
490 cdb[7] = bytes[7]; /* LBA HIGH */
491
492 cdb[8] = bytes[10] & 0x4F; /* SELECT */
493 cdb[9] = (uint8_t) command;
494
495 memset(sense, 0, sizeof(sense));
496
497 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len ? *len : 0, sense, sizeof(sense))) < 0)
498 return ret;
499
500 if (sense[0] != 0x72 || desc[0] != 0x9 || desc[1] != 0x0c) {
501 errno = EIO;
502 return -1;
503 }
504
505 memset(bytes, 0, 12);
506
507 bytes[1] = desc[3]; /* FEATURES */
508 bytes[2] = desc[4]; /* STATUS */
509 bytes[3] = desc[5]; /* SECTORS */
510 bytes[9] = desc[7]; /* LBA LOW */
511 bytes[8] = desc[9]; /* LBA MID */
512 bytes[7] = desc[11]; /* LBA HIGH */
513 bytes[10] = desc[12]; /* SELECT */
514 bytes[11] = desc[13]; /* ERROR */
515
516 return ret;
517}
518
519static int disk_sunplus_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
520 uint8_t *bytes = cmd_data;
521 uint8_t cdb[12];
522 uint8_t sense[32], buf[8];
523 int ret;
524 static const int direction_map[] = {
525 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
526 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
527 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
528 };
529
530 assert(d->type == SK_DISK_TYPE_SUNPLUS);
531
532 /* SunplusIT specific SCSI ATA pass-thru. Inspired by smartmonutils' support for these bridges */
533
534 memset(cdb, 0, sizeof(cdb));
535
536 cdb[0] = 0xF8; /* OPERATION CODE: Sunplus specific */
537 cdb[1] = 0x00; /* Subcommand: Pass-thru */
538 cdb[2] = 0x22;
539
540 if (direction == SK_DIRECTION_NONE)
541 cdb[3] = 0x00; /* protocol */
542 else if (direction == SK_DIRECTION_IN)
543 cdb[3] = 0x10; /* protocol */
544 else if (direction == SK_DIRECTION_OUT)
545 cdb[3] = 0x11; /* protocol */
546
547 cdb[4] = bytes[3]; /* size? */
548 cdb[5] = bytes[1]; /* FEATURES */
549 cdb[6] = bytes[3]; /* SECTORS */
550 cdb[7] = bytes[9]; /* LBA LOW */
551 cdb[8] = bytes[8]; /* LBA MID */
552 cdb[9] = bytes[7]; /* LBA HIGH */
553 cdb[10] = bytes[10] | 0xA0; /* SELECT */
554 cdb[11] = (uint8_t) command;
555
556 memset(sense, 0, sizeof(sense));
557
558 /* Issue request */
559 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len ? *len : 0, sense, sizeof(sense))) < 0)
560 return ret;
561
562 memset(cdb, 0, sizeof(cdb));
563
564 cdb[0] = 0xF8;
565 cdb[1] = 0x00;
566 cdb[2] = 0x21;
567
568 memset(buf, 0, sizeof(buf));
569
570 /* Ask for response */
571 if ((ret = sg_io(d->fd, SG_DXFER_FROM_DEV, cdb, sizeof(cdb), buf, sizeof(buf), sense, sizeof(sense))) < 0)
572 return ret;
573
574 memset(bytes, 0, 12);
575
576 bytes[2] = buf[1]; /* ERROR */
577 bytes[3] = buf[2]; /* SECTORS */
578 bytes[9] = buf[3]; /* LBA LOW */
579 bytes[8] = buf[4]; /* LBA MID */
580 bytes[7] = buf[5]; /* LBA HIGH */
581 bytes[10] = buf[6]; /* SELECT */
582 bytes[11] = buf[7]; /* STATUS */
583
584 return ret;
585}
586
587static int disk_jmicron_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* _data, size_t *_len) {
588 uint8_t *bytes = cmd_data;
589 uint8_t cdb[12];
590 uint8_t sense[32];
591 uint8_t port;
592 int ret;
593 SkBool is_smart_status = FALSE;
594 void *data = _data;
595 size_t len = _len ? *_len : 0;
596 uint8_t smart_status = 0;
597
598 static const int direction_map[] = {
599 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
600 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
601 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
602 };
603
604 assert(d->type == SK_DISK_TYPE_JMICRON);
605
606 /* JMicron specific SCSI ATA pass-thru. Inspired by smartmonutils' support for these bridges */
607
608 memset(cdb, 0, sizeof(cdb));
609
610 cdb[0] = 0xdf; /* operation code */
611 cdb[1] = 0x10;
612 cdb[2] = 0x00;
613 cdb[3] = 0x00; /* size HI */
614 cdb[4] = sizeof(port); /* size LO */
615 cdb[5] = 0x00;
616 cdb[6] = 0x72; /* register address HI */
617 cdb[7] = 0x0f; /* register address LO */
618 cdb[8] = 0x00;
619 cdb[9] = 0x00;
620 cdb[10] = 0x00;
621 cdb[11] = 0xfd;
622
623 memset(sense, 0, sizeof(sense));
624
625 if ((ret = sg_io(d->fd, SG_DXFER_FROM_DEV, cdb, sizeof(cdb), &port, sizeof(port), sense, sizeof(sense))) < 0)
626 return ret;
627
628 /* Port & 0x04 is port #0, Port & 0x40 is port #1 */
629 if (!(port & 0x44))
630 return -EIO;
631
632 cdb[0] = 0xdf; /* OPERATION CODE: 12 byte pass through */
633
634 if (command == SK_ATA_COMMAND_SMART && bytes[1] == SK_SMART_COMMAND_RETURN_STATUS) {
635 /* We need to rewrite the SMART status request */
636 is_smart_status = TRUE;
637 direction = SK_DIRECTION_IN;
638 data = &smart_status;
639 len = sizeof(smart_status);
640 cdb[1] = 0x10;
641 } else if (direction == SK_DIRECTION_NONE)
642 cdb[1] = 0x10;
643 else if (direction == SK_DIRECTION_IN)
644 cdb[1] = 0x10;
645 else if (direction == SK_DIRECTION_OUT)
646 cdb[1] = 0x00;
647
648 cdb[2] = 0x00;
649
650 cdb[3] = (uint8_t) (len >> 8);
651 cdb[4] = (uint8_t) (len & 0xFF);
652
653 cdb[5] = bytes[1]; /* FEATURES */
654 cdb[6] = bytes[3]; /* SECTORS */
655
656 cdb[7] = bytes[9]; /* LBA LOW */
657 cdb[8] = bytes[8]; /* LBA MID */
658 cdb[9] = bytes[7]; /* LBA HIGH */
659
660 cdb[10] = bytes[10] | ((port & 0x04) ? 0xA0 : 0xB0); /* SELECT */
661 cdb[11] = (uint8_t) command;
662
663 memset(sense, 0, sizeof(sense));
664
665 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len, sense, sizeof(sense))) < 0)
666 return ret;
667
668 memset(bytes, 0, 12);
669
670 if (is_smart_status) {
671 if (smart_status == 0x01 || smart_status == 0xc2) {
672 bytes[7] = 0xc2; /* LBA HIGH */
673 bytes[8] = 0x4f; /* LBA MID */
674 } else if (smart_status == 0x00 || smart_status == 0x2c) {
675 bytes[7] = 0x2c; /* LBA HIGH */
676 bytes[8] = 0xf4; /* LBA MID */
677 } else
678 return -EIO;
679 } else {
680 uint8_t regbuf[16];
681
682 cdb[0] = 0xdf; /* operation code */
683 cdb[1] = 0x10;
684 cdb[2] = 0x00;
685 cdb[3] = 0x00; /* size HI */
686 cdb[4] = sizeof(regbuf); /* size LO */
687 cdb[5] = 0x00;
688 cdb[6] = (port & 0x04) ? 0x80 : 0x90; /* register address HI */
689 cdb[7] = 0x00; /* register address LO */
690 cdb[8] = 0x00;
691 cdb[9] = 0x00;
692 cdb[10] = 0x00;
693 cdb[11] = 0xfd;
694
695 if ((ret = sg_io(d->fd, SG_DXFER_FROM_DEV, cdb, sizeof(cdb), regbuf, sizeof(regbuf), sense, sizeof(sense))) < 0)
696 return ret;
697
698 bytes[2] = regbuf[14]; /* STATUS */
699 bytes[3] = regbuf[0]; /* SECTORS */
700 bytes[9] = regbuf[6]; /* LBA LOW */
701 bytes[8] = regbuf[4]; /* LBA MID */
702 bytes[7] = regbuf[10]; /* LBA HIGH */
703 bytes[10] = regbuf[9]; /* SELECT */
704 bytes[11] = regbuf[13]; /* ERROR */
705 }
706
707 return ret;
708}
709
710static int disk_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
711
712 static int (* const disk_command_table[_SK_DISK_TYPE_MAX]) (SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) = {
713 [SK_DISK_TYPE_LINUX_IDE] = disk_linux_ide_command,
714 [SK_DISK_TYPE_ATA_PASSTHROUGH_12] = disk_passthrough_12_command,
715 [SK_DISK_TYPE_ATA_PASSTHROUGH_16] = disk_passthrough_16_command,
716 [SK_DISK_TYPE_SUNPLUS] = disk_sunplus_command,
717 [SK_DISK_TYPE_JMICRON] = disk_jmicron_command,
718 [SK_DISK_TYPE_BLOB] = NULL,
719 [SK_DISK_TYPE_AUTO] = NULL,
720 [SK_DISK_TYPE_NONE] = NULL
721 };
722
723 assert(d);
724 assert(d->type <= _SK_DISK_TYPE_MAX);
725 assert(direction <= _SK_DIRECTION_MAX);
726
727 assert(direction == SK_DIRECTION_NONE || (data && len && *len > 0));
728 assert(direction != SK_DIRECTION_NONE || (!data && !len));
729
730 if (!disk_command_table[d->type]) {
731 errno = -ENOTSUP;
732 return -1;
733 }
734
735 return disk_command_table[d->type](d, command, direction, cmd_data, data, len);
736}
737
738static int disk_identify_device(SkDisk *d) {
739 uint16_t cmd[6];
740 int ret;
741 size_t len = 512;
742 const uint8_t *p;
743
744 if (d->type == SK_DISK_TYPE_BLOB)
745 return 0;
746
747 memset(d->identify, 0, len);
748 memset(cmd, 0, sizeof(cmd));
749
750 cmd[1] = htons(1);
751
752 if ((ret = disk_command(d, SK_ATA_COMMAND_IDENTIFY_DEVICE, SK_DIRECTION_IN, cmd, d->identify, &len)) < 0)
753 return ret;
754
755 if (len != 512) {
756 errno = EIO;
757 return -1;
758 }
759
760 /* Check if IDENTIFY data is all NULs */
761 for (p = d->identify; p < (const uint8_t*) d->identify+len; p++)
762 if (*p) {
763 p = NULL;
764 break;
765 }
766
767 if (p) {
768 errno = EIO;
769 return -1;
770 }
771
772 d->identify_valid = TRUE;
773
774 return 0;
775}
776
777int sk_disk_check_sleep_mode(SkDisk *d, SkBool *awake) {
778 int ret;
779 uint16_t cmd[6];
780 uint8_t status;
781
782 if (!d->identify_valid) {
783 errno = ENOTSUP;
784 return -1;
785 }
786
787 if (d->type == SK_DISK_TYPE_BLOB) {
788 errno = ENOTSUP;
789 return -1;
790 }
791
792 memset(cmd, 0, sizeof(cmd));
793
794 if ((ret = disk_command(d, SK_ATA_COMMAND_CHECK_POWER_MODE, SK_DIRECTION_NONE, cmd, NULL, 0)) < 0)
795 return ret;
796
797 if (cmd[0] != 0 || (ntohs(cmd[5]) & 1) != 0) {
798 errno = EIO;
799 return -1;
800 }
801
802 status = ntohs(cmd[1]) & 0xFF;
803 *awake = status == 0xFF || status == 0x80; /* idle and active/idle is considered awake */
804
805 return 0;
806}
807
808static int disk_smart_enable(SkDisk *d, SkBool b) {
809 uint16_t cmd[6];
810
811 if (!disk_smart_is_available(d)) {
812 errno = ENOTSUP;
813 return -1;
814 }
815
816 if (d->type == SK_DISK_TYPE_BLOB) {
817 errno = ENOTSUP;
818 return -1;
819 }
820
821 memset(cmd, 0, sizeof(cmd));
822
823 cmd[0] = htons(b ? SK_SMART_COMMAND_ENABLE_OPERATIONS : SK_SMART_COMMAND_DISABLE_OPERATIONS);
824 cmd[2] = htons(0x0000U);
825 cmd[3] = htons(0x00C2U);
826 cmd[4] = htons(0x4F00U);
827
828 return disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, 0);
829}
830
831int sk_disk_smart_read_data(SkDisk *d) {
832 uint16_t cmd[6];
833 int ret;
834 size_t len = 512;
835
836 if (init_smart(d) < 0)
837 return -1;
838
839 if (!disk_smart_is_available(d)) {
840 errno = ENOTSUP;
841 return -1;
842 }
843
844 if (d->type == SK_DISK_TYPE_BLOB)
845 return 0;
846
847 memset(cmd, 0, sizeof(cmd));
848
849 cmd[0] = htons(SK_SMART_COMMAND_READ_DATA);
850 cmd[1] = htons(1);
851 cmd[2] = htons(0x0000U);
852 cmd[3] = htons(0x00C2U);
853 cmd[4] = htons(0x4F00U);
854
855 if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_data, &len)) < 0)
856 return ret;
857
858 d->smart_data_valid = TRUE;
859
860 return ret;
861}
862
863static int disk_smart_read_thresholds(SkDisk *d) {
864 uint16_t cmd[6];
865 int ret;
866 size_t len = 512;
867
868 if (!disk_smart_is_available(d)) {
869 errno = ENOTSUP;
870 return -1;
871 }
872
873 if (d->type == SK_DISK_TYPE_BLOB)
874 return 0;
875
876 memset(cmd, 0, sizeof(cmd));
877
878 cmd[0] = htons(SK_SMART_COMMAND_READ_THRESHOLDS);
879 cmd[1] = htons(1);
880 cmd[2] = htons(0x0000U);
881 cmd[3] = htons(0x00C2U);
882 cmd[4] = htons(0x4F00U);
883
884 if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_thresholds, &len)) < 0)
885 return ret;
886
887 d->smart_thresholds_valid = TRUE;
888
889 return ret;
890}
891
892int sk_disk_smart_status(SkDisk *d, SkBool *good) {
893 uint16_t cmd[6];
894 int ret;
895
896 if (init_smart(d) < 0)
897 return -1;
898
899 if (!disk_smart_is_available(d)) {
900 errno = ENOTSUP;
901 return -1;
902 }
903
904 if (d->type == SK_DISK_TYPE_BLOB) {
905
906 if (d->blob_smart_status_valid) {
907 *good = d->blob_smart_status;
908 return 0;
909 }
910
911 errno = ENXIO;
912 return -1;
913 }
914
915 memset(cmd, 0, sizeof(cmd));
916
917 cmd[0] = htons(SK_SMART_COMMAND_RETURN_STATUS);
918 cmd[1] = htons(0x0000U);
919 cmd[3] = htons(0x00C2U);
920 cmd[4] = htons(0x4F00U);
921
922 if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, 0)) < 0)
923 return ret;
924
925 /* SAT/USB bridges truncate packets, so we only check for 4F,
926 * not for 2C on those */
927 if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x00C2U)) &&
928 cmd[4] == htons(0x4F00U))
929 *good = TRUE;
930 else if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x002CU)) &&
931 cmd[4] == htons(0xF400U))
932 *good = FALSE;
933 else {
934 errno = EIO;
935 return -1;
936 }
937
938 return ret;
939}
940
941int sk_disk_smart_self_test(SkDisk *d, SkSmartSelfTest test) {
942 uint16_t cmd[6];
943 int ret;
944
945 if (init_smart(d) < 0)
946 return -1;
947
948 if (!disk_smart_is_available(d)) {
949 errno = ENOTSUP;
950 return -1;
951 }
952
953 if (d->type == SK_DISK_TYPE_BLOB) {
954 errno = ENOTSUP;
955 return -1;
956 }
957
958 if (!d->smart_data_valid)
959 if ((ret = sk_disk_smart_read_data(d)) < 0)
960 return -1;
961
962 assert(d->smart_data_valid);
963
964 if (test != SK_SMART_SELF_TEST_SHORT &&
965 test != SK_SMART_SELF_TEST_EXTENDED &&
966 test != SK_SMART_SELF_TEST_CONVEYANCE &&
967 test != SK_SMART_SELF_TEST_ABORT) {
968 errno = EINVAL;
969 return -1;
970 }
971
972 if (!disk_smart_is_start_test_available(d)
973 || (test == SK_SMART_SELF_TEST_ABORT && !disk_smart_is_abort_test_available(d))
974 || ((test == SK_SMART_SELF_TEST_SHORT || test == SK_SMART_SELF_TEST_EXTENDED) && !disk_smart_is_short_and_extended_test_available(d))
975 || (test == SK_SMART_SELF_TEST_CONVEYANCE && !disk_smart_is_conveyance_test_available(d))) {
976 errno = ENOTSUP;
977 return -1;
978 }
979
980 if (test == SK_SMART_SELF_TEST_ABORT &&
981 !disk_smart_is_abort_test_available(d)) {
982 errno = ENOTSUP;
983 return -1;
984 }
985
986 memset(cmd, 0, sizeof(cmd));
987
988 cmd[0] = htons(SK_SMART_COMMAND_EXECUTE_OFFLINE_IMMEDIATE);
989 cmd[2] = htons(0x0000U);
990 cmd[3] = htons(0x00C2U);
991 cmd[4] = htons(0x4F00U | (uint16_t) test);
992
993 return disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, NULL);
994}
995
996static void swap_strings(char *s, size_t len) {
997 assert((len & 1) == 0);
998
999 for (; len > 0; s += 2, len -= 2) {
1000 char t;
1001 t = s[0];
1002 s[0] = s[1];
1003 s[1] = t;
1004 }
1005}
1006
1007static void clean_strings(char *s) {
1008 char *e;
1009
1010 for (e = s; *e; e++)
1011 if (*e < ' ' || *e >= 127)
1012 *e = ' ';
1013}
1014
1015static void drop_spaces(char *s) {
1016 char *d = s;
1017 SkBool prev_space = FALSE;
1018
1019 s += strspn(s, " ");
1020
1021 for (;*s; s++) {
1022
1023 if (prev_space) {
1024 if (*s != ' ') {
1025 prev_space = FALSE;
1026 *(d++) = ' ';
1027 *(d++) = *s;
1028 }
1029 } else {
1030 if (*s == ' ')
1031 prev_space = TRUE;
1032 else
1033 *(d++) = *s;
1034 }
1035 }
1036
1037 *d = 0;
1038}
1039
1040static void read_string(char *d, uint8_t *s, size_t len) {
1041 memcpy(d, s, len);
1042 d[len] = 0;
1043 swap_strings(d, len);
1044 clean_strings(d);
1045 drop_spaces(d);
1046}
1047
1048int sk_disk_identify_parse(SkDisk *d, const SkIdentifyParsedData **ipd) {
1049 assert(d);
1050 assert(ipd);
1051
1052 if (!d->identify_valid) {
1053 errno = ENOENT;
1054 return -1;
1055 }
1056
1057 read_string(d->identify_parsed_data.serial, d->identify+20, 20);
1058 read_string(d->identify_parsed_data.firmware, d->identify+46, 8);
1059 read_string(d->identify_parsed_data.model, d->identify+54, 40);
1060
1061 *ipd = &d->identify_parsed_data;
1062
1063 return 0;
1064}
1065
1066int sk_disk_smart_is_available(SkDisk *d, SkBool *b) {
1067 assert(d);
1068 assert(b);
1069
1070 if (!d->identify_valid) {
1071 errno = ENOTSUP;
1072 return -1;
1073 }
1074
1075 *b = disk_smart_is_available(d);
1076 return 0;
1077}
1078
1079int sk_disk_identify_is_available(SkDisk *d, SkBool *b) {
1080 assert(d);
1081 assert(b);
1082
1083 *b = d->identify_valid;
1084 return 0;
1085}
1086
1087const char *sk_smart_offline_data_collection_status_to_string(SkSmartOfflineDataCollectionStatus status) {
1088
1089 /* %STRINGPOOLSTART% */
1090 static const char* const map[] = {
1091 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER] = "Off-line data collection activity was never started.",
1092 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS] = "Off-line data collection activity was completed without error.",
1093 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS] = "Off-line activity in progress.",
1094 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED] = "Off-line data collection activity was suspended by an interrupting command from host.",
1095 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED] = "Off-line data collection activity was aborted by an interrupting command from host.",
1096 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL] = "Off-line data collection activity was aborted by the device with a fatal error.",
1097 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN] = "Unknown status"
1098 };
1099 /* %STRINGPOOLSTOP% */
1100
1101 if (status >= _SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_MAX)
1102 return NULL;
1103
1104 return _P(map[status]);
1105}
1106
1107const char *sk_smart_self_test_execution_status_to_string(SkSmartSelfTestExecutionStatus status) {
1108
1109 /* %STRINGPOOLSTART% */
1110 static const char* const map[] = {
1111 [SK_SMART_SELF_TEST_EXECUTION_STATUS_SUCCESS_OR_NEVER] = "The previous self-test routine completed without error or no self-test has ever been run.",
1112 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ABORTED] = "The self-test routine was aborted by the host.",
1113 [SK_SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED] = "The self-test routine was interrupted by the host with a hardware or software reset.",
1114 [SK_SMART_SELF_TEST_EXECUTION_STATUS_FATAL] = "A fatal error or unknown test error occurred while the device was executing its self-test routine and the device was unable to complete the self-test routine.",
1115 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_UNKNOWN] = "The previous self-test completed having a test element that failed and the test element that failed.",
1116 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL] = "The previous self-test completed having the electrical element of the test failed.",
1117 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_SERVO] = "The previous self-test completed having the servo (and/or seek) test element of the test failed.",
1118 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ] = "The previous self-test completed having the read element of the test failed.",
1119 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_HANDLING] = "The previous self-test completed having a test element that failed and the device is suspected of having handling damage.",
1120 [SK_SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS] = "Self-test routine in progress"
1121 };
1122 /* %STRINGPOOLSTOP% */
1123
1124 if (status >= _SK_SMART_SELF_TEST_EXECUTION_STATUS_MAX)
1125 return NULL;
1126
1127 return _P(map[status]);
1128}
1129
1130const char* sk_smart_self_test_to_string(SkSmartSelfTest test) {
1131
1132 switch (test) {
1133 case SK_SMART_SELF_TEST_SHORT:
1134 return "short";
1135 case SK_SMART_SELF_TEST_EXTENDED:
1136 return "extended";
1137 case SK_SMART_SELF_TEST_CONVEYANCE:
1138 return "conveyance";
1139 case SK_SMART_SELF_TEST_ABORT:
1140 return "abort";
1141 }
1142
1143 return NULL;
1144}
1145
1146SkBool sk_smart_self_test_available(const SkSmartParsedData *d, SkSmartSelfTest test) {
1147 assert(d);
1148
1149 if (!d->start_test_available)
1150 return FALSE;
1151
1152 switch (test) {
1153 case SK_SMART_SELF_TEST_SHORT:
1154 case SK_SMART_SELF_TEST_EXTENDED:
1155 return d->short_and_extended_test_available;
1156 case SK_SMART_SELF_TEST_CONVEYANCE:
1157 return d->conveyance_test_available;
1158 case SK_SMART_SELF_TEST_ABORT:
1159 return d->abort_test_available;
1160 default:
1161 return FALSE;
1162 }
1163}
1164
1165unsigned sk_smart_self_test_polling_minutes(const SkSmartParsedData *d, SkSmartSelfTest test) {
1166 assert(d);
1167
1168 if (!sk_smart_self_test_available(d, test))
1169 return 0;
1170
1171 switch (test) {
1172 case SK_SMART_SELF_TEST_SHORT:
1173 return d->short_test_polling_minutes;
1174 case SK_SMART_SELF_TEST_EXTENDED:
1175 return d->extended_test_polling_minutes;
1176 case SK_SMART_SELF_TEST_CONVEYANCE:
1177 return d->conveyance_test_polling_minutes;
1178 default:
1179 return 0;
1180 }
1181}
1182
1183static void make_pretty(SkSmartAttributeParsedData *a) {
1184 uint64_t fourtyeight;
1185
1186 if (!a->name)
1187 return;
1188
1189 if (a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_UNKNOWN)
1190 return;
1191
1192 fourtyeight =
1193 ((uint64_t) a->raw[0]) |
1194 (((uint64_t) a->raw[1]) << 8) |
1195 (((uint64_t) a->raw[2]) << 16) |
1196 (((uint64_t) a->raw[3]) << 24) |
1197 (((uint64_t) a->raw[4]) << 32) |
1198 (((uint64_t) a->raw[5]) << 40);
1199
1200 if (!strcmp(a->name, "spin-up-time"))
1201 a->pretty_value = fourtyeight & 0xFFFF;
1202 else if (!strcmp(a->name, "airflow-temperature-celsius") ||
1203 !strcmp(a->name, "temperature-celsius") ||
1204 !strcmp(a->name, "temperature-celsius-2"))
1205 a->pretty_value = (fourtyeight & 0xFFFF)*1000 + 273150;
1206 else if (!strcmp(a->name, "temperature-centi-celsius"))
1207 a->pretty_value = (fourtyeight & 0xFFFF)*100 + 273150;
1208 else if (!strcmp(a->name, "power-on-minutes"))
1209 a->pretty_value = fourtyeight * 60 * 1000;
1210 else if (!strcmp(a->name, "power-on-seconds") ||
1211 !strcmp(a->name, "power-on-seconds-2"))
1212 a->pretty_value = fourtyeight * 1000;
1213 else if (!strcmp(a->name, "power-on-half-minutes"))
1214 a->pretty_value = fourtyeight * 30 * 1000;
1215 else if (!strcmp(a->name, "power-on-hours") ||
1216 !strcmp(a->name, "loaded-hours") ||
1217 !strcmp(a->name, "head-flying-hours"))
1218 a->pretty_value = (fourtyeight & 0xFFFFFFFFU) * 60 * 60 * 1000;
1219 else if (!strcmp(a->name, "reallocated-sector-count") ||
1220 !strcmp(a->name, "current-pending-sector"))
1221 a->pretty_value = fourtyeight & 0xFFFFFFFFU;
1222 else if (!strcmp(a->name, "endurance-remaining") ||
1223 !strcmp(a->name, "available-reserved-space"))
1224 a->pretty_value = a->current_value;
1225 else if (!strcmp(a->name, "total-lbas-written") ||
1226 !strcmp(a->name, "total-lbas-read"))
1227 a->pretty_value = fourtyeight * 65536LLU * 512LLU / 1000000LLU;
1228 else if (!strcmp(a->name, "timed-workload-media-wear") ||
1229 !strcmp(a->name, "timed-workload-host-reads"))
1230 a->pretty_value = (double)fourtyeight / 1024LLU;
1231 else if (!strcmp(a->name, "workload-timer"))
1232 a->pretty_value = fourtyeight * 60 * 1000;
1233 else
1234 a->pretty_value = fourtyeight;
1235}
1236
1237typedef void (*SkSmartAttributeVerify)(SkDisk *d, SkSmartAttributeParsedData *a);
1238
1239typedef struct SkSmartAttributeInfo {
1240 const char *name;
1241 SkSmartAttributeUnit unit;
1242 SkSmartAttributeVerify verify;
1243} SkSmartAttributeInfo;
1244
1245static void verify_temperature(SkDisk *d, SkSmartAttributeParsedData *a) {
1246 assert(a);
1247 assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_MKELVIN);
1248
1249 if (a->pretty_value < SK_MKELVIN_VALID_MIN ||
1250 a->pretty_value > SK_MKELVIN_VALID_MAX) {
1251 a->pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1252 d->attribute_verification_bad = TRUE;
1253 }
1254}
1255
1256static void verify_short_time(SkDisk *d, SkSmartAttributeParsedData *a) {
1257 assert(a);
1258 assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_MSECONDS);
1259
1260 if (a->pretty_value < SK_MSECOND_VALID_MIN ||
1261 a->pretty_value > SK_MSECOND_VALID_SHORT_MAX) {
1262 a->pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1263 d->attribute_verification_bad = TRUE;
1264 }
1265}
1266
1267static void verify_long_time(SkDisk *d, SkSmartAttributeParsedData *a) {
1268 assert(a);
1269 assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_MSECONDS);
1270
1271 if (a->pretty_value < SK_MSECOND_VALID_MIN ||
1272 a->pretty_value > SK_MSECOND_VALID_LONG_MAX) {
1273 a->pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1274 d->attribute_verification_bad = TRUE;
1275 }
1276}
1277
1278static void verify_sectors(SkDisk *d, SkSmartAttributeParsedData *a) {
1279 uint64_t max_sectors;
1280
1281 assert(d);
1282 assert(a);
1283 assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_SECTORS);
1284
1285 max_sectors = d->size / 512ULL;
1286
1287 if (a->pretty_value == 0xffffffffULL ||
1288 a->pretty_value == 0xffffffffffffULL ||
1289 (max_sectors > 0 && a->pretty_value > max_sectors)) {
1290 a->pretty_value = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1291 d->attribute_verification_bad = TRUE;
1292 } else {
1293 if ((!strcmp(a->name, "reallocated-sector-count") ||
1294 !strcmp(a->name, "current-pending-sector")) &&
1295 a->pretty_value > 0)
1296 a->warn = TRUE;
1297 }
1298}
1299
1300/* This data is stolen from smartmontools */
1301
1302/* %STRINGPOOLSTART% */
1303static const SkSmartAttributeInfo const attribute_info[256] = {
1304 [1] = { "raw-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1305 [2] = { "throughput-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1306 [3] = { "spin-up-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_short_time },
1307 [4] = { "start-stop-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1308 [5] = { "reallocated-sector-count", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1309 [6] = { "read-channel-margin", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1310 [7] = { "seek-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1311 [8] = { "seek-time-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1312 [9] = { "power-on-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time },
1313 [10] = { "spin-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1314 [11] = { "calibration-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1315 [12] = { "power-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1316 [13] = { "read-soft-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1317 [170] = { "available-reserved-space", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL },
1318 [171] = { "program-fail-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1319 [172] = { "erase-fail-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1320 [175] = { "program-fail-count-chip", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1321 [176] = { "erase-fail-count-chip", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1322 [177] = { "wear-leveling-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1323 [178] = { "used-reserved-blocks-chip", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1324 [179] = { "used-reserved-blocks-total", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1325 [180] = { "unused-reserved-blocks", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1326 [181] = { "program-fail-count-total", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1327 [182] = { "erase-fail-count-total", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1328 [183] = { "runtime-bad-block-total", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1329 [184] = { "end-to-end-error", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1330 [187] = { "reported-uncorrect", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1331 [188] = { "command-timeout", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1332 [189] = { "high-fly-writes", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1333 [190] = { "airflow-temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature },
1334 [191] = { "g-sense-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1335 [192] = { "power-off-retract-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1336 [193] = { "load-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1337 [194] = { "temperature-celsius-2", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature },
1338 [195] = { "hardware-ecc-recovered", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1339 [196] = { "reallocated-event-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1340 [197] = { "current-pending-sector", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1341 [198] = { "offline-uncorrectable", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1342 [199] = { "udma-crc-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1343 [200] = { "multi-zone-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1344 [201] = { "soft-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1345 [202] = { "ta-increase-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1346 [203] = { "run-out-cancel", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1347 [204] = { "shock-count-write-open", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1348 [205] = { "shock-rate-write-open", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1349 [206] = { "flying-height", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1350 [207] = { "spin-high-current", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1351 [208] = { "spin-buzz", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1352 [209] = { "offline-seek-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1353 [220] = { "disk-shift", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1354 [221] = { "g-sense-error-rate-2", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1355 [222] = { "loaded-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time },
1356 [223] = { "load-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1357 [224] = { "load-friction", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1358 [225] = { "load-cycle-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1359 [226] = { "load-in-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_short_time },
1360 [227] = { "torq-amp-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1361 [228] = { "power-off-retract-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1362 [230] = { "head-amplitude", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1363 [231] = { "temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature },
1364
1365 /* http://www.adtron.com/pdf/SMART_for_XceedLite_SATA_RevA.pdf */
1366 [232] = { "endurance-remaining", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL },
1367 [233] = { "power-on-seconds-2", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1368 [234] = { "uncorrectable-ecc-count", SK_SMART_ATTRIBUTE_UNIT_SECTORS, NULL },
1369 [235] = { "good-block-rate", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1370
1371 [240] = { "head-flying-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time },
1372 [241] = { "total-lbas-written", SK_SMART_ATTRIBUTE_UNIT_MB, NULL },
1373 [242] = { "total-lbas-read", SK_SMART_ATTRIBUTE_UNIT_MB, NULL },
1374 [250] = { "read-error-retry-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }
1375};
1376/* %STRINGPOOLSTOP% */
1377
1378typedef enum SkSmartQuirk {
1379 SK_SMART_QUIRK_9_POWERONMINUTES = 0x000001,
1380 SK_SMART_QUIRK_9_POWERONSECONDS = 0x000002,
1381 SK_SMART_QUIRK_9_POWERONHALFMINUTES = 0x000004,
1382 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT = 0x000008,
1383 SK_SMART_QUIRK_193_LOADUNLOAD = 0x000010,
1384 SK_SMART_QUIRK_194_10XCELSIUS = 0x000020,
1385 SK_SMART_QUIRK_194_UNKNOWN = 0x000040,
1386 SK_SMART_QUIRK_200_WRITEERRORCOUNT = 0x000080,
1387 SK_SMART_QUIRK_201_DETECTEDTACOUNT = 0x000100,
1388 SK_SMART_QUIRK_5_UNKNOWN = 0x000200,
1389 SK_SMART_QUIRK_9_UNKNOWN = 0x000400,
1390 SK_SMART_QUIRK_197_UNKNOWN = 0x000800,
1391 SK_SMART_QUIRK_198_UNKNOWN = 0x001000,
1392 SK_SMART_QUIRK_190_UNKNOWN = 0x002000,
1393 SK_SMART_QUIRK_232_AVAILABLERESERVEDSPACE = 0x004000,
1394 SK_SMART_QUIRK_233_MEDIAWEAROUTINDICATOR = 0x008000,
1395 SK_SMART_QUIRK_225_TOTALLBASWRITTEN = 0x010000,
1396 SK_SMART_QUIRK_4_UNUSED = 0x020000,
1397 SK_SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR = 0x040000,
1398 SK_SMART_QUIRK_227_TIMEWORKLOADHOSTREADS = 0x080000,
1399 SK_SMART_QUIRK_228_WORKLOADTIMER = 0x100000,
1400 SK_SMART_QUIRK_3_UNUSED = 0x200000
1401} SkSmartQuirk;
1402
1403/* %STRINGPOOLSTART% */
1404static const char *quirk_name[] = {
1405 "9_POWERONMINUTES",
1406 "9_POWERONSECONDS",
1407 "9_POWERONHALFMINUTES",
1408 "192_EMERGENCYRETRACTCYCLECT",
1409 "193_LOADUNLOAD",
1410 "194_10XCELSIUS",
1411 "194_UNKNOWN",
1412 "200_WRITEERRORCOUNT",
1413 "201_DETECTEDTACOUNT",
1414 "5_UNKNOWN",
1415 "9_UNKNOWN",
1416 "197_UNKNOWN",
1417 "198_UNKNOWN",
1418 "190_UNKNOWN",
1419 "232_AVAILABLERESERVEDSPACE",
1420 "233_MEDIAWEAROUTINDICATOR",
1421 "225_TOTALLBASWRITTEN",
1422 "4_UNUSED",
1423 "226_TIMEWORKLOADMEDIAWEAR",
1424 "227_TIMEWORKLOADHOSTREADS",
1425 "228_WORKLOADTIMER",
1426 "3_UNUSED",
1427 NULL
1428};
1429/* %STRINGPOOLSTOP% */
1430
1431typedef struct SkSmartQuirkDatabase {
1432 const char *model;
1433 const char *firmware;
1434 SkSmartQuirk quirk;
1435} SkSmartQuirkDatabase;
1436
1437static const SkSmartQuirkDatabase quirk_database[] = { {
1438
1439 /*** Fujitsu */
1440 "^("
1441 "FUJITSU MHY2120BH|"
1442 "FUJITSU MHY2250BH"
1443 ")$",
1444 "^0085000B$", /* seems to be specific to this firmware */
1445 SK_SMART_QUIRK_9_POWERONMINUTES|
1446 SK_SMART_QUIRK_197_UNKNOWN|
1447 SK_SMART_QUIRK_198_UNKNOWN
1448 }, {
1449 "^FUJITSU MHR2040AT$",
1450 NULL,
1451 SK_SMART_QUIRK_9_POWERONSECONDS|
1452 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1453 SK_SMART_QUIRK_200_WRITEERRORCOUNT
1454 }, {
1455 "^FUJITSU MHS20[6432]0AT( .)?$",
1456 NULL,
1457 SK_SMART_QUIRK_9_POWERONSECONDS|
1458 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1459 SK_SMART_QUIRK_200_WRITEERRORCOUNT|
1460 SK_SMART_QUIRK_201_DETECTEDTACOUNT
1461 }, {
1462 "^("
1463 "FUJITSU M1623TAU|"
1464 "FUJITSU MHG2...ATU?.*|"
1465 "FUJITSU MHH2...ATU?.*|"
1466 "FUJITSU MHJ2...ATU?.*|"
1467 "FUJITSU MHK2...ATU?.*|"
1468 "FUJITSU MHL2300AT|"
1469 "FUJITSU MHM2(20|15|10|06)0AT|"
1470 "FUJITSU MHN2...AT|"
1471 "FUJITSU MHR2020AT|"
1472 "FUJITSU MHT2...(AH|AS|AT|BH)U?.*|"
1473 "FUJITSU MHU2...ATU?.*|"
1474 "FUJITSU MHV2...(AH|AS|AT|BH|BS|BT).*|"
1475 "FUJITSU MP[A-G]3...A[HTEV]U?.*"
1476 ")$",
1477 NULL,
1478 SK_SMART_QUIRK_9_POWERONSECONDS
1479 }, {
1480
1481 /*** Samsung ***/
1482 "^("
1483 "SAMSUNG SV4012H|"
1484 "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]"
1485 ")$",
1486 NULL,
1487 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1488 }, {
1489 "^("
1490 "SAMSUNG SV0412H|"
1491 "SAMSUNG SV1204H"
1492 ")$",
1493 NULL,
1494 SK_SMART_QUIRK_9_POWERONHALFMINUTES|
1495 SK_SMART_QUIRK_194_10XCELSIUS
1496 }, {
1497 "^SAMSUNG SP40A2H$",
1498 "^RR100-07$",
1499 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1500 }, {
1501 "^SAMSUNG SP80A4H$",
1502 "^RT100-06$",
1503 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1504 }, {
1505 "^SAMSUNG SP8004H$",
1506 "^QW100-61$",
1507 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1508 }, {
1509
1510 /*** Maxtor */
1511 "^("
1512 "Maxtor 2B0(0[468]|1[05]|20)H1|"
1513 "Maxtor 4G(120J6|160J[68])|"
1514 "Maxtor 4D0(20H1|40H2|60H3|80H4)"
1515 ")$",
1516 NULL,
1517 SK_SMART_QUIRK_9_POWERONMINUTES|
1518 SK_SMART_QUIRK_194_UNKNOWN
1519 }, {
1520 "^("
1521 "Maxtor 2F0[234]0[JL]0|"
1522 "Maxtor 8(1280A2|2160A4|2560A4|3840A6|4000A6|5120A8)|"
1523 "Maxtor 8(2160D2|3228D3|3240D3|4320D4|6480D6|8400D8|8455D8)|"
1524 "Maxtor 9(0510D4|0576D4|0648D5|0720D5|0840D6|0845D6|0864D6|1008D7|1080D8|1152D8)|"
1525 "Maxtor 9(1(360|350|202)D8|1190D7|10[12]0D6|0840D5|06[48]0D4|0510D3|1(350|202)E8|1010E6|0840E5|0640E4)|"
1526 "Maxtor 9(0512D2|0680D3|0750D3|0913D4|1024D4|1360D6|1536D6|1792D7|2048D8)|"
1527 "Maxtor 9(2732U8|2390U7|204[09]U6|1707U5|1366U4|1024U3|0845U3|0683U2)|"
1528 "Maxtor 4(R0[68]0[JL]0|R1[26]0L0|A160J0|R120L4)|"
1529 "Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D[34]|90432D2)|"
1530 "Maxtor 9(0431U1|0641U2|0871U2|1301U3|1741U4)|"
1531 "Maxtor (94091U8|93071U6|92561U5|92041U4|91731U4|91531U3|91361U3|91021U2|90841U2|90651U2)|"
1532 "Maxtor (33073U4|32049U3|31536U2|30768U1|33073H4|32305H3|31536H2|30768H1)|"
1533 "Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)|"
1534 "Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)|"
1535 "Maxtor (54098[UH]8|53073[UH]6|52732[UH]6|52049[UH]4|51536[UH]3|51369[UH]3|51024[UH]2)|"
1536 "Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?|"
1537 "Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)|"
1538 "Maxtor 9(1023U2|1536U2|2049U3|2305U3|3073U4|4610U6|6147U8)|"
1539 "Maxtor 9(1023H2|1536H2|2049H3|2305H3|3073H4|4098H6|4610H6|6147H8)|"
1540 "Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)|"
1541 "Maxtor (98196H8|96147H6)|"
1542 "Maxtor 4W(100H6|080H6|060H4|040H3|030H2)|"
1543 "Maxtor 6(E0[234]|K04)0L0|"
1544 "Maxtor 6(B(30|25|20|16|12|10|08)0[MPRS]|L(080[MLP]|(100|120)[MP]|160[MP]|200[MPRS]|250[RS]|300[RS]))0|"
1545 "Maxtor 6Y((060|080|120|160)L0|(060|080|120|160|200|250)P0|(060|080|120|160|200|250)M0)|"
1546 "Maxtor 7Y250[PM]0|"
1547 "Maxtor [45]A(25|30|32)0[JN]0|"
1548 "Maxtor 7L(25|30)0[SR]0"
1549 ")$",
1550 NULL,
1551 SK_SMART_QUIRK_9_POWERONMINUTES
1552 }, {
1553
1554
1555 /*** Hitachi */
1556 "^("
1557 "HITACHI_DK14FA-20B|"
1558 "HITACHI_DK23..-..B?|"
1559 "HITACHI_DK23FA-20J|HTA422020F9AT[JN]0|"
1560 "HE[JN]4230[23]0F9AT00|"
1561 "HTC4260[23]0G5CE00|HTC4260[56]0G8CE00"
1562 ")$",
1563 NULL,
1564 SK_SMART_QUIRK_9_POWERONMINUTES|
1565 SK_SMART_QUIRK_193_LOADUNLOAD
1566 }, {
1567 "^HTS541010G9SA00$",
1568 "^MBZOC60P$",
1569 SK_SMART_QUIRK_5_UNKNOWN
1570 }, {
1571
1572 /*** Apple SSD (?) http://bugs.freedesktop.org/show_bug.cgi?id=24700
1573 https://bugs.launchpad.net/ubuntu/+source/gnome-disk-utility/+bug/438136/comments/4 */
1574 "^MCCOE64GEMPP$",
1575 "^2.9.0[3-9]$",
1576 SK_SMART_QUIRK_5_UNKNOWN|
1577 SK_SMART_QUIRK_190_UNKNOWN
1578 }, {
1579
1580 /*** Intel */
1581 "^INTEL SSDSA2(CT|BT|CW|BW)[0-9]{3}G3.*$", /* 320 Series */
1582 NULL,
1583 SK_SMART_QUIRK_3_UNUSED|
1584 SK_SMART_QUIRK_4_UNUSED|
1585 SK_SMART_QUIRK_225_TOTALLBASWRITTEN|
1586 SK_SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR|
1587 SK_SMART_QUIRK_227_TIMEWORKLOADHOSTREADS|
1588 SK_SMART_QUIRK_228_WORKLOADTIMER|
1589 SK_SMART_QUIRK_232_AVAILABLERESERVEDSPACE|
1590 SK_SMART_QUIRK_233_MEDIAWEAROUTINDICATOR
1591 }, {
1592 NULL,
1593 NULL,
1594 0
1595 }
1596};
1597
1598static int match(const char*regex, const char *s, SkBool *result) {
1599 int k;
1600 regex_t re;
1601
1602 *result = FALSE;
1603
1604 if (regcomp(&re, regex, REG_EXTENDED|REG_NOSUB) != 0) {
1605 errno = EINVAL;
1606 return -1;
1607 }
1608
1609 if ((k = regexec(&re, s, 0, NULL, 0)) != 0) {
1610
1611 if (k != REG_NOMATCH) {
1612 regfree(&re);
1613 errno = EINVAL;
1614 return -1;
1615 }
1616
1617 } else
1618 *result = TRUE;
1619
1620 regfree(&re);
1621
1622 return 0;
1623}
1624
1625static int lookup_quirks(const char *model, const char *firmware, SkSmartQuirk *quirk) {
1626 int k;
1627 const SkSmartQuirkDatabase *db;
1628
1629 *quirk = 0;
1630
1631 for (db = quirk_database; db->model || db->firmware; db++) {
1632
1633 if (db->model) {
1634 SkBool matching = FALSE;
1635
1636 if ((k = match(db->model, model, &matching)) < 0)
1637 return k;
1638
1639 if (!matching)
1640 continue;
1641 }
1642
1643 if (db->firmware) {
1644 SkBool matching = FALSE;
1645
1646 if ((k = match(db->firmware, firmware, &matching)) < 0)
1647 return k;
1648
1649 if (!matching)
1650 continue;
1651 }
1652
1653 *quirk = db->quirk;
1654 return 0;
1655 }
1656
1657 return 0;
1658}
1659
1660static const SkSmartAttributeInfo *lookup_attribute(SkDisk *d, uint8_t id) {
1661 const SkIdentifyParsedData *ipd;
1662 SkSmartQuirk quirk = 0;
1663
1664 /* These are the complex ones */
1665 if (sk_disk_identify_parse(d, &ipd) < 0)
1666 return NULL;
1667
1668 if (lookup_quirks(ipd->model, ipd->firmware, &quirk) < 0)
1669 return NULL;
1670
1671 if (quirk) {
1672 switch (id) {
1673 case 3:
1674 /* %STRINGPOOLSTART% */
1675 if (quirk & SK_SMART_QUIRK_3_UNUSED) {
1676 static const SkSmartAttributeInfo a = {
1677 "spin-up-time", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL
1678 };
1679 return &a;
1680 }
1681 /* %STRINGPOOLSTOP% */
1682
1683 break;
1684
1685 case 4:
1686 /* %STRINGPOOLSTART% */
1687 if (quirk & SK_SMART_QUIRK_4_UNUSED) {
1688 static const SkSmartAttributeInfo a = {
1689 "start-stop-count", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL
1690 };
1691 return &a;
1692 }
1693 /* %STRINGPOOLSTOP% */
1694
1695 break;
1696
1697 case 5:
1698 if (quirk & SK_SMART_QUIRK_5_UNKNOWN)
1699 return NULL;
1700
1701 break;
1702
1703 case 9:
1704 /* %STRINGPOOLSTART% */
1705 if (quirk & SK_SMART_QUIRK_9_POWERONMINUTES) {
1706 static const SkSmartAttributeInfo a = {
1707 "power-on-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time
1708 };
1709 return &a;
1710
1711 } else if (quirk & SK_SMART_QUIRK_9_POWERONSECONDS) {
1712 static const SkSmartAttributeInfo a = {
1713 "power-on-seconds", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time
1714 };
1715 return &a;
1716
1717 } else if (quirk & SK_SMART_QUIRK_9_POWERONHALFMINUTES) {
1718 static const SkSmartAttributeInfo a = {
1719 "power-on-half-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time
1720 };
1721 return &a;
1722 } else if (quirk & SK_SMART_QUIRK_9_UNKNOWN)
1723 return NULL;
1724 /* %STRINGPOOLSTOP% */
1725
1726 break;
1727
1728 case 190:
1729 if (quirk & SK_SMART_QUIRK_190_UNKNOWN)
1730 return NULL;
1731
1732 break;
1733
1734 case 192:
1735 /* %STRINGPOOLSTART% */
1736 if (quirk & SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT) {
1737 static const SkSmartAttributeInfo a = {
1738 "emergency-retract-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL
1739 };
1740 return &a;
1741 }
1742 /* %STRINGPOOLSTOP% */
1743
1744 break;
1745
1746 case 194:
1747 /* %STRINGPOOLSTART% */
1748 if (quirk & SK_SMART_QUIRK_194_10XCELSIUS) {
1749 static const SkSmartAttributeInfo a = {
1750 "temperature-centi-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature
1751 };
1752 return &a;
1753 } else if (quirk & SK_SMART_QUIRK_194_UNKNOWN)
1754 return NULL;
1755 /* %STRINGPOOLSTOP% */
1756
1757 break;
1758
1759 case 197:
1760 if (quirk & SK_SMART_QUIRK_197_UNKNOWN)
1761 return NULL;
1762
1763 break;
1764
1765 case 198:
1766 if (quirk & SK_SMART_QUIRK_198_UNKNOWN)
1767 return NULL;
1768
1769 break;
1770
1771 case 200:
1772 /* %STRINGPOOLSTART% */
1773 if (quirk & SK_SMART_QUIRK_200_WRITEERRORCOUNT) {
1774 static const SkSmartAttributeInfo a = {
1775 "write-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL
1776 };
1777 return &a;
1778 }
1779 /* %STRINGPOOLSTOP% */
1780
1781 break;
1782
1783 case 201:
1784 /* %STRINGPOOLSTART% */
1785 if (quirk & SK_SMART_QUIRK_201_DETECTEDTACOUNT) {
1786 static const SkSmartAttributeInfo a = {
1787 "detected-ta-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL
1788 };
1789 return &a;
1790 }
1791 /* %STRINGPOOLSTOP% */
1792
1793 break;
1794
1795 case 225:
1796 /* %STRINGPOOLSTART% */
1797 if (quirk & SK_SMART_QUIRK_225_TOTALLBASWRITTEN) {
1798 static const SkSmartAttributeInfo a = {
1799 "total-lbas-written", SK_SMART_ATTRIBUTE_UNIT_MB, NULL
1800 };
1801 return &a;
1802 }
1803 /* %STRINGPOOLSTOP% */
1804
1805 break;
1806
1807 case 226:
1808 /* %STRINGPOOLSTART% */
1809 if (quirk & SK_SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR) {
1810 static const SkSmartAttributeInfo a = {
1811 "timed-workload-media-wear", SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT, NULL
1812 };
1813 return &a;
1814 }
1815 /* %STRINGPOOLSTOP% */
1816
1817 break;
1818
1819 case 227:
1820 /* %STRINGPOOLSTART% */
1821 if (quirk & SK_SMART_QUIRK_227_TIMEWORKLOADHOSTREADS) {
1822 static const SkSmartAttributeInfo a = {
1823 "timed-workload-host-reads", SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT, NULL
1824 };
1825 return &a;
1826 }
1827 /* %STRINGPOOLSTOP% */
1828
1829 break;
1830
1831 case 228:
1832 /* %STRINGPOOLSTART% */
1833 if (quirk & SK_SMART_QUIRK_228_WORKLOADTIMER) {
1834 static const SkSmartAttributeInfo a = {
1835 "workload-timer", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, NULL
1836 };
1837 return &a;
1838 }
1839 /* %STRINGPOOLSTOP% */
1840
1841 break;
1842
1843 case 232:
1844 /* %STRINGPOOLSTART% */
1845 if (quirk & SK_SMART_QUIRK_232_AVAILABLERESERVEDSPACE) {
1846 static const SkSmartAttributeInfo a = {
1847 "available-reserved-space", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL
1848 };
1849 return &a;
1850 }
1851 /* %STRINGPOOLSTOP% */
1852 break;
1853
1854 case 233:
1855 /* %STRINGPOOLSTART% */
1856 if (quirk & SK_SMART_QUIRK_233_MEDIAWEAROUTINDICATOR) {
1857 static const SkSmartAttributeInfo a = {
1858 "media-wearout-indicator", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL
1859 };
1860 return &a;
1861 }
1862 /* %STRINGPOOLSTOP% */
1863 break;
1864
1865 }
1866 }
1867
1868 /* These are the simple cases */
1869 if (attribute_info[id].name)
1870 return &attribute_info[id];
1871
1872 return NULL;
1873}
1874
1875int sk_disk_smart_parse(SkDisk *d, const SkSmartParsedData **spd) {
1876
1877 if (!d->smart_data_valid) {
1878 errno = ENOENT;
1879 return -1;
1880 }
1881
1882 switch (d->smart_data[362]) {
1883 case 0x00:
1884 case 0x80:
1885 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER;
1886 break;
1887
1888 case 0x02:
1889 case 0x82:
1890 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS;
1891 break;
1892
1893 case 0x03:
1894 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS;
1895 break;
1896
1897 case 0x04:
1898 case 0x84:
1899 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED;
1900 break;
1901
1902 case 0x05:
1903 case 0x85:
1904 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED;
1905 break;
1906
1907 case 0x06:
1908 case 0x86:
1909 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL;
1910 break;
1911
1912 default:
1913 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN;
1914 break;
1915 }
1916
1917 d->smart_parsed_data.self_test_execution_percent_remaining = 10*(d->smart_data[363] & 0xF);
1918 d->smart_parsed_data.self_test_execution_status = (d->smart_data[363] >> 4) & 0xF;
1919
1920 d->smart_parsed_data.total_offline_data_collection_seconds = (uint16_t) d->smart_data[364] | ((uint16_t) d->smart_data[365] << 8);
1921
1922 d->smart_parsed_data.conveyance_test_available = disk_smart_is_conveyance_test_available(d);
1923 d->smart_parsed_data.short_and_extended_test_available = disk_smart_is_short_and_extended_test_available(d);
1924 d->smart_parsed_data.start_test_available = disk_smart_is_start_test_available(d);
1925 d->smart_parsed_data.abort_test_available = disk_smart_is_abort_test_available(d);
1926
1927 d->smart_parsed_data.short_test_polling_minutes = d->smart_data[372];
1928 d->smart_parsed_data.extended_test_polling_minutes = d->smart_data[373] != 0xFF ? d->smart_data[373] : ((uint16_t) d->smart_data[376] << 8 | (uint16_t) d->smart_data[375]);
1929 d->smart_parsed_data.conveyance_test_polling_minutes = d->smart_data[374];
1930
1931 *spd = &d->smart_parsed_data;
1932
1933 return 0;
1934}
1935
1936static void find_threshold(SkDisk *d, SkSmartAttributeParsedData *a) {
1937 uint8_t *p;
1938 unsigned n;
1939
1940 if (!d->smart_thresholds_valid)
1941 goto fail;
1942
1943 for (n = 0, p = d->smart_thresholds+2; n < 30; n++, p+=12)
1944 if (p[0] == a->id)
1945 break;
1946
1947 if (n >= 30)
1948 goto fail;
1949
1950 a->threshold = p[1];
1951 a->threshold_valid = p[1] != 0xFE;
1952
1953 a->good_now_valid = FALSE;
1954 a->good_now = TRUE;
1955 a->good_in_the_past_valid = FALSE;
1956 a->good_in_the_past = TRUE;
1957
1958 /* Always-Fail and Always-Passing thresholds are not relevant
1959 * for our assessment. */
1960 if (p[1] >= 1 && p[1] <= 0xFD) {
1961
1962 if (a->worst_value_valid) {
1963 a->good_in_the_past = a->good_in_the_past && (a->worst_value > a->threshold);
1964 a->good_in_the_past_valid = TRUE;
1965 }
1966
1967 if (a->current_value_valid) {
1968 a->good_now = a->good_now && (a->current_value > a->threshold);
1969 a->good_now_valid = TRUE;
1970 }
1971 }
1972
1973 a->warn =
1974 (a->good_now_valid && !a->good_now) ||
1975 (a->good_in_the_past_valid && !a->good_in_the_past);
1976
1977 return;
1978
1979fail:
1980 a->threshold_valid = FALSE;
1981 a->good_now_valid = FALSE;
1982 a->good_in_the_past_valid = FALSE;
1983 a->warn = FALSE;
1984}
1985
1986int sk_disk_smart_parse_attributes(SkDisk *d, SkSmartAttributeParseCallback cb, void* userdata) {
1987 uint8_t *p;
1988 unsigned n;
1989
1990 if (!d->smart_data_valid) {
1991 errno = ENOENT;
1992 return -1;
1993 }
1994
1995 for (n = 0, p = d->smart_data + 2; n < 30; n++, p+=12) {
1996 SkSmartAttributeParsedData a;
1997 const SkSmartAttributeInfo *i;
1998 char *an = NULL;
1999
2000 if (p[0] == 0)
2001 continue;
2002
2003 memset(&a, 0, sizeof(a));
2004 a.id = p[0];
2005 a.current_value = p[3];
2006 a.current_value_valid = p[3] >= 1 && p[3] <= 0xFD;
2007 a.worst_value = p[4];
2008 a.worst_value_valid = p[4] >= 1 && p[4] <= 0xFD;
2009
2010 a.flags = ((uint16_t) p[2] << 8) | p[1];
2011 a.prefailure = !!(p[1] & 1);
2012 a.online = !!(p[1] & 2);
2013
2014 memcpy(a.raw, p+5, 6);
2015
2016 if ((i = lookup_attribute(d, p[0]))) {
2017 a.name = _P(i->name);
2018 a.pretty_unit = i->unit;
2019 } else {
2020 if (asprintf(&an, "attribute-%u", a.id) < 0) {
2021 errno = ENOMEM;
2022 return -1;
2023 }
2024
2025 a.name = an;
2026 a.pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
2027 }
2028
2029 make_pretty(&a);
2030
2031 find_threshold(d, &a);
2032
2033 if (i && i->verify)
2034 i->verify(d, &a);
2035
2036 cb(d, &a, userdata);
2037 free(an);
2038 }
2039
2040 return 0;
2041}
2042
2043static const char *yes_no(SkBool b) {
2044 return b ? "yes" : "no";
2045}
2046
2047const char* sk_smart_attribute_unit_to_string(SkSmartAttributeUnit unit) {
2048
2049 /* %STRINGPOOLSTART% */
2050 const char * const map[] = {
2051 [SK_SMART_ATTRIBUTE_UNIT_UNKNOWN] = NULL,
2052 [SK_SMART_ATTRIBUTE_UNIT_NONE] = "",
2053 [SK_SMART_ATTRIBUTE_UNIT_MSECONDS] = "ms",
2054 [SK_SMART_ATTRIBUTE_UNIT_SECTORS] = "sectors",
2055 [SK_SMART_ATTRIBUTE_UNIT_MKELVIN] = "mK",
2056 [SK_SMART_ATTRIBUTE_UNIT_PERCENT] = "%",
2057 [SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT] = "%",
2058 [SK_SMART_ATTRIBUTE_UNIT_MB] = "MB"
2059 };
2060 /* %STRINGPOOLSTOP% */
2061
2062 if (unit >= _SK_SMART_ATTRIBUTE_UNIT_MAX)
2063 return NULL;
2064
2065 return _P(map[unit]);
2066}
2067
2068struct attr_helper {
2069 uint64_t *value;
2070 SkBool found;
2071};
2072
2073static void temperature_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
2074
2075 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MKELVIN)
2076 return;
2077
2078 if (!strcmp(a->name, "temperature-centi-celsius") ||
2079 !strcmp(a->name, "temperature-celsius") ||
2080 !strcmp(a->name, "temperature-celsius-2") ||
2081 !strcmp(a->name, "airflow-temperature-celsius")) {
2082
2083 if (!ah->found || a->pretty_value > *ah->value)
2084 *ah->value = a->pretty_value;
2085
2086 ah->found = TRUE;
2087 }
2088}
2089
2090int sk_disk_smart_get_temperature(SkDisk *d, uint64_t *kelvin) {
2091 struct attr_helper ah;
2092
2093 assert(d);
2094 assert(kelvin);
2095
2096 ah.found = FALSE;
2097 ah.value = kelvin;
2098
2099 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) temperature_cb, &ah) < 0)
2100 return -1;
2101
2102 if (!ah.found) {
2103 errno = ENOENT;
2104 return -1;
2105 }
2106
2107 return 0;
2108}
2109
2110static void power_on_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
2111
2112 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MSECONDS)
2113 return;
2114
2115 if (!strcmp(a->name, "power-on-minutes") ||
2116 !strcmp(a->name, "power-on-seconds") ||
2117 !strcmp(a->name, "power-on-seconds-2") ||
2118 !strcmp(a->name, "power-on-half-minutes") ||
2119 !strcmp(a->name, "power-on-hours")) {
2120
2121 if (!ah->found || a->pretty_value > *ah->value)
2122 *ah->value = a->pretty_value;
2123
2124 ah->found = TRUE;
2125 }
2126}
2127
2128int sk_disk_smart_get_power_on(SkDisk *d, uint64_t *mseconds) {
2129 struct attr_helper ah;
2130
2131 assert(d);
2132 assert(mseconds);
2133
2134 ah.found = FALSE;
2135 ah.value = mseconds;
2136
2137 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_on_cb, &ah) < 0)
2138 return -1;
2139
2140 if (!ah.found) {
2141 errno = ENOENT;
2142 return -1;
2143 }
2144
2145 return 0;
2146}
2147
2148static void power_cycle_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
2149
2150 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_NONE)
2151 return;
2152
2153 if (!strcmp(a->name, "power-cycle-count")) {
2154
2155 if (!ah->found || a->pretty_value > *ah->value)
2156 *ah->value = a->pretty_value;
2157
2158 ah->found = TRUE;
2159 }
2160}
2161
2162int sk_disk_smart_get_power_cycle(SkDisk *d, uint64_t *count) {
2163 struct attr_helper ah;
2164
2165 assert(d);
2166 assert(count);
2167
2168 ah.found = FALSE;
2169 ah.value = count;
2170
2171 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_cycle_cb, &ah) < 0)
2172 return -1;
2173
2174 if (!ah.found) {
2175 errno = ENOENT;
2176 return -1;
2177 }
2178
2179 return 0;
2180}
2181
2182static void fill_cache_cb(SkDisk *d, const SkSmartAttributeParsedData *a, void* userdata) {
2183
2184 if (a->prefailure) {
2185 if (a->good_now_valid && !a->good_now)
2186 d->bad_attribute_now = TRUE;
2187
2188 if (a->good_in_the_past_valid && !a->good_in_the_past)
2189 d->bad_attribute_in_the_past = TRUE;
2190 }
2191
2192 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_SECTORS)
2193 return;
2194
2195 if (!a->current_value_valid)
2196 return;
2197
2198 if (!strcmp(a->name, "reallocated-sector-count")) {
2199 if (a->pretty_value > d->reallocated_sector_count)
2200 d->reallocated_sector_count = a->pretty_value;
2201 d->reallocated_sector_count_found = TRUE;
2202 if (a->good_now_valid && !a->good_now)
2203 d->reallocated_sector_count_bad = TRUE;
2204 }
2205
2206 if (!strcmp(a->name, "current-pending-sector")) {
2207 if (a->pretty_value > d->current_pending_sector)
2208 d->current_pending_sector = a->pretty_value;
2209 d->current_pending_sector_found = TRUE;
2210 if (a->good_now_valid && !a->good_now)
2211 d->current_pending_sector_bad = TRUE;
2212 }
2213}
2214
2215static int fill_cache(SkDisk *d) {
2216 if (d->attribute_cache_valid)
2217 return 0;
2218
2219 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) fill_cache_cb, NULL) >= 0) {
2220 d->attribute_cache_valid = TRUE;
2221 return 0;
2222 } else
2223 return -1;
2224}
2225
2226int sk_disk_smart_get_bad(SkDisk *d, uint64_t *sectors) {
2227 assert(d);
2228 assert(sectors);
2229
2230 if (fill_cache (d) < 0)
2231 return -1;
2232
2233 if (!d->reallocated_sector_count_found && !d->current_pending_sector_found) {
2234 errno = ENOENT;
2235 return -1;
2236 }
2237
2238 if (d->reallocated_sector_count_found && d->current_pending_sector_found)
2239 *sectors = d->reallocated_sector_count + d->current_pending_sector;
2240 else if (d->reallocated_sector_count_found)
2241 *sectors = d->reallocated_sector_count;
2242 else
2243 *sectors = d->current_pending_sector;
2244
2245 return 0;
2246}
2247
2248const char* sk_smart_overall_to_string(SkSmartOverall overall) {
2249
2250 /* %STRINGPOOLSTART% */
2251 const char * const map[] = {
2252 [SK_SMART_OVERALL_GOOD] = "GOOD",
2253 [SK_SMART_OVERALL_BAD_ATTRIBUTE_IN_THE_PAST] = "BAD_ATTRIBUTE_IN_THE_PAST",
2254 [SK_SMART_OVERALL_BAD_SECTOR] = "BAD_SECTOR",
2255 [SK_SMART_OVERALL_BAD_ATTRIBUTE_NOW] = "BAD_ATTRIBUTE_NOW",
2256 [SK_SMART_OVERALL_BAD_SECTOR_MANY] = "BAD_SECTOR_MANY",
2257 [SK_SMART_OVERALL_BAD_STATUS] = "BAD_STATUS",
2258 };
2259 /* %STRINGPOOLSTOP% */
2260
2261 if (overall >= _SK_SMART_OVERALL_MAX)
2262 return NULL;
2263
2264 return _P(map[overall]);
2265}
2266
2267int sk_disk_smart_get_overall(SkDisk *d, SkSmartOverall *overall) {
2268 SkBool good;
2269 uint64_t sectors;
2270
2271 assert(d);
2272 assert(overall);
2273
2274 /* First, check SMART self-assesment */
2275 if (sk_disk_smart_status(d, &good) < 0)
2276 return -1;
2277
2278 if (!good) {
2279 *overall = SK_SMART_OVERALL_BAD_STATUS;
2280 return 0;
2281 }
2282
2283 /* Second, check if the number of bad sectors is greater than
2284 * a certain threshold */
2285 if (sk_disk_smart_get_bad(d, &sectors) < 0) {
2286 if (errno != ENOENT)
2287 return -1;
2288 sectors = 0;
2289 } else {
2290 if (d->reallocated_sector_count_bad || d->current_pending_sector_bad) {
2291 *overall = SK_SMART_OVERALL_BAD_SECTOR_MANY;
2292 return 0;
2293 }
2294 }
2295
2296 /* Third, check if any of the SMART attributes is bad */
2297 if (fill_cache (d) < 0)
2298 return -1;
2299
2300 if (d->bad_attribute_now) {
2301 *overall = SK_SMART_OVERALL_BAD_ATTRIBUTE_NOW;
2302 return 0;
2303 }
2304
2305 /* Fourth, check if there are any bad sectors at all */
2306 if (sectors > 0) {
2307 *overall = SK_SMART_OVERALL_BAD_SECTOR;
2308 return 0;
2309 }
2310
2311 /* Fifth, check if any of the SMART attributes ever was bad */
2312 if (d->bad_attribute_in_the_past) {
2313 *overall = SK_SMART_OVERALL_BAD_ATTRIBUTE_IN_THE_PAST;
2314 return 0;
2315 }
2316
2317 /* Sixth, there's really nothing to complain about, so give it a pass */
2318 *overall = SK_SMART_OVERALL_GOOD;
2319 return 0;
2320}
2321
2322static char* print_name(char *s, size_t len, uint8_t id, const char *k) {
2323
2324 if (k)
2325 strncpy(s, k, len);
2326 else
2327 snprintf(s, len, "%u", id);
2328
2329 s[len-1] = 0;
2330
2331 return s;
2332}
2333
2334static char *print_value(char *s, size_t len, uint64_t pretty_value, SkSmartAttributeUnit pretty_unit) {
2335
2336 switch (pretty_unit) {
2337 case SK_SMART_ATTRIBUTE_UNIT_MSECONDS:
2338
2339 if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*365LLU)
2340 snprintf(s, len, "%0.1f years", ((double) pretty_value)/(1000.0*60*60*24*365));
2341 else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*30LLU)
2342 snprintf(s, len, "%0.1f months", ((double) pretty_value)/(1000.0*60*60*24*30));
2343 else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU)
2344 snprintf(s, len, "%0.1f days", ((double) pretty_value)/(1000.0*60*60*24));
2345 else if (pretty_value >= 1000LLU*60LLU*60LLU)
2346 snprintf(s, len, "%0.1f h", ((double) pretty_value)/(1000.0*60*60));
2347 else if (pretty_value >= 1000LLU*60LLU)
2348 snprintf(s, len, "%0.1f min", ((double) pretty_value)/(1000.0*60));
2349 else if (pretty_value >= 1000LLU)
2350 snprintf(s, len, "%0.1f s", ((double) pretty_value)/(1000.0));
2351 else
2352 snprintf(s, len, "%llu ms", (unsigned long long) pretty_value);
2353
2354 break;
2355
2356 case SK_SMART_ATTRIBUTE_UNIT_MKELVIN:
2357 snprintf(s, len, "%0.1f C", ((double) pretty_value - 273150) / 1000);
2358 break;
2359
2360 case SK_SMART_ATTRIBUTE_UNIT_SECTORS:
2361 snprintf(s, len, "%llu sectors", (unsigned long long) pretty_value);
2362 break;
2363
2364 case SK_SMART_ATTRIBUTE_UNIT_PERCENT:
2365 snprintf(s, len, "%llu%%", (unsigned long long) pretty_value);
2366 break;
2367
2368 case SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT:
2369 snprintf(s, len, "%0.3f%%", (double) pretty_value);
2370 break;
2371
2372 case SK_SMART_ATTRIBUTE_UNIT_MB:
2373 if (pretty_value >= 1000000LLU)
2374 snprintf(s, len, "%0.3f TB", (double) pretty_value / 1000000LLU);
2375 else if (pretty_value >= 1000LLU)
2376 snprintf(s, len, "%0.3f GB", (double) pretty_value / 1000LLU);
2377 else
2378 snprintf(s, len, "%llu MB", (unsigned long long) pretty_value);
2379 break;
2380
2381 case SK_SMART_ATTRIBUTE_UNIT_NONE:
2382 snprintf(s, len, "%llu", (unsigned long long) pretty_value);
2383 break;
2384
2385 case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN:
2386 snprintf(s, len, "n/a");
2387 break;
2388
2389 case _SK_SMART_ATTRIBUTE_UNIT_MAX:
2390 assert(FALSE);
2391 }
2392
2393 s[len-1] = 0;
2394
2395 return s;
2396}
2397
2398#define HIGHLIGHT "\x1B[1m"
2399#define ENDHIGHLIGHT "\x1B[0m"
2400
2401static void disk_dump_attributes(SkDisk *d, const SkSmartAttributeParsedData *a, void* userdata) {
2402 char name[32];
2403 char pretty[32];
2404 char tt[32], tw[32], tc[32];
2405 SkBool highlight;
2406
2407 snprintf(tt, sizeof(tt), "%3u", a->threshold);
2408 tt[sizeof(tt)-1] = 0;
2409 snprintf(tw, sizeof(tw), "%3u", a->worst_value);
2410 tw[sizeof(tw)-1] = 0;
2411 snprintf(tc, sizeof(tc), "%3u", a->current_value);
2412 tc[sizeof(tc)-1] = 0;
2413
2414 highlight = a->warn && isatty(1);
2415
2416 if (highlight)
2417 fprintf(stderr, HIGHLIGHT);
2418
2419 printf("%3u %-27s %-3s %-3s %-3s %-11s 0x%02x%02x%02x%02x%02x%02x %-7s %-7s %-4s %-4s\n",
2420 a->id,
2421 print_name(name, sizeof(name), a->id, a->name),
2422 a->current_value_valid ? tc : "n/a",
2423 a->worst_value_valid ? tw : "n/a",
2424 a->threshold_valid ? tt : "n/a",
2425 print_value(pretty, sizeof(pretty), a->pretty_value, a->pretty_unit),
2426 a->raw[0], a->raw[1], a->raw[2], a->raw[3], a->raw[4], a->raw[5],
2427 a->prefailure ? "prefail" : "old-age",
2428 a->online ? "online" : "offline",
2429 a->good_now_valid ? yes_no(a->good_now) : "n/a",
2430 a->good_in_the_past_valid ? yes_no(a->good_in_the_past) : "n/a");
2431
2432 if (highlight)
2433 fprintf(stderr, ENDHIGHLIGHT);
2434}
2435
2436int sk_disk_dump(SkDisk *d) {
2437 int ret;
2438 SkBool awake = FALSE;
2439 uint64_t size;
2440
2441 assert(d);
2442
2443 printf("Device: %s%s%s\n"
2444 "Type: %s\n",
2445 d->name ? disk_type_to_prefix_string(d->type) : "",
2446 d->name ? ":" : "",
2447 d->name ? d->name : "n/a",
2448 disk_type_to_human_string(d->type));
2449
2450 ret = sk_disk_get_size(d, &size);
2451 if (ret >= 0)
2452 printf("Size: %lu MiB\n", (unsigned long) (d->size/1024/1024));
2453 else
2454 printf("Size: %s\n", strerror(errno));
2455
2456 if (d->identify_valid) {
2457 const SkIdentifyParsedData *ipd;
2458 SkSmartQuirk quirk = 0;
2459 unsigned i;
2460
2461 if ((ret = sk_disk_identify_parse(d, &ipd)) < 0)
2462 return ret;
2463
2464 printf("Model: [%s]\n"
2465 "Serial: [%s]\n"
2466 "Firmware: [%s]\n"
2467 "SMART Available: %s\n",
2468 ipd->model,
2469 ipd->serial,
2470 ipd->firmware,
2471 yes_no(disk_smart_is_available(d)));
2472
2473 if ((ret = lookup_quirks(ipd->model, ipd->firmware, &quirk)))
2474 return ret;
2475
2476 printf("Quirks:");
2477
2478 for (i = 0; quirk_name[i]; i++)
2479 if (quirk & (1<<i))
2480 printf(" %s", _P(quirk_name[i]));
2481
2482 printf("\n");
2483 }
2484
2485 ret = sk_disk_check_sleep_mode(d, &awake);
2486 printf("Awake: %s\n",
2487 ret >= 0 ? yes_no(awake) : strerror(errno));
2488
2489 if (disk_smart_is_available(d)) {
2490 SkSmartOverall overall;
2491 const SkSmartParsedData *spd;
2492 SkBool good;
2493 char pretty[32];
2494 uint64_t value, power_on;
2495
2496 ret = sk_disk_smart_status(d, &good);
2497 printf("%sSMART Disk Health Good: %s%s\n",
2498 ret >= 0 && !good ? HIGHLIGHT : "",
2499 ret >= 0 ? yes_no(good) : strerror(errno),
2500 ret >= 0 && !good ? ENDHIGHLIGHT : "");
2501 if ((ret = sk_disk_smart_read_data(d)) < 0)
2502 return ret;
2503
2504 if ((ret = sk_disk_smart_parse(d, &spd)) < 0)
2505 return ret;
2506
2507 printf("Off-line Data Collection Status: [%s]\n"
2508 "Total Time To Complete Off-Line Data Collection: %u s\n"
2509 "Self-Test Execution Status: [%s]\n"
2510 "Percent Self-Test Remaining: %u%%\n"
2511 "Conveyance Self-Test Available: %s\n"
2512 "Short/Extended Self-Test Available: %s\n"
2513 "Start Self-Test Available: %s\n"
2514 "Abort Self-Test Available: %s\n"
2515 "Short Self-Test Polling Time: %u min\n"
2516 "Extended Self-Test Polling Time: %u min\n"
2517 "Conveyance Self-Test Polling Time: %u min\n",
2518 sk_smart_offline_data_collection_status_to_string(spd->offline_data_collection_status),
2519 spd->total_offline_data_collection_seconds,
2520 sk_smart_self_test_execution_status_to_string(spd->self_test_execution_status),
2521 spd->self_test_execution_percent_remaining,
2522 yes_no(spd->conveyance_test_available),
2523 yes_no(spd->short_and_extended_test_available),
2524 yes_no(spd->start_test_available),
2525 yes_no(spd->abort_test_available),
2526 spd->short_test_polling_minutes,
2527 spd->extended_test_polling_minutes,
2528 spd->conveyance_test_polling_minutes);
2529
2530 if (sk_disk_smart_get_bad(d, &value) < 0)
2531 printf("Bad Sectors: %s\n", strerror(errno));
2532 else
2533 printf("%sBad Sectors: %s%s\n",
2534 value > 0 ? HIGHLIGHT : "",
2535 print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_SECTORS),
2536 value > 0 ? ENDHIGHLIGHT : "");
2537
2538 if (sk_disk_smart_get_power_on(d, &power_on) < 0) {
2539 printf("Powered On: %s\n", strerror(errno));
2540 power_on = 0;
2541 } else
2542 printf("Powered On: %s\n", print_value(pretty, sizeof(pretty), power_on, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2543
2544 if (sk_disk_smart_get_power_cycle(d, &value) < 0)
2545 printf("Power Cycles: %s\n", strerror(errno));
2546 else {
2547 printf("Power Cycles: %llu\n", (unsigned long long) value);
2548
2549 if (value > 0 && power_on > 0)
2550 printf("Average Powered On Per Power Cycle: %s\n", print_value(pretty, sizeof(pretty), power_on/value, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2551 }
2552
2553 if (sk_disk_smart_get_temperature(d, &value) < 0)
2554 printf("Temperature: %s\n", strerror(errno));
2555 else
2556 printf("Temperature: %s\n", print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_MKELVIN));
2557
2558 printf("Attribute Parsing Verification: %s\n",
2559 d->attribute_verification_bad ? "Bad" : "Good");
2560
2561 if (sk_disk_smart_get_overall(d, &overall) < 0)
2562 printf("Overall Status: %s\n", strerror(errno));
2563 else
2564 printf("%sOverall Status: %s%s\n",
2565 overall != SK_SMART_OVERALL_GOOD ? HIGHLIGHT : "",
2566 sk_smart_overall_to_string(overall),
2567 overall != SK_SMART_OVERALL_GOOD ? ENDHIGHLIGHT : "");
2568
2569 printf("%3s %-27s %5s %5s %5s %-11s %-14s %-7s %-7s %-4s %-4s\n",
2570 "ID#",
2571 "Name",
2572 "Value",
2573 "Worst",
2574 "Thres",
2575 "Pretty",
2576 "Raw",
2577 "Type",
2578 "Updates",
2579 "Good",
2580 "Good/Past");
2581
2582 if ((ret = sk_disk_smart_parse_attributes(d, disk_dump_attributes, NULL)) < 0)
2583 return ret;
2584 } else
2585 printf("ATA SMART not supported.\n");
2586
2587 return 0;
2588}
2589
2590int sk_disk_get_size(SkDisk *d, uint64_t *bytes) {
2591 assert(d);
2592 assert(bytes);
2593
2594 if (d->size == (uint64_t) -1) {
2595 errno = ENODATA;
2596 return -1;
2597 }
2598
2599 *bytes = d->size;
2600 return 0;
2601}
2602
2603static int disk_find_type(SkDisk *d, dev_t devnum) {
2604 struct udev *udev;
2605 struct udev_device *dev = NULL, *usb;
2606 int r = -1;
2607 const char *a;
2608
2609 assert(d);
2610
2611 if (!(udev = udev_new())) {
2612 errno = ENXIO;
2613 goto finish;
2614 }
2615
2616 if (!(dev = udev_device_new_from_devnum(udev, 'b', devnum))) {
2617 errno = ENODEV;
2618 goto finish;
2619 }
2620
2621 if ((a = udev_device_get_property_value(dev, "ID_ATA_SMART_ACCESS"))) {
2622 unsigned u;
2623
2624 for (u = 0; u < _SK_DISK_TYPE_MAX; u++) {
2625 const char *t;
2626
2627 if (!(t = disk_type_to_prefix_string(u)))
2628 continue;
2629
2630 if (!strcmp(a, t)) {
2631 d->type = u;
2632 r = 0;
2633 goto finish;
2634 }
2635 }
2636
2637 d->type = SK_DISK_TYPE_NONE;
2638 r = 0;
2639 goto finish;
2640 }
2641
2642 if ((usb = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"))) {
2643 const char *product, *vendor;
2644 uint32_t pid, vid;
2645
2646 if (!(product = udev_device_get_sysattr_value(usb, "idProduct")) ||
2647 sscanf(product, "%04x", &pid) != 1) {
2648 errno = ENODEV;
2649 goto finish;
2650 }
2651
2652 if (!(vendor = udev_device_get_sysattr_value(usb, "idVendor")) ||
2653 sscanf(vendor, "%04x", &vid) != 1) {
2654 errno = ENODEV;
2655 goto finish;
2656 }
2657
2658 if ((vid == 0x0928 && pid == 0x0000))
2659 /* This Oxford Semiconductor bridge seems to
2660 * choke on SAT commands. Let's explicitly
2661 * black list it here.
2662 *
2663 * http://bugs.freedesktop.org/show_bug.cgi?id=24951 */
2664 d->type = SK_DISK_TYPE_NONE;
2665 else if ((vid == 0x152d && pid == 0x2329) ||
2666 (vid == 0x152d && pid == 0x2338) ||
2667 (vid == 0x152d && pid == 0x2339))
2668 /* Some JMicron bridges seem to choke on SMART
2669 * commands, so let's explicitly black list
2670 * them here.
2671 *
2672 * https://bugzilla.redhat.com/show_bug.cgi?id=515881
2673 *
2674 * At least some of the JMicron bridges with
2675 * these vids/pids choke on the jmicron access
2676 * mode. To make sure we don't break things
2677 * for people we now disable this by
2678 * default. */
2679 d->type = SK_DISK_TYPE_NONE;
2680 else if ((vid == 0x152d && pid == 0x2336))
2681 /* This JMicron bridge seems to always work
2682 * with SMART commands send with the jmicron
2683 * access mode. */
2684 d->type = SK_DISK_TYPE_JMICRON;
2685 else if ((vid == 0x0c0b && pid == 0xb159) ||
2686 (vid == 0x04fc && pid == 0x0c25) ||
2687 (vid == 0x04fc && pid == 0x0c15))
2688 d->type = SK_DISK_TYPE_SUNPLUS;
2689 else
2690 d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_12;
2691
2692 } else if (udev_device_get_parent_with_subsystem_devtype(dev, "ide", NULL))
2693 d->type = SK_DISK_TYPE_LINUX_IDE;
2694 else if (udev_device_get_parent_with_subsystem_devtype(dev, "scsi", NULL))
2695 d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_16;
2696 else
2697 d->type = SK_DISK_TYPE_AUTO;
2698
2699 r = 0;
2700
2701finish:
2702 if (dev)
2703 udev_device_unref(dev);
2704
2705 if (udev)
2706 udev_unref(udev);
2707
2708 return r;
2709}
2710
2711static int init_smart(SkDisk *d) {
2712 /* We don't do the SMART initialization right-away, since some
2713 * drivers spin up when we do that */
2714
2715 int ret;
2716
2717 if (d->smart_initialized)
2718 return 0;
2719
2720 d->smart_initialized = TRUE;
2721
2722 /* Check if driver can do SMART, and enable if necessary */
2723 if (!disk_smart_is_available(d))
2724 return 0;
2725
2726 if (!disk_smart_is_enabled(d)) {
2727 if ((ret = disk_smart_enable(d, TRUE)) < 0)
2728 goto fail;
2729
2730 if ((ret = disk_identify_device(d)) < 0)
2731 goto fail;
2732
2733 if (!disk_smart_is_enabled(d)) {
2734 errno = EIO;
2735 ret = -1;
2736 goto fail;
2737 }
2738 }
2739
2740 disk_smart_read_thresholds(d);
2741 ret = 0;
2742
2743fail:
2744 return ret;
2745}
2746
2747int sk_disk_open(const char *name, SkDisk **_d) {
2748 SkDisk *d;
2749 int ret = -1;
2750 struct stat st;
2751
2752 assert(_d);
2753
2754 if (!(d = calloc(1, sizeof(SkDisk)))) {
2755 errno = ENOMEM;
2756 goto fail;
2757 }
2758
2759 d->fd = -1;
2760 d->size = (uint64_t) -1;
2761
2762 if (!name)
2763 d->type = SK_DISK_TYPE_BLOB;
2764 else {
2765 const char *dn;
2766
2767 d->type = SK_DISK_TYPE_AUTO;
2768
2769 if (!(dn = disk_type_from_string(name, &d->type)))
2770 dn = name;
2771
2772 if (!(d->name = strdup(dn))) {
2773 errno = ENOMEM;
2774 goto fail;
2775 }
2776
2777 if ((d->fd = open(d->name,
2778 O_RDONLY|O_NOCTTY|O_NONBLOCK
2779#ifdef O_CLOEXEC
2780 |O_CLOEXEC
2781#endif
2782
2783 )) < 0) {
2784 ret = d->fd;
2785 goto fail;
2786 }
2787
2788 if ((ret = fstat(d->fd, &st)) < 0)
2789 goto fail;
2790
2791 if (!S_ISBLK(st.st_mode)) {
2792 errno = ENODEV;
2793 ret = -1;
2794 goto fail;
2795 }
2796
2797 /* So, it's a block device. Let's make sure the ioctls work */
2798 if ((ret = ioctl(d->fd, BLKGETSIZE64, &d->size)) < 0)
2799 goto fail;
2800
2801 if (d->size <= 0 || d->size == (uint64_t) -1) {
2802 errno = EIO;
2803 ret = -1;
2804 goto fail;
2805 }
2806
2807 /* OK, it's a real block device with a size. Now let's find the suitable API */
2808 if (d->type == SK_DISK_TYPE_AUTO)
2809 if ((ret = disk_find_type(d, st.st_rdev)) < 0)
2810 goto fail;
2811
2812 if (d->type == SK_DISK_TYPE_AUTO) {
2813 /* We have no clue, so let's autotest for a working API */
2814 for (d->type = 0; d->type < _SK_DISK_TYPE_TEST_MAX; d->type++)
2815 if (disk_identify_device(d) >= 0)
2816 break;
2817 if (d->type >= _SK_DISK_TYPE_TEST_MAX)
2818 d->type = SK_DISK_TYPE_NONE;
2819 } else
2820 disk_identify_device(d);
2821 }
2822
2823 *_d = d;
2824
2825 return 0;
2826
2827fail:
2828
2829 if (d)
2830 sk_disk_free(d);
2831
2832 return ret;
2833}
2834
2835void sk_disk_free(SkDisk *d) {
2836 assert(d);
2837
2838 if (d->fd >= 0)
2839 close(d->fd);
2840
2841 free(d->name);
2842 free(d->blob);
2843 free(d);
2844}
2845
2846int sk_disk_get_blob(SkDisk *d, const void **blob, size_t *rsize) {
2847 size_t size;
2848 SkBool good, have_good = FALSE;
2849 uint32_t *p;
2850
2851 assert(d);
2852 assert(blob);
2853 assert(rsize);
2854
2855 size =
2856 (d->identify_valid ? 8 + sizeof(d->identify) : 0) +
2857 (d->smart_data_valid ? 8 + sizeof(d->smart_data) : 0) +
2858 (d->smart_thresholds_valid ? 8 + sizeof(d->smart_thresholds) : 0);
2859
2860 if (sk_disk_smart_status(d, &good) >= 0) {
2861 size += 12;
2862 have_good = TRUE;
2863 }
2864
2865 if (size <= 0) {
2866 errno = ENODATA;
2867 return -1;
2868 }
2869
2870 free(d->blob);
2871 if (!(d->blob = malloc(size))) {
2872 errno = ENOMEM;
2873 return -1;
2874 }
2875
2876 p = d->blob;
2877
2878 /* These memory accesses are only OK as long as all our
2879 * objects are sensibly aligned, which they are... */
2880
2881 if (d->identify_valid) {
2882 p[0] = SK_BLOB_TAG_IDENTIFY;
2883 p[1] = htonl(sizeof(d->identify));
2884 p += 2;
2885
2886 memcpy(p, d->identify, sizeof(d->identify));
2887 p = (uint32_t*) ((uint8_t*) p + sizeof(d->identify));
2888 }
2889
2890 if (have_good) {
2891 p[0] = SK_BLOB_TAG_SMART_STATUS;
2892 p[1] = htonl(4);
2893 p[2] = htonl(!!good);
2894 p += 3;
2895 }
2896
2897 if (d->smart_data_valid) {
2898 p[0] = SK_BLOB_TAG_SMART_DATA;
2899 p[1] = htonl(sizeof(d->smart_data));
2900 p += 2;
2901
2902 memcpy(p, d->smart_data, sizeof(d->smart_data));
2903 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_data));
2904 }
2905
2906 if (d->smart_thresholds_valid) {
2907 p[0] = SK_BLOB_TAG_SMART_THRESHOLDS;
2908 p[1] = htonl(sizeof(d->smart_thresholds));
2909 p += 2;
2910
2911 memcpy(p, d->smart_thresholds, sizeof(d->smart_thresholds));
2912 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_thresholds));
2913 }
2914
2915 assert((size_t) ((uint8_t*) p - (uint8_t*) d->blob) == size);
2916
2917 *blob = d->blob;
2918 *rsize = size;
2919
2920 return 0;
2921}
2922
2923int sk_disk_set_blob(SkDisk *d, const void *blob, size_t size) {
2924 const uint32_t *p;
2925 size_t left;
2926 SkBool idv = FALSE, sdv = FALSE, stv = FALSE, bssv = FALSE;
2927
2928 assert(d);
2929 assert(blob);
2930
2931 if (d->type != SK_DISK_TYPE_BLOB) {
2932 errno = ENODEV;
2933 return -1;
2934 }
2935
2936 if (size <= 0) {
2937 errno = EINVAL;
2938 return -1;
2939 }
2940
2941 /* First run, verify if everything makes sense */
2942 p = blob;
2943 left = size;
2944 while (left > 0) {
2945 uint32_t tag, tsize;
2946
2947 if (left < 8) {
2948 errno = EINVAL;
2949 return -1;
2950 }
2951
2952 memcpy(&tag, p, 4);
2953 memcpy(&tsize, p+1, 4);
2954 p += 2;
2955 left -= 8;
2956
2957 if (left < ntohl(tsize)) {
2958 errno = EINVAL;
2959 return -1;
2960 }
2961
2962 switch (tag) {
2963
2964 case SK_BLOB_TAG_IDENTIFY:
2965 if (ntohl(tsize) != sizeof(d->identify) || idv) {
2966 errno = EINVAL;
2967 return -1;
2968 }
2969 idv = TRUE;
2970 break;
2971
2972 case SK_BLOB_TAG_SMART_STATUS:
2973 if (ntohl(tsize) != 4 || bssv) {
2974 errno = EINVAL;
2975 return -1;
2976 }
2977 bssv = TRUE;
2978 break;
2979
2980 case SK_BLOB_TAG_SMART_DATA:
2981 if (ntohl(tsize) != sizeof(d->smart_data) || sdv) {
2982 errno = EINVAL;
2983 return -1;
2984 }
2985 sdv = TRUE;
2986 break;
2987
2988 case SK_BLOB_TAG_SMART_THRESHOLDS:
2989 if (ntohl(tsize) != sizeof(d->smart_thresholds) || stv) {
2990 errno = EINVAL;
2991 return -1;
2992 }
2993 stv = TRUE;
2994 break;
2995 }
2996
2997 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
2998 left -= ntohl(tsize);
2999 }
3000
3001 if (!idv) {
3002 errno = -ENODATA;
3003 return -1;
3004 }
3005
3006 d->identify_valid = idv;
3007 d->smart_data_valid = sdv;
3008 d->smart_thresholds_valid = stv;
3009 d->blob_smart_status_valid = bssv;
3010
3011 /* Second run, actually copy things in */
3012 p = blob;
3013 left = size;
3014 while (left > 0) {
3015 uint32_t tag, tsize;
3016
3017 assert(left >= 8);
3018 memcpy(&tag, p, 4);
3019 memcpy(&tsize, p+1, 4);
3020 p += 2;
3021 left -= 8;
3022
3023 assert(left >= ntohl(tsize));
3024
3025 switch (tag) {
3026
3027 case SK_BLOB_TAG_IDENTIFY:
3028 assert(ntohl(tsize) == sizeof(d->identify));
3029 memcpy(d->identify, p, sizeof(d->identify));
3030 break;
3031
3032 case SK_BLOB_TAG_SMART_STATUS: {
3033 uint32_t ok;
3034 assert(ntohl(tsize) == 4);
3035 memcpy(&ok, p, 4);
3036 d->blob_smart_status = !!ok;
3037 break;
3038 }
3039
3040 case SK_BLOB_TAG_SMART_DATA:
3041 assert(ntohl(tsize) == sizeof(d->smart_data));
3042 memcpy(d->smart_data, p, sizeof(d->smart_data));
3043 break;
3044
3045 case SK_BLOB_TAG_SMART_THRESHOLDS:
3046 assert(ntohl(tsize) == sizeof(d->smart_thresholds));
3047 memcpy(d->smart_thresholds, p, sizeof(d->smart_thresholds));
3048 break;
3049 }
3050
3051 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
3052 left -= ntohl(tsize);
3053 }
3054
3055 return 0;
3056}
03057
=== modified file 'atasmart.c'
--- atasmart.c 2012-05-23 08:44:25 +0000
+++ atasmart.c 2013-03-18 23:14:22 +0000
@@ -925,10 +925,10 @@
925 /* SAT/USB bridges truncate packets, so we only check for 4F,925 /* SAT/USB bridges truncate packets, so we only check for 4F,
926 * not for 2C on those */926 * not for 2C on those */
927 if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x00C2U)) &&927 if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x00C2U)) &&
928 cmd[4] == htons(0x4F00U))928 (cmd[4] & htons(0xFF00U)) == htons(0x4F00U))
929 *good = TRUE;929 *good = TRUE;
930 else if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x002CU)) &&930 else if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x002CU)) &&
931 cmd[4] == htons(0xF400U))931 (cmd[4] & htons(0xFF00U)) == htons(0xF400U))
932 *good = FALSE;932 *good = FALSE;
933 else {933 else {
934 errno = EIO;934 errno = EIO;
935935
=== modified file 'debian/changelog'
--- debian/changelog 2013-03-13 07:01:06 +0000
+++ debian/changelog 2013-03-18 23:14:22 +0000
@@ -1,3 +1,11 @@
1libatasmart (0.19-1git1ubuntu1) raring; urgency=low
2
3 * debian/patches/fix-status-io-error.patch: Fix an error that
4 resulted in an IO error reading the SMART health of some drives.
5 (LP: #1143495)
6
7 -- Phillip Susi <psusi@ubuntu.com> Mon, 18 Mar 2013 18:32:58 -0400
8
1libatasmart (0.19-1git1build1) raring; urgency=low9libatasmart (0.19-1git1build1) raring; urgency=low
210
3 * No-change rebuild against libudev111 * No-change rebuild against libudev1
412
=== added file 'debian/patches/fix-status-io-error.patch'
--- debian/patches/fix-status-io-error.patch 1970-01-01 00:00:00 +0000
+++ debian/patches/fix-status-io-error.patch 2013-03-18 23:14:22 +0000
@@ -0,0 +1,26 @@
1Author: Phillip Susi <psusi@ubuntu.com>
2Subject: fix an incorrect IO error reading SMART status
3Description: The read SMART status command's return status
4 was testing for a success/failure value that included 8
5 bits that are "N/A" according to the standard, and required
6 that they be zeros. At least some drives do not fill them
7 with zeros, so correct this by masking off the undefined
8 bits.
9
10Index: b/atasmart.c
11===================================================================
12--- a/atasmart.c
13+++ b/atasmart.c
14@@ -925,10 +925,10 @@
15 /* SAT/USB bridges truncate packets, so we only check for 4F,
16 * not for 2C on those */
17 if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x00C2U)) &&
18- cmd[4] == htons(0x4F00U))
19+ (cmd[4] & htons(0xFF00U)) == htons(0x4F00U))
20 *good = TRUE;
21 else if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x002CU)) &&
22- cmd[4] == htons(0xF400U))
23+ (cmd[4] & htons(0xFF00U)) == htons(0xF400U))
24 *good = FALSE;
25 else {
26 errno = EIO;
027
=== modified file 'debian/patches/series'
--- debian/patches/series 2012-05-23 08:44:25 +0000
+++ debian/patches/series 2013-03-18 23:14:22 +0000
@@ -1,2 +1,3 @@
1# Debian patches for libatasmart1# Debian patches for libatasmart
20002-Drop-our-own-many-bad-sectors-heuristic.patch20002-Drop-our-own-many-bad-sectors-heuristic.patch
3fix-status-io-error.patch

Subscribers

People subscribed via source and target branches

to all changes: