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