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