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