add missing 'else'
[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_ATA,                /* 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_ATA] = "Native Linux ATA",
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_ata_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_ATA);
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_ATA] = disk_ata_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                 a->pretty_value = fourtyeight & 0xFFFFFFFFU;
1134         else
1135                 a->pretty_value = fourtyeight;
1136 }
1137
1138 typedef struct SkSmartAttributeInfo {
1139         const char *name;
1140         SkSmartAttributeUnit unit;
1141 } SkSmartAttributeInfo;
1142
1143 /* This data is stolen from smartmontools */
1144
1145 /* %STRINGPOOLSTART% */
1146 static const SkSmartAttributeInfo const attribute_info[256] = {
1147         [1]   = { "raw-read-error-rate",         SK_SMART_ATTRIBUTE_UNIT_NONE },
1148         [2]   = { "throughput-performance",      SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1149         [3]   = { "spin-up-time",                SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
1150         [4]   = { "start-stop-count",            SK_SMART_ATTRIBUTE_UNIT_NONE },
1151         [5]   = { "reallocated-sector-count",    SK_SMART_ATTRIBUTE_UNIT_SECTORS },
1152         [6]   = { "read-channel-margin",         SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1153         [7]   = { "seek-error-rate",             SK_SMART_ATTRIBUTE_UNIT_NONE },
1154         [8]   = { "seek-time-performance",       SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1155         [9]   = { "power-on-hours",              SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
1156         [10]  = { "spin-retry-count",            SK_SMART_ATTRIBUTE_UNIT_NONE },
1157         [11]  = { "calibration-retry-count",     SK_SMART_ATTRIBUTE_UNIT_NONE },
1158         [12]  = { "power-cycle-count",           SK_SMART_ATTRIBUTE_UNIT_NONE },
1159         [13]  = { "read-soft-error-rate",        SK_SMART_ATTRIBUTE_UNIT_NONE },
1160         [187] = { "reported-uncorrect",          SK_SMART_ATTRIBUTE_UNIT_SECTORS },
1161         [189] = { "high-fly-writes",             SK_SMART_ATTRIBUTE_UNIT_NONE },
1162         [190] = { "airflow-temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN },
1163         [191] = { "g-sense-error-rate",          SK_SMART_ATTRIBUTE_UNIT_NONE },
1164         [192] = { "power-off-retract-count",     SK_SMART_ATTRIBUTE_UNIT_NONE },
1165         [193] = { "load-cycle-count",            SK_SMART_ATTRIBUTE_UNIT_NONE },
1166         [194] = { "temperature-celsius-2",       SK_SMART_ATTRIBUTE_UNIT_MKELVIN },
1167         [195] = { "hardware-ecc-recovered",      SK_SMART_ATTRIBUTE_UNIT_NONE },
1168         [196] = { "reallocated-event-count",     SK_SMART_ATTRIBUTE_UNIT_NONE },
1169         [197] = { "current-pending-sector",      SK_SMART_ATTRIBUTE_UNIT_SECTORS },
1170         [198] = { "offline-uncorrectable",       SK_SMART_ATTRIBUTE_UNIT_SECTORS },
1171         [199] = { "udma-crc-error-count",        SK_SMART_ATTRIBUTE_UNIT_NONE },
1172         [200] = { "multi-zone-error-rate",       SK_SMART_ATTRIBUTE_UNIT_NONE },
1173         [201] = { "soft-read-error-rate",        SK_SMART_ATTRIBUTE_UNIT_NONE },
1174         [202] = { "ta-increase-count",           SK_SMART_ATTRIBUTE_UNIT_NONE },
1175         [203] = { "run-out-cancel",              SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1176         [204] = { "shock-count-write-open",      SK_SMART_ATTRIBUTE_UNIT_NONE },
1177         [205] = { "shock-rate-write-open",       SK_SMART_ATTRIBUTE_UNIT_NONE },
1178         [206] = { "flying-height",               SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1179         [207] = { "spin-high-current",           SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1180         [208] = { "spin-buzz",                   SK_SMART_ATTRIBUTE_UNIT_UNKNOWN},
1181         [209] = { "offline-seek-performance",    SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1182         [220] = { "disk-shift",                  SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1183         [221] = { "g-sense-error-rate-2",        SK_SMART_ATTRIBUTE_UNIT_NONE },
1184         [222] = { "loaded-hours",                SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
1185         [223] = { "load-retry-count",            SK_SMART_ATTRIBUTE_UNIT_NONE },
1186         [224] = { "load-friction",               SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1187         [225] = { "load-cycle-count-2",          SK_SMART_ATTRIBUTE_UNIT_NONE },
1188         [226] = { "load-in-time",                SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
1189         [227] = { "torq-amp-count",              SK_SMART_ATTRIBUTE_UNIT_NONE },
1190         [228] = { "power-off-retract-count-2",   SK_SMART_ATTRIBUTE_UNIT_NONE },
1191         [230] = { "head-amplitude",              SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1192         [231] = { "temperature-celsius",         SK_SMART_ATTRIBUTE_UNIT_MKELVIN },
1193         [240] = { "head-flying-hours",           SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
1194         [250] = { "read-error-retry-rate",       SK_SMART_ATTRIBUTE_UNIT_NONE }
1195 };
1196 /* %STRINGPOOLSTOP% */
1197
1198 typedef enum SkSmartQuirk {
1199         SK_SMART_QUIRK_9_POWERONMINUTES = 1,
1200         SK_SMART_QUIRK_9_POWERONSECONDS = 2,
1201         SK_SMART_QUIRK_9_POWERONHALFMINUTES = 4,
1202         SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT = 8,
1203         SK_SMART_QUIRK_193_LOADUNLOAD = 16,
1204         SK_SMART_QUIRK_194_10XCELSIUS = 32,
1205         SK_SMART_QUIRK_194_UNKNOWN = 64,
1206         SK_SMART_QUIRK_200_WRITEERRORCOUNT = 128,
1207         SK_SMART_QUIRK_201_DETECTEDTACOUNT = 256
1208 } SkSmartQuirk;
1209
1210 /* %STRINGPOOLSTART% */
1211 static const char *quirk_name[] = {
1212         "9_POWERONMINUTES",
1213         "9_POWERONSECONDS",
1214         "9_POWERONHALFMINUTES",
1215         "192_EMERGENCYRETRACTCYCLECT",
1216         "193_LOADUNLOAD",
1217         "194_10XCELSIUS",
1218         "194_UNKNOWN",
1219         "200_WRITEERRORCOUNT",
1220         "201_DETECTEDTACOUNT",
1221         NULL
1222 };
1223 /* %STRINGPOOLSTOP% */
1224
1225 typedef struct SkSmartQuirkDatabase {
1226         const char *model;
1227         const char *firmware;
1228         SkSmartQuirk quirk;
1229 } SkSmartQuirkDatabase;
1230
1231 static const SkSmartQuirkDatabase quirk_database[] = { {
1232
1233         /*** Fujitsu */
1234                 "^FUJITSU MHR2040AT$",
1235                 NULL,
1236                 SK_SMART_QUIRK_9_POWERONSECONDS|
1237                 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1238                 SK_SMART_QUIRK_200_WRITEERRORCOUNT
1239         }, {
1240                 "^FUJITSU MHS20[6432]0AT(  .)?$",
1241                 NULL,
1242                 SK_SMART_QUIRK_9_POWERONSECONDS|
1243                 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1244                 SK_SMART_QUIRK_200_WRITEERRORCOUNT|
1245                 SK_SMART_QUIRK_201_DETECTEDTACOUNT
1246         }, {
1247                 "^("
1248                 "FUJITSU M1623TAU|"
1249                 "FUJITSU MHG2...ATU?.*|"
1250                 "FUJITSU MHH2...ATU?.*|"
1251                 "FUJITSU MHJ2...ATU?.*|"
1252                 "FUJITSU MHK2...ATU?.*|"
1253                 "FUJITSU MHL2300AT|"
1254                 "FUJITSU MHM2(20|15|10|06)0AT|"
1255                 "FUJITSU MHN2...AT|"
1256                 "FUJITSU MHR2020AT|"
1257                 "FUJITSU MHT2...(AH|AS|AT|BH)U?.*|"
1258                 "FUJITSU MHU2...ATU?.*|"
1259                 "FUJITSU MHV2...(AH|AS|AT|BH|BS|BT).*|"
1260                 "FUJITSU MP[A-G]3...A[HTEV]U?.*"
1261                 ")$",
1262                 NULL,
1263                 SK_SMART_QUIRK_9_POWERONSECONDS
1264         }, {
1265
1266         /*** Samsung ***/
1267                 "^("
1268                 "SAMSUNG SV4012H|"
1269                 "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]"
1270                 ")$",
1271                 NULL,
1272                 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1273         }, {
1274                 "^("
1275                 "SAMSUNG SV0412H|"
1276                 "SAMSUNG SV1204H"
1277                 ")$",
1278                 NULL,
1279                 SK_SMART_QUIRK_9_POWERONHALFMINUTES|
1280                 SK_SMART_QUIRK_194_10XCELSIUS
1281         }, {
1282                 "^SAMSUNG SP40A2H$",
1283                 "^RR100-07$",
1284                 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1285         }, {
1286                 "^SAMSUNG SP80A4H$",
1287                 "^RT100-06$",
1288                 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1289         }, {
1290                 "^SAMSUNG SP8004H$",
1291                 "^QW100-61$",
1292                 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1293         }, {
1294
1295         /*** Maxtor */
1296                 "^("
1297                 "Maxtor 2B0(0[468]|1[05]|20)H1|"
1298                 "Maxtor 4G(120J6|160J[68])|"
1299                 "Maxtor 4D0(20H1|40H2|60H3|80H4)"
1300                 ")$",
1301                 NULL,
1302                 SK_SMART_QUIRK_9_POWERONMINUTES|
1303                 SK_SMART_QUIRK_194_UNKNOWN
1304         }, {
1305                 "^("
1306                 "Maxtor 2F0[234]0[JL]0|"
1307                 "Maxtor 8(1280A2|2160A4|2560A4|3840A6|4000A6|5120A8)|"
1308                 "Maxtor 8(2160D2|3228D3|3240D3|4320D4|6480D6|8400D8|8455D8)|"
1309                 "Maxtor 9(0510D4|0576D4|0648D5|0720D5|0840D6|0845D6|0864D6|1008D7|1080D8|1152D8)|"
1310                 "Maxtor 9(1(360|350|202)D8|1190D7|10[12]0D6|0840D5|06[48]0D4|0510D3|1(350|202)E8|1010E6|0840E5|0640E4)|"
1311                 "Maxtor 9(0512D2|0680D3|0750D3|0913D4|1024D4|1360D6|1536D6|1792D7|2048D8)|"
1312                 "Maxtor 9(2732U8|2390U7|204[09]U6|1707U5|1366U4|1024U3|0845U3|0683U2)|"
1313                 "Maxtor 4(R0[68]0[JL]0|R1[26]0L0|A160J0|R120L4)|"
1314                 "Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D[34]|90432D2)|"
1315                 "Maxtor 9(0431U1|0641U2|0871U2|1301U3|1741U4)|"
1316                 "Maxtor (94091U8|93071U6|92561U5|92041U4|91731U4|91531U3|91361U3|91021U2|90841U2|90651U2)|"
1317                 "Maxtor (33073U4|32049U3|31536U2|30768U1|33073H4|32305H3|31536H2|30768H1)|"
1318                 "Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)|"
1319                 "Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)|"
1320                 "Maxtor (54098[UH]8|53073[UH]6|52732[UH]6|52049[UH]4|51536[UH]3|51369[UH]3|51024[UH]2)|"
1321                 "Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?|"
1322                 "Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)|"
1323                 "Maxtor 9(1023U2|1536U2|2049U3|2305U3|3073U4|4610U6|6147U8)|"
1324                 "Maxtor 9(1023H2|1536H2|2049H3|2305H3|3073H4|4098H6|4610H6|6147H8)|"
1325                 "Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)|"
1326                 "Maxtor (98196H8|96147H6)|"
1327                 "Maxtor 4W(100H6|080H6|060H4|040H3|030H2)|"
1328                 "Maxtor 6(E0[234]|K04)0L0|"
1329                 "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|"
1330                 "Maxtor 6Y((060|080|120|160)L0|(060|080|120|160|200|250)P0|(060|080|120|160|200|250)M0)|"
1331                 "Maxtor 7Y250[PM]0|"
1332                 "Maxtor [45]A(25|30|32)0[JN]0|"
1333                 "Maxtor 7L(25|30)0[SR]0"
1334                 ")$",
1335                 NULL,
1336                 SK_SMART_QUIRK_9_POWERONMINUTES
1337         }, {
1338
1339
1340         /*** Hitachi */
1341                 "^("
1342                 "HITACHI_DK14FA-20B|"
1343                 "HITACHI_DK23..-..B?|"
1344                 "HITACHI_DK23FA-20J|HTA422020F9AT[JN]0|"
1345                 "HE[JN]4230[23]0F9AT00|"
1346                 "HTC4260[23]0G5CE00|HTC4260[56]0G8CE00"
1347                 ")$",
1348                 NULL,
1349                 SK_SMART_QUIRK_9_POWERONMINUTES|
1350                 SK_SMART_QUIRK_193_LOADUNLOAD
1351         }, {
1352
1353                 NULL,
1354                 NULL,
1355                 0
1356         }
1357 };
1358
1359 static int match(const char*regex, const char *s, SkBool *result) {
1360         int k;
1361         regex_t re;
1362
1363         *result = FALSE;
1364
1365         if (regcomp(&re, regex, REG_EXTENDED|REG_NOSUB) != 0) {
1366                 errno = EINVAL;
1367                 return -1;
1368         }
1369
1370         if ((k = regexec(&re, s, 0, NULL, 0)) != 0) {
1371
1372                 if (k != REG_NOMATCH) {
1373                         regfree(&re);
1374                         errno = EINVAL;
1375                         return -1;
1376                 }
1377
1378         } else
1379                 *result = TRUE;
1380
1381         regfree(&re);
1382
1383         return 0;
1384 }
1385
1386 static int lookup_quirks(const char *model, const char *firmware, SkSmartQuirk *quirk) {
1387         int k;
1388         const SkSmartQuirkDatabase *db;
1389
1390         *quirk = 0;
1391
1392         for (db = quirk_database; db->model || db->firmware; db++) {
1393
1394                 if (db->model) {
1395                         SkBool matching = FALSE;
1396
1397                         if ((k = match(db->model, model, &matching)) < 0)
1398                                 return k;
1399
1400                         if (!matching)
1401                                 continue;
1402                 }
1403
1404                 if (db->firmware) {
1405                         SkBool matching = FALSE;
1406
1407                         if ((k = match(db->firmware, firmware, &matching)) < 0)
1408                                 return k;
1409
1410                         if (!matching)
1411                                 continue;
1412                 }
1413
1414                 *quirk = db->quirk;
1415                 return 0;
1416         }
1417
1418         return 0;
1419 }
1420
1421 static const SkSmartAttributeInfo *lookup_attribute(SkDisk *d, uint8_t id) {
1422         const SkIdentifyParsedData *ipd;
1423         SkSmartQuirk quirk = 0;
1424
1425         /* These are the complex ones */
1426         if (sk_disk_identify_parse(d, &ipd) < 0)
1427                 return NULL;
1428
1429         if (lookup_quirks(ipd->model, ipd->firmware, &quirk) < 0)
1430                 return NULL;
1431
1432         if (quirk) {
1433                 switch (id) {
1434
1435                         case 9:
1436                                 /* %STRINGPOOLSTART% */
1437                                 if (quirk & SK_SMART_QUIRK_9_POWERONMINUTES) {
1438                                         static const SkSmartAttributeInfo a = {
1439                                                 "power-on-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS
1440                                         };
1441                                         return &a;
1442
1443                                 } else if (quirk & SK_SMART_QUIRK_9_POWERONSECONDS) {
1444                                         static const SkSmartAttributeInfo a = {
1445                                                 "power-on-seconds", SK_SMART_ATTRIBUTE_UNIT_MSECONDS
1446                                         };
1447                                         return &a;
1448
1449                                 } else if (quirk & SK_SMART_QUIRK_9_POWERONHALFMINUTES) {
1450                                         static const SkSmartAttributeInfo a = {
1451                                                 "power-on-half-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS
1452                                         };
1453                                         return &a;
1454                                 }
1455                                 /* %STRINGPOOLSTOP% */
1456
1457                                 break;
1458
1459                         case 192:
1460                                 /* %STRINGPOOLSTART% */
1461                                 if (quirk & SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT) {
1462                                         static const SkSmartAttributeInfo a = {
1463                                                 "emergency-retract-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE
1464                                         };
1465                                         return &a;
1466                                 }
1467                                 /* %STRINGPOOLSTOP% */
1468
1469                                 break;
1470
1471                         case 194:
1472                                 /* %STRINGPOOLSTART% */
1473                                 if (quirk & SK_SMART_QUIRK_194_10XCELSIUS) {
1474                                         static const SkSmartAttributeInfo a = {
1475                                                 "temperature-centi-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN
1476                                         };
1477                                         return &a;
1478                                 } else if (quirk & SK_SMART_QUIRK_194_UNKNOWN)
1479                                         return NULL;
1480                                 /* %STRINGPOOLSTOP% */
1481
1482                                 break;
1483
1484                         case 200:
1485                                 /* %STRINGPOOLSTART% */
1486                                 if (quirk & SK_SMART_QUIRK_200_WRITEERRORCOUNT) {
1487                                         static const SkSmartAttributeInfo a = {
1488                                                 "write-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE
1489                                         };
1490                                         return &a;
1491                                 }
1492                                 /* %STRINGPOOLSTOP% */
1493
1494                                 break;
1495
1496                         case 201:
1497                                 /* %STRINGPOOLSTART% */
1498                                 if (quirk & SK_SMART_QUIRK_201_DETECTEDTACOUNT) {
1499                                         static const SkSmartAttributeInfo a = {
1500                                                 "detected-ta-count", SK_SMART_ATTRIBUTE_UNIT_NONE
1501                                         };
1502                                         return &a;
1503                                 }
1504                                 /* %STRINGPOOLSTOP% */
1505
1506                                 break;
1507                 }
1508         }
1509
1510         /* These are the simple cases */
1511         if (attribute_info[id].name)
1512                 return &attribute_info[id];
1513
1514         return NULL;
1515 }
1516
1517 int sk_disk_smart_parse(SkDisk *d, const SkSmartParsedData **spd) {
1518
1519         if (!d->smart_data_valid) {
1520                 errno = ENOENT;
1521                 return -1;
1522         }
1523
1524         switch (d->smart_data[362]) {
1525                 case 0x00:
1526                 case 0x80:
1527                         d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER;
1528                         break;
1529
1530                 case 0x02:
1531                 case 0x82:
1532                         d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS;
1533                         break;
1534
1535                 case 0x03:
1536                         d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS;
1537                         break;
1538
1539                 case 0x04:
1540                 case 0x84:
1541                         d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED;
1542                         break;
1543
1544                 case 0x05:
1545                 case 0x85:
1546                         d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED;
1547                         break;
1548
1549                 case 0x06:
1550                 case 0x86:
1551                         d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL;
1552                         break;
1553
1554                 default:
1555                         d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN;
1556                         break;
1557         }
1558
1559         d->smart_parsed_data.self_test_execution_percent_remaining = 10*(d->smart_data[363] & 0xF);
1560         d->smart_parsed_data.self_test_execution_status = (d->smart_data[363] >> 4) & 0xF;
1561
1562         d->smart_parsed_data.total_offline_data_collection_seconds = (uint16_t) d->smart_data[364] | ((uint16_t) d->smart_data[365] << 8);
1563
1564         d->smart_parsed_data.conveyance_test_available = disk_smart_is_conveyance_test_available(d);
1565         d->smart_parsed_data.short_and_extended_test_available = disk_smart_is_short_and_extended_test_available(d);
1566         d->smart_parsed_data.start_test_available = disk_smart_is_start_test_available(d);
1567         d->smart_parsed_data.abort_test_available = disk_smart_is_abort_test_available(d);
1568
1569         d->smart_parsed_data.short_test_polling_minutes = d->smart_data[372];
1570         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]);
1571         d->smart_parsed_data.conveyance_test_polling_minutes = d->smart_data[374];
1572
1573         *spd = &d->smart_parsed_data;
1574
1575         return 0;
1576 }
1577
1578 static void find_threshold(SkDisk *d, SkSmartAttributeParsedData *a) {
1579         uint8_t *p;
1580         unsigned n;
1581
1582         if (!d->smart_thresholds_valid) {
1583                 a->threshold_valid = FALSE;
1584                 return;
1585         }
1586
1587         for (n = 0, p = d->smart_thresholds+2; n < 30; n++, p+=12)
1588                 if (p[0] == a->id)
1589                         break;
1590
1591         if (n >= 30) {
1592                 a->threshold_valid = FALSE;
1593                 a->good_valid = FALSE;
1594                 return;
1595         }
1596
1597         a->threshold = p[1];
1598         a->threshold_valid = p[1] != 0xFE;
1599
1600         a->good_valid = FALSE;
1601         a->good = TRUE;
1602
1603         /* Always-Fail and Always-Passing thresholds are not relevant
1604          * for our assessment. */
1605         if (p[1] >= 1 && p[1] <= 0xFD) {
1606
1607                 if (a->worst_value_valid) {
1608                         a->good = a->good && (a->worst_value > a->threshold);
1609                         a->good_valid = TRUE;
1610                 }
1611
1612                 if (a->current_value_valid) {
1613                         a->good = a->good && (a->current_value > a->threshold);
1614                         a->good_valid = TRUE;
1615                 }
1616         }
1617 }
1618
1619 int sk_disk_smart_parse_attributes(SkDisk *d, SkSmartAttributeParseCallback cb, void* userdata) {
1620         uint8_t *p;
1621         unsigned n;
1622
1623         if (!d->smart_data_valid) {
1624                 errno = ENOENT;
1625                 return -1;
1626         }
1627
1628         for (n = 0, p = d->smart_data + 2; n < 30; n++, p+=12) {
1629                 SkSmartAttributeParsedData a;
1630                 const SkSmartAttributeInfo *i;
1631                 char *an = NULL;
1632
1633                 if (p[0] == 0)
1634                         continue;
1635
1636                 memset(&a, 0, sizeof(a));
1637                 a.id = p[0];
1638                 a.current_value = p[3];
1639                 a.current_value_valid = p[3] >= 1 && p[3] <= 0xFD;
1640                 a.worst_value = p[4];
1641                 a.worst_value_valid = p[4] >= 1 && p[4] <= 0xFD;
1642
1643                 a.flags = ((uint16_t) p[2] << 8) | p[1];
1644                 a.prefailure = !!(p[1] & 1);
1645                 a.online = !!(p[1] & 2);
1646
1647                 memcpy(a.raw, p+5, 6);
1648
1649                 if ((i = lookup_attribute(d, p[0]))) {
1650                         a.name = _P(i->name);
1651                         a.pretty_unit = i->unit;
1652                 } else {
1653                         if (asprintf(&an, "attribute-%u", a.id) < 0) {
1654                                 errno = ENOMEM;
1655                                 return -1;
1656                         }
1657
1658                         a.name = an;
1659                         a.pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1660                 }
1661
1662                 make_pretty(&a);
1663
1664                 find_threshold(d, &a);
1665
1666                 /* Handle a few fields specially */
1667                 if ((!strcmp(a.name, "reallocated-sector-count") ||
1668                      !strcmp(a.name, "current-pending-sector")) &&
1669                     a.pretty_unit == SK_SMART_ATTRIBUTE_UNIT_SECTORS &&
1670                     a.pretty_value > 0) {
1671                         a.good = FALSE;
1672                         a.good_valid = TRUE;
1673                 }
1674
1675                 cb(d, &a, userdata);
1676                 free(an);
1677         }
1678
1679         return 0;
1680 }
1681
1682 static const char *yes_no(SkBool b) {
1683         return  b ? "yes" : "no";
1684 }
1685
1686 const char* sk_smart_attribute_unit_to_string(SkSmartAttributeUnit unit) {
1687
1688         /* %STRINGPOOLSTART% */
1689         const char * const map[] = {
1690                 [SK_SMART_ATTRIBUTE_UNIT_UNKNOWN] = NULL,
1691                 [SK_SMART_ATTRIBUTE_UNIT_NONE] = "",
1692                 [SK_SMART_ATTRIBUTE_UNIT_MSECONDS] = "ms",
1693                 [SK_SMART_ATTRIBUTE_UNIT_SECTORS] = "sectors",
1694                 [SK_SMART_ATTRIBUTE_UNIT_MKELVIN] = "mK"
1695         };
1696         /* %STRINGPOOLSTOP% */
1697
1698         if (unit >= _SK_SMART_ATTRIBUTE_UNIT_MAX)
1699                 return NULL;
1700
1701         return _P(map[unit]);
1702 }
1703
1704 struct attr_helper {
1705         uint64_t *value;
1706         SkBool found;
1707 };
1708
1709 static void temperature_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1710
1711         if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MKELVIN)
1712                 return;
1713
1714         if (!strcmp(a->name, "temperature-centi-celsius") ||
1715             !strcmp(a->name, "temperature-celsius") ||
1716             !strcmp(a->name, "temperature-celsius-2") ||
1717             !strcmp(a->name, "airflow-temperature-celsius")) {
1718
1719                 if (!ah->found || a->pretty_value > *ah->value)
1720                         *ah->value = a->pretty_value;
1721
1722                 ah->found = TRUE;
1723         }
1724 }
1725
1726 int sk_disk_smart_get_temperature(SkDisk *d, uint64_t *kelvin) {
1727         struct attr_helper ah;
1728
1729         assert(d);
1730         assert(kelvin);
1731
1732         ah.found = FALSE;
1733         ah.value = kelvin;
1734
1735         if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) temperature_cb, &ah) < 0)
1736                 return -1;
1737
1738         if (!ah.found) {
1739                 errno = ENOENT;
1740                 return -1;
1741         }
1742
1743         return 0;
1744 }
1745
1746 static void power_on_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1747
1748         if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MSECONDS)
1749                 return;
1750
1751         if (!strcmp(a->name, "power-on-minutes") ||
1752             !strcmp(a->name, "power-on-seconds") ||
1753             !strcmp(a->name, "power-on-half-minutes") ||
1754             !strcmp(a->name, "power-on-hours")) {
1755
1756                 if (!ah->found || a->pretty_value > *ah->value)
1757                         *ah->value = a->pretty_value;
1758
1759                 ah->found = TRUE;
1760         }
1761 }
1762
1763 int sk_disk_smart_get_power_on(SkDisk *d, uint64_t *mseconds) {
1764         struct attr_helper ah;
1765
1766         assert(d);
1767         assert(mseconds);
1768
1769         ah.found = FALSE;
1770         ah.value = mseconds;
1771
1772         if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_on_cb, &ah) < 0)
1773                 return -1;
1774
1775         if (!ah.found) {
1776                 errno = ENOENT;
1777                 return -1;
1778         }
1779
1780         return 0;
1781 }
1782
1783 static void power_cycle_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1784
1785         if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_NONE)
1786                 return;
1787
1788         if (!strcmp(a->name, "power-cycle-count")) {
1789
1790                 if (!ah->found || a->pretty_value > *ah->value)
1791                         *ah->value = a->pretty_value;
1792
1793                 ah->found = TRUE;
1794         }
1795 }
1796
1797 int sk_disk_smart_get_power_cycle(SkDisk *d, uint64_t *count) {
1798         struct attr_helper ah;
1799
1800         assert(d);
1801         assert(count);
1802
1803         ah.found = FALSE;
1804         ah.value = count;
1805
1806         if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_cycle_cb, &ah) < 0)
1807                 return -1;
1808
1809         if (!ah.found) {
1810                 errno = ENOENT;
1811                 return -1;
1812         }
1813
1814         return 0;
1815 }
1816
1817 static void reallocated_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1818
1819         if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_SECTORS)
1820                 return;
1821
1822         if (!strcmp(a->name, "reallocated-sector-count")) {
1823
1824                 if (!ah->found || a->pretty_value > *ah->value)
1825                         *ah->value = a->pretty_value;
1826
1827                 ah->found = TRUE;
1828         }
1829 }
1830
1831 static void pending_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1832
1833         if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_SECTORS)
1834                 return;
1835
1836         if (!strcmp(a->name, "current-pending-sector")) {
1837
1838                 if (!ah->found || a->pretty_value > *ah->value)
1839                         *ah->value = a->pretty_value;
1840
1841                 ah->found = TRUE;
1842         }
1843 }
1844
1845 int sk_disk_smart_get_bad(SkDisk *d, uint64_t *sectors) {
1846         struct attr_helper ah1, ah2;
1847         uint64_t sectors1, sectors2;
1848
1849         assert(d);
1850         assert(sectors);
1851
1852         ah1.found = FALSE;
1853         ah1.value = &sectors1;
1854
1855         if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) reallocated_cb, &ah1) < 0)
1856                 return -1;
1857
1858         ah2.found = FALSE;
1859         ah2.value = &sectors2;
1860
1861         if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) pending_cb, &ah2) < 0)
1862                 return -1;
1863
1864         if (!ah1.found && !ah2.found) {
1865                 errno = ENOENT;
1866                 return -1;
1867         }
1868
1869         if (ah1.found && ah2.found)
1870                 *sectors = sectors1 + sectors2;
1871         else if (ah1.found)
1872                 *sectors = sectors1;
1873         else
1874                 *sectors = sectors2;
1875
1876         return 0;
1877 }
1878
1879 const char* sk_smart_overall_to_string(SkSmartOverall overall) {
1880
1881         /* %STRINGPOOLSTART% */
1882         const char * const map[] = {
1883                 [SK_SMART_OVERALL_GOOD] = "GOOD",
1884                 [SK_SMART_OVERALL_BAD_STATUS] = "BAD_STATUS",
1885                 [SK_SMART_OVERALL_BAD_ATTRIBUTE] = "BAD_ATTRIBUTE",
1886                 [SK_SMART_OVERALL_BAD_SECTOR] = "BAD_SECTOR"
1887         };
1888         /* %STRINGPOOLSTOP% */
1889
1890         if (overall >= _SK_SMART_OVERALL_MAX)
1891                 return NULL;
1892
1893         return _P(map[overall]);
1894 }
1895
1896 static void bad_attribute_cb(SkDisk *d, const SkSmartAttributeParsedData *a, SkBool *good) {
1897         if (a->prefailure && a->good_valid && !a->good)
1898                 *good = FALSE;
1899 }
1900
1901 int sk_disk_smart_get_overall(SkDisk *d, SkSmartOverall *overall) {
1902         SkBool good;
1903         uint64_t sectors;
1904
1905         assert(d);
1906         assert(overall);
1907
1908         if (sk_disk_smart_status(d, &good) < 0)
1909                 return -1;
1910
1911         if (!good) {
1912                 *overall = SK_SMART_OVERALL_BAD_STATUS;
1913                 return 0;
1914         }
1915
1916         if (sk_disk_smart_get_bad(d, &sectors) < 0) {
1917                 if (errno != ENOENT)
1918                         return -1;
1919         } else if (sectors > 0) {
1920                 *overall = SK_SMART_OVERALL_BAD_SECTOR;
1921                 return 0;
1922         }
1923
1924         good = TRUE;
1925         if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) bad_attribute_cb, &good) < 0)
1926                 return -1;
1927
1928         if (!good) {
1929                 *overall = SK_SMART_OVERALL_BAD_ATTRIBUTE;
1930                 return 0;
1931         }
1932
1933         *overall = SK_SMART_OVERALL_GOOD;
1934         return 0;
1935 }
1936
1937 static char* print_name(char *s, size_t len, uint8_t id, const char *k) {
1938
1939         if (k)
1940                 strncpy(s, k, len);
1941         else
1942                 snprintf(s, len, "%u", id);
1943
1944         s[len-1] = 0;
1945
1946         return s;
1947 }
1948
1949 static char *print_value(char *s, size_t len, uint64_t pretty_value, SkSmartAttributeUnit pretty_unit) {
1950
1951         switch (pretty_unit) {
1952                 case SK_SMART_ATTRIBUTE_UNIT_MSECONDS:
1953
1954                         if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*365LLU)
1955                                 snprintf(s, len, "%0.1f years", ((double) pretty_value)/(1000.0*60*60*24*365));
1956                         else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*30LLU)
1957                                 snprintf(s, len, "%0.1f months", ((double) pretty_value)/(1000.0*60*60*24*30));
1958                         else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU)
1959                                 snprintf(s, len, "%0.1f days", ((double) pretty_value)/(1000.0*60*60*24));
1960                         else if (pretty_value >= 1000LLU*60LLU*60LLU)
1961                                 snprintf(s, len, "%0.1f h", ((double) pretty_value)/(1000.0*60*60));
1962                         else if (pretty_value >= 1000LLU*60LLU)
1963                                 snprintf(s, len, "%0.1f min", ((double) pretty_value)/(1000.0*60));
1964                         else if (pretty_value >= 1000LLU)
1965                                 snprintf(s, len, "%0.1f s", ((double) pretty_value)/(1000.0));
1966                         else
1967                                 snprintf(s, len, "%llu ms", (unsigned long long) pretty_value);
1968
1969                         break;
1970
1971                 case SK_SMART_ATTRIBUTE_UNIT_MKELVIN:
1972                         snprintf(s, len, "%0.1f C", ((double) pretty_value - 273150) / 1000);
1973                         break;
1974
1975                 case SK_SMART_ATTRIBUTE_UNIT_SECTORS:
1976                         snprintf(s, len, "%llu sectors", (unsigned long long) pretty_value);
1977                         break;
1978
1979                 case SK_SMART_ATTRIBUTE_UNIT_NONE:
1980                         snprintf(s, len, "%llu", (unsigned long long) pretty_value);
1981                         break;
1982
1983                 case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN:
1984                         snprintf(s, len, "n/a");
1985                         break;
1986
1987                 case _SK_SMART_ATTRIBUTE_UNIT_MAX:
1988                         assert(FALSE);
1989         }
1990
1991         s[len-1] = 0;
1992
1993         return s;
1994 }
1995
1996 #define HIGHLIGHT "\x1B[1m"
1997 #define ENDHIGHLIGHT "\x1B[0m"
1998
1999 static void disk_dump_attributes(SkDisk *d, const SkSmartAttributeParsedData *a, void* userdata) {
2000         char name[32];
2001         char pretty[32];
2002         char tt[32], tw[32], tc[32];
2003         SkBool highlight;
2004
2005         snprintf(tt, sizeof(tt), "%3u", a->threshold);
2006         tt[sizeof(tt)-1] = 0;
2007         snprintf(tw, sizeof(tw), "%3u", a->worst_value);
2008         tw[sizeof(tw)-1] = 0;
2009         snprintf(tc, sizeof(tc), "%3u", a->current_value);
2010         tc[sizeof(tc)-1] = 0;
2011
2012         highlight = a->good_valid && !a->good && isatty(1);
2013
2014         if (highlight)
2015                 fprintf(stderr, HIGHLIGHT);
2016
2017         printf("%3u %-27s %-3s   %-3s   %-3s   %-11s 0x%02x%02x%02x%02x%02x%02x %-7s %-7s %-3s\n",
2018                a->id,
2019                print_name(name, sizeof(name), a->id, a->name),
2020                a->current_value_valid ? tc : "n/a",
2021                a->worst_value_valid ? tw : "n/a",
2022                a->threshold_valid ? tt : "n/a",
2023                print_value(pretty, sizeof(pretty), a->pretty_value, a->pretty_unit),
2024                a->raw[0], a->raw[1], a->raw[2], a->raw[3], a->raw[4], a->raw[5],
2025                a->prefailure ? "prefail" : "old-age",
2026                a->online ? "online" : "offline",
2027                a->good_valid ? yes_no(a->good) : "n/a");
2028
2029         if (highlight)
2030                 fprintf(stderr, ENDHIGHLIGHT);
2031 }
2032
2033 int sk_disk_dump(SkDisk *d) {
2034         int ret;
2035         SkBool awake = FALSE;
2036         uint64_t size;
2037
2038         assert(d);
2039
2040         printf("Device: %s\n"
2041                "Type: %s\n",
2042                d->name ? d->name : "n/a",
2043                disk_type_to_string(d->type));
2044
2045         ret = sk_disk_get_size(d, &size);
2046         if (ret >= 0)
2047                 printf("Size: %lu MiB\n", (unsigned long) (d->size/1024/1024));
2048         else
2049                 printf("Size: %s\n", strerror(errno));
2050
2051         if (d->identify_valid) {
2052                 const SkIdentifyParsedData *ipd;
2053                 SkSmartQuirk quirk = 0;
2054                 unsigned i;
2055
2056                 if ((ret = sk_disk_identify_parse(d, &ipd)) < 0)
2057                         return ret;
2058
2059                 printf("Model: [%s]\n"
2060                        "Serial: [%s]\n"
2061                        "Firmware: [%s]\n"
2062                        "SMART Available: %s\n",
2063                        ipd->model,
2064                        ipd->serial,
2065                        ipd->firmware,
2066                        yes_no(disk_smart_is_available(d)));
2067
2068                 if ((ret = lookup_quirks(ipd->model, ipd->firmware, &quirk)))
2069                         return ret;
2070
2071                 printf("Quirks:");
2072
2073                 for (i = 0; quirk_name[i]; i++)
2074                         if (quirk & (1<<i))
2075                                 printf(" %s", _P(quirk_name[i]));
2076
2077                 printf("\n");
2078
2079         }
2080
2081         ret = sk_disk_check_sleep_mode(d, &awake);
2082         printf("Awake: %s\n",
2083                ret >= 0 ? yes_no(awake) : strerror(errno));
2084
2085         if (disk_smart_is_available(d)) {
2086                 SkSmartOverall overall;
2087                 const SkSmartParsedData *spd;
2088                 SkBool good;
2089                 char pretty[32];
2090                 uint64_t value, power_on;
2091
2092                 ret = sk_disk_smart_status(d, &good);
2093                 printf("SMART Disk Health Good: %s\n",
2094                        ret >= 0 ? yes_no(good) : strerror(errno));
2095
2096                 if ((ret = sk_disk_smart_read_data(d)) < 0)
2097                         return ret;
2098
2099                 if ((ret = sk_disk_smart_parse(d, &spd)) < 0)
2100                         return ret;
2101
2102                 printf("Off-line Data Collection Status: [%s]\n"
2103                        "Total Time To Complete Off-Line Data Collection: %u s\n"
2104                        "Self-Test Execution Status: [%s]\n"
2105                        "Percent Self-Test Remaining: %u%%\n"
2106                        "Conveyance Self-Test Available: %s\n"
2107                        "Short/Extended Self-Test Available: %s\n"
2108                        "Start Self-Test Available: %s\n"
2109                        "Abort Self-Test Available: %s\n"
2110                        "Short Self-Test Polling Time: %u min\n"
2111                        "Extended Self-Test Polling Time: %u min\n"
2112                        "Conveyance Self-Test Polling Time: %u min\n",
2113                        sk_smart_offline_data_collection_status_to_string(spd->offline_data_collection_status),
2114                        spd->total_offline_data_collection_seconds,
2115                        sk_smart_self_test_execution_status_to_string(spd->self_test_execution_status),
2116                        spd->self_test_execution_percent_remaining,
2117                        yes_no(spd->conveyance_test_available),
2118                        yes_no(spd->short_and_extended_test_available),
2119                        yes_no(spd->start_test_available),
2120                        yes_no(spd->abort_test_available),
2121                        spd->short_test_polling_minutes,
2122                        spd->extended_test_polling_minutes,
2123                        spd->conveyance_test_polling_minutes);
2124
2125                 if (sk_disk_smart_get_bad(d, &value) < 0)
2126                         printf("Bad Sectors: %s\n", strerror(errno));
2127                 else
2128                         printf("%sBad Sectors: %s%s\n",
2129                                value > 0 ? HIGHLIGHT : "",
2130                                print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_SECTORS),
2131                                value > 0 ? ENDHIGHLIGHT : "");
2132
2133                 if (sk_disk_smart_get_power_on(d, &power_on) < 0) {
2134                         printf("Powered On: %s\n", strerror(errno));
2135                         power_on = 0;
2136                 } else
2137                         printf("Powered On: %s\n", print_value(pretty, sizeof(pretty), power_on, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2138
2139                 if (sk_disk_smart_get_power_cycle(d, &value) < 0)
2140                         printf("Power Cycles: %s\n", strerror(errno));
2141                 else {
2142                         printf("Power Cycles: %llu\n", (unsigned long long) value);
2143
2144                         if (value > 0 && power_on > 0)
2145                                 printf("Average Powered On Per Power Cycle: %s\n", print_value(pretty, sizeof(pretty), power_on/value, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2146                 }
2147
2148                 if (sk_disk_smart_get_temperature(d, &value) < 0)
2149                         printf("Temperature: %s\n", strerror(errno));
2150                 else
2151                         printf("Temperature: %s\n", print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_MKELVIN));
2152
2153                 if (sk_disk_smart_get_overall(d, &overall) < 0)
2154                         printf("Overall Status: %s\n", strerror(errno));
2155                 else
2156                         printf("%sOverall Status: %s%s\n",
2157                                overall != SK_SMART_OVERALL_GOOD ? HIGHLIGHT : "",
2158                                sk_smart_overall_to_string(overall),
2159                                overall != SK_SMART_OVERALL_GOOD ? ENDHIGHLIGHT : "");
2160
2161                 printf("%3s %-27s %5s %5s %5s %-11s %-14s %-7s %-7s %-3s\n",
2162                        "ID#",
2163                        "Name",
2164                        "Value",
2165                        "Worst",
2166                        "Thres",
2167                        "Pretty",
2168                        "Raw",
2169                        "Type",
2170                        "Updates",
2171                        "Good");
2172
2173                 if ((ret = sk_disk_smart_parse_attributes(d, disk_dump_attributes, NULL)) < 0)
2174                         return ret;
2175         } else
2176                 printf("ATA SMART not supported.\n");
2177
2178         return 0;
2179 }
2180
2181 int sk_disk_get_size(SkDisk *d, uint64_t *bytes) {
2182         assert(d);
2183         assert(bytes);
2184
2185         if (d->size == (uint64_t) -1) {
2186                 errno = ENODATA;
2187                 return -1;
2188         }
2189
2190         *bytes = d->size;
2191         return 0;
2192 }
2193
2194 static int disk_find_type(SkDisk *d, dev_t devnum) {
2195         struct udev *udev;
2196         struct udev_device *dev = NULL, *usb;
2197         int r = -1;
2198
2199         assert(d);
2200
2201         if (!(udev = udev_new())) {
2202                 errno = ENXIO;
2203                 goto finish;
2204         }
2205
2206         if (!(dev = udev_device_new_from_devnum(udev, 'b', devnum))) {
2207                 errno = ENODEV;
2208                 goto finish;
2209         }
2210
2211         if ((usb = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"))) {
2212                 const char *product, *vendor;
2213                 uint32_t pid, vid;
2214
2215                 if (!(product = udev_device_get_sysattr_value(usb, "idProduct")) ||
2216                     sscanf(product, "%04x", &pid) != 1) {
2217                         errno = ENODEV;
2218                         goto finish;
2219                 }
2220
2221                 if (!(vendor = udev_device_get_sysattr_value(usb, "idVendor")) ||
2222                     sscanf(vendor, "%04x", &vid) != 1) {
2223                         errno = ENODEV;
2224                         goto finish;
2225                 }
2226
2227                 if ((vid == 0x0c0b && pid == 0xb159) ||
2228                     (vid == 0x04fc && pid == 0x0c25))
2229                         d->type = SK_DISK_TYPE_SUNPLUS;
2230                 else if ((vid == 0x152d && pid == 0x2329) ||
2231                     (vid == 0x152d && pid == 0x2336) ||
2232                     (vid == 0x152d && pid == 0x2338) ||
2233                     (vid == 0x152d && pid == 0x2339))
2234                         d->type = SK_DISK_TYPE_JMICRON;
2235                 else
2236                         d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_12;
2237
2238         } else if (udev_device_get_parent_with_subsystem_devtype(dev, "ide", NULL))
2239                 d->type = SK_DISK_TYPE_ATA;
2240         else if (udev_device_get_parent_with_subsystem_devtype(dev, "scsi", NULL))
2241                 d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_16;
2242         else
2243                 d->type = SK_DISK_TYPE_UNKNOWN;
2244
2245         r = 0;
2246
2247 finish:
2248         if (dev)
2249                 udev_device_unref(dev);
2250
2251         if (udev)
2252                 udev_unref(udev);
2253
2254         return r;
2255 }
2256
2257 int sk_disk_open(const char *name, SkDisk **_d) {
2258         SkDisk *d;
2259         int ret = -1;
2260         struct stat st;
2261
2262         assert(_d);
2263
2264         if (!(d = calloc(1, sizeof(SkDisk)))) {
2265                 errno = ENOMEM;
2266                 goto fail;
2267         }
2268
2269         if (!name) {
2270                 d->fd = -1;
2271                 d->type = SK_DISK_TYPE_BLOB;
2272                 d->size = (uint64_t) -1;
2273         } else {
2274
2275                 if (!(d->name = strdup(name))) {
2276                         errno = ENOMEM;
2277                         goto fail;
2278                 }
2279
2280                 if ((d->fd = open(name,
2281                                   O_RDONLY|O_NOCTTY|O_NONBLOCK
2282 #ifdef O_CLOEXEC
2283                                   |O_CLOEXEC
2284 #endif
2285
2286                      )) < 0) {
2287                         ret = d->fd;
2288                         goto fail;
2289                 }
2290
2291                 if ((ret = fstat(d->fd, &st)) < 0)
2292                         goto fail;
2293
2294                 if (!S_ISBLK(st.st_mode)) {
2295                         errno = ENODEV;
2296                         ret = -1;
2297                         goto fail;
2298                 }
2299
2300                 /* So, it's a block device. Let's make sure the ioctls work */
2301                 if ((ret = ioctl(d->fd, BLKGETSIZE64, &d->size)) < 0)
2302                         goto fail;
2303
2304                 if (d->size <= 0 || d->size == (uint64_t) -1) {
2305                         errno = EIO;
2306                         ret = -1;
2307                         goto fail;
2308                 }
2309
2310                 /* OK, it's a real block device with a size. Now let's find the suitable API */
2311                 if ((ret = disk_find_type(d, st.st_rdev)) < 0)
2312                         goto fail;
2313
2314                 if (d->type == SK_DISK_TYPE_UNKNOWN) {
2315                         /* We have no clue, so let's autotest for a working API */
2316                         for (d->type = 0; d->type < _SK_DISK_TYPE_TEST_MAX; d->type++)
2317                                 if (disk_identify_device(d) >= 0)
2318                                         break;
2319                         if (d->type >= _SK_DISK_TYPE_TEST_MAX)
2320                                 d->type = SK_DISK_TYPE_UNKNOWN;
2321                 } else
2322                         disk_identify_device(d);
2323
2324                 /* Check if driver can do SMART, and enable if necessary */
2325                 if (disk_smart_is_available(d)) {
2326
2327                         if (!disk_smart_is_enabled(d)) {
2328                                 if ((ret = disk_smart_enable(d, TRUE)) < 0)
2329                                         goto fail;
2330
2331                                 if ((ret = disk_identify_device(d)) < 0)
2332                                         goto fail;
2333
2334                                 if (!disk_smart_is_enabled(d)) {
2335                                         errno = EIO;
2336                                         ret = -1;
2337                                         goto fail;
2338                                 }
2339                         }
2340
2341                         disk_smart_read_thresholds(d);
2342                 }
2343         }
2344
2345         *_d = d;
2346
2347         return 0;
2348
2349 fail:
2350
2351         if (d)
2352                 sk_disk_free(d);
2353
2354         return ret;
2355 }
2356
2357 void sk_disk_free(SkDisk *d) {
2358         assert(d);
2359
2360         if (d->fd >= 0)
2361                 close(d->fd);
2362
2363         free(d->name);
2364         free(d->blob);
2365         free(d);
2366 }
2367
2368 int sk_disk_get_blob(SkDisk *d, const void **blob, size_t *rsize) {
2369         size_t size;
2370         SkBool good, have_good = FALSE;
2371         uint32_t *p;
2372
2373         assert(d);
2374         assert(blob);
2375         assert(rsize);
2376
2377         size =
2378                 (d->identify_valid ? 8 + sizeof(d->identify) : 0) +
2379                 (d->smart_data_valid ? 8 + sizeof(d->smart_data) : 0) +
2380                 (d->smart_thresholds ? 8 + sizeof(d->smart_thresholds) : 0);
2381
2382         if (sk_disk_smart_status(d, &good) >= 0) {
2383                 size += 12;
2384                 have_good = TRUE;
2385         }
2386
2387         if (size <= 0) {
2388                 errno = ENODATA;
2389                 return -1;
2390         }
2391
2392         free(d->blob);
2393         if (!(d->blob = malloc(size))) {
2394                 errno = ENOMEM;
2395                 return -1;
2396         }
2397
2398         p = d->blob;
2399
2400         /* These memory accesses are only OK as long as all our
2401          * objects are sensibly aligned, which they are... */
2402
2403         if (d->identify_valid) {
2404                 p[0] = SK_BLOB_TAG_IDENTIFY;
2405                 p[1] = htonl(sizeof(d->identify));
2406                 p += 2;
2407
2408                 memcpy(p, d->identify, sizeof(d->identify));
2409                 p = (uint32_t*) ((uint8_t*) p + sizeof(d->identify));
2410         }
2411
2412         if (have_good) {
2413                 p[0] = SK_BLOB_TAG_SMART_STATUS;
2414                 p[1] = htonl(4);
2415                 p[2] = htonl(!!good);
2416                 p += 3;
2417         }
2418
2419         if (d->smart_data_valid) {
2420                 p[0] = SK_BLOB_TAG_SMART_DATA;
2421                 p[1] = htonl(sizeof(d->smart_data));
2422                 p += 2;
2423
2424                 memcpy(p, d->smart_data, sizeof(d->smart_data));
2425                 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_data));
2426         }
2427
2428         if (d->smart_thresholds_valid) {
2429                 p[0] = SK_BLOB_TAG_SMART_THRESHOLDS;
2430                 p[1] = htonl(sizeof(d->smart_thresholds));
2431                 p += 2;
2432
2433                 memcpy(p, d->smart_thresholds, sizeof(d->smart_thresholds));
2434                 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_thresholds));
2435         }
2436
2437         assert((size_t) ((uint8_t*) p - (uint8_t*) d->blob) == size);
2438
2439         *blob = d->blob;
2440         *rsize = size;
2441
2442         return 0;
2443 }
2444
2445 int sk_disk_set_blob(SkDisk *d, const void *blob, size_t size) {
2446         const uint32_t *p;
2447         size_t left;
2448         SkBool idv = FALSE, sdv = FALSE, stv = FALSE, bssv = FALSE;
2449
2450         assert(d);
2451         assert(blob);
2452
2453         if (d->type != SK_DISK_TYPE_BLOB) {
2454                 errno = ENODEV;
2455                 return -1;
2456         }
2457
2458         if (size <= 0) {
2459                 errno = EINVAL;
2460                 return -1;
2461         }
2462
2463         /* First run, verify if everything makes sense */
2464         p = blob;
2465         left = size;
2466         while (left > 0) {
2467                 uint32_t tag, tsize;
2468
2469                 if (left < 8) {
2470                         errno = EINVAL;
2471                         return -1;
2472                 }
2473
2474                 memcpy(&tag, p, 4);
2475                 memcpy(&tsize, p+1, 4);
2476                 p += 2;
2477                 left -= 8;
2478
2479                 if (left < ntohl(tsize)) {
2480                         errno = EINVAL;
2481                         return -1;
2482                 }
2483
2484                 switch (tag) {
2485
2486                         case SK_BLOB_TAG_IDENTIFY:
2487                                 if (ntohl(tsize) != sizeof(d->identify) || idv) {
2488                                         errno = EINVAL;
2489                                         return -1;
2490                                 }
2491                                 idv = TRUE;
2492                                 break;
2493
2494                         case SK_BLOB_TAG_SMART_STATUS:
2495                                 if (ntohl(tsize) != 4 || bssv) {
2496                                         errno = EINVAL;
2497                                         return -1;
2498                                 }
2499                                 bssv = TRUE;
2500                                 break;
2501
2502                         case SK_BLOB_TAG_SMART_DATA:
2503                                 if (ntohl(tsize) != sizeof(d->smart_data) || sdv) {
2504                                         errno = EINVAL;
2505                                         return -1;
2506                                 }
2507                                 sdv = TRUE;
2508                                 break;
2509
2510                         case SK_BLOB_TAG_SMART_THRESHOLDS:
2511                                 if (ntohl(tsize) != sizeof(d->smart_thresholds) || stv) {
2512                                         errno = EINVAL;
2513                                         return -1;
2514                                 }
2515                                 stv = TRUE;
2516                                 break;
2517                 }
2518
2519                 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
2520                 left -= ntohl(tsize);
2521         }
2522
2523         if (!idv) {
2524                 errno = -ENODATA;
2525                 return -1;
2526         }
2527
2528         d->identify_valid = idv;
2529         d->smart_data_valid = sdv;
2530         d->smart_thresholds_valid = stv;
2531         d->blob_smart_status_valid = bssv;
2532
2533         /* Second run, actually copy things in */
2534         p = blob;
2535         left = size;
2536         while (left > 0) {
2537                 uint32_t tag, tsize;
2538
2539                 assert(left >= 8);
2540                 memcpy(&tag, p, 4);
2541                 memcpy(&tsize, p+1, 4);
2542                 p += 2;
2543                 left -= 8;
2544
2545                 assert(left >= ntohl(tsize));
2546
2547                 switch (tag) {
2548
2549                         case SK_BLOB_TAG_IDENTIFY:
2550                                 assert(ntohl(tsize) == sizeof(d->identify));
2551                                 memcpy(d->identify, p, sizeof(d->identify));
2552                                 break;
2553
2554                         case SK_BLOB_TAG_SMART_STATUS: {
2555                                 uint32_t ok;
2556                                 assert(ntohl(tsize) == 4);
2557                                 memcpy(&ok, p, 4);
2558                                 d->blob_smart_status = !!ok;
2559                                 break;
2560                         }
2561
2562                         case SK_BLOB_TAG_SMART_DATA:
2563                                 assert(ntohl(tsize) == sizeof(d->smart_data));
2564                                 memcpy(d->smart_data, p, sizeof(d->smart_data));
2565                                 break;
2566
2567                         case SK_BLOB_TAG_SMART_THRESHOLDS:
2568                                 assert(ntohl(tsize) == sizeof(d->smart_thresholds));
2569                                 memcpy(d->smart_thresholds, p, sizeof(d->smart_thresholds));
2570                                 break;
2571                 }
2572
2573                 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
2574                 left -= ntohl(tsize);
2575         }
2576
2577         return 0;
2578 }