Add quirk names
[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 (max_sectors > 0 && a->pretty_value > max_sectors) {
1277                 a->pretty_value = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1278                 d->attribute_verification_bad = TRUE;
1279         } else {
1280                 if ((!strcmp(a->name, "reallocated-sector-count") ||
1281                      !strcmp(a->name, "current-pending-sector")) &&
1282                     a->pretty_value > 0)
1283                         a->warn = TRUE;
1284         }
1285 }
1286
1287 /* This data is stolen from smartmontools */
1288
1289 /* %STRINGPOOLSTART% */
1290 static const SkSmartAttributeInfo const attribute_info[256] = {
1291         [1]   = { "raw-read-error-rate",         SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1292         [2]   = { "throughput-performance",      SK_SMART_ATTRIBUTE_UNIT_UNKNOWN,  NULL },
1293         [3]   = { "spin-up-time",                SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_short_time },
1294         [4]   = { "start-stop-count",            SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1295         [5]   = { "reallocated-sector-count",    SK_SMART_ATTRIBUTE_UNIT_SECTORS,  verify_sectors },
1296         [6]   = { "read-channel-margin",         SK_SMART_ATTRIBUTE_UNIT_UNKNOWN,  NULL },
1297         [7]   = { "seek-error-rate",             SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1298         [8]   = { "seek-time-performance",       SK_SMART_ATTRIBUTE_UNIT_UNKNOWN,  NULL },
1299         [9]   = { "power-on-hours",              SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time },
1300         [10]  = { "spin-retry-count",            SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1301         [11]  = { "calibration-retry-count",     SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1302         [12]  = { "power-cycle-count",           SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1303         [13]  = { "read-soft-error-rate",        SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1304         [170] = { "available-reserved-space",    SK_SMART_ATTRIBUTE_UNIT_PERCENT,  NULL },
1305         [171] = { "program-fail-count",          SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1306         [172] = { "erase-fail-count",            SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1307         [184] = { "end-to-end-error",            SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1308         [187] = { "reported-uncorrect",          SK_SMART_ATTRIBUTE_UNIT_SECTORS,  verify_sectors },
1309         [188] = { "command-timeout",             SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1310         [189] = { "high-fly-writes",             SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1311         [190] = { "airflow-temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN,  verify_temperature },
1312         [191] = { "g-sense-error-rate",          SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1313         [192] = { "power-off-retract-count",     SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1314         [193] = { "load-cycle-count",            SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1315         [194] = { "temperature-celsius-2",       SK_SMART_ATTRIBUTE_UNIT_MKELVIN,  verify_temperature },
1316         [195] = { "hardware-ecc-recovered",      SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1317         [196] = { "reallocated-event-count",     SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1318         [197] = { "current-pending-sector",      SK_SMART_ATTRIBUTE_UNIT_SECTORS,  verify_sectors },
1319         [198] = { "offline-uncorrectable",       SK_SMART_ATTRIBUTE_UNIT_SECTORS,  verify_sectors },
1320         [199] = { "udma-crc-error-count",        SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1321         [200] = { "multi-zone-error-rate",       SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1322         [201] = { "soft-read-error-rate",        SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1323         [202] = { "ta-increase-count",           SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1324         [203] = { "run-out-cancel",              SK_SMART_ATTRIBUTE_UNIT_UNKNOWN,  NULL },
1325         [204] = { "shock-count-write-open",      SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1326         [205] = { "shock-rate-write-open",       SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1327         [206] = { "flying-height",               SK_SMART_ATTRIBUTE_UNIT_UNKNOWN,  NULL },
1328         [207] = { "spin-high-current",           SK_SMART_ATTRIBUTE_UNIT_UNKNOWN,  NULL },
1329         [208] = { "spin-buzz",                   SK_SMART_ATTRIBUTE_UNIT_UNKNOWN,  NULL },
1330         [209] = { "offline-seek-performance",    SK_SMART_ATTRIBUTE_UNIT_UNKNOWN,  NULL },
1331         [220] = { "disk-shift",                  SK_SMART_ATTRIBUTE_UNIT_UNKNOWN,  NULL },
1332         [221] = { "g-sense-error-rate-2",        SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1333         [222] = { "loaded-hours",                SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time },
1334         [223] = { "load-retry-count",            SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1335         [224] = { "load-friction",               SK_SMART_ATTRIBUTE_UNIT_UNKNOWN,  NULL },
1336         [225] = { "load-cycle-count-2",          SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1337         [226] = { "load-in-time",                SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_short_time },
1338         [227] = { "torq-amp-count",              SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1339         [228] = { "power-off-retract-count-2",   SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL },
1340         [230] = { "head-amplitude",              SK_SMART_ATTRIBUTE_UNIT_UNKNOWN,  NULL },
1341         [231] = { "temperature-celsius",         SK_SMART_ATTRIBUTE_UNIT_MKELVIN,  verify_temperature },
1342
1343         /* http://www.adtron.com/pdf/SMART_for_XceedLite_SATA_RevA.pdf */
1344         [232] = { "endurance-remaining",         SK_SMART_ATTRIBUTE_UNIT_PERCENT,  NULL },
1345         [233] = { "power-on-seconds-2",          SK_SMART_ATTRIBUTE_UNIT_UNKNOWN,  NULL },
1346         [234] = { "uncorrectable-ecc-count",     SK_SMART_ATTRIBUTE_UNIT_SECTORS,  NULL },
1347         [235] = { "good-block-rate",             SK_SMART_ATTRIBUTE_UNIT_UNKNOWN,  NULL },
1348
1349         [240] = { "head-flying-hours",           SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time },
1350         [241] = { "total-lbas-written",          SK_SMART_ATTRIBUTE_UNIT_MB,  NULL },
1351         [242] = { "total-lbas-read",             SK_SMART_ATTRIBUTE_UNIT_MB,  NULL },
1352         [250] = { "read-error-retry-rate",       SK_SMART_ATTRIBUTE_UNIT_NONE,     NULL }
1353 };
1354 /* %STRINGPOOLSTOP% */
1355
1356 typedef enum SkSmartQuirk {
1357         SK_SMART_QUIRK_9_POWERONMINUTES            = 0x000001,
1358         SK_SMART_QUIRK_9_POWERONSECONDS            = 0x000002,
1359         SK_SMART_QUIRK_9_POWERONHALFMINUTES        = 0x000004,
1360         SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT = 0x000008,
1361         SK_SMART_QUIRK_193_LOADUNLOAD              = 0x000010,
1362         SK_SMART_QUIRK_194_10XCELSIUS              = 0x000020,
1363         SK_SMART_QUIRK_194_UNKNOWN                 = 0x000040,
1364         SK_SMART_QUIRK_200_WRITEERRORCOUNT         = 0x000080,
1365         SK_SMART_QUIRK_201_DETECTEDTACOUNT         = 0x000100,
1366         SK_SMART_QUIRK_5_UNKNOWN                   = 0x000200,
1367         SK_SMART_QUIRK_9_UNKNOWN                   = 0x000400,
1368         SK_SMART_QUIRK_197_UNKNOWN                 = 0x000800,
1369         SK_SMART_QUIRK_198_UNKNOWN                 = 0x001000,
1370         SK_SMART_QUIRK_190_UNKNOWN                 = 0x002000,
1371         SK_SMART_QUIRK_232_AVAILABLERESERVEDSPACE  = 0x004000,
1372         SK_SMART_QUIRK_233_MEDIAWEAROUTINDICATOR   = 0x008000,
1373         SK_SMART_QUIRK_225_TOTALLBASWRITTEN        = 0x010000,
1374         SK_SMART_QUIRK_4_UNUSED                    = 0x020000,
1375         SK_SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR   = 0x040000,
1376         SK_SMART_QUIRK_227_TIMEWORKLOADHOSTREADS   = 0x080000,
1377         SK_SMART_QUIRK_228_WORKLOADTIMER           = 0x100000,
1378         SK_SMART_QUIRK_3_UNUSED                    = 0x200000
1379 } SkSmartQuirk;
1380
1381 /* %STRINGPOOLSTART% */
1382 static const char *quirk_name[] = {
1383         "9_POWERONMINUTES",
1384         "9_POWERONSECONDS",
1385         "9_POWERONHALFMINUTES",
1386         "192_EMERGENCYRETRACTCYCLECT",
1387         "193_LOADUNLOAD",
1388         "194_10XCELSIUS",
1389         "194_UNKNOWN",
1390         "200_WRITEERRORCOUNT",
1391         "201_DETECTEDTACOUNT",
1392         "5_UNKNOWN",
1393         "9_UNKNOWN",
1394         "197_UNKNOWN",
1395         "198_UNKNOWN",
1396         "190_UNKNOWN",
1397         "232_AVAILABLERESERVEDSPACE",
1398         "233_MEDIAWEAROUTINDICATOR",
1399         "225_TOTALLBASWRITTEN",
1400         "4_UNUSED",
1401         "226_TIMEWORKLOADMEDIAWEAR",
1402         "227_TIMEWORKLOADHOSTREADS",
1403         "228_WORKLOADTIMER",
1404         "3_UNUSED",
1405         NULL
1406 };
1407 /* %STRINGPOOLSTOP% */
1408
1409 typedef struct SkSmartQuirkDatabase {
1410         const char *model;
1411         const char *firmware;
1412         SkSmartQuirk quirk;
1413 } SkSmartQuirkDatabase;
1414
1415 static const SkSmartQuirkDatabase quirk_database[] = { {
1416
1417         /*** Fujitsu */
1418                 "^("
1419                 "FUJITSU MHY2120BH|"
1420                 "FUJITSU MHY2250BH"
1421                 ")$",
1422                 "^0085000B$", /* seems to be specific to this firmware */
1423                 SK_SMART_QUIRK_9_POWERONMINUTES|
1424                 SK_SMART_QUIRK_197_UNKNOWN|
1425                 SK_SMART_QUIRK_198_UNKNOWN
1426         }, {
1427                 "^FUJITSU MHR2040AT$",
1428                 NULL,
1429                 SK_SMART_QUIRK_9_POWERONSECONDS|
1430                 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1431                 SK_SMART_QUIRK_200_WRITEERRORCOUNT
1432         }, {
1433                 "^FUJITSU MHS20[6432]0AT(  .)?$",
1434                 NULL,
1435                 SK_SMART_QUIRK_9_POWERONSECONDS|
1436                 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1437                 SK_SMART_QUIRK_200_WRITEERRORCOUNT|
1438                 SK_SMART_QUIRK_201_DETECTEDTACOUNT
1439         }, {
1440                 "^("
1441                 "FUJITSU M1623TAU|"
1442                 "FUJITSU MHG2...ATU?.*|"
1443                 "FUJITSU MHH2...ATU?.*|"
1444                 "FUJITSU MHJ2...ATU?.*|"
1445                 "FUJITSU MHK2...ATU?.*|"
1446                 "FUJITSU MHL2300AT|"
1447                 "FUJITSU MHM2(20|15|10|06)0AT|"
1448                 "FUJITSU MHN2...AT|"
1449                 "FUJITSU MHR2020AT|"
1450                 "FUJITSU MHT2...(AH|AS|AT|BH)U?.*|"
1451                 "FUJITSU MHU2...ATU?.*|"
1452                 "FUJITSU MHV2...(AH|AS|AT|BH|BS|BT).*|"
1453                 "FUJITSU MP[A-G]3...A[HTEV]U?.*"
1454                 ")$",
1455                 NULL,
1456                 SK_SMART_QUIRK_9_POWERONSECONDS
1457         }, {
1458
1459         /*** Samsung ***/
1460                 "^("
1461                 "SAMSUNG SV4012H|"
1462                 "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]"
1463                 ")$",
1464                 NULL,
1465                 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1466         }, {
1467                 "^("
1468                 "SAMSUNG SV0412H|"
1469                 "SAMSUNG SV1204H"
1470                 ")$",
1471                 NULL,
1472                 SK_SMART_QUIRK_9_POWERONHALFMINUTES|
1473                 SK_SMART_QUIRK_194_10XCELSIUS
1474         }, {
1475                 "^SAMSUNG SP40A2H$",
1476                 "^RR100-07$",
1477                 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1478         }, {
1479                 "^SAMSUNG SP80A4H$",
1480                 "^RT100-06$",
1481                 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1482         }, {
1483                 "^SAMSUNG SP8004H$",
1484                 "^QW100-61$",
1485                 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1486         }, {
1487
1488         /*** Maxtor */
1489                 "^("
1490                 "Maxtor 2B0(0[468]|1[05]|20)H1|"
1491                 "Maxtor 4G(120J6|160J[68])|"
1492                 "Maxtor 4D0(20H1|40H2|60H3|80H4)"
1493                 ")$",
1494                 NULL,
1495                 SK_SMART_QUIRK_9_POWERONMINUTES|
1496                 SK_SMART_QUIRK_194_UNKNOWN
1497         }, {
1498                 "^("
1499                 "Maxtor 2F0[234]0[JL]0|"
1500                 "Maxtor 8(1280A2|2160A4|2560A4|3840A6|4000A6|5120A8)|"
1501                 "Maxtor 8(2160D2|3228D3|3240D3|4320D4|6480D6|8400D8|8455D8)|"
1502                 "Maxtor 9(0510D4|0576D4|0648D5|0720D5|0840D6|0845D6|0864D6|1008D7|1080D8|1152D8)|"
1503                 "Maxtor 9(1(360|350|202)D8|1190D7|10[12]0D6|0840D5|06[48]0D4|0510D3|1(350|202)E8|1010E6|0840E5|0640E4)|"
1504                 "Maxtor 9(0512D2|0680D3|0750D3|0913D4|1024D4|1360D6|1536D6|1792D7|2048D8)|"
1505                 "Maxtor 9(2732U8|2390U7|204[09]U6|1707U5|1366U4|1024U3|0845U3|0683U2)|"
1506                 "Maxtor 4(R0[68]0[JL]0|R1[26]0L0|A160J0|R120L4)|"
1507                 "Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D[34]|90432D2)|"
1508                 "Maxtor 9(0431U1|0641U2|0871U2|1301U3|1741U4)|"
1509                 "Maxtor (94091U8|93071U6|92561U5|92041U4|91731U4|91531U3|91361U3|91021U2|90841U2|90651U2)|"
1510                 "Maxtor (33073U4|32049U3|31536U2|30768U1|33073H4|32305H3|31536H2|30768H1)|"
1511                 "Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)|"
1512                 "Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)|"
1513                 "Maxtor (54098[UH]8|53073[UH]6|52732[UH]6|52049[UH]4|51536[UH]3|51369[UH]3|51024[UH]2)|"
1514                 "Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?|"
1515                 "Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)|"
1516                 "Maxtor 9(1023U2|1536U2|2049U3|2305U3|3073U4|4610U6|6147U8)|"
1517                 "Maxtor 9(1023H2|1536H2|2049H3|2305H3|3073H4|4098H6|4610H6|6147H8)|"
1518                 "Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)|"
1519                 "Maxtor (98196H8|96147H6)|"
1520                 "Maxtor 4W(100H6|080H6|060H4|040H3|030H2)|"
1521                 "Maxtor 6(E0[234]|K04)0L0|"
1522                 "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|"
1523                 "Maxtor 6Y((060|080|120|160)L0|(060|080|120|160|200|250)P0|(060|080|120|160|200|250)M0)|"
1524                 "Maxtor 7Y250[PM]0|"
1525                 "Maxtor [45]A(25|30|32)0[JN]0|"
1526                 "Maxtor 7L(25|30)0[SR]0"
1527                 ")$",
1528                 NULL,
1529                 SK_SMART_QUIRK_9_POWERONMINUTES
1530         }, {
1531
1532
1533         /*** Hitachi */
1534                 "^("
1535                 "HITACHI_DK14FA-20B|"
1536                 "HITACHI_DK23..-..B?|"
1537                 "HITACHI_DK23FA-20J|HTA422020F9AT[JN]0|"
1538                 "HE[JN]4230[23]0F9AT00|"
1539                 "HTC4260[23]0G5CE00|HTC4260[56]0G8CE00"
1540                 ")$",
1541                 NULL,
1542                 SK_SMART_QUIRK_9_POWERONMINUTES|
1543                 SK_SMART_QUIRK_193_LOADUNLOAD
1544         }, {
1545                 "^HTS541010G9SA00$",
1546                 "^MBZOC60P$",
1547                 SK_SMART_QUIRK_5_UNKNOWN
1548         }, {
1549
1550         /*** Apple SSD (?) http://bugs.freedesktop.org/show_bug.cgi?id=24700
1551                            https://bugs.launchpad.net/ubuntu/+source/gnome-disk-utility/+bug/438136/comments/4 */
1552                 "^MCCOE64GEMPP$",
1553                 "^2.9.0[3-9]$",
1554                 SK_SMART_QUIRK_5_UNKNOWN|
1555                 SK_SMART_QUIRK_190_UNKNOWN
1556         }, {
1557
1558         /*** Intel */
1559                 "^INTEL SSDSA2CW[0-9]{3}G3$",
1560                 NULL,
1561                 SK_SMART_QUIRK_3_UNUSED|
1562                 SK_SMART_QUIRK_4_UNUSED|
1563                 SK_SMART_QUIRK_225_TOTALLBASWRITTEN|
1564                 SK_SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR|
1565                 SK_SMART_QUIRK_227_TIMEWORKLOADHOSTREADS|
1566                 SK_SMART_QUIRK_228_WORKLOADTIMER|
1567                 SK_SMART_QUIRK_232_AVAILABLERESERVEDSPACE|
1568                 SK_SMART_QUIRK_233_MEDIAWEAROUTINDICATOR
1569         }, {
1570                 NULL,
1571                 NULL,
1572                 0
1573         }
1574 };
1575
1576 static int match(const char*regex, const char *s, SkBool *result) {
1577         int k;
1578         regex_t re;
1579
1580         *result = FALSE;
1581
1582         if (regcomp(&re, regex, REG_EXTENDED|REG_NOSUB) != 0) {
1583                 errno = EINVAL;
1584                 return -1;
1585         }
1586
1587         if ((k = regexec(&re, s, 0, NULL, 0)) != 0) {
1588
1589                 if (k != REG_NOMATCH) {
1590                         regfree(&re);
1591                         errno = EINVAL;
1592                         return -1;
1593                 }
1594
1595         } else
1596                 *result = TRUE;
1597
1598         regfree(&re);
1599
1600         return 0;
1601 }
1602
1603 static int lookup_quirks(const char *model, const char *firmware, SkSmartQuirk *quirk) {
1604         int k;
1605         const SkSmartQuirkDatabase *db;
1606
1607         *quirk = 0;
1608
1609         for (db = quirk_database; db->model || db->firmware; db++) {
1610
1611                 if (db->model) {
1612                         SkBool matching = FALSE;
1613
1614                         if ((k = match(db->model, model, &matching)) < 0)
1615                                 return k;
1616
1617                         if (!matching)
1618                                 continue;
1619                 }
1620
1621                 if (db->firmware) {
1622                         SkBool matching = FALSE;
1623
1624                         if ((k = match(db->firmware, firmware, &matching)) < 0)
1625                                 return k;
1626
1627                         if (!matching)
1628                                 continue;
1629                 }
1630
1631                 *quirk = db->quirk;
1632                 return 0;
1633         }
1634
1635         return 0;
1636 }
1637
1638 static const SkSmartAttributeInfo *lookup_attribute(SkDisk *d, uint8_t id) {
1639         const SkIdentifyParsedData *ipd;
1640         SkSmartQuirk quirk = 0;
1641
1642         /* These are the complex ones */
1643         if (sk_disk_identify_parse(d, &ipd) < 0)
1644                 return NULL;
1645
1646         if (lookup_quirks(ipd->model, ipd->firmware, &quirk) < 0)
1647                 return NULL;
1648
1649         if (quirk) {
1650                 switch (id) {
1651                         case 3:
1652                                 /* %STRINGPOOLSTART% */
1653                                 if (quirk & SK_SMART_QUIRK_3_UNUSED) {
1654                                         static const SkSmartAttributeInfo a = {
1655                                                 "spin-up-time", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL
1656                                         };
1657                                         return &a;
1658                                 }
1659                                 /* %STRINGPOOLSTOP% */
1660
1661                                 break;
1662
1663                         case 4:
1664                                 /* %STRINGPOOLSTART% */
1665                                 if (quirk & SK_SMART_QUIRK_4_UNUSED) {
1666                                         static const SkSmartAttributeInfo a = {
1667                                                 "start-stop-count", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL
1668                                         };
1669                                         return &a;
1670                                 }
1671                                 /* %STRINGPOOLSTOP% */
1672
1673                                 break;
1674
1675                         case 5:
1676                                 if (quirk & SK_SMART_QUIRK_5_UNKNOWN)
1677                                         return NULL;
1678
1679                                 break;
1680
1681                         case 9:
1682                                 /* %STRINGPOOLSTART% */
1683                                 if (quirk & SK_SMART_QUIRK_9_POWERONMINUTES) {
1684                                         static const SkSmartAttributeInfo a = {
1685                                                 "power-on-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time
1686                                         };
1687                                         return &a;
1688
1689                                 } else if (quirk & SK_SMART_QUIRK_9_POWERONSECONDS) {
1690                                         static const SkSmartAttributeInfo a = {
1691                                                 "power-on-seconds", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time
1692                                         };
1693                                         return &a;
1694
1695                                 } else if (quirk & SK_SMART_QUIRK_9_POWERONHALFMINUTES) {
1696                                         static const SkSmartAttributeInfo a = {
1697                                                 "power-on-half-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time
1698                                         };
1699                                         return &a;
1700                                 } else if (quirk & SK_SMART_QUIRK_9_UNKNOWN)
1701                                         return NULL;
1702                                 /* %STRINGPOOLSTOP% */
1703
1704                                 break;
1705
1706                         case 190:
1707                                 if (quirk & SK_SMART_QUIRK_190_UNKNOWN)
1708                                         return NULL;
1709
1710                                 break;
1711
1712                         case 192:
1713                                 /* %STRINGPOOLSTART% */
1714                                 if (quirk & SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT) {
1715                                         static const SkSmartAttributeInfo a = {
1716                                                 "emergency-retract-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL
1717                                         };
1718                                         return &a;
1719                                 }
1720                                 /* %STRINGPOOLSTOP% */
1721
1722                                 break;
1723
1724                         case 194:
1725                                 /* %STRINGPOOLSTART% */
1726                                 if (quirk & SK_SMART_QUIRK_194_10XCELSIUS) {
1727                                         static const SkSmartAttributeInfo a = {
1728                                                 "temperature-centi-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature
1729                                         };
1730                                         return &a;
1731                                 } else if (quirk & SK_SMART_QUIRK_194_UNKNOWN)
1732                                         return NULL;
1733                                 /* %STRINGPOOLSTOP% */
1734
1735                                 break;
1736
1737                         case 197:
1738                                 if (quirk & SK_SMART_QUIRK_197_UNKNOWN)
1739                                         return NULL;
1740
1741                                 break;
1742
1743                         case 198:
1744                                 if (quirk & SK_SMART_QUIRK_198_UNKNOWN)
1745                                         return NULL;
1746
1747                                 break;
1748
1749                         case 200:
1750                                 /* %STRINGPOOLSTART% */
1751                                 if (quirk & SK_SMART_QUIRK_200_WRITEERRORCOUNT) {
1752                                         static const SkSmartAttributeInfo a = {
1753                                                 "write-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL
1754                                         };
1755                                         return &a;
1756                                 }
1757                                 /* %STRINGPOOLSTOP% */
1758
1759                                 break;
1760
1761                         case 201:
1762                                 /* %STRINGPOOLSTART% */
1763                                 if (quirk & SK_SMART_QUIRK_201_DETECTEDTACOUNT) {
1764                                         static const SkSmartAttributeInfo a = {
1765                                                 "detected-ta-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL
1766                                         };
1767                                         return &a;
1768                                 }
1769                                 /* %STRINGPOOLSTOP% */
1770
1771                                 break;
1772
1773                         case 225:
1774                                 /* %STRINGPOOLSTART% */
1775                                 if (quirk & SK_SMART_QUIRK_225_TOTALLBASWRITTEN) {
1776                                         static const SkSmartAttributeInfo a = {
1777                                                 "total-lbas-written", SK_SMART_ATTRIBUTE_UNIT_MB, NULL
1778                                         };
1779                                         return &a;
1780                                 }
1781                                 /* %STRINGPOOLSTOP% */
1782
1783                                 break;
1784
1785                         case 226:
1786                                 /* %STRINGPOOLSTART% */
1787                                 if (quirk & SK_SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR) {
1788                                         static const SkSmartAttributeInfo a = {
1789                                                 "timed-workload-media-wear", SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT, NULL
1790                                         };
1791                                         return &a;
1792                                 }
1793                                 /* %STRINGPOOLSTOP% */
1794
1795                                 break;
1796
1797                         case 227:
1798                                 /* %STRINGPOOLSTART% */
1799                                 if (quirk & SK_SMART_QUIRK_227_TIMEWORKLOADHOSTREADS) {
1800                                         static const SkSmartAttributeInfo a = {
1801                                                 "timed-workload-host-reads", SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT, NULL
1802                                         };
1803                                         return &a;
1804                                 }
1805                                 /* %STRINGPOOLSTOP% */
1806
1807                                 break;
1808
1809                         case 228:
1810                                 /* %STRINGPOOLSTART% */
1811                                 if (quirk & SK_SMART_QUIRK_228_WORKLOADTIMER) {
1812                                         static const SkSmartAttributeInfo a = {
1813                                                 "workload-timer", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, NULL
1814                                         };
1815                                         return &a;
1816                                 }
1817                                 /* %STRINGPOOLSTOP% */
1818
1819                                 break;
1820
1821                         case 232:
1822                                 /* %STRINGPOOLSTART% */
1823                                 if (quirk & SK_SMART_QUIRK_232_AVAILABLERESERVEDSPACE) {
1824                                         static const SkSmartAttributeInfo a = {
1825                                                 "available-reserved-space", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL
1826                                         };
1827                                         return &a;
1828                                 }
1829                                 /* %STRINGPOOLSTOP% */
1830                                 break;
1831
1832                         case 233:
1833                                 /* %STRINGPOOLSTART% */
1834                                 if (quirk & SK_SMART_QUIRK_233_MEDIAWEAROUTINDICATOR) {
1835                                         static const SkSmartAttributeInfo a = {
1836                                                 "media-wearout-indicator", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL
1837                                         };
1838                                         return &a;
1839                                 }
1840                                 /* %STRINGPOOLSTOP% */
1841                                 break;
1842
1843                 }
1844         }
1845
1846         /* These are the simple cases */
1847         if (attribute_info[id].name)
1848                 return &attribute_info[id];
1849
1850         return NULL;
1851 }
1852
1853 int sk_disk_smart_parse(SkDisk *d, const SkSmartParsedData **spd) {
1854
1855         if (!d->smart_data_valid) {
1856                 errno = ENOENT;
1857                 return -1;
1858         }
1859
1860         switch (d->smart_data[362]) {
1861                 case 0x00:
1862                 case 0x80:
1863                         d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER;
1864                         break;
1865
1866                 case 0x02:
1867                 case 0x82:
1868                         d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS;
1869                         break;
1870
1871                 case 0x03:
1872                         d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS;
1873                         break;
1874
1875                 case 0x04:
1876                 case 0x84:
1877                         d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED;
1878                         break;
1879
1880                 case 0x05:
1881                 case 0x85:
1882                         d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED;
1883                         break;
1884
1885                 case 0x06:
1886                 case 0x86:
1887                         d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL;
1888                         break;
1889
1890                 default:
1891                         d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN;
1892                         break;
1893         }
1894
1895         d->smart_parsed_data.self_test_execution_percent_remaining = 10*(d->smart_data[363] & 0xF);
1896         d->smart_parsed_data.self_test_execution_status = (d->smart_data[363] >> 4) & 0xF;
1897
1898         d->smart_parsed_data.total_offline_data_collection_seconds = (uint16_t) d->smart_data[364] | ((uint16_t) d->smart_data[365] << 8);
1899
1900         d->smart_parsed_data.conveyance_test_available = disk_smart_is_conveyance_test_available(d);
1901         d->smart_parsed_data.short_and_extended_test_available = disk_smart_is_short_and_extended_test_available(d);
1902         d->smart_parsed_data.start_test_available = disk_smart_is_start_test_available(d);
1903         d->smart_parsed_data.abort_test_available = disk_smart_is_abort_test_available(d);
1904
1905         d->smart_parsed_data.short_test_polling_minutes = d->smart_data[372];
1906         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]);
1907         d->smart_parsed_data.conveyance_test_polling_minutes = d->smart_data[374];
1908
1909         *spd = &d->smart_parsed_data;
1910
1911         return 0;
1912 }
1913
1914 static void find_threshold(SkDisk *d, SkSmartAttributeParsedData *a) {
1915         uint8_t *p;
1916         unsigned n;
1917
1918         if (!d->smart_thresholds_valid)
1919                 goto fail;
1920
1921         for (n = 0, p = d->smart_thresholds+2; n < 30; n++, p+=12)
1922                 if (p[0] == a->id)
1923                         break;
1924
1925         if (n >= 30)
1926                 goto fail;
1927
1928         a->threshold = p[1];
1929         a->threshold_valid = p[1] != 0xFE;
1930
1931         a->good_now_valid = FALSE;
1932         a->good_now = TRUE;
1933         a->good_in_the_past_valid = FALSE;
1934         a->good_in_the_past = TRUE;
1935
1936         /* Always-Fail and Always-Passing thresholds are not relevant
1937          * for our assessment. */
1938         if (p[1] >= 1 && p[1] <= 0xFD) {
1939
1940                 if (a->worst_value_valid) {
1941                         a->good_in_the_past = a->good_in_the_past && (a->worst_value > a->threshold);
1942                         a->good_in_the_past_valid = TRUE;
1943                 }
1944
1945                 if (a->current_value_valid) {
1946                         a->good_now = a->good_now && (a->current_value > a->threshold);
1947                         a->good_now_valid = TRUE;
1948                 }
1949         }
1950
1951         a->warn =
1952                 (a->good_now_valid && !a->good_now) ||
1953                 (a->good_in_the_past_valid && !a->good_in_the_past);
1954
1955         return;
1956
1957 fail:
1958         a->threshold_valid = FALSE;
1959         a->good_now_valid = FALSE;
1960         a->good_in_the_past_valid = FALSE;
1961         a->warn = FALSE;
1962 }
1963
1964 int sk_disk_smart_parse_attributes(SkDisk *d, SkSmartAttributeParseCallback cb, void* userdata) {
1965         uint8_t *p;
1966         unsigned n;
1967
1968         if (!d->smart_data_valid) {
1969                 errno = ENOENT;
1970                 return -1;
1971         }
1972
1973         for (n = 0, p = d->smart_data + 2; n < 30; n++, p+=12) {
1974                 SkSmartAttributeParsedData a;
1975                 const SkSmartAttributeInfo *i;
1976                 char *an = NULL;
1977
1978                 if (p[0] == 0)
1979                         continue;
1980
1981                 memset(&a, 0, sizeof(a));
1982                 a.id = p[0];
1983                 a.current_value = p[3];
1984                 a.current_value_valid = p[3] >= 1 && p[3] <= 0xFD;
1985                 a.worst_value = p[4];
1986                 a.worst_value_valid = p[4] >= 1 && p[4] <= 0xFD;
1987
1988                 a.flags = ((uint16_t) p[2] << 8) | p[1];
1989                 a.prefailure = !!(p[1] & 1);
1990                 a.online = !!(p[1] & 2);
1991
1992                 memcpy(a.raw, p+5, 6);
1993
1994                 if ((i = lookup_attribute(d, p[0]))) {
1995                         a.name = _P(i->name);
1996                         a.pretty_unit = i->unit;
1997                 } else {
1998                         if (asprintf(&an, "attribute-%u", a.id) < 0) {
1999                                 errno = ENOMEM;
2000                                 return -1;
2001                         }
2002
2003                         a.name = an;
2004                         a.pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
2005                 }
2006
2007                 make_pretty(&a);
2008
2009                 find_threshold(d, &a);
2010
2011                 if (i && i->verify)
2012                         i->verify(d, &a);
2013
2014                 cb(d, &a, userdata);
2015                 free(an);
2016         }
2017
2018         return 0;
2019 }
2020
2021 static const char *yes_no(SkBool b) {
2022         return  b ? "yes" : "no";
2023 }
2024
2025 const char* sk_smart_attribute_unit_to_string(SkSmartAttributeUnit unit) {
2026
2027         /* %STRINGPOOLSTART% */
2028         const char * const map[] = {
2029                 [SK_SMART_ATTRIBUTE_UNIT_UNKNOWN] = NULL,
2030                 [SK_SMART_ATTRIBUTE_UNIT_NONE] = "",
2031                 [SK_SMART_ATTRIBUTE_UNIT_MSECONDS] = "ms",
2032                 [SK_SMART_ATTRIBUTE_UNIT_SECTORS] = "sectors",
2033                 [SK_SMART_ATTRIBUTE_UNIT_MKELVIN] = "mK",
2034                 [SK_SMART_ATTRIBUTE_UNIT_PERCENT] = "%",
2035                 [SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT] = "%",
2036                 [SK_SMART_ATTRIBUTE_UNIT_MB] = "MB"
2037         };
2038         /* %STRINGPOOLSTOP% */
2039
2040         if (unit >= _SK_SMART_ATTRIBUTE_UNIT_MAX)
2041                 return NULL;
2042
2043         return _P(map[unit]);
2044 }
2045
2046 struct attr_helper {
2047         uint64_t *value;
2048         SkBool found;
2049 };
2050
2051 static void temperature_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
2052
2053         if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MKELVIN)
2054                 return;
2055
2056         if (!strcmp(a->name, "temperature-centi-celsius") ||
2057             !strcmp(a->name, "temperature-celsius") ||
2058             !strcmp(a->name, "temperature-celsius-2") ||
2059             !strcmp(a->name, "airflow-temperature-celsius")) {
2060
2061                 if (!ah->found || a->pretty_value > *ah->value)
2062                         *ah->value = a->pretty_value;
2063
2064                 ah->found = TRUE;
2065         }
2066 }
2067
2068 int sk_disk_smart_get_temperature(SkDisk *d, uint64_t *kelvin) {
2069         struct attr_helper ah;
2070
2071         assert(d);
2072         assert(kelvin);
2073
2074         ah.found = FALSE;
2075         ah.value = kelvin;
2076
2077         if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) temperature_cb, &ah) < 0)
2078                 return -1;
2079
2080         if (!ah.found) {
2081                 errno = ENOENT;
2082                 return -1;
2083         }
2084
2085         return 0;
2086 }
2087
2088 static void power_on_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
2089
2090         if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MSECONDS)
2091                 return;
2092
2093         if (!strcmp(a->name, "power-on-minutes") ||
2094             !strcmp(a->name, "power-on-seconds") ||
2095             !strcmp(a->name, "power-on-seconds-2") ||
2096             !strcmp(a->name, "power-on-half-minutes") ||
2097             !strcmp(a->name, "power-on-hours")) {
2098
2099                 if (!ah->found || a->pretty_value > *ah->value)
2100                         *ah->value = a->pretty_value;
2101
2102                 ah->found = TRUE;
2103         }
2104 }
2105
2106 int sk_disk_smart_get_power_on(SkDisk *d, uint64_t *mseconds) {
2107         struct attr_helper ah;
2108
2109         assert(d);
2110         assert(mseconds);
2111
2112         ah.found = FALSE;
2113         ah.value = mseconds;
2114
2115         if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_on_cb, &ah) < 0)
2116                 return -1;
2117
2118         if (!ah.found) {
2119                 errno = ENOENT;
2120                 return -1;
2121         }
2122
2123         return 0;
2124 }
2125
2126 static void power_cycle_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
2127
2128         if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_NONE)
2129                 return;
2130
2131         if (!strcmp(a->name, "power-cycle-count")) {
2132
2133                 if (!ah->found || a->pretty_value > *ah->value)
2134                         *ah->value = a->pretty_value;
2135
2136                 ah->found = TRUE;
2137         }
2138 }
2139
2140 int sk_disk_smart_get_power_cycle(SkDisk *d, uint64_t *count) {
2141         struct attr_helper ah;
2142
2143         assert(d);
2144         assert(count);
2145
2146         ah.found = FALSE;
2147         ah.value = count;
2148
2149         if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_cycle_cb, &ah) < 0)
2150                 return -1;
2151
2152         if (!ah.found) {
2153                 errno = ENOENT;
2154                 return -1;
2155         }
2156
2157         return 0;
2158 }
2159
2160 static void reallocated_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
2161
2162         if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_SECTORS)
2163                 return;
2164
2165         if (!strcmp(a->name, "reallocated-sector-count")) {
2166
2167                 if (!ah->found || a->pretty_value > *ah->value)
2168                         *ah->value = a->pretty_value;
2169
2170                 ah->found = TRUE;
2171         }
2172 }
2173
2174 static void pending_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
2175
2176         if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_SECTORS)
2177                 return;
2178
2179         if (!strcmp(a->name, "current-pending-sector")) {
2180
2181                 if (!ah->found || a->pretty_value > *ah->value)
2182                         *ah->value = a->pretty_value;
2183
2184                 ah->found = TRUE;
2185         }
2186 }
2187
2188 int sk_disk_smart_get_bad(SkDisk *d, uint64_t *sectors) {
2189         struct attr_helper ah1, ah2;
2190         uint64_t sectors1, sectors2;
2191
2192         assert(d);
2193         assert(sectors);
2194
2195         ah1.found = FALSE;
2196         ah1.value = &sectors1;
2197
2198         if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) reallocated_cb, &ah1) < 0)
2199                 return -1;
2200
2201         ah2.found = FALSE;
2202         ah2.value = &sectors2;
2203
2204         if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) pending_cb, &ah2) < 0)
2205                 return -1;
2206
2207         if (!ah1.found && !ah2.found) {
2208                 errno = ENOENT;
2209                 return -1;
2210         }
2211
2212         if (ah1.found && ah2.found)
2213                 *sectors = sectors1 + sectors2;
2214         else if (ah1.found)
2215                 *sectors = sectors1;
2216         else
2217                 *sectors = sectors2;
2218
2219         return 0;
2220 }
2221
2222 const char* sk_smart_overall_to_string(SkSmartOverall overall) {
2223
2224         /* %STRINGPOOLSTART% */
2225         const char * const map[] = {
2226                 [SK_SMART_OVERALL_GOOD] = "GOOD",
2227                 [SK_SMART_OVERALL_BAD_ATTRIBUTE_IN_THE_PAST] = "BAD_ATTRIBUTE_IN_THE_PAST",
2228                 [SK_SMART_OVERALL_BAD_SECTOR] = "BAD_SECTOR",
2229                 [SK_SMART_OVERALL_BAD_ATTRIBUTE_NOW] = "BAD_ATTRIBUTE_NOW",
2230                 [SK_SMART_OVERALL_BAD_SECTOR_MANY] = "BAD_SECTOR_MANY",
2231                 [SK_SMART_OVERALL_BAD_STATUS] = "BAD_STATUS",
2232         };
2233         /* %STRINGPOOLSTOP% */
2234
2235         if (overall >= _SK_SMART_OVERALL_MAX)
2236                 return NULL;
2237
2238         return _P(map[overall]);
2239 }
2240
2241 static void bad_attribute_now_cb(SkDisk *d, const SkSmartAttributeParsedData *a, SkBool *good) {
2242         if (a->prefailure && a->good_now_valid && !a->good_now)
2243                 *good = FALSE;
2244 }
2245
2246 static void bad_attribute_in_the_past_cb(SkDisk *d, const SkSmartAttributeParsedData *a, SkBool *good) {
2247         if (a->prefailure && a->good_in_the_past_valid && !a->good_in_the_past)
2248                 *good = FALSE;
2249 }
2250
2251 static uint64_t u64log2(uint64_t n) {
2252         unsigned r;
2253
2254         if (n <= 1)
2255                 return 0;
2256
2257         r = 0;
2258         for (;;) {
2259                 n = n >> 1;
2260                 if (!n)
2261                         return r;
2262                 r++;
2263         }
2264 }
2265
2266 int sk_disk_smart_get_overall(SkDisk *d, SkSmartOverall *overall) {
2267         SkBool good;
2268         uint64_t sectors, sector_threshold;
2269
2270         assert(d);
2271         assert(overall);
2272
2273         /* First, check SMART self-assesment */
2274         if (sk_disk_smart_status(d, &good) < 0)
2275                 return -1;
2276
2277         if (!good) {
2278                 *overall = SK_SMART_OVERALL_BAD_STATUS;
2279                 return 0;
2280         }
2281
2282         /* Second, check if the number of bad sectors is greater than
2283          * a certain threshold */
2284         if (sk_disk_smart_get_bad(d, &sectors) < 0) {
2285                 if (errno != ENOENT)
2286                         return -1;
2287                 sectors = 0;
2288         } else {
2289
2290                 /* We use log2(n_sectors) as a threshold here. We had to pick
2291                  * something, and this makes a bit of sense, or doesn't it? */
2292                 sector_threshold = u64log2(d->size/512);
2293
2294                 if (sectors >= sector_threshold) {
2295                         *overall = SK_SMART_OVERALL_BAD_SECTOR_MANY;
2296                         return 0;
2297                 }
2298         }
2299
2300         /* Third, check if any of the SMART attributes is bad */
2301         good = TRUE;
2302         if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) bad_attribute_now_cb, &good) < 0)
2303                 return -1;
2304
2305         if (!good) {
2306                 *overall = SK_SMART_OVERALL_BAD_ATTRIBUTE_NOW;
2307                 return 0;
2308         }
2309
2310         /* Fourth, check if there are any bad sectors at all */
2311         if (sectors > 0) {
2312                 *overall = SK_SMART_OVERALL_BAD_SECTOR;
2313                 return 0;
2314         }
2315
2316         /* Fifth, check if any of the SMART attributes ever was bad */
2317         good = TRUE;
2318         if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) bad_attribute_in_the_past_cb, &good) < 0)
2319                 return -1;
2320
2321         if (!good) {
2322                 *overall = SK_SMART_OVERALL_BAD_ATTRIBUTE_IN_THE_PAST;
2323                 return 0;
2324         }
2325
2326         /* Sixth, there's really nothing to complain about, so give it a pass */
2327         *overall = SK_SMART_OVERALL_GOOD;
2328         return 0;
2329 }
2330
2331 static char* print_name(char *s, size_t len, uint8_t id, const char *k) {
2332
2333         if (k)
2334                 strncpy(s, k, len);
2335         else
2336                 snprintf(s, len, "%u", id);
2337
2338         s[len-1] = 0;
2339
2340         return s;
2341 }
2342
2343 static char *print_value(char *s, size_t len, uint64_t pretty_value, SkSmartAttributeUnit pretty_unit) {
2344
2345         switch (pretty_unit) {
2346                 case SK_SMART_ATTRIBUTE_UNIT_MSECONDS:
2347
2348                         if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*365LLU)
2349                                 snprintf(s, len, "%0.1f years", ((double) pretty_value)/(1000.0*60*60*24*365));
2350                         else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*30LLU)
2351                                 snprintf(s, len, "%0.1f months", ((double) pretty_value)/(1000.0*60*60*24*30));
2352                         else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU)
2353                                 snprintf(s, len, "%0.1f days", ((double) pretty_value)/(1000.0*60*60*24));
2354                         else if (pretty_value >= 1000LLU*60LLU*60LLU)
2355                                 snprintf(s, len, "%0.1f h", ((double) pretty_value)/(1000.0*60*60));
2356                         else if (pretty_value >= 1000LLU*60LLU)
2357                                 snprintf(s, len, "%0.1f min", ((double) pretty_value)/(1000.0*60));
2358                         else if (pretty_value >= 1000LLU)
2359                                 snprintf(s, len, "%0.1f s", ((double) pretty_value)/(1000.0));
2360                         else
2361                                 snprintf(s, len, "%llu ms", (unsigned long long) pretty_value);
2362
2363                         break;
2364
2365                 case SK_SMART_ATTRIBUTE_UNIT_MKELVIN:
2366                         snprintf(s, len, "%0.1f C", ((double) pretty_value - 273150) / 1000);
2367                         break;
2368
2369                 case SK_SMART_ATTRIBUTE_UNIT_SECTORS:
2370                         snprintf(s, len, "%llu sectors", (unsigned long long) pretty_value);
2371                         break;
2372
2373                 case SK_SMART_ATTRIBUTE_UNIT_PERCENT:
2374                         snprintf(s, len, "%llu%%", (unsigned long long) pretty_value);
2375                         break;
2376
2377                 case SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT:
2378                         snprintf(s, len, "%0.3f%%", (double) pretty_value);
2379                         break;
2380
2381                 case SK_SMART_ATTRIBUTE_UNIT_MB:
2382                         if (pretty_value >= 1000000LLU)
2383                           snprintf(s, len, "%0.3f TB",  (double) pretty_value / 1000000LLU);
2384                         else if (pretty_value >= 1000LLU)
2385                           snprintf(s, len, "%0.3f GB",  (double) pretty_value / 1000LLU);
2386                         else
2387                           snprintf(s, len, "%llu MB", (unsigned long long) pretty_value);
2388                         break;
2389
2390                 case SK_SMART_ATTRIBUTE_UNIT_NONE:
2391                         snprintf(s, len, "%llu", (unsigned long long) pretty_value);
2392                         break;
2393
2394                 case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN:
2395                         snprintf(s, len, "n/a");
2396                         break;
2397
2398                 case _SK_SMART_ATTRIBUTE_UNIT_MAX:
2399                         assert(FALSE);
2400         }
2401
2402         s[len-1] = 0;
2403
2404         return s;
2405 }
2406
2407 #define HIGHLIGHT "\x1B[1m"
2408 #define ENDHIGHLIGHT "\x1B[0m"
2409
2410 static void disk_dump_attributes(SkDisk *d, const SkSmartAttributeParsedData *a, void* userdata) {
2411         char name[32];
2412         char pretty[32];
2413         char tt[32], tw[32], tc[32];
2414         SkBool highlight;
2415
2416         snprintf(tt, sizeof(tt), "%3u", a->threshold);
2417         tt[sizeof(tt)-1] = 0;
2418         snprintf(tw, sizeof(tw), "%3u", a->worst_value);
2419         tw[sizeof(tw)-1] = 0;
2420         snprintf(tc, sizeof(tc), "%3u", a->current_value);
2421         tc[sizeof(tc)-1] = 0;
2422
2423         highlight = a->warn && isatty(1);
2424
2425         if (highlight)
2426                 fprintf(stderr, HIGHLIGHT);
2427
2428         printf("%3u %-27s %-3s   %-3s   %-3s   %-11s 0x%02x%02x%02x%02x%02x%02x %-7s %-7s %-4s %-4s\n",
2429                a->id,
2430                print_name(name, sizeof(name), a->id, a->name),
2431                a->current_value_valid ? tc : "n/a",
2432                a->worst_value_valid ? tw : "n/a",
2433                a->threshold_valid ? tt : "n/a",
2434                print_value(pretty, sizeof(pretty), a->pretty_value, a->pretty_unit),
2435                a->raw[0], a->raw[1], a->raw[2], a->raw[3], a->raw[4], a->raw[5],
2436                a->prefailure ? "prefail" : "old-age",
2437                a->online ? "online" : "offline",
2438                a->good_now_valid ? yes_no(a->good_now) : "n/a",
2439                a->good_in_the_past_valid ? yes_no(a->good_in_the_past) : "n/a");
2440
2441         if (highlight)
2442                 fprintf(stderr, ENDHIGHLIGHT);
2443 }
2444
2445 int sk_disk_dump(SkDisk *d) {
2446         int ret;
2447         SkBool awake = FALSE;
2448         uint64_t size;
2449
2450         assert(d);
2451
2452         printf("Device: %s%s%s\n"
2453                "Type: %s\n",
2454                d->name ? disk_type_to_prefix_string(d->type) : "",
2455                d->name ? ":" : "",
2456                d->name ? d->name : "n/a",
2457                disk_type_to_human_string(d->type));
2458
2459         ret = sk_disk_get_size(d, &size);
2460         if (ret >= 0)
2461                 printf("Size: %lu MiB\n", (unsigned long) (d->size/1024/1024));
2462         else
2463                 printf("Size: %s\n", strerror(errno));
2464
2465         if (d->identify_valid) {
2466                 const SkIdentifyParsedData *ipd;
2467                 SkSmartQuirk quirk = 0;
2468                 unsigned i;
2469
2470                 if ((ret = sk_disk_identify_parse(d, &ipd)) < 0)
2471                         return ret;
2472
2473                 printf("Model: [%s]\n"
2474                        "Serial: [%s]\n"
2475                        "Firmware: [%s]\n"
2476                        "SMART Available: %s\n",
2477                        ipd->model,
2478                        ipd->serial,
2479                        ipd->firmware,
2480                        yes_no(disk_smart_is_available(d)));
2481
2482                 if ((ret = lookup_quirks(ipd->model, ipd->firmware, &quirk)))
2483                         return ret;
2484
2485                 printf("Quirks:");
2486
2487                 for (i = 0; quirk_name[i]; i++)
2488                         if (quirk & (1<<i))
2489                                 printf(" %s", _P(quirk_name[i]));
2490
2491                 printf("\n");
2492         }
2493
2494         ret = sk_disk_check_sleep_mode(d, &awake);
2495         printf("Awake: %s\n",
2496                ret >= 0 ? yes_no(awake) : strerror(errno));
2497
2498         if (disk_smart_is_available(d)) {
2499                 SkSmartOverall overall;
2500                 const SkSmartParsedData *spd;
2501                 SkBool good;
2502                 char pretty[32];
2503                 uint64_t value, power_on;
2504
2505                 ret = sk_disk_smart_status(d, &good);
2506                 printf("%sSMART Disk Health Good: %s%s\n",
2507                        ret >= 0 && !good ? HIGHLIGHT : "",
2508                        ret >= 0 ? yes_no(good) : strerror(errno),
2509                        ret >= 0 && !good ? ENDHIGHLIGHT : "");
2510                 if ((ret = sk_disk_smart_read_data(d)) < 0)
2511                         return ret;
2512
2513                 if ((ret = sk_disk_smart_parse(d, &spd)) < 0)
2514                         return ret;
2515
2516                 printf("Off-line Data Collection Status: [%s]\n"
2517                        "Total Time To Complete Off-Line Data Collection: %u s\n"
2518                        "Self-Test Execution Status: [%s]\n"
2519                        "Percent Self-Test Remaining: %u%%\n"
2520                        "Conveyance Self-Test Available: %s\n"
2521                        "Short/Extended Self-Test Available: %s\n"
2522                        "Start Self-Test Available: %s\n"
2523                        "Abort Self-Test Available: %s\n"
2524                        "Short Self-Test Polling Time: %u min\n"
2525                        "Extended Self-Test Polling Time: %u min\n"
2526                        "Conveyance Self-Test Polling Time: %u min\n",
2527                        sk_smart_offline_data_collection_status_to_string(spd->offline_data_collection_status),
2528                        spd->total_offline_data_collection_seconds,
2529                        sk_smart_self_test_execution_status_to_string(spd->self_test_execution_status),
2530                        spd->self_test_execution_percent_remaining,
2531                        yes_no(spd->conveyance_test_available),
2532                        yes_no(spd->short_and_extended_test_available),
2533                        yes_no(spd->start_test_available),
2534                        yes_no(spd->abort_test_available),
2535                        spd->short_test_polling_minutes,
2536                        spd->extended_test_polling_minutes,
2537                        spd->conveyance_test_polling_minutes);
2538
2539                 if (sk_disk_smart_get_bad(d, &value) < 0)
2540                         printf("Bad Sectors: %s\n", strerror(errno));
2541                 else
2542                         printf("%sBad Sectors: %s%s\n",
2543                                value > 0 ? HIGHLIGHT : "",
2544                                print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_SECTORS),
2545                                value > 0 ? ENDHIGHLIGHT : "");
2546
2547                 if (sk_disk_smart_get_power_on(d, &power_on) < 0) {
2548                         printf("Powered On: %s\n", strerror(errno));
2549                         power_on = 0;
2550                 } else
2551                         printf("Powered On: %s\n", print_value(pretty, sizeof(pretty), power_on, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2552
2553                 if (sk_disk_smart_get_power_cycle(d, &value) < 0)
2554                         printf("Power Cycles: %s\n", strerror(errno));
2555                 else {
2556                         printf("Power Cycles: %llu\n", (unsigned long long) value);
2557
2558                         if (value > 0 && power_on > 0)
2559                                 printf("Average Powered On Per Power Cycle: %s\n", print_value(pretty, sizeof(pretty), power_on/value, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2560                 }
2561
2562                 if (sk_disk_smart_get_temperature(d, &value) < 0)
2563                         printf("Temperature: %s\n", strerror(errno));
2564                 else
2565                         printf("Temperature: %s\n", print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_MKELVIN));
2566
2567                 printf("Attribute Parsing Verification: %s\n",
2568                        d->attribute_verification_bad ? "Bad" : "Good");
2569
2570                 if (sk_disk_smart_get_overall(d, &overall) < 0)
2571                         printf("Overall Status: %s\n", strerror(errno));
2572                 else
2573                         printf("%sOverall Status: %s%s\n",
2574                                overall != SK_SMART_OVERALL_GOOD ? HIGHLIGHT : "",
2575                                sk_smart_overall_to_string(overall),
2576                                overall != SK_SMART_OVERALL_GOOD ? ENDHIGHLIGHT : "");
2577
2578                 printf("%3s %-27s %5s %5s %5s %-11s %-14s %-7s %-7s %-4s %-4s\n",
2579                        "ID#",
2580                        "Name",
2581                        "Value",
2582                        "Worst",
2583                        "Thres",
2584                        "Pretty",
2585                        "Raw",
2586                        "Type",
2587                        "Updates",
2588                        "Good",
2589                        "Good/Past");
2590
2591                 if ((ret = sk_disk_smart_parse_attributes(d, disk_dump_attributes, NULL)) < 0)
2592                         return ret;
2593         } else
2594                 printf("ATA SMART not supported.\n");
2595
2596         return 0;
2597 }
2598
2599 int sk_disk_get_size(SkDisk *d, uint64_t *bytes) {
2600         assert(d);
2601         assert(bytes);
2602
2603         if (d->size == (uint64_t) -1) {
2604                 errno = ENODATA;
2605                 return -1;
2606         }
2607
2608         *bytes = d->size;
2609         return 0;
2610 }
2611
2612 static int disk_find_type(SkDisk *d, dev_t devnum) {
2613         struct udev *udev;
2614         struct udev_device *dev = NULL, *usb;
2615         int r = -1;
2616         const char *a;
2617
2618         assert(d);
2619
2620         if (!(udev = udev_new())) {
2621                 errno = ENXIO;
2622                 goto finish;
2623         }
2624
2625         if (!(dev = udev_device_new_from_devnum(udev, 'b', devnum))) {
2626                 errno = ENODEV;
2627                 goto finish;
2628         }
2629
2630         if ((a = udev_device_get_property_value(dev, "ID_ATA_SMART_ACCESS"))) {
2631                 unsigned u;
2632
2633                 for (u = 0; u < _SK_DISK_TYPE_MAX; u++) {
2634                         const char *t;
2635
2636                         if (!(t = disk_type_to_prefix_string(u)))
2637                                 continue;
2638
2639                         if (!strcmp(a, t)) {
2640                                 d->type = u;
2641                                 r = 0;
2642                                 goto finish;
2643                         }
2644                 }
2645
2646                 d->type = SK_DISK_TYPE_NONE;
2647                 r = 0;
2648                 goto finish;
2649         }
2650
2651         if ((usb = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"))) {
2652                 const char *product, *vendor;
2653                 uint32_t pid, vid;
2654
2655                 if (!(product = udev_device_get_sysattr_value(usb, "idProduct")) ||
2656                     sscanf(product, "%04x", &pid) != 1) {
2657                         errno = ENODEV;
2658                         goto finish;
2659                 }
2660
2661                 if (!(vendor = udev_device_get_sysattr_value(usb, "idVendor")) ||
2662                     sscanf(vendor, "%04x", &vid) != 1) {
2663                         errno = ENODEV;
2664                         goto finish;
2665                 }
2666
2667                 if ((vid == 0x0928 && pid == 0x0000))
2668                         /* This Oxford Semiconductor bridge seems to
2669                          * choke on SAT commands. Let's explicitly
2670                          * black list it here.
2671                          *
2672                          * http://bugs.freedesktop.org/show_bug.cgi?id=24951 */
2673                         d->type = SK_DISK_TYPE_NONE;
2674                 else if ((vid == 0x152d && pid == 0x2329) ||
2675                          (vid == 0x152d && pid == 0x2338) ||
2676                          (vid == 0x152d && pid == 0x2339))
2677                         /* Some JMicron bridges seem to choke on SMART
2678                          * commands, so let's explicitly black list
2679                          * them here.
2680                          *
2681                          * https://bugzilla.redhat.com/show_bug.cgi?id=515881
2682                          *
2683                          * At least some of the JMicron bridges with
2684                          * these vids/pids choke on the jmicron access
2685                          * mode. To make sure we don't break things
2686                          * for people we now disable this by
2687                          * default. */
2688                         d->type = SK_DISK_TYPE_NONE;
2689                 else if ((vid == 0x152d && pid == 0x2336))
2690                         /* This JMicron bridge seems to always work
2691                          * with SMART commands send with the jmicron
2692                          * access mode. */
2693                         d->type = SK_DISK_TYPE_JMICRON;
2694                 else if ((vid == 0x0c0b && pid == 0xb159) ||
2695                     (vid == 0x04fc && pid == 0x0c25) ||
2696                     (vid == 0x04fc && pid == 0x0c15))
2697                         d->type = SK_DISK_TYPE_SUNPLUS;
2698                 else
2699                         d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_12;
2700
2701         } else if (udev_device_get_parent_with_subsystem_devtype(dev, "ide", NULL))
2702                 d->type = SK_DISK_TYPE_LINUX_IDE;
2703         else if (udev_device_get_parent_with_subsystem_devtype(dev, "scsi", NULL))
2704                 d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_16;
2705         else
2706                 d->type = SK_DISK_TYPE_AUTO;
2707
2708         r = 0;
2709
2710 finish:
2711         if (dev)
2712                 udev_device_unref(dev);
2713
2714         if (udev)
2715                 udev_unref(udev);
2716
2717         return r;
2718 }
2719
2720 static int init_smart(SkDisk *d) {
2721         /* We don't do the SMART initialization right-away, since some
2722          * drivers spin up when we do that */
2723
2724         int ret;
2725
2726         if (d->smart_initialized)
2727                 return 0;
2728
2729         d->smart_initialized = TRUE;
2730
2731         /* Check if driver can do SMART, and enable if necessary */
2732         if (!disk_smart_is_available(d))
2733                 return 0;
2734
2735         if (!disk_smart_is_enabled(d)) {
2736                 if ((ret = disk_smart_enable(d, TRUE)) < 0)
2737                         goto fail;
2738
2739                 if ((ret = disk_identify_device(d)) < 0)
2740                         goto fail;
2741
2742                 if (!disk_smart_is_enabled(d)) {
2743                         errno = EIO;
2744                         ret = -1;
2745                         goto fail;
2746                 }
2747         }
2748
2749         disk_smart_read_thresholds(d);
2750         ret = 0;
2751
2752 fail:
2753         return ret;
2754 }
2755
2756 int sk_disk_open(const char *name, SkDisk **_d) {
2757         SkDisk *d;
2758         int ret = -1;
2759         struct stat st;
2760
2761         assert(_d);
2762
2763         if (!(d = calloc(1, sizeof(SkDisk)))) {
2764                 errno = ENOMEM;
2765                 goto fail;
2766         }
2767
2768         d->fd = -1;
2769         d->size = (uint64_t) -1;
2770
2771         if (!name)
2772                 d->type = SK_DISK_TYPE_BLOB;
2773         else {
2774                 const char *dn;
2775
2776                 d->type = SK_DISK_TYPE_AUTO;
2777
2778                 if (!(dn = disk_type_from_string(name, &d->type)))
2779                         dn = name;
2780
2781                 if (!(d->name = strdup(dn))) {
2782                         errno = ENOMEM;
2783                         goto fail;
2784                 }
2785
2786                 if ((d->fd = open(d->name,
2787                                   O_RDONLY|O_NOCTTY|O_NONBLOCK
2788 #ifdef O_CLOEXEC
2789                                   |O_CLOEXEC
2790 #endif
2791
2792                      )) < 0) {
2793                         ret = d->fd;
2794                         goto fail;
2795                 }
2796
2797                 if ((ret = fstat(d->fd, &st)) < 0)
2798                         goto fail;
2799
2800                 if (!S_ISBLK(st.st_mode)) {
2801                         errno = ENODEV;
2802                         ret = -1;
2803                         goto fail;
2804                 }
2805
2806                 /* So, it's a block device. Let's make sure the ioctls work */
2807                 if ((ret = ioctl(d->fd, BLKGETSIZE64, &d->size)) < 0)
2808                         goto fail;
2809
2810                 if (d->size <= 0 || d->size == (uint64_t) -1) {
2811                         errno = EIO;
2812                         ret = -1;
2813                         goto fail;
2814                 }
2815
2816                 /* OK, it's a real block device with a size. Now let's find the suitable API */
2817                 if (d->type == SK_DISK_TYPE_AUTO)
2818                         if ((ret = disk_find_type(d, st.st_rdev)) < 0)
2819                                 goto fail;
2820
2821                 if (d->type == SK_DISK_TYPE_AUTO) {
2822                         /* We have no clue, so let's autotest for a working API */
2823                         for (d->type = 0; d->type < _SK_DISK_TYPE_TEST_MAX; d->type++)
2824                                 if (disk_identify_device(d) >= 0)
2825                                         break;
2826                         if (d->type >= _SK_DISK_TYPE_TEST_MAX)
2827                                 d->type = SK_DISK_TYPE_NONE;
2828                 } else
2829                         disk_identify_device(d);
2830         }
2831
2832         *_d = d;
2833
2834         return 0;
2835
2836 fail:
2837
2838         if (d)
2839                 sk_disk_free(d);
2840
2841         return ret;
2842 }
2843
2844 void sk_disk_free(SkDisk *d) {
2845         assert(d);
2846
2847         if (d->fd >= 0)
2848                 close(d->fd);
2849
2850         free(d->name);
2851         free(d->blob);
2852         free(d);
2853 }
2854
2855 int sk_disk_get_blob(SkDisk *d, const void **blob, size_t *rsize) {
2856         size_t size;
2857         SkBool good, have_good = FALSE;
2858         uint32_t *p;
2859
2860         assert(d);
2861         assert(blob);
2862         assert(rsize);
2863
2864         size =
2865                 (d->identify_valid ? 8 + sizeof(d->identify) : 0) +
2866                 (d->smart_data_valid ? 8 + sizeof(d->smart_data) : 0) +
2867                 (d->smart_thresholds_valid ? 8 + sizeof(d->smart_thresholds) : 0);
2868
2869         if (sk_disk_smart_status(d, &good) >= 0) {
2870                 size += 12;
2871                 have_good = TRUE;
2872         }
2873
2874         if (size <= 0) {
2875                 errno = ENODATA;
2876                 return -1;
2877         }
2878
2879         free(d->blob);
2880         if (!(d->blob = malloc(size))) {
2881                 errno = ENOMEM;
2882                 return -1;
2883         }
2884
2885         p = d->blob;
2886
2887         /* These memory accesses are only OK as long as all our
2888          * objects are sensibly aligned, which they are... */
2889
2890         if (d->identify_valid) {
2891                 p[0] = SK_BLOB_TAG_IDENTIFY;
2892                 p[1] = htonl(sizeof(d->identify));
2893                 p += 2;
2894
2895                 memcpy(p, d->identify, sizeof(d->identify));
2896                 p = (uint32_t*) ((uint8_t*) p + sizeof(d->identify));
2897         }
2898
2899         if (have_good) {
2900                 p[0] = SK_BLOB_TAG_SMART_STATUS;
2901                 p[1] = htonl(4);
2902                 p[2] = htonl(!!good);
2903                 p += 3;
2904         }
2905
2906         if (d->smart_data_valid) {
2907                 p[0] = SK_BLOB_TAG_SMART_DATA;
2908                 p[1] = htonl(sizeof(d->smart_data));
2909                 p += 2;
2910
2911                 memcpy(p, d->smart_data, sizeof(d->smart_data));
2912                 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_data));
2913         }
2914
2915         if (d->smart_thresholds_valid) {
2916                 p[0] = SK_BLOB_TAG_SMART_THRESHOLDS;
2917                 p[1] = htonl(sizeof(d->smart_thresholds));
2918                 p += 2;
2919
2920                 memcpy(p, d->smart_thresholds, sizeof(d->smart_thresholds));
2921                 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_thresholds));
2922         }
2923
2924         assert((size_t) ((uint8_t*) p - (uint8_t*) d->blob) == size);
2925
2926         *blob = d->blob;
2927         *rsize = size;
2928
2929         return 0;
2930 }
2931
2932 int sk_disk_set_blob(SkDisk *d, const void *blob, size_t size) {
2933         const uint32_t *p;
2934         size_t left;
2935         SkBool idv = FALSE, sdv = FALSE, stv = FALSE, bssv = FALSE;
2936
2937         assert(d);
2938         assert(blob);
2939
2940         if (d->type != SK_DISK_TYPE_BLOB) {
2941                 errno = ENODEV;
2942                 return -1;
2943         }
2944
2945         if (size <= 0) {
2946                 errno = EINVAL;
2947                 return -1;
2948         }
2949
2950         /* First run, verify if everything makes sense */
2951         p = blob;
2952         left = size;
2953         while (left > 0) {
2954                 uint32_t tag, tsize;
2955
2956                 if (left < 8) {
2957                         errno = EINVAL;
2958                         return -1;
2959                 }
2960
2961                 memcpy(&tag, p, 4);
2962                 memcpy(&tsize, p+1, 4);
2963                 p += 2;
2964                 left -= 8;
2965
2966                 if (left < ntohl(tsize)) {
2967                         errno = EINVAL;
2968                         return -1;
2969                 }
2970
2971                 switch (tag) {
2972
2973                         case SK_BLOB_TAG_IDENTIFY:
2974                                 if (ntohl(tsize) != sizeof(d->identify) || idv) {
2975                                         errno = EINVAL;
2976                                         return -1;
2977                                 }
2978                                 idv = TRUE;
2979                                 break;
2980
2981                         case SK_BLOB_TAG_SMART_STATUS:
2982                                 if (ntohl(tsize) != 4 || bssv) {
2983                                         errno = EINVAL;
2984                                         return -1;
2985                                 }
2986                                 bssv = TRUE;
2987                                 break;
2988
2989                         case SK_BLOB_TAG_SMART_DATA:
2990                                 if (ntohl(tsize) != sizeof(d->smart_data) || sdv) {
2991                                         errno = EINVAL;
2992                                         return -1;
2993                                 }
2994                                 sdv = TRUE;
2995                                 break;
2996
2997                         case SK_BLOB_TAG_SMART_THRESHOLDS:
2998                                 if (ntohl(tsize) != sizeof(d->smart_thresholds) || stv) {
2999                                         errno = EINVAL;
3000                                         return -1;
3001                                 }
3002                                 stv = TRUE;
3003                                 break;
3004                 }
3005
3006                 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
3007                 left -= ntohl(tsize);
3008         }
3009
3010         if (!idv) {
3011                 errno = -ENODATA;
3012                 return -1;
3013         }
3014
3015         d->identify_valid = idv;
3016         d->smart_data_valid = sdv;
3017         d->smart_thresholds_valid = stv;
3018         d->blob_smart_status_valid = bssv;
3019
3020         /* Second run, actually copy things in */
3021         p = blob;
3022         left = size;
3023         while (left > 0) {
3024                 uint32_t tag, tsize;
3025
3026                 assert(left >= 8);
3027                 memcpy(&tag, p, 4);
3028                 memcpy(&tsize, p+1, 4);
3029                 p += 2;
3030                 left -= 8;
3031
3032                 assert(left >= ntohl(tsize));
3033
3034                 switch (tag) {
3035
3036                         case SK_BLOB_TAG_IDENTIFY:
3037                                 assert(ntohl(tsize) == sizeof(d->identify));
3038                                 memcpy(d->identify, p, sizeof(d->identify));
3039                                 break;
3040
3041                         case SK_BLOB_TAG_SMART_STATUS: {
3042                                 uint32_t ok;
3043                                 assert(ntohl(tsize) == 4);
3044                                 memcpy(&ok, p, 4);
3045                                 d->blob_smart_status = !!ok;
3046                                 break;
3047                         }
3048
3049                         case SK_BLOB_TAG_SMART_DATA:
3050                                 assert(ntohl(tsize) == sizeof(d->smart_data));
3051                                 memcpy(d->smart_data, p, sizeof(d->smart_data));
3052                                 break;
3053
3054                         case SK_BLOB_TAG_SMART_THRESHOLDS:
3055                                 assert(ntohl(tsize) == sizeof(d->smart_thresholds));
3056                                 memcpy(d->smart_thresholds, p, sizeof(d->smart_thresholds));
3057                                 break;
3058                 }
3059
3060                 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
3061                 left -= ntohl(tsize);
3062         }
3063
3064         return 0;
3065 }