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