Browse Source

Identity: implement generic error logging and permission checks

- Added robust validation check for empty model string payload to gracefully
  detect faulty drives or bridge timeouts without dropping execution path.
- Integrated universal descriptive [ERROR] and [WARNING] logging to  stderr.
- Implemented proactive EACCES/EPERM routing checks at libcam initialization
  to safely guide users regarding necessary root/sudo/devfs operational
  rights.
- Cleaned up string boundary array warning layouts inside src/libbsdiskinfo.c.
master
Sergey Kiselev 3 months ago
parent
commit
8d9b050dd9
  1. 4
      include/bsdiskinfo.h
  2. 8
      src/cli.c
  3. 139
      src/libbsdiskinfo.c

4
include/bsdiskinfo.h

@ -43,8 +43,8 @@
#define MAX_SATA_LEN 64 /* Buffer size for SATA signaling capabilities and link speed */
#define MAX_SEC_LEN 32 /* Buffer size for internal ATA Security feature state */
#define MAX_MOUNTS 128
#define MAX_DISKS 64
#define MAX_MOUNTS 128 /* Maximum tracked active filesystem mount topologies */
#define MAX_DISKS 64 /* Maximum concurrent physical storage units allowed */
/*
* Base hardware identity properties (similar to smartctl -i output).

8
src/cli.c

@ -87,22 +87,22 @@ static void print_identity(const char *disk_name, struct disk_properties *props)
printf("Serial Number: %s\n", props->serial);
printf("Firmware Version: %s\n", props->fw_version);
printf("LU WWN Device Id: %s\n", props->wwn);
/* Display advanced ATA hardware descriptors unpacked by the core library */
printf("Form Factor: %s\n", props->form_factor); /* Nominal physical unit size dimensions */
printf("ATA Version: %s\n", props->ata_version); /* Major compliance standard tracking */
printf("SATA Version: %s\n", props->sata_version); /* Signalling standard interface capabilities */
printf("ATA Security: %s\n", props->security_status); /* Operational hardware security locks state */
printf("Determined Type: %s\n", props->type_str);
/* Render storage payload storage capacity with explicit units computing */
double gib = (double)props->mediasize / (1024.0 * 1024.0 * 1024.0);
if (gib >= 1024.0) {
printf("User Capacity: %llu bytes (%.2f TiB)\n",
printf("User Capacity: %llu bytes (%.2f TiB)\n",
(unsigned long long)props->mediasize, gib / 1024.0);
} else {
printf("User Capacity: %llu bytes (%.2f GiB)\n",
printf("User Capacity: %llu bytes (%.2f GiB)\n",
(unsigned long long)props->mediasize, gib);
}

139
src/libbsdiskinfo.c

@ -30,6 +30,7 @@
#define _GNU_SOURCE /* For strcasestr support */
#include <errno.h>
#include <sys/param.h>
#include <sys/ucred.h>
#include <sys/mount.h>
@ -246,24 +247,25 @@ static void parse_advanced_ata_fields(struct ata_params *buf, struct disk_proper
/* 2. Major ATA Version standard handling (Word 80 & Word 222 Transport Framework) */
if (major == 0xFFFF || major == 0x0000) {
strlcpy(props->ata_version, "Unknown", sizeof(props->ata_version));
fprintf(stderr, "[WARNING] ATA Version unreadable: Word 80 reports invalid boundary (0x%04X)\n", major);
strlcpy(props->ata_version, "Unknown (Invalid Word 80)", sizeof(props->ata_version));
}
/*
* Detect actual ATA/ACS revision specification standard level by
* cross-verifying backward-compatible Word 80 flags against physical Word 222 transport bits.
*/
else if ((major & (1 << 14)) || ((major & (1 << 11)) && (transport & 0x1000) && (transport & 0x0040))) {
/* Word 80 Bit 14 OR (ATA8-ACS bit + SATA 3.1+ transport bit 12 + SATA 3.3 bit 6) -> ACS-4 */
strlcpy(props->ata_version, "ACS-4", sizeof(props->ata_version));
} else if ((major & (1 << 13)) || ((major & (1 << 10)) && (transport & 0x1000))) {
/* Word 80 Bit 13 OR (ATA-7+ bit + SATA 3.1+ transport bit 12) -> ACS-3 */
strlcpy(props->ata_version, "ACS-3", sizeof(props->ata_version));
} else if (major & (1 << 12)) {
strlcpy(props->ata_version, "ACS-2", sizeof(props->ata_version));
} else if (major & (1 << 11)) {
} else if ((major & (1 << 11)) || (major & (1 << 8))) {
strlcpy(props->ata_version, "ATA8-ACS", sizeof(props->ata_version));
} else if (major & (1 << 10)) {
} else if (major & (1 << 7)) {
strlcpy(props->ata_version, "ATA/ATAPI-7", sizeof(props->ata_version));
} else if (major & (1 << 6)) {
strlcpy(props->ata_version, "ATA/ATAPI-6", sizeof(props->ata_version));
} else {
snprintf(props->ata_version, sizeof(props->ata_version), "ATA Minor/Legacy (0x%04X)", major);
}
@ -276,7 +278,6 @@ static void parse_advanced_ata_fields(struct ata_params *buf, struct disk_proper
if (transport != 0xFFFF && transport != 0x0000 && (transport & 0x1000)) {
if (strcmp(props->ata_version, "ACS-4") == 0) {
/* ACS-4 devices can scale between SATA 3.2 and SATA 3.3 */
if ((transport & (1 << 6)) && (transport & 0x0100)) {
ver = "SATA 3.3";
} else {
@ -295,7 +296,6 @@ static void parse_advanced_ata_fields(struct ata_params *buf, struct disk_proper
else if (sata_cap & 0x0002) ver = "SATA 1.0a";
}
/* Append signaling interface speed specs mapping data */
if (sata_cap & 0x0006) {
snprintf(props->sata_version, sizeof(props->sata_version), "%s, 6.0 Gb/s", ver);
} else if (sata_cap & 0x0004) {
@ -342,6 +342,17 @@ static void query_cam_properties(
strlcpy(props->sata_version, "N/A", sizeof(props->sata_version));
strlcpy(props->security_status, "N/A", sizeof(props->security_status));
/* CRITICAL Check: Permissions routing validation */
cam_dev = cam_open_device(base_disk, O_RDWR);
if (cam_dev == NULL) {
if (errno == EACCES || errno == EPERM) {
fprintf(stderr, "[ERROR] %s: Permission denied. Accessing libcam transport layers requires root privileges (run with sudo).\n", base_disk);
} else {
fprintf(stderr, "[ERROR] %s: Failed to open CAM device node (errno: %d)\n", base_disk, errno);
}
return;
}
/* CRITICAL: Must use O_RDWR for transport-layer commands execution path to unlock */
cam_dev = cam_open_device(base_disk, O_RDWR);
if (cam_dev == NULL) return;
@ -418,6 +429,11 @@ static void query_cam_properties(
success = 1;
}
}
/* Filter out fake success on SAT(12) loop as well */
if (success && ident_buf->model[0] == 0) {
success = 0;
}
}
if (success) {
@ -463,7 +479,7 @@ static void query_cam_properties(
/* Parse advanced hardware descriptors from the verified buffer */
parse_advanced_ata_fields(ident_buf, props);
/* Parse Sector Sizes (Word 106: Physical sector size) */
/* Parse Sector Sizes (Word 106: Physical sector size / Advanced Format) */
if ((ident_buf->pss & 0x4000) && (ident_buf->pss & 0x2000)) {
props->physical_sector_size = props->logical_sector_size * (1 << (ident_buf->pss & 0x000F));
} else {
@ -478,6 +494,7 @@ static void query_cam_properties(
props->smart_supported = 0; props->smart_enabled = 0;
}
} else {
fprintf(stderr, "[ERROR] %s: ATA Pass-Through rejected or returned empty payload (hardware timeout/fault)\n", base_disk);
*pt_failed = 1;
}
}
@ -493,61 +510,67 @@ static void query_cam_properties(
ccb->ataio.cmd.command = 0xEC;
if (cam_send_ccb(cam_dev, ccb) == 0 && (ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
char real_model[41];
memcpy(real_model, ident_buf->model, sizeof(ident_buf->model));
real_model[sizeof(ident_buf->model)] = '\0';
ata_str_fix(real_model, sizeof(real_model));
if (strlen(real_model) > 0) strlcpy(props->model, real_model, sizeof(props->model));
char real_serial[21];
memcpy(real_serial, ident_buf->serial, sizeof(ident_buf->serial));
real_serial[sizeof(ident_buf->serial)] = '\0';
ata_str_fix(real_serial, sizeof(real_serial));
if (strlen(real_serial) > 0) strlcpy(props->serial, real_serial, sizeof(props->serial));
char real_fw[9];
memcpy(real_fw, ident_buf->revision, sizeof(ident_buf->revision));
real_fw[sizeof(ident_buf->revision)] = '\0';
ata_str_fix(real_fw, sizeof(real_fw));
if (strlen(real_fw) > 0) strlcpy(props->fw_version, real_fw, sizeof(props->fw_version));
/* Parse LU WWN Device ID */
uint64_t wwn_val = 0;
for (int w = 0; w < 4; w++) {
wwn_val |= ((uint64_t)ident_buf->wwn[w]) << ((3 - w) * 16);
}
if (wwn_val != 0) snprintf(props->wwn, sizeof(props->wwn), "%016llx", (unsigned long long)wwn_val);
else strlcpy(props->wwn, "N/A", sizeof(props->wwn));
/* Parse Rotation Rate and SSD state flags */
if (ident_buf->media_rotation_rate == 1) {
props->rotation_rate = 0;
*is_ssd = 1;
} else if (ident_buf->media_rotation_rate > 1 && ident_buf->media_rotation_rate < 0xFFFF) {
props->rotation_rate = ident_buf->media_rotation_rate;
*is_ssd = 0;
if (ident_buf->model[0] == 0) {
fprintf(stderr, "[ERROR] %s: IDENTIFY DEVICE returned empty payload (hardware fault)\n", base_disk);
} else {
props->rotation_rate = -1;
*is_ssd = 0;
}
char real_model[41];
memcpy(real_model, ident_buf->model, sizeof(ident_buf->model));
real_model[sizeof(ident_buf->model)] = '\0';
ata_str_fix(real_model, sizeof(real_model));
if (strlen(real_model) > 0) strlcpy(props->model, real_model, sizeof(props->model));
/* Parse advanced hardware descriptors from the direct AHCI buffer */
parse_advanced_ata_fields(ident_buf, props);
char real_serial[21];
memcpy(real_serial, ident_buf->serial, sizeof(ident_buf->serial));
real_serial[sizeof(ident_buf->serial)] = '\0';
ata_str_fix(real_serial, sizeof(real_serial));
if (strlen(real_serial) > 0) strlcpy(props->serial, real_serial, sizeof(props->serial));
/* Parse Sector Sizes (Word 106: Physical sector size) */
if ((ident_buf->pss & 0x4000) && (ident_buf->pss & 0x2000)) {
props->physical_sector_size = props->logical_sector_size * (1 << (ident_buf->pss & 0x000F));
} else {
props->physical_sector_size = props->logical_sector_size;
}
char real_fw[9];
memcpy(real_fw, ident_buf->revision, sizeof(ident_buf->revision));
real_fw[sizeof(ident_buf->revision)] = '\0';
ata_str_fix(real_fw, sizeof(real_fw));
if (strlen(real_fw) > 0) strlcpy(props->fw_version, real_fw, sizeof(props->fw_version));
/* Parse S.M.A.R.T. enablement states */
if (ident_buf->support.command1 & 0x0001) {
props->smart_supported = 1;
props->smart_enabled = (ident_buf->enabled.command1 & 0x0001) ? 1 : 0;
} else {
props->smart_supported = 0; props->smart_enabled = 0;
/* Parse LU WWN Device ID */
uint64_t wwn_val = 0;
for (int w = 0; w < 4; w++) {
wwn_val |= ((uint64_t)ident_buf->wwn[w]) << ((3 - w) * 16);
}
if (wwn_val != 0) snprintf(props->wwn, sizeof(props->wwn), "%016llx", (unsigned long long)wwn_val);
else strlcpy(props->wwn, "N/A", sizeof(props->wwn));
/* Parse Rotation Rate and SSD state flags */
if (ident_buf->media_rotation_rate == 1) {
props->rotation_rate = 0;
*is_ssd = 1;
} else if (ident_buf->media_rotation_rate > 1 && ident_buf->media_rotation_rate < 0xFFFF) {
props->rotation_rate = ident_buf->media_rotation_rate;
*is_ssd = 0;
} else {
props->rotation_rate = -1;
*is_ssd = 0;
}
/* Parse advanced hardware descriptors from the direct AHCI buffer */
parse_advanced_ata_fields(ident_buf, props);
/* Parse Sector Sizes (Word 106: Physical sector size / Advanced Format) */
if ((ident_buf->pss & 0x4000) && (ident_buf->pss & 0x2000)) {
props->physical_sector_size = props->logical_sector_size * (1 << (ident_buf->pss & 0x000F));
} else {
props->physical_sector_size = props->logical_sector_size;
}
/* Parse S.M.A.R.T. enablement states */
if (ident_buf->support.command1 & 0x0001) {
props->smart_supported = 1;
props->smart_enabled = (ident_buf->enabled.command1 & 0x0001) ? 1 : 0;
} else {
props->smart_supported = 0; props->smart_enabled = 0;
}
}
} else {
fprintf(stderr, "[ERROR] %s: Native ATA IDENTIFY command rejected by the controller\n", base_disk);
}
}

Loading…
Cancel
Save