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