~ubuntu-branches/ubuntu/saucy/libatasmart/saucy

« back to all changes in this revision

Viewing changes to .pc/0003-Add-S.M.A.R.T-attributes-for-Samsung-SSD.patch/atasmart.c

  • Committer: Package Import Robot
  • Author(s): Michael Terry, Tobias Wolf
  • Date: 2011-12-01 13:44:47 UTC
  • Revision ID: package-import@ubuntu.com-20111201134447-l0uqvwauhl302p9p
Tags: 0.18-1ubuntu1
[ Tobias Wolf ]
* debian/patches/0003-Add-S.M.A.R.T-attributes-for-Samsung-SSD.patch:
  - Add some more SMART attribute names to make output easier to
    understand.  LP: #885869

Show diffs side-by-side

added added

removed removed

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