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