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