summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2009-04-02 23:44:25 +0200
committerLennart Poettering <lennart@poettering.net>2009-04-02 23:44:25 +0200
commit4e6c8eb3eb054030a3a4149043aed1cb5b1aab4d (patch)
tree7a1bf41266c373d57b1f2a1da3be193d5de32345
parent3890fef931af32b2006bdd2660c23cc9d9c6b1b1 (diff)
add functions to save/load smart data blobs directly
-rw-r--r--atasmart.c281
-rw-r--r--atasmart.h7
2 files changed, 206 insertions, 82 deletions
diff --git a/atasmart.c b/atasmart.c
index 99c1453..585b0ff 100644
--- a/atasmart.c
+++ b/atasmart.c
@@ -63,6 +63,7 @@ typedef enum SkDiskType {
SK_DISK_TYPE_ATA_PASSTHROUGH_16, /* ATA passthrough over SCSI transport */
SK_DISK_TYPE_ATA,
SK_DISK_TYPE_UNKNOWN,
+ SK_DISK_TYPE_BLOB,
_SK_DISK_TYPE_MAX
} SkDiskType;
@@ -73,9 +74,11 @@ struct SkDisk {
uint64_t size;
- uint8_t identify[512];
- uint8_t smart_data[512];
- uint8_t smart_threshold_data[512];
+ struct {
+ uint8_t identify[512];
+ uint8_t smart_data[512];
+ uint8_t smart_threshold_data[512];
+ } blob;
SkBool identify_data_valid:1;
SkBool smart_data_valid:1;
@@ -110,6 +113,8 @@ static const char *disk_type_to_string(SkDiskType type) {
[SK_DISK_TYPE_ATA_PASSTHROUGH_16] = "16 Byte SCSI ATA SAT Passthru",
[SK_DISK_TYPE_ATA_PASSTHROUGH_12] = "12 Byte SCSI ATA SAT Passthru",
[SK_DISK_TYPE_ATA] = "Native ATA",
+ [SK_DISK_TYPE_BLOB] = "Blob"
+
};
/* %STRINGPOOLSTOP% */
@@ -120,34 +125,34 @@ static const char *disk_type_to_string(SkDiskType type) {
}
static SkBool disk_smart_is_available(SkDisk *d) {
- return d->identify_data_valid && !!(d->identify[164] & 1);
+ return d->identify_data_valid && !!(d->blob.identify[164] & 1);
}
static SkBool disk_smart_is_enabled(SkDisk *d) {
- return d->identify_data_valid && !!(d->identify[170] & 1);
+ return d->identify_data_valid && !!(d->blob.identify[170] & 1);
}
static SkBool disk_smart_is_conveyance_test_available(SkDisk *d) {
assert(d->smart_data_valid);
- return !!(d->smart_data[367] & 32);
+ return !!(d->blob.smart_data[367] & 32);
}
static SkBool disk_smart_is_short_and_extended_test_available(SkDisk *d) {
assert(d->smart_data_valid);
- return !!(d->smart_data[367] & 16);
+ return !!(d->blob.smart_data[367] & 16);
}
static SkBool disk_smart_is_start_test_available(SkDisk *d) {
assert(d->smart_data_valid);
- return !!(d->smart_data[367] & 1);
+ return !!(d->blob.smart_data[367] & 1);
}
static SkBool disk_smart_is_abort_test_available(SkDisk *d) {
assert(d->smart_data_valid);
- return !!(d->smart_data[367] & 41);
+ return !!(d->blob.smart_data[367] & 41);
}
static int disk_ata_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
@@ -424,11 +429,14 @@ static int disk_identify_device(SkDisk *d) {
int ret;
size_t len = 512;
+ if (d->type == SK_DISK_TYPE_BLOB)
+ return 0;
+
memset(cmd, 0, sizeof(cmd));
cmd[1] = htons(1);
- if ((ret = disk_command(d, SK_ATA_COMMAND_IDENTIFY_DEVICE, SK_DIRECTION_IN, cmd, d->identify, &len)) < 0)
+ if ((ret = disk_command(d, SK_ATA_COMMAND_IDENTIFY_DEVICE, SK_DIRECTION_IN, cmd, d->blob.identify, &len)) < 0)
return ret;
if (len != 512) {
@@ -450,6 +458,11 @@ int sk_disk_check_sleep_mode(SkDisk *d, SkBool *awake) {
return -1;
}
+ if (d->type == SK_DISK_TYPE_BLOB) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
memset(cmd, 0, sizeof(cmd));
if ((ret = disk_command(d, SK_ATA_COMMAND_CHECK_POWER_MODE, SK_DIRECTION_NONE, cmd, NULL, 0)) < 0)
@@ -473,6 +486,11 @@ static int disk_smart_enable(SkDisk *d, SkBool b) {
return -1;
}
+ if (d->type == SK_DISK_TYPE_BLOB) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
memset(cmd, 0, sizeof(cmd));
cmd[0] = htons(b ? SK_SMART_COMMAND_ENABLE_OPERATIONS : SK_SMART_COMMAND_DISABLE_OPERATIONS);
@@ -493,6 +511,9 @@ int sk_disk_smart_read_data(SkDisk *d) {
return -1;
}
+ if (d->type == SK_DISK_TYPE_BLOB)
+ return 0;
+
memset(cmd, 0, sizeof(cmd));
cmd[0] = htons(SK_SMART_COMMAND_READ_DATA);
@@ -501,7 +522,7 @@ int sk_disk_smart_read_data(SkDisk *d) {
cmd[3] = htons(0x00C2U);
cmd[4] = htons(0x4F00U);
- if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_data, &len)) < 0)
+ if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->blob.smart_data, &len)) < 0)
return ret;
d->smart_data_valid = TRUE;
@@ -519,6 +540,9 @@ static int disk_smart_read_thresholds(SkDisk *d) {
return -1;
}
+ if (d->type == SK_DISK_TYPE_BLOB)
+ return 0;
+
memset(cmd, 0, sizeof(cmd));
cmd[0] = htons(SK_SMART_COMMAND_READ_THRESHOLDS);
@@ -527,7 +551,7 @@ static int disk_smart_read_thresholds(SkDisk *d) {
cmd[3] = htons(0x00C2U);
cmd[4] = htons(0x4F00U);
- if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_threshold_data, &len)) < 0)
+ if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->blob.smart_threshold_data, &len)) < 0)
return ret;
d->smart_threshold_data_valid = TRUE;
@@ -544,6 +568,11 @@ int sk_disk_smart_status(SkDisk *d, SkBool *good) {
return -1;
}
+ if (d->type == SK_DISK_TYPE_BLOB) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
memset(cmd, 0, sizeof(cmd));
cmd[0] = htons(SK_SMART_COMMAND_RETURN_STATUS);
@@ -577,6 +606,11 @@ int sk_disk_smart_self_test(SkDisk *d, SkSmartSelfTest test) {
return -1;
}
+ if (d->type == SK_DISK_TYPE_BLOB) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
if (!d->smart_data_valid)
if ((ret = sk_disk_smart_read_data(d)) < 0)
return -1;
@@ -668,15 +702,17 @@ static void read_string(char *d, uint8_t *s, size_t len) {
}
int sk_disk_identify_parse(SkDisk *d, const SkIdentifyParsedData **ipd) {
+ assert(d);
+ assert(ipd);
if (!d->identify_data_valid) {
errno = ENOENT;
return -1;
}
- read_string(d->identify_parsed_data.serial, d->identify+20, 20);
- read_string(d->identify_parsed_data.firmware, d->identify+46, 8);
- read_string(d->identify_parsed_data.model, d->identify+54, 40);
+ read_string(d->identify_parsed_data.serial, d->blob.identify+20, 20);
+ read_string(d->identify_parsed_data.firmware, d->blob.identify+46, 8);
+ read_string(d->identify_parsed_data.model, d->blob.identify+54, 40);
*ipd = &d->identify_parsed_data;
@@ -684,6 +720,8 @@ int sk_disk_identify_parse(SkDisk *d, const SkIdentifyParsedData **ipd) {
}
int sk_disk_smart_is_available(SkDisk *d, SkBool *b) {
+ assert(d);
+ assert(b);
if (!d->identify_data_valid) {
errno = ENOTSUP;
@@ -695,6 +733,8 @@ int sk_disk_smart_is_available(SkDisk *d, SkBool *b) {
}
int sk_disk_identify_is_available(SkDisk *d, SkBool *b) {
+ assert(d);
+ assert(b);
*b = d->identify_data_valid;
return 0;
@@ -760,6 +800,7 @@ const char* sk_smart_self_test_to_string(SkSmartSelfTest test) {
}
SkBool sk_smart_self_test_available(const SkSmartParsedData *d, SkSmartSelfTest test) {
+ assert(d);
if (!d->start_test_available)
return FALSE;
@@ -778,6 +819,7 @@ SkBool sk_smart_self_test_available(const SkSmartParsedData *d, SkSmartSelfTest
}
unsigned sk_smart_self_test_polling_minutes(const SkSmartParsedData *d, SkSmartSelfTest test) {
+ assert(d);
if (!sk_smart_self_test_available(d, test))
return 0;
@@ -1219,7 +1261,7 @@ int sk_disk_smart_parse(SkDisk *d, const SkSmartParsedData **spd) {
return -1;
}
- switch (d->smart_data[362]) {
+ switch (d->blob.smart_data[362]) {
case 0x00:
case 0x80:
d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER;
@@ -1254,19 +1296,19 @@ int sk_disk_smart_parse(SkDisk *d, const SkSmartParsedData **spd) {
break;
}
- d->smart_parsed_data.self_test_execution_percent_remaining = 10*(d->smart_data[363] & 0xF);
- d->smart_parsed_data.self_test_execution_status = (d->smart_data[363] >> 4) & 0xF;
+ d->smart_parsed_data.self_test_execution_percent_remaining = 10*(d->blob.smart_data[363] & 0xF);
+ d->smart_parsed_data.self_test_execution_status = (d->blob.smart_data[363] >> 4) & 0xF;
- d->smart_parsed_data.total_offline_data_collection_seconds = (uint16_t) d->smart_data[364] | ((uint16_t) d->smart_data[365] << 8);
+ d->smart_parsed_data.total_offline_data_collection_seconds = (uint16_t) d->blob.smart_data[364] | ((uint16_t) d->blob.smart_data[365] << 8);
d->smart_parsed_data.conveyance_test_available = disk_smart_is_conveyance_test_available(d);
d->smart_parsed_data.short_and_extended_test_available = disk_smart_is_short_and_extended_test_available(d);
d->smart_parsed_data.start_test_available = disk_smart_is_start_test_available(d);
d->smart_parsed_data.abort_test_available = disk_smart_is_abort_test_available(d);
- d->smart_parsed_data.short_test_polling_minutes = d->smart_data[372];
- 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]);
- d->smart_parsed_data.conveyance_test_polling_minutes = d->smart_data[374];
+ d->smart_parsed_data.short_test_polling_minutes = d->blob.smart_data[372];
+ d->smart_parsed_data.extended_test_polling_minutes = d->blob.smart_data[373] != 0xFF ? d->blob.smart_data[373] : ((uint16_t) d->blob.smart_data[376] << 8 | (uint16_t) d->blob.smart_data[375]);
+ d->smart_parsed_data.conveyance_test_polling_minutes = d->blob.smart_data[374];
*spd = &d->smart_parsed_data;
@@ -1282,7 +1324,7 @@ static void find_threshold(SkDisk *d, SkSmartAttributeParsedData *a) {
return;
}
- for (n = 0, p = d->smart_threshold_data+2; n < 30; n++, p+=12)
+ for (n = 0, p = d->blob.smart_threshold_data+2; n < 30; n++, p+=12)
if (p[0] == a->id)
break;
@@ -1323,7 +1365,7 @@ int sk_disk_smart_parse_attributes(SkDisk *d, SkSmartAttributeParseCallback cb,
return -1;
}
- for (n = 0, p = d->smart_data + 2; n < 30; n++, p+=12) {
+ for (n = 0, p = d->blob.smart_data + 2; n < 30; n++, p+=12) {
SkSmartAttributeParsedData a;
const SkSmartAttributeInfo *i;
char *an = NULL;
@@ -1572,6 +1614,11 @@ int sk_disk_smart_get_overall(SkDisk *d, SkSmartOverall *overall) {
assert(d);
assert(overall);
+ if (d->type == SK_DISK_TYPE_BLOB) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
if (sk_disk_smart_status(d, &good) < 0)
return -1;
@@ -1700,14 +1747,21 @@ static void disk_dump_attributes(SkDisk *d, const SkSmartAttributeParsedData *a,
int sk_disk_dump(SkDisk *d) {
int ret;
SkBool awake = FALSE;
+ uint64_t size;
+
+ assert(d);
printf("Device: %s\n"
- "Size: %lu MiB\n"
"Type: %s\n",
- d->name,
- (unsigned long) (d->size/1024/1024),
+ d->name ? d->name : "n/a",
disk_type_to_string(d->type));
+ ret = sk_disk_get_size(d, &size);
+ if (ret >= 0)
+ printf("Size: %lu MiB\n", (unsigned long) (d->size/1024/1024));
+ else
+ printf("Size: %s\n", strerror(errno));
+
if (d->identify_data_valid) {
const SkIdentifyParsedData *ipd;
SkSmartQuirk quirk = 0;
@@ -1740,7 +1794,7 @@ int sk_disk_dump(SkDisk *d) {
ret = sk_disk_check_sleep_mode(d, &awake);
printf("Awake: %s\n",
- ret >= 0 ? yes_no(awake) : "unknown");
+ ret >= 0 ? yes_no(awake) : strerror(errno));
if (disk_smart_is_available(d)) {
SkSmartOverall overall;
@@ -1749,11 +1803,9 @@ int sk_disk_dump(SkDisk *d) {
char pretty[32];
uint64_t value;
- if ((ret = sk_disk_smart_status(d, &good)) < 0)
- return ret;
-
+ ret = sk_disk_smart_status(d, &good);
printf("SMART Disk Health Good: %s\n",
- yes_no(good));
+ ret >= 0 ? yes_no(good) : strerror(errno));
if ((ret = sk_disk_smart_read_data(d)) < 0)
return ret;
@@ -1802,13 +1854,13 @@ int sk_disk_dump(SkDisk *d) {
else
printf("Temperature: %s\n", print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_MKELVIN));
- if ((ret = sk_disk_smart_get_overall(d, &overall)) < 0)
- return ret;
-
- printf("%sOverall Status: %s%s\n",
- overall != SK_SMART_OVERALL_GOOD ? HIGHLIGHT : "",
- sk_smart_overall_to_string(overall),
- overall != SK_SMART_OVERALL_GOOD ? ENDHIGHLIGHT : "");
+ if (sk_disk_smart_get_overall(d, &overall) < 0)
+ printf("Overall Status: %s\n", strerror(errno));
+ else
+ printf("%sOverall Status: %s%s\n",
+ overall != SK_SMART_OVERALL_GOOD ? HIGHLIGHT : "",
+ sk_smart_overall_to_string(overall),
+ overall != SK_SMART_OVERALL_GOOD ? ENDHIGHLIGHT : "");
printf("%3s %-27s %5s %5s %5s %-11s %-14s %-7s %-7s %-3s\n",
"ID#",
@@ -1830,6 +1882,13 @@ int sk_disk_dump(SkDisk *d) {
}
int sk_disk_get_size(SkDisk *d, uint64_t *bytes) {
+ assert(d);
+ assert(bytes);
+
+ if (d->size == (uint64_t) -1) {
+ errno = ENODATA;
+ return -1;
+ }
*bytes = d->size;
return 0;
@@ -1840,7 +1899,6 @@ int sk_disk_open(const char *name, SkDisk **_d) {
int ret = -1;
struct stat st;
- assert(name);
assert(_d);
if (!(d = calloc(1, sizeof(SkDisk)))) {
@@ -1848,60 +1906,66 @@ int sk_disk_open(const char *name, SkDisk **_d) {
goto fail;
}
- if (!(d->name = strdup(name))) {
- errno = ENOMEM;
- goto fail;
- }
+ if (!name) {
+ d->fd = -1;
+ d->type = SK_DISK_TYPE_BLOB;
+ d->size = (uint64_t) -1;
+ } else {
- if ((d->fd = open(name, O_RDWR|O_NOCTTY)) < 0) {
- ret = d->fd;
- goto fail;
- }
+ if (!(d->name = strdup(name))) {
+ errno = ENOMEM;
+ goto fail;
+ }
- if ((ret = fstat(d->fd, &st)) < 0)
- goto fail;
+ if ((d->fd = open(name, O_RDWR|O_NOCTTY)) < 0) {
+ ret = d->fd;
+ goto fail;
+ }
- if (!S_ISBLK(st.st_mode)) {
- errno = ENODEV;
- ret = -1;
- goto fail;
- }
+ if ((ret = fstat(d->fd, &st)) < 0)
+ goto fail;
- /* So, it's a block device. Let's make sure the ioctls work */
+ if (!S_ISBLK(st.st_mode)) {
+ errno = ENODEV;
+ ret = -1;
+ goto fail;
+ }
- if ((ret = ioctl(d->fd, BLKGETSIZE64, &d->size)) < 0)
- goto fail;
+ /* So, it's a block device. Let's make sure the ioctls work */
+ if ((ret = ioctl(d->fd, BLKGETSIZE64, &d->size)) < 0)
+ goto fail;
- if (d->size <= 0 || d->size == (uint64_t) -1) {
- errno = EIO;
- ret = -1;
- goto fail;
- }
+ if (d->size <= 0 || d->size == (uint64_t) -1) {
+ errno = EIO;
+ ret = -1;
+ goto fail;
+ }
- /* OK, it's a real block device with a size. Find a way to
- * identify the device. */
- for (d->type = 0; d->type != SK_DISK_TYPE_UNKNOWN; d->type++)
- if (disk_identify_device(d) >= 0)
- break;
+ /* OK, it's a real block device with a size. Find a way to
+ * identify the device. */
+ for (d->type = 0; d->type != SK_DISK_TYPE_UNKNOWN; d->type++)
+ if (disk_identify_device(d) >= 0)
+ break;
- /* Check if driver can do SMART, and enable if necessary */
- if (disk_smart_is_available(d)) {
+ /* Check if driver can do SMART, and enable if necessary */
+ if (disk_smart_is_available(d)) {
- if (!disk_smart_is_enabled(d)) {
- if ((ret = disk_smart_enable(d, TRUE)) < 0)
- goto fail;
+ if (!disk_smart_is_enabled(d)) {
+ if ((ret = disk_smart_enable(d, TRUE)) < 0)
+ goto fail;
- if ((ret = disk_identify_device(d)) < 0)
- goto fail;
+ if ((ret = disk_identify_device(d)) < 0)
+ goto fail;
- if (!disk_smart_is_enabled(d)) {
- errno = EIO;
- ret = -1;
- goto fail;
+ if (!disk_smart_is_enabled(d)) {
+ errno = EIO;
+ ret = -1;
+ goto fail;
+ }
}
- }
- disk_smart_read_thresholds(d);
+ disk_smart_read_thresholds(d);
+ }
}
*_d = d;
@@ -1925,3 +1989,58 @@ void sk_disk_free(SkDisk *d) {
free(d->name);
free(d);
}
+
+int sk_disk_get_blob(SkDisk *d, const void **blob, size_t *size) {
+ assert(d);
+ assert(blob);
+ assert(size);
+
+ if (!d->identify_data_valid) {
+ errno = ENODATA;
+ return -1;
+ }
+
+ *blob = &d->blob;
+ *size = sizeof(d->blob.identify);
+
+ if (d->smart_data_valid) {
+ *size += sizeof(d->blob.smart_data);
+
+ if (d->smart_threshold_data_valid)
+ *size += sizeof(d->blob.smart_threshold_data);
+ }
+
+ return 0;
+}
+
+int sk_disk_set_blob(SkDisk *d, const void *blob, size_t size) {
+ assert(d);
+ assert(blob);
+
+ if (d->type != SK_DISK_TYPE_BLOB) {
+ errno = ENODEV;
+ return -1;
+ }
+
+ if (size == sizeof(d->blob.identify)+sizeof(d->blob.smart_data)+sizeof(d->blob.smart_threshold_data)) {
+ d->identify_data_valid = TRUE;
+ d->smart_data_valid = TRUE;
+ d->smart_threshold_data_valid = TRUE;
+ } else if (size == sizeof(d->blob.identify)+sizeof(d->blob.smart_data)) {
+ d->identify_data_valid = TRUE;
+ d->smart_data_valid = TRUE;
+ d->smart_threshold_data_valid = FALSE;
+ } else if (size == sizeof(d->blob.identify)) {
+ d->identify_data_valid = TRUE;
+ d->smart_data_valid = FALSE;
+ d->smart_threshold_data_valid = FALSE;
+ } else {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(&d->blob, 0, sizeof(d->blob));
+ memcpy(&d->blob, blob, size);
+
+ return 0;
+}
diff --git a/atasmart.h b/atasmart.h
index 096af93..db2d208 100644
--- a/atasmart.h
+++ b/atasmart.h
@@ -182,6 +182,10 @@ int sk_disk_smart_status(SkDisk *d, SkBool *good);
* sk_disk_check_power_mode() to check wether the disk is sleeping and
* skip the read if so. */
int sk_disk_smart_read_data(SkDisk *d);
+
+int sk_disk_get_blob(SkDisk *d, const void **blob, size_t *size);
+int sk_disk_set_blob(SkDisk *d, const void *blob, size_t size);
+
int sk_disk_smart_parse(SkDisk *d, const SkSmartParsedData **data);
int sk_disk_smart_parse_attributes(SkDisk *d, SkSmartAttributeParseCallback cb, void* userdata);
int sk_disk_smart_self_test(SkDisk *d, SkSmartSelfTest test);
@@ -193,11 +197,12 @@ int sk_disk_smart_get_power_on(SkDisk *d, uint64_t *mseconds);
int sk_disk_smart_get_bad(SkDisk *d, uint64_t *sectors);
/* High level API to get the temperature */
-int sk_disk_smart_get_temperature(SkDisk *d, uint64_t *kelvin);
+int sk_disk_smart_get_temperature(SkDisk *d, uint64_t *mkelvin);
/* Get overall status. This integrates the values of a couple of fields into a single overall status */
int sk_disk_smart_get_overall(SkDisk *d, SkSmartOverall *overall);
+/* Dump the current parsed status to STDOUT */
int sk_disk_dump(SkDisk *d);
void sk_disk_free(SkDisk *d);