Ubuntu

Merge lp:~psusi/ubuntu/raring/libatasmart/fix-status into lp:ubuntu/raring/libatasmart

Proposed by Phillip Susi on 2013-03-18
Status: Merged
Merge reported by: Martin Pitt
Merged at revision: not available
Proposed branch: lp:~psusi/ubuntu/raring/libatasmart/fix-status
Merge into: lp:ubuntu/raring/libatasmart
Diff against target: 3141 lines (+3094/-2) 6 files modified
To merge this branch: bzr merge lp:~psusi/ubuntu/raring/libatasmart/fix-status
Reviewer Review Type Date Requested Status
Martin Pitt 2013-03-18 Approve on 2013-03-25
Marc Deslauriers Needs Fixing on 2013-03-19
Review via email: mp+153954@code.launchpad.net
To post a comment you must log in.
Marc Deslauriers (mdeslaur) wrote :

Could you please update the patch tagging to add "Bug" and "Forwarded" tags so we can trace where this patch has been submitted upstream?

Thanks.

review: Needs Fixing
Martin Pitt (pitti) wrote :

Thanks for this! I just noticed the bug before, and applied the patch you sent upstream to Debian. I'll sync the Debian package once it's imported, so this MP is obsolete. Thanks!

review: Approve

Preview Diff

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

Subscribers

People subscribed via source and target branches

to all changes: