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