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