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