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