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