2020-11-09 05:25:50 +01:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
2010-06-21 16:55:16 +02:00
|
|
|
/*
|
2005-06-25 23:54:28 +02:00
|
|
|
* ata_id - reads product/serial number from ATA drives
|
|
|
|
*
|
2018-06-12 17:15:23 +02:00
|
|
|
* Copyright © 2009-2010 David Zeuthen <zeuthen@gmail.com>
|
2005-06-25 23:54:28 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <errno.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <fcntl.h>
|
2007-05-01 14:19:31 +02:00
|
|
|
#include <getopt.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <linux/bsg.h>
|
|
|
|
#include <linux/hdreg.h>
|
2009-11-04 16:34:22 +01:00
|
|
|
#include <scsi/scsi.h>
|
|
|
|
#include <scsi/scsi_ioctl.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <scsi/sg.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2005-06-25 23:54:28 +02:00
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/stat.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <unistd.h>
|
2005-06-25 23:54:28 +02:00
|
|
|
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "fd-util.h"
|
2018-11-16 03:44:17 +01:00
|
|
|
#include "libudev-util.h"
|
2012-04-08 16:06:20 +02:00
|
|
|
#include "log.h"
|
2019-03-13 12:02:21 +01:00
|
|
|
#include "memory-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "udev-util.h"
|
2005-06-25 23:54:28 +02:00
|
|
|
|
2010-10-30 17:44:06 +02:00
|
|
|
#define COMMAND_TIMEOUT_MSEC (30 * 1000)
|
2009-11-04 16:34:22 +01:00
|
|
|
|
2018-08-22 10:06:26 +02:00
|
|
|
static int disk_scsi_inquiry_command(
|
|
|
|
int fd,
|
|
|
|
void *buf,
|
|
|
|
size_t buf_len) {
|
|
|
|
|
2013-04-11 00:58:44 +02:00
|
|
|
uint8_t cdb[6] = {
|
2018-08-22 10:06:26 +02:00
|
|
|
/* INQUIRY, see SPC-4 section 6.4 */
|
2013-04-11 00:58:44 +02:00
|
|
|
[0] = 0x12, /* OPERATION CODE: INQUIRY */
|
|
|
|
[3] = (buf_len >> 8), /* ALLOCATION LENGTH */
|
|
|
|
[4] = (buf_len & 0xff),
|
|
|
|
};
|
|
|
|
uint8_t sense[32] = {};
|
|
|
|
struct sg_io_v4 io_v4 = {
|
|
|
|
.guard = 'Q',
|
|
|
|
.protocol = BSG_PROTOCOL_SCSI,
|
|
|
|
.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD,
|
|
|
|
.request_len = sizeof(cdb),
|
|
|
|
.request = (uintptr_t) cdb,
|
|
|
|
.max_response_len = sizeof(sense),
|
|
|
|
.response = (uintptr_t) sense,
|
|
|
|
.din_xfer_len = buf_len,
|
|
|
|
.din_xferp = (uintptr_t) buf,
|
|
|
|
.timeout = COMMAND_TIMEOUT_MSEC,
|
|
|
|
};
|
2012-01-10 01:34:15 +01:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ioctl(fd, SG_IO, &io_v4);
|
|
|
|
if (ret != 0) {
|
|
|
|
/* could be that the driver doesn't do version 4, try version 3 */
|
|
|
|
if (errno == EINVAL) {
|
2013-04-11 00:58:44 +02:00
|
|
|
struct sg_io_hdr io_hdr = {
|
|
|
|
.interface_id = 'S',
|
|
|
|
.cmdp = (unsigned char*) cdb,
|
|
|
|
.cmd_len = sizeof (cdb),
|
|
|
|
.dxferp = buf,
|
|
|
|
.dxfer_len = buf_len,
|
|
|
|
.sbp = sense,
|
|
|
|
.mx_sb_len = sizeof(sense),
|
|
|
|
.dxfer_direction = SG_DXFER_FROM_DEV,
|
|
|
|
.timeout = COMMAND_TIMEOUT_MSEC,
|
|
|
|
};
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
ret = ioctl(fd, SG_IO, &io_hdr);
|
|
|
|
if (ret != 0)
|
2013-04-11 00:58:44 +02:00
|
|
|
return ret;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
/* even if the ioctl succeeds, we need to check the return value */
|
|
|
|
if (!(io_hdr.status == 0 &&
|
|
|
|
io_hdr.host_status == 0 &&
|
|
|
|
io_hdr.driver_status == 0)) {
|
|
|
|
errno = EIO;
|
2013-04-11 00:58:44 +02:00
|
|
|
return -1;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
2013-04-11 00:58:44 +02:00
|
|
|
} else
|
|
|
|
return ret;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* even if the ioctl succeeds, we need to check the return value */
|
|
|
|
if (!(io_v4.device_status == 0 &&
|
|
|
|
io_v4.transport_status == 0 &&
|
|
|
|
io_v4.driver_status == 0)) {
|
|
|
|
errno = EIO;
|
2013-04-11 00:58:44 +02:00
|
|
|
return -1;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
2010-10-30 17:44:06 +02:00
|
|
|
|
2013-04-11 00:58:44 +02:00
|
|
|
return 0;
|
2009-11-04 16:34:22 +01:00
|
|
|
}
|
|
|
|
|
2018-08-22 10:06:26 +02:00
|
|
|
static int disk_identify_command(
|
|
|
|
int fd,
|
|
|
|
void *buf,
|
|
|
|
size_t buf_len) {
|
|
|
|
|
2013-04-11 00:58:44 +02:00
|
|
|
uint8_t cdb[12] = {
|
|
|
|
/*
|
|
|
|
* ATA Pass-Through 12 byte command, as described in
|
|
|
|
*
|
|
|
|
* T10 04-262r8 ATA Command Pass-Through
|
|
|
|
*
|
|
|
|
* from http://www.t10.org/ftp/t10/document.04/04-262r8.pdf
|
|
|
|
*/
|
|
|
|
[0] = 0xa1, /* OPERATION CODE: 12 byte pass through */
|
|
|
|
[1] = 4 << 1, /* PROTOCOL: PIO Data-in */
|
|
|
|
[2] = 0x2e, /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
|
|
|
|
[3] = 0, /* FEATURES */
|
|
|
|
[4] = 1, /* SECTORS */
|
|
|
|
[5] = 0, /* LBA LOW */
|
|
|
|
[6] = 0, /* LBA MID */
|
|
|
|
[7] = 0, /* LBA HIGH */
|
|
|
|
[8] = 0 & 0x4F, /* SELECT */
|
|
|
|
[9] = 0xEC, /* Command: ATA IDENTIFY DEVICE */
|
|
|
|
};
|
|
|
|
uint8_t sense[32] = {};
|
|
|
|
uint8_t *desc = sense + 8;
|
|
|
|
struct sg_io_v4 io_v4 = {
|
|
|
|
.guard = 'Q',
|
|
|
|
.protocol = BSG_PROTOCOL_SCSI,
|
|
|
|
.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD,
|
|
|
|
.request_len = sizeof(cdb),
|
|
|
|
.request = (uintptr_t) cdb,
|
|
|
|
.max_response_len = sizeof(sense),
|
|
|
|
.response = (uintptr_t) sense,
|
|
|
|
.din_xfer_len = buf_len,
|
|
|
|
.din_xferp = (uintptr_t) buf,
|
|
|
|
.timeout = COMMAND_TIMEOUT_MSEC,
|
|
|
|
};
|
2012-01-10 01:34:15 +01:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ioctl(fd, SG_IO, &io_v4);
|
|
|
|
if (ret != 0) {
|
|
|
|
/* could be that the driver doesn't do version 4, try version 3 */
|
|
|
|
if (errno == EINVAL) {
|
2013-04-11 00:58:44 +02:00
|
|
|
struct sg_io_hdr io_hdr = {
|
|
|
|
.interface_id = 'S',
|
|
|
|
.cmdp = (unsigned char*) cdb,
|
|
|
|
.cmd_len = sizeof (cdb),
|
|
|
|
.dxferp = buf,
|
|
|
|
.dxfer_len = buf_len,
|
|
|
|
.sbp = sense,
|
|
|
|
.mx_sb_len = sizeof (sense),
|
|
|
|
.dxfer_direction = SG_DXFER_FROM_DEV,
|
|
|
|
.timeout = COMMAND_TIMEOUT_MSEC,
|
|
|
|
};
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
ret = ioctl(fd, SG_IO, &io_hdr);
|
|
|
|
if (ret != 0)
|
2013-04-11 00:58:44 +02:00
|
|
|
return ret;
|
|
|
|
} else
|
|
|
|
return ret;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
|
ata_id: Add check for fixed format sense codes (#13654)
Original revisions of the SAT (SCSI-ATA Translation) specification
required that all sense data be reported in Descriptor Format (72h).
Later revisions specifcally allow and account for sense data being
reported in Fixed Format (70h).
The current code checks for a Descriptor Format sense structure (0x72),
then looks specifically at the first byte of the first descriptor for the
ATA specific code 0x9, cross referencing it with the first byte which is
just a length field 0x0c (as a sanity check).
In the Fixed Format case(0x70), we can fall back to using the top-level
SCSI Sense data for the Additional Sense code (0x0) and then the
Additional Sense Code Qualifier (0x1d),
That identifies that the sense data is of the format associated with:
`ATA PASS THROUGH INFORMATION AVAILABLE`.
This fallback mechanism retains support for SATLs compliant with ANSI
INCITS 431-2007, and enables support for Fixed Format Sense data
enabled by SATLs with later revisions.
Glad to do so. This patch allows ata_id to export attributes correctly. I believe that any drive can potentially return information in this format on any SATL using the libata-scsi (the Linux builtin SATL), but in this particular case, it appears it is the SATL itself. Attaching the disk to the AHCI controller changes the behavior impacted here. (Not entirely surprisingly, SATLs are are pretty inconsistent).
Test:
This case specifically is an LSI SATL. I'll illustrate that without the patch, ata_id does not return
any output for a valid SATA drive but after the patch does.
1. Verify the device is ATA, by looking at the vpd page specific to ATA drives
```
root@machine:~# sg_vpd -p ai /dev/sdn
ATA information VPD page:
SAT Vendor identification: LSI
SAT Product identification: LSI SATL
SAT Product revision level: 0008
Device signature indicates SATA transport
ATA command IDENTIFY DEVICE response summary:
model: HGST HUH728080ALE604
serial number: ZZZZH3VX
firmware revision: A4GNW7J0
```
2. Look at what udev thinks of the disk, it says ID_BUS=scsi
ATA information says ID_MODEL should be HGST_HUH728080ALE604
udev says it is HGST_HUH728080AL (Missing E604, 4 bytes), and no ATA attributes are
populated.
```
root@machine:~# udevadm info -q all /dev/sdn
P: /devices/pci0000:00/0000:00:03.0/0000:05:00.0/host2/port-2:0/expander-2:0/port-2:0:11/end_device-2:0:11/target2:0:11/2:0:11:0/block/sdn
N: sdn
S: disk/by-id/scsi-35000cca23be1dc3c
S: disk/by-id/wwn-0x5000cca23be1dc3c
S: disk/by-path/pci-0000:05:00.0-sas-exp0x500605b0000272bf-phy11-lun-0
E: DEVLINKS=/dev/disk/by-id/wwn-0x5000cca23be1dc3c /dev/disk/by-id/scsi-35000cca23be1dc3c /dev/disk/by-path/pci-0000:05:00.0-sas-exp0x500605b0000272bf-phy11-lun-0
E: DEVNAME=/dev/sdn
E: DEVPATH=/devices/pci0000:00/0000:00:03.0/0000:05:00.0/host2/port-2:0/expander-2:0/port-2:0:11/end_device-2:0:11/target2:0:11/2:0:11:0/block/sdn
E: DEVTYPE=disk
E: ID_BUS=scsi
E: ID_MODEL=HGST_HUH728080AL
E: ID_MODEL_ENC=HGST\x20HUH728080AL
E: ID_PATH=pci-0000:05:00.0-sas-exp0x500605b0000272bf-phy11-lun-0
E: ID_PATH_TAG=pci-0000_05_00_0-sas-exp0x500605b0000272bf-phy11-lun-0
E: ID_REVISION=W7J0
E: ID_SCSI=1
E: ID_SCSI_SERIAL=ZZZZH3VX
E: ID_SERIAL=35000cca23be1dc3c
E: ID_SERIAL_SHORT=5000cca23be1dc3c
E: ID_TYPE=disk
E: ID_VENDOR=ATA
E: ID_VENDOR_ENC=ATA\x20\x20\x20\x20\x20
E: ID_WWN=0x5000cca23be1dc3c
E: ID_WWN_WITH_EXTENSION=0x5000cca23be1dc3c
E: MAJOR=8
E: MINOR=208
E: SUBSYSTEM=block
E: TAGS=:systemd:
```
3. Run ata_id (unpatched) (Outputs nothing, RC=2)
```
root@machine:~# strace -e ioctl /lib/udev/ata_id /dev/sdn -x
ioctl(3, SG_IO, {'Q', BSG_PROTOCOL_SCSI, BSG_SUB_PROTOCOL_SCSI_CMD, request[6]=[12, 00, 00, 00, 24, 00], request_tag=0, request_attr=0, request_priority=0, request_extra=0, max_response_len=32, dout_iovec_count=0, dout_xfer_len=0, din_iovec_count=0, din_xfer_len=36, timeout=30000, flags=0, usr_ptr=0, spare_in=0, dout[0]=NULL}) = -1 EINVAL (Invalid argument)
ioctl(3, SG_IO, {'S', SG_DXFER_FROM_DEV, cmd[6]=[12, 00, 00, 00, 24, 00], mx_sb_len=32, iovec_count=0, dxfer_len=36, timeout=30000, flags=0, data[36]=[00, 00, 06, 12, 45, 00, 00, 02, 41, 54, 41, 20, 20, 20, 20, 20, 48, 47, 53, 54, 20, 48, 55, 48, 37, 32, 38, 30, 38, 30, 41, 4c, ...], status=00, masked_status=00, sb[0]=[], host_status=0, driver_status=0, resid=0, duration=1, info=0}) = 0
ioctl(3, SG_IO, {'Q', BSG_PROTOCOL_SCSI, BSG_SUB_PROTOCOL_SCSI_CMD, request[12]=[a1, 08, 2e, 00, 01, 00, 00, 00, 00, ec, 00, 00], request_tag=0, request_attr=0, request_priority=0, request_extra=0, max_response_len=32, dout_iovec_count=0, dout_xfer_len=0, din_iovec_count=0, din_xfer_len=512, timeout=30000, flags=0, usr_ptr=0, spare_in=0, dout[0]=NULL}) = -1 EINVAL (Invalid argument)
ioctl(3, SG_IO, {'S', SG_DXFER_FROM_DEV, cmd[12]=[a1, 08, 2e, 00, 01, 00, 00, 00, 00, ec, 00, 00], mx_sb_len=32, iovec_count=0, dxfer_len=512, timeout=30000, flags=0, data[0]=[], status=02, masked_status=01, sb[18]=[70, 00, 01, 00, 00, 00, 00, 0a, 00, 00, 00, 00, 00, 1d, 00, 00, 00, 00], host_status=0, driver_status=0x8, resid=512, duration=0, info=0x1}) = 0
ioctl(3, HDIO_GET_IDENTITY, 0x7ffe408f7590) = -1 EINVAL (Invalid argument)
+++ exited with 2 +++
```
Sense buffers visible with the strace:
`sb[18]=[70, 00, 01, 00, 00, 00, 00, 0a, 00, 00, 00, 00, 00, 1d, 00, 00, 00, 00]` is the important bit, see 70, 0a and 1d bytes
4. Run patched version: model is HGST_HUH728080ALE604 as expected, ATA attributes are
correctly populated.
```
root@machine:~# ./ata_id /dev/sdn -x
ID_ATA=1
ID_TYPE=disk
ID_BUS=ata
ID_MODEL=HGST_HUH728080ALE604
ID_MODEL_ENC=HGST\x20HUH728080ALE604\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
ID_REVISION=A4GNW7J0
ID_SERIAL=HGST_HUH728080ALE604_ZZZZH3VX
ID_SERIAL_SHORT=ZZZZH3VX
ID_ATA_WRITE_CACHE=1
ID_ATA_WRITE_CACHE_ENABLED=1
ID_ATA_FEATURE_SET_HPA=1
ID_ATA_FEATURE_SET_HPA_ENABLED=1
ID_ATA_FEATURE_SET_PM=1
ID_ATA_FEATURE_SET_PM_ENABLED=1
ID_ATA_FEATURE_SET_SECURITY=1
ID_ATA_FEATURE_SET_SECURITY_ENABLED=0
ID_ATA_FEATURE_SET_SECURITY_ERASE_UNIT_MIN=66522
ID_ATA_FEATURE_SET_SMART=1
ID_ATA_FEATURE_SET_SMART_ENABLED=1
ID_ATA_FEATURE_SET_PUIS=1
ID_ATA_FEATURE_SET_PUIS_ENABLED=0
ID_ATA_FEATURE_SET_APM=1
ID_ATA_FEATURE_SET_APM_ENABLED=1
ID_ATA_FEATURE_SET_APM_CURRENT_VALUE=254
ID_ATA_DOWNLOAD_MICROCODE=1
ID_ATA_SATA=1
ID_ATA_SATA_SIGNAL_RATE_GEN2=1
ID_ATA_SATA_SIGNAL_RATE_GEN1=1
ID_ATA_ROTATION_RATE_RPM=7200
ID_WWN=0x5000cca23be1dc3c
ID_WWN_WITH_EXTENSION=0x5000cca23be1dc3c
```
5. Drop it in place and verify: we see that ata_id does work.
```
root@hw1-b01left-2212a:~# udevadm test /block/sdn
<truncated>
GROUP 6 /lib/udev/rules.d/50-udev-default.rules:55
IMPORT 'ata_id --export /dev/sdn' /lib/udev/rules.d/60-persistent-storage.rules:33
starting 'ata_id --export /dev/sdn'
'ata_id --export /dev/sdn'(out) 'ID_ATA=1'
'ata_id --export /dev/sdn'(out) 'ID_TYPE=disk'
'ata_id --export /dev/sdn'(out) 'ID_BUS=ata'
'ata_id --export /dev/sdn'(out) 'ID_MODEL=HGST_HUH728080ALE604'
'ata_id --export /dev/sdn'(out) 'ID_MODEL_ENC=HGST\x20HUH728080ALE604\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20'
'ata_id --export /dev/sdn'(out) 'ID_REVISION=A4GNW7J0'
'ata_id --export /dev/sdn'(out) 'ID_SERIAL=HGST_HUH728080ALE604_ZZZZH3VX'
'ata_id --export /dev/sdn'(out) 'ID_SERIAL_SHORT=ZZZZH3VX'
'ata_id --export /dev/sdn'(out) 'ID_ATA_WRITE_CACHE=1'
'ata_id --export /dev/sdn'(out) 'ID_ATA_WRITE_CACHE_ENABLED=1'
'ata_id --export /dev/sdn'(out) 'ID_ATA_FEATURE_SET_HPA=1'
'ata_id --export /dev/sdn'(out) 'ID_ATA_FEATURE_SET_HPA_ENABLED=1'
'ata_id --export /dev/sdn'(out) 'ID_ATA_FEATURE_SET_PM=1'
'ata_id --export /dev/sdn'(out) 'ID_ATA_FEATURE_SET_PM_ENABLED=1'
'ata_id --export /dev/sdn'(out) 'ID_ATA_FEATURE_SET_SECURITY=1'
'ata_id --export /dev/sdn'(out) 'ID_ATA_FEATURE_SET_SECURITY_ENABLED=0'
'ata_id --export /dev/sdn'(out) 'ID_ATA_FEATURE_SET_SECURITY_ERASE_UNIT_MIN=66522'
'ata_id --export /dev/sdn'(out) 'ID_ATA_FEATURE_SET_SMART=1'
'ata_id --export /dev/sdn'(out) 'ID_ATA_FEATURE_SET_SMART_ENABLED=1'
'ata_id --export /dev/sdn'(out) 'ID_ATA_FEATURE_SET_PUIS=1'
'ata_id --export /dev/sdn'(out) 'ID_ATA_FEATURE_SET_PUIS_ENABLED=0'
'ata_id --export /dev/sdn'(out) 'ID_ATA_FEATURE_SET_APM=1'
'ata_id --export /dev/sdn'(out) 'ID_ATA_FEATURE_SET_APM_ENABLED=1'
'ata_id --export /dev/sdn'(out) 'ID_ATA_FEATURE_SET_APM_CURRENT_VALUE=254'
'ata_id --export /dev/sdn'(out) 'ID_ATA_DOWNLOAD_MICROCODE=1'
'ata_id --export /dev/sdn'(out) 'ID_ATA_SATA=1'
'ata_id --export /dev/sdn'(out) 'ID_ATA_SATA_SIGNAL_RATE_GEN2=1'
'ata_id --export /dev/sdn'(out) 'ID_ATA_SATA_SIGNAL_RATE_GEN1=1'
'ata_id --export /dev/sdn'(out) 'ID_ATA_ROTATION_RATE_RPM=7200'
'ata_id --export /dev/sdn'(out) 'ID_WWN=0x5000cca23be1dc3c'
'ata_id --export /dev/sdn'(out) 'ID_WWN_WITH_EXTENSION=0x5000cca23be1dc3c'
Process 'ata_id --export /dev/sdn' succeeded.
LINK 'disk/by-id/ata-HGST_HUH728080ALE604_ZZZZH3VX' /lib/udev/rules.d/60-persistent-storage.rules:47
IMPORT builtin 'path_id' /lib/udev/rules.d/60-persistent-storage.rules:65
LINK 'disk/by-path/pci-0000:05:00.0-sas-exp0x500605b0000272bf-phy11-lun-0' /lib/udev/rules.d/60-persistent-storage.rules:66
IMPORT builtin 'blkid' /lib/udev/rules.d/60-persistent-storage.rules:81
probe /dev/sdn raid offset=0
LINK 'disk/by-id/wwn-0x5000cca23be1dc3c' /lib/udev/rules.d/60-persistent-storage.rules:88
RUN '/usr/lib/python-dsnet-appliance/hotplug disk udev-disk-add' /etc/udev/rules.d/99-appliance-hotplug.rules:1
update old name, '/dev/disk/by-id/scsi-35000cca23be1dc3c' no longer belonging to '/devices/pci0000:00/0000:00:03.0/0000:05:00.0/host2/port-2:0/expander-2:0/port-2:0:11/end_device-2:0:11/target2:0:11/2:0:11:0/block/sdn'
no reference left, remove '/dev/disk/by-id/scsi-35000cca23be1dc3c'
handling device node '/dev/sdn', devnum=b8:208, mode=0660, uid=0, gid=6
preserve permissions /dev/sdn, 060660, uid=0, gid=6
preserve already existing symlink '/dev/block/8:208' to '../sdn'
creating link '/dev/disk/by-id/ata-HGST_HUH728080ALE604_ZZZZH3VX' to '/dev/sdn'
creating symlink '/dev/disk/by-id/ata-HGST_HUH728080ALE604_ZZZZH3VX' to '../../sdn'
found 'b8:208' claiming '/run/udev/links/\x2fdisk\x2fby-id\x2fwwn-0x5000cca23be1dc3c'
creating link '/dev/disk/by-id/wwn-0x5000cca23be1dc3c' to '/dev/sdn'
preserve already existing symlink '/dev/disk/by-id/wwn-0x5000cca23be1dc3c' to '../../sdn'
found 'b8:208' claiming '/run/udev/links/\x2fdisk\x2fby-path\x2fpci-0000:05:00.0-sas-exp0x500605b0000272bf-phy11-lun-0'
creating link '/dev/disk/by-path/pci-0000:05:00.0-sas-exp0x500605b0000272bf-phy11-lun-0' to '/dev/sdn'
preserve already existing symlink '/dev/disk/by-path/pci-0000:05:00.0-sas-exp0x500605b0000272bf-phy11-lun-0' to '../../sdn'
created db file '/run/udev/data/b8:208' for '/devices/pci0000:00/0000:00:03.0/0000:05:00.0/host2/port-2:0/expander-2:0/port-2:0:11/end_device-2:0:11/target2:0:11/2:0:11:0/block/sdn'
ACTION=add
DEVLINKS=/dev/disk/by-path/pci-0000:05:00.0-sas-exp0x500605b0000272bf-phy11-lun-0 /dev/disk/by-id/ata-HGST_HUH728080ALE604_ZZZZH3VX /dev/disk/by-id/wwn-0x5000cca23be1dc3c
DEVNAME=/dev/sdn
DEVPATH=/devices/pci0000:00/0000:00:03.0/0000:05:00.0/host2/port-2:0/expander-2:0/port-2:0:11/end_device-2:0:11/target2:0:11/2:0:11:0/block/sdn
DEVTYPE=disk
ID_ATA=1
ID_ATA_DOWNLOAD_MICROCODE=1
ID_ATA_FEATURE_SET_APM=1
ID_ATA_FEATURE_SET_APM_CURRENT_VALUE=254
ID_ATA_FEATURE_SET_APM_ENABLED=1
ID_ATA_FEATURE_SET_HPA=1
ID_ATA_FEATURE_SET_HPA_ENABLED=1
ID_ATA_FEATURE_SET_PM=1
ID_ATA_FEATURE_SET_PM_ENABLED=1
ID_ATA_FEATURE_SET_PUIS=1
ID_ATA_FEATURE_SET_PUIS_ENABLED=0
ID_ATA_FEATURE_SET_SECURITY=1
ID_ATA_FEATURE_SET_SECURITY_ENABLED=0
ID_ATA_FEATURE_SET_SECURITY_ERASE_UNIT_MIN=66522
ID_ATA_FEATURE_SET_SMART=1
ID_ATA_FEATURE_SET_SMART_ENABLED=1
ID_ATA_ROTATION_RATE_RPM=7200
ID_ATA_SATA=1
ID_ATA_SATA_SIGNAL_RATE_GEN1=1
ID_ATA_SATA_SIGNAL_RATE_GEN2=1
ID_ATA_WRITE_CACHE=1
ID_ATA_WRITE_CACHE_ENABLED=1
ID_BUS=ata
ID_MODEL=HGST_HUH728080ALE604
ID_MODEL_ENC=HGST\x20HUH728080ALE604\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
ID_PATH=pci-0000:05:00.0-sas-exp0x500605b0000272bf-phy11-lun-0
ID_PATH_TAG=pci-0000_05_00_0-sas-exp0x500605b0000272bf-phy11-lun-0
ID_REVISION=A4GNW7J0
ID_SERIAL=HGST_HUH728080ALE604_ZZZZH3VX
ID_SERIAL_SHORT=ZZZZH3VX
ID_TYPE=disk
ID_WWN=0x5000cca23be1dc3c
ID_WWN_WITH_EXTENSION=0x5000cca23be1dc3c
MAJOR=8
MINOR=208
SUBSYSTEM=block
TAGS=:systemd:
USEC_INITIALIZED=6055690
run: '/usr/lib/python-dsnet-appliance/hotplug disk udev-disk-add'
Unload module index
Unloaded link configuration context.
```
6. Query just to double check: (ID_BUS=ata, model correct, etc).
```
root@machine:~# udevadm info /dev/sdn
P: /devices/pci0000:00/0000:00:03.0/0000:05:00.0/host2/port-2:0/expander-2:0/port-2:0:11/end_device-2:0:11/target2:0:11/2:0:11:0/block/sdn
N: sdn
S: disk/by-id/ata-HGST_HUH728080ALE604_ZZZZH3VX
S: disk/by-id/wwn-0x5000cca23be1dc3c
S: disk/by-path/pci-0000:05:00.0-sas-exp0x500605b0000272bf-phy11-lun-0
E: DEVLINKS=/dev/disk/by-id/wwn-0x5000cca23be1dc3c /dev/disk/by-path/pci-0000:05:00.0-sas-exp0x500605b0000272bf-phy11-lun-0 /dev/disk/by-id/ata-HGST_HUH728080ALE604_ZZZZH3VX
E: DEVNAME=/dev/sdn
E: DEVPATH=/devices/pci0000:00/0000:00:03.0/0000:05:00.0/host2/port-2:0/expander-2:0/port-2:0:11/end_device-2:0:11/target2:0:11/2:0:11:0/block/sdn
E: DEVTYPE=disk
E: ID_ATA=1
E: ID_ATA_DOWNLOAD_MICROCODE=1
E: ID_ATA_FEATURE_SET_APM=1
E: ID_ATA_FEATURE_SET_APM_CURRENT_VALUE=254
E: ID_ATA_FEATURE_SET_APM_ENABLED=1
E: ID_ATA_FEATURE_SET_HPA=1
E: ID_ATA_FEATURE_SET_HPA_ENABLED=1
E: ID_ATA_FEATURE_SET_PM=1
E: ID_ATA_FEATURE_SET_PM_ENABLED=1
E: ID_ATA_FEATURE_SET_PUIS=1
E: ID_ATA_FEATURE_SET_PUIS_ENABLED=0
E: ID_ATA_FEATURE_SET_SECURITY=1
E: ID_ATA_FEATURE_SET_SECURITY_ENABLED=0
E: ID_ATA_FEATURE_SET_SECURITY_ERASE_UNIT_MIN=66522
E: ID_ATA_FEATURE_SET_SMART=1
E: ID_ATA_FEATURE_SET_SMART_ENABLED=1
E: ID_ATA_ROTATION_RATE_RPM=7200
E: ID_ATA_SATA=1
E: ID_ATA_SATA_SIGNAL_RATE_GEN1=1
E: ID_ATA_SATA_SIGNAL_RATE_GEN2=1
E: ID_ATA_WRITE_CACHE=1
E: ID_ATA_WRITE_CACHE_ENABLED=1
E: ID_BUS=ata
E: ID_MODEL=HGST_HUH728080ALE604
E: ID_MODEL_ENC=HGST\x20HUH728080ALE604\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
E: ID_PATH=pci-0000:05:00.0-sas-exp0x500605b0000272bf-phy11-lun-0
E: ID_PATH_TAG=pci-0000_05_00_0-sas-exp0x500605b0000272bf-phy11-lun-0
E: ID_REVISION=A4GNW7J0
E: ID_SERIAL=HGST_HUH728080ALE604_ZZZZH3VX
E: ID_SERIAL_SHORT=ZZZZH3VX
E: ID_TYPE=disk
E: ID_WWN=0x5000cca23be1dc3c
E: ID_WWN_WITH_EXTENSION=0x5000cca23be1dc3c
E: MAJOR=8
E: MINOR=208
E: SUBSYSTEM=block
E: TAGS=:systemd:
E: USEC_INITIALIZED=6055690
```
If I install the same disk into a machine using an ATA driver, this behavior changes:
```
root@machine2:~# sg_vpd -p ai /dev/sdb
ATA information VPD page:
SAT Vendor identification: linux
SAT Product identification: libata
SAT Product revision level: 3.00
Device signature indicates SATA transport
ATA command IDENTIFY DEVICE response summary:
model: HGST HUH728080ALE604
serial number: ZZZZH3VX
firmware revision: A4GNW7J0
root@machine-2:~# /lib/udev/ata_id -x /dev/sdb
ID_ATA=1
ID_TYPE=disk
ID_BUS=ata
ID_MODEL=HGST_HUH728080ALE604
ID_MODEL_ENC=HGST\x20HUH728080ALE604\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
ID_REVISION=A4GNW7J0
<truncated>
```
2019-10-04 14:52:49 +02:00
|
|
|
if (!(sense[0] == 0x72 && desc[0] == 0x9 && desc[1] == 0x0c) &&
|
|
|
|
!(sense[0] == 0x70 && sense[12] == 0x00 && sense[13] == 0x1d)) {
|
2012-01-10 01:34:15 +01:00
|
|
|
errno = EIO;
|
2013-04-11 00:58:44 +02:00
|
|
|
return -1;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
2009-11-04 16:34:22 +01:00
|
|
|
|
2013-04-11 00:58:44 +02:00
|
|
|
return 0;
|
2009-11-04 16:34:22 +01:00
|
|
|
}
|
|
|
|
|
2018-08-22 10:06:26 +02:00
|
|
|
static int disk_identify_packet_device_command(
|
|
|
|
int fd,
|
|
|
|
void *buf,
|
|
|
|
size_t buf_len) {
|
|
|
|
|
2013-04-11 00:58:44 +02:00
|
|
|
uint8_t cdb[16] = {
|
|
|
|
/*
|
|
|
|
* ATA Pass-Through 16 byte command, as described in
|
|
|
|
*
|
|
|
|
* T10 04-262r8 ATA Command Pass-Through
|
|
|
|
*
|
|
|
|
* from http://www.t10.org/ftp/t10/document.04/04-262r8.pdf
|
|
|
|
*/
|
|
|
|
[0] = 0x85, /* OPERATION CODE: 16 byte pass through */
|
|
|
|
[1] = 4 << 1, /* PROTOCOL: PIO Data-in */
|
|
|
|
[2] = 0x2e, /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
|
|
|
|
[3] = 0, /* FEATURES */
|
|
|
|
[4] = 0, /* FEATURES */
|
|
|
|
[5] = 0, /* SECTORS */
|
|
|
|
[6] = 1, /* SECTORS */
|
|
|
|
[7] = 0, /* LBA LOW */
|
|
|
|
[8] = 0, /* LBA LOW */
|
|
|
|
[9] = 0, /* LBA MID */
|
|
|
|
[10] = 0, /* LBA MID */
|
|
|
|
[11] = 0, /* LBA HIGH */
|
|
|
|
[12] = 0, /* LBA HIGH */
|
|
|
|
[13] = 0, /* DEVICE */
|
|
|
|
[14] = 0xA1, /* Command: ATA IDENTIFY PACKET DEVICE */
|
|
|
|
[15] = 0, /* CONTROL */
|
|
|
|
};
|
|
|
|
uint8_t sense[32] = {};
|
|
|
|
uint8_t *desc = sense + 8;
|
|
|
|
struct sg_io_v4 io_v4 = {
|
|
|
|
.guard = 'Q',
|
|
|
|
.protocol = BSG_PROTOCOL_SCSI,
|
|
|
|
.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD,
|
|
|
|
.request_len = sizeof (cdb),
|
|
|
|
.request = (uintptr_t) cdb,
|
|
|
|
.max_response_len = sizeof (sense),
|
|
|
|
.response = (uintptr_t) sense,
|
|
|
|
.din_xfer_len = buf_len,
|
|
|
|
.din_xferp = (uintptr_t) buf,
|
|
|
|
.timeout = COMMAND_TIMEOUT_MSEC,
|
|
|
|
};
|
2012-01-10 01:34:15 +01:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ioctl(fd, SG_IO, &io_v4);
|
|
|
|
if (ret != 0) {
|
|
|
|
/* could be that the driver doesn't do version 4, try version 3 */
|
|
|
|
if (errno == EINVAL) {
|
2013-04-11 00:58:44 +02:00
|
|
|
struct sg_io_hdr io_hdr = {
|
|
|
|
.interface_id = 'S',
|
|
|
|
.cmdp = (unsigned char*) cdb,
|
|
|
|
.cmd_len = sizeof (cdb),
|
|
|
|
.dxferp = buf,
|
|
|
|
.dxfer_len = buf_len,
|
|
|
|
.sbp = sense,
|
|
|
|
.mx_sb_len = sizeof (sense),
|
|
|
|
.dxfer_direction = SG_DXFER_FROM_DEV,
|
|
|
|
.timeout = COMMAND_TIMEOUT_MSEC,
|
|
|
|
};
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
ret = ioctl(fd, SG_IO, &io_hdr);
|
|
|
|
if (ret != 0)
|
2013-04-11 00:58:44 +02:00
|
|
|
return ret;
|
|
|
|
} else
|
|
|
|
return ret;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!(sense[0] == 0x72 && desc[0] == 0x9 && desc[1] == 0x0c)) {
|
|
|
|
errno = EIO;
|
2013-04-11 00:58:44 +02:00
|
|
|
return -1;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
2010-11-04 13:55:58 +01:00
|
|
|
|
2013-04-11 00:58:44 +02:00
|
|
|
return 0;
|
2010-11-04 13:55:58 +01:00
|
|
|
}
|
|
|
|
|
2009-11-04 16:34:22 +01:00
|
|
|
/**
|
|
|
|
* disk_identify_get_string:
|
|
|
|
* @identify: A block of IDENTIFY data
|
|
|
|
* @offset_words: Offset of the string to get, in words.
|
|
|
|
* @dest: Destination buffer for the string.
|
|
|
|
* @dest_len: Length of destination buffer, in bytes.
|
|
|
|
*
|
|
|
|
* Copies the ATA string from @identify located at @offset_words into @dest.
|
|
|
|
*/
|
2018-08-22 10:06:26 +02:00
|
|
|
static void disk_identify_get_string(
|
|
|
|
uint8_t identify[512],
|
2018-10-19 20:00:46 +02:00
|
|
|
unsigned offset_words,
|
2018-08-22 10:06:26 +02:00
|
|
|
char *dest,
|
|
|
|
size_t dest_len) {
|
|
|
|
|
2018-10-19 20:00:46 +02:00
|
|
|
unsigned c1;
|
|
|
|
unsigned c2;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
while (dest_len > 0) {
|
2012-02-01 12:50:48 +01:00
|
|
|
c1 = identify[offset_words * 2 + 1];
|
|
|
|
c2 = identify[offset_words * 2];
|
2012-01-10 01:34:15 +01:00
|
|
|
*dest = c1;
|
|
|
|
dest++;
|
|
|
|
*dest = c2;
|
|
|
|
dest++;
|
|
|
|
offset_words++;
|
|
|
|
dest_len -= 2;
|
|
|
|
}
|
2009-11-04 16:34:22 +01:00
|
|
|
}
|
|
|
|
|
2018-08-22 10:06:26 +02:00
|
|
|
static void disk_identify_fixup_string(
|
|
|
|
uint8_t identify[512],
|
2018-10-19 20:00:46 +02:00
|
|
|
unsigned offset_words,
|
2018-08-22 10:06:26 +02:00
|
|
|
size_t len) {
|
2012-01-10 01:34:15 +01:00
|
|
|
disk_identify_get_string(identify, offset_words,
|
|
|
|
(char *) identify + offset_words * 2, len);
|
2009-11-04 16:34:22 +01:00
|
|
|
}
|
|
|
|
|
2018-10-19 20:00:46 +02:00
|
|
|
static void disk_identify_fixup_uint16 (uint8_t identify[512], unsigned offset_words) {
|
2012-01-10 01:34:15 +01:00
|
|
|
uint16_t *p;
|
2009-11-04 16:34:22 +01:00
|
|
|
|
2012-01-10 01:34:15 +01:00
|
|
|
p = (uint16_t *) identify;
|
|
|
|
p[offset_words] = le16toh (p[offset_words]);
|
2009-11-04 16:34:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* disk_identify:
|
|
|
|
* @fd: File descriptor for the block device.
|
|
|
|
* @out_identify: Return location for IDENTIFY data.
|
2010-11-04 13:55:58 +01:00
|
|
|
* @out_is_packet_device: Return location for whether returned data is from a IDENTIFY PACKET DEVICE.
|
2009-11-04 16:34:22 +01:00
|
|
|
*
|
2010-11-04 13:55:58 +01:00
|
|
|
* Sends the IDENTIFY DEVICE or IDENTIFY PACKET DEVICE command to the
|
|
|
|
* device represented by @fd. If successful, then the result will be
|
|
|
|
* copied into @out_identify and @out_is_packet_device.
|
2009-11-04 16:34:22 +01:00
|
|
|
*
|
2018-07-03 01:27:02 +02:00
|
|
|
* This routine is based on code from libatasmart, LGPL v2.1.
|
2009-11-04 16:34:22 +01:00
|
|
|
*
|
2010-11-04 13:55:58 +01:00
|
|
|
* Returns: 0 if the data was successfully obtained, otherwise
|
|
|
|
* non-zero with errno set.
|
2009-11-04 16:34:22 +01:00
|
|
|
*/
|
2018-08-22 10:05:57 +02:00
|
|
|
static int disk_identify(int fd,
|
2013-04-11 00:58:44 +02:00
|
|
|
uint8_t out_identify[512],
|
2018-08-22 10:05:57 +02:00
|
|
|
int *out_is_packet_device) {
|
2012-01-10 01:34:15 +01:00
|
|
|
int ret;
|
|
|
|
uint8_t inquiry_buf[36];
|
|
|
|
int peripheral_device_type;
|
|
|
|
int all_nul_bytes;
|
|
|
|
int n;
|
2013-04-15 00:48:45 +02:00
|
|
|
int is_packet_device = 0;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
/* init results */
|
2013-04-15 00:48:45 +02:00
|
|
|
memzero(out_identify, 512);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
/* If we were to use ATA PASS_THROUGH (12) on an ATAPI device
|
|
|
|
* we could accidentally blank media. This is because MMC's BLANK
|
|
|
|
* command has the same op-code (0x61).
|
|
|
|
*
|
|
|
|
* To prevent this from happening we bail out if the device
|
|
|
|
* isn't a Direct Access Block Device, e.g. SCSI type 0x00
|
|
|
|
* (CD/DVD devices are type 0x05). So we send a SCSI INQUIRY
|
|
|
|
* command first... libata is handling this via its SCSI
|
|
|
|
* emulation layer.
|
|
|
|
*
|
|
|
|
* This also ensures that we're actually dealing with a device
|
|
|
|
* that understands SCSI commands.
|
|
|
|
*
|
|
|
|
* (Yes, it is a bit perverse that we're tunneling the ATA
|
|
|
|
* command through SCSI and relying on the ATA driver
|
|
|
|
* emulating SCSI well-enough...)
|
|
|
|
*
|
|
|
|
* (See commit 160b069c25690bfb0c785994c7c3710289179107 for
|
|
|
|
* the original bug-fix and see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=556635
|
|
|
|
* for the original bug-report.)
|
|
|
|
*/
|
|
|
|
ret = disk_scsi_inquiry_command (fd, inquiry_buf, sizeof (inquiry_buf));
|
|
|
|
if (ret != 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* SPC-4, section 6.4.2: Standard INQUIRY data */
|
|
|
|
peripheral_device_type = inquiry_buf[0] & 0x1f;
|
|
|
|
if (peripheral_device_type == 0x05)
|
|
|
|
{
|
|
|
|
is_packet_device = 1;
|
|
|
|
ret = disk_identify_packet_device_command(fd, out_identify, 512);
|
|
|
|
goto check_nul_bytes;
|
|
|
|
}
|
2020-02-27 08:29:41 +01:00
|
|
|
if (!IN_SET(peripheral_device_type, 0x00, 0x14)) {
|
2012-01-10 01:34:15 +01:00
|
|
|
ret = -1;
|
|
|
|
errno = EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* OK, now issue the IDENTIFY DEVICE command */
|
|
|
|
ret = disk_identify_command(fd, out_identify, 512);
|
|
|
|
if (ret != 0)
|
|
|
|
goto out;
|
2009-11-04 16:34:22 +01:00
|
|
|
|
2010-11-04 13:55:58 +01:00
|
|
|
check_nul_bytes:
|
2012-01-10 01:34:15 +01:00
|
|
|
/* Check if IDENTIFY data is all NUL bytes - if so, bail */
|
|
|
|
all_nul_bytes = 1;
|
|
|
|
for (n = 0; n < 512; n++) {
|
|
|
|
if (out_identify[n] != '\0') {
|
|
|
|
all_nul_bytes = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (all_nul_bytes) {
|
|
|
|
ret = -1;
|
|
|
|
errno = EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
2009-11-04 16:34:22 +01:00
|
|
|
|
2010-10-30 17:44:06 +02:00
|
|
|
out:
|
2019-04-28 14:28:49 +02:00
|
|
|
if (out_is_packet_device)
|
2013-04-15 00:48:45 +02:00
|
|
|
*out_is_packet_device = is_packet_device;
|
2012-01-10 01:34:15 +01:00
|
|
|
return ret;
|
2009-11-04 16:34:22 +01:00
|
|
|
}
|
|
|
|
|
tree-wide: drop redundant _cleanup_ macros (#8810)
This drops a good number of type-specific _cleanup_ macros, and patches
all users to just use the generic ones.
In most recent code we abstained from defining type-specific macros, and
this basically removes all those added already, with the exception of
the really low-level ones.
Having explicit macros for this is not too useful, as the expression
without the extra macro is generally just 2ch wider. We should generally
emphesize generic code, unless there are really good reasons for
specific code, hence let's follow this in this case too.
Note that _cleanup_free_ and similar really low-level, libc'ish, Linux
API'ish macros continue to be defined, only the really high-level OO
ones are dropped. From now on this should really be the rule: for really
low-level stuff, such as memory allocation, fd handling and so one, go
ahead and define explicit per-type macros, but for high-level, specific
program code, just use the generic _cleanup_() macro directly, in order
to keep things simple and as readable as possible for the uninitiated.
Note that before this patch some of the APIs (notable libudev ones) were
already used with the high-level macros at some places and with the
generic _cleanup_ macro at others. With this patch we hence unify on the
latter.
2018-04-25 12:31:45 +02:00
|
|
|
int main(int argc, char *argv[]) {
|
2012-01-10 01:34:15 +01:00
|
|
|
struct hd_driveid id;
|
2014-12-24 00:04:37 +01:00
|
|
|
union {
|
|
|
|
uint8_t byte[512];
|
|
|
|
uint16_t wyde[256];
|
|
|
|
} identify;
|
2012-01-10 01:34:15 +01:00
|
|
|
char model[41];
|
|
|
|
char model_enc[256];
|
|
|
|
char serial[21];
|
|
|
|
char revision[9];
|
|
|
|
const char *node = NULL;
|
|
|
|
int export = 0;
|
2014-12-25 16:42:23 +01:00
|
|
|
_cleanup_close_ int fd = -1;
|
2012-01-10 01:34:15 +01:00
|
|
|
uint16_t word;
|
|
|
|
int is_packet_device = 0;
|
|
|
|
static const struct option options[] = {
|
|
|
|
{ "export", no_argument, NULL, 'x' },
|
|
|
|
{ "help", no_argument, NULL, 'h' },
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
2017-02-23 09:16:44 +01:00
|
|
|
log_set_target(LOG_TARGET_AUTO);
|
|
|
|
udev_parse_config();
|
2014-08-11 20:13:38 +02:00
|
|
|
log_parse_environment();
|
|
|
|
log_open();
|
|
|
|
|
2015-09-09 14:33:32 +02:00
|
|
|
for (;;) {
|
2012-01-10 01:34:15 +01:00
|
|
|
int option;
|
|
|
|
|
|
|
|
option = getopt_long(argc, argv, "xh", options, NULL);
|
|
|
|
if (option == -1)
|
|
|
|
break;
|
|
|
|
|
|
|
|
switch (option) {
|
|
|
|
case 'x':
|
|
|
|
export = 1;
|
|
|
|
break;
|
|
|
|
case 'h':
|
2019-10-11 14:41:58 +02:00
|
|
|
printf("Usage: %s [--export] [--help] <device>\n"
|
2015-01-01 18:11:22 +01:00
|
|
|
" -x,--export print values as environment keys\n"
|
2019-10-11 14:41:58 +02:00
|
|
|
" -h,--help print this help text\n\n",
|
|
|
|
program_invocation_short_name);
|
2014-12-25 16:42:23 +01:00
|
|
|
return 0;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
node = argv[optind];
|
2019-04-28 14:28:49 +02:00
|
|
|
if (!node) {
|
2013-12-24 16:39:37 +01:00
|
|
|
log_error("no node specified");
|
2014-12-25 16:42:23 +01:00
|
|
|
return 1;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
|
2014-02-13 14:59:56 +01:00
|
|
|
fd = open(node, O_RDONLY|O_NONBLOCK|O_CLOEXEC);
|
2012-01-10 01:34:15 +01:00
|
|
|
if (fd < 0) {
|
2013-12-24 16:39:37 +01:00
|
|
|
log_error("unable to open '%s'", node);
|
2014-12-25 16:42:23 +01:00
|
|
|
return 1;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
|
2018-08-22 10:05:57 +02:00
|
|
|
if (disk_identify(fd, identify.byte, &is_packet_device) == 0) {
|
2012-01-10 01:34:15 +01:00
|
|
|
/*
|
|
|
|
* fix up only the fields from the IDENTIFY data that we are going to
|
|
|
|
* use and copy it into the hd_driveid struct for convenience
|
|
|
|
*/
|
2014-12-24 00:04:37 +01:00
|
|
|
disk_identify_fixup_string(identify.byte, 10, 20); /* serial */
|
|
|
|
disk_identify_fixup_string(identify.byte, 23, 8); /* fwrev */
|
|
|
|
disk_identify_fixup_string(identify.byte, 27, 40); /* model */
|
|
|
|
disk_identify_fixup_uint16(identify.byte, 0); /* configuration */
|
|
|
|
disk_identify_fixup_uint16(identify.byte, 75); /* queue depth */
|
2015-09-22 18:00:52 +02:00
|
|
|
disk_identify_fixup_uint16(identify.byte, 76); /* SATA capabilities */
|
2014-12-24 00:04:37 +01:00
|
|
|
disk_identify_fixup_uint16(identify.byte, 82); /* command set supported */
|
|
|
|
disk_identify_fixup_uint16(identify.byte, 83); /* command set supported */
|
|
|
|
disk_identify_fixup_uint16(identify.byte, 84); /* command set supported */
|
|
|
|
disk_identify_fixup_uint16(identify.byte, 85); /* command set supported */
|
|
|
|
disk_identify_fixup_uint16(identify.byte, 86); /* command set supported */
|
|
|
|
disk_identify_fixup_uint16(identify.byte, 87); /* command set supported */
|
|
|
|
disk_identify_fixup_uint16(identify.byte, 89); /* time required for SECURITY ERASE UNIT */
|
|
|
|
disk_identify_fixup_uint16(identify.byte, 90); /* time required for enhanced SECURITY ERASE UNIT */
|
|
|
|
disk_identify_fixup_uint16(identify.byte, 91); /* current APM values */
|
|
|
|
disk_identify_fixup_uint16(identify.byte, 94); /* current AAM value */
|
2015-09-22 18:00:52 +02:00
|
|
|
disk_identify_fixup_uint16(identify.byte, 108); /* WWN */
|
|
|
|
disk_identify_fixup_uint16(identify.byte, 109); /* WWN */
|
|
|
|
disk_identify_fixup_uint16(identify.byte, 110); /* WWN */
|
|
|
|
disk_identify_fixup_uint16(identify.byte, 111); /* WWN */
|
2014-12-24 00:04:37 +01:00
|
|
|
disk_identify_fixup_uint16(identify.byte, 128); /* device lock function */
|
|
|
|
disk_identify_fixup_uint16(identify.byte, 217); /* nominal media rotation rate */
|
|
|
|
memcpy(&id, identify.byte, sizeof id);
|
2012-01-10 01:34:15 +01:00
|
|
|
} else {
|
|
|
|
/* If this fails, then try HDIO_GET_IDENTITY */
|
|
|
|
if (ioctl(fd, HDIO_GET_IDENTITY, &id) != 0) {
|
2014-11-28 19:29:59 +01:00
|
|
|
log_debug_errno(errno, "HDIO_GET_IDENTITY failed for '%s': %m", node);
|
2014-12-25 16:42:23 +01:00
|
|
|
return 2;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-18 21:52:26 +02:00
|
|
|
memcpy(model, id.model, 40);
|
2012-01-10 01:34:15 +01:00
|
|
|
model[40] = '\0';
|
|
|
|
udev_util_encode_string(model, model_enc, sizeof(model_enc));
|
2020-12-14 08:11:51 +01:00
|
|
|
udev_replace_whitespace((char *) id.model, model, 40);
|
2012-01-10 01:34:15 +01:00
|
|
|
util_replace_chars(model, NULL);
|
2020-12-14 08:11:51 +01:00
|
|
|
udev_replace_whitespace((char *) id.serial_no, serial, 20);
|
2012-01-10 01:34:15 +01:00
|
|
|
util_replace_chars(serial, NULL);
|
2020-12-14 08:11:51 +01:00
|
|
|
udev_replace_whitespace((char *) id.fw_rev, revision, 8);
|
2012-01-10 01:34:15 +01:00
|
|
|
util_replace_chars(revision, NULL);
|
|
|
|
|
|
|
|
if (export) {
|
|
|
|
/* Set this to convey the disk speaks the ATA protocol */
|
|
|
|
printf("ID_ATA=1\n");
|
|
|
|
|
|
|
|
if ((id.config >> 8) & 0x80) {
|
|
|
|
/* This is an ATAPI device */
|
|
|
|
switch ((id.config >> 8) & 0x1f) {
|
|
|
|
case 0:
|
|
|
|
printf("ID_TYPE=cd\n");
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
printf("ID_TYPE=tape\n");
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
printf("ID_TYPE=cd\n");
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
printf("ID_TYPE=optical\n");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printf("ID_TYPE=generic\n");
|
|
|
|
break;
|
|
|
|
}
|
2020-04-12 18:26:05 +02:00
|
|
|
} else
|
2012-01-10 01:34:15 +01:00
|
|
|
printf("ID_TYPE=disk\n");
|
|
|
|
printf("ID_BUS=ata\n");
|
|
|
|
printf("ID_MODEL=%s\n", model);
|
|
|
|
printf("ID_MODEL_ENC=%s\n", model_enc);
|
|
|
|
printf("ID_REVISION=%s\n", revision);
|
|
|
|
if (serial[0] != '\0') {
|
|
|
|
printf("ID_SERIAL=%s_%s\n", model, serial);
|
|
|
|
printf("ID_SERIAL_SHORT=%s\n", serial);
|
2020-04-12 18:26:05 +02:00
|
|
|
} else
|
2012-01-10 01:34:15 +01:00
|
|
|
printf("ID_SERIAL=%s\n", model);
|
|
|
|
|
|
|
|
if (id.command_set_1 & (1<<5)) {
|
2014-12-25 16:42:23 +01:00
|
|
|
printf("ID_ATA_WRITE_CACHE=1\n");
|
|
|
|
printf("ID_ATA_WRITE_CACHE_ENABLED=%d\n", (id.cfs_enable_1 & (1<<5)) ? 1 : 0);
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
if (id.command_set_1 & (1<<10)) {
|
|
|
|
printf("ID_ATA_FEATURE_SET_HPA=1\n");
|
|
|
|
printf("ID_ATA_FEATURE_SET_HPA_ENABLED=%d\n", (id.cfs_enable_1 & (1<<10)) ? 1 : 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TODO: use the READ NATIVE MAX ADDRESS command to get the native max address
|
|
|
|
* so it is easy to check whether the protected area is in use.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
if (id.command_set_1 & (1<<3)) {
|
|
|
|
printf("ID_ATA_FEATURE_SET_PM=1\n");
|
|
|
|
printf("ID_ATA_FEATURE_SET_PM_ENABLED=%d\n", (id.cfs_enable_1 & (1<<3)) ? 1 : 0);
|
|
|
|
}
|
|
|
|
if (id.command_set_1 & (1<<1)) {
|
|
|
|
printf("ID_ATA_FEATURE_SET_SECURITY=1\n");
|
|
|
|
printf("ID_ATA_FEATURE_SET_SECURITY_ENABLED=%d\n", (id.cfs_enable_1 & (1<<1)) ? 1 : 0);
|
|
|
|
printf("ID_ATA_FEATURE_SET_SECURITY_ERASE_UNIT_MIN=%d\n", id.trseuc * 2);
|
|
|
|
if ((id.cfs_enable_1 & (1<<1))) /* enabled */ {
|
|
|
|
if (id.dlf & (1<<8))
|
|
|
|
printf("ID_ATA_FEATURE_SET_SECURITY_LEVEL=maximum\n");
|
|
|
|
else
|
|
|
|
printf("ID_ATA_FEATURE_SET_SECURITY_LEVEL=high\n");
|
|
|
|
}
|
|
|
|
if (id.dlf & (1<<5))
|
|
|
|
printf("ID_ATA_FEATURE_SET_SECURITY_ENHANCED_ERASE_UNIT_MIN=%d\n", id.trsEuc * 2);
|
|
|
|
if (id.dlf & (1<<4))
|
|
|
|
printf("ID_ATA_FEATURE_SET_SECURITY_EXPIRE=1\n");
|
|
|
|
if (id.dlf & (1<<3))
|
|
|
|
printf("ID_ATA_FEATURE_SET_SECURITY_FROZEN=1\n");
|
|
|
|
if (id.dlf & (1<<2))
|
|
|
|
printf("ID_ATA_FEATURE_SET_SECURITY_LOCKED=1\n");
|
|
|
|
}
|
|
|
|
if (id.command_set_1 & (1<<0)) {
|
|
|
|
printf("ID_ATA_FEATURE_SET_SMART=1\n");
|
|
|
|
printf("ID_ATA_FEATURE_SET_SMART_ENABLED=%d\n", (id.cfs_enable_1 & (1<<0)) ? 1 : 0);
|
|
|
|
}
|
|
|
|
if (id.command_set_2 & (1<<9)) {
|
|
|
|
printf("ID_ATA_FEATURE_SET_AAM=1\n");
|
|
|
|
printf("ID_ATA_FEATURE_SET_AAM_ENABLED=%d\n", (id.cfs_enable_2 & (1<<9)) ? 1 : 0);
|
|
|
|
printf("ID_ATA_FEATURE_SET_AAM_VENDOR_RECOMMENDED_VALUE=%d\n", id.acoustic >> 8);
|
|
|
|
printf("ID_ATA_FEATURE_SET_AAM_CURRENT_VALUE=%d\n", id.acoustic & 0xff);
|
|
|
|
}
|
|
|
|
if (id.command_set_2 & (1<<5)) {
|
|
|
|
printf("ID_ATA_FEATURE_SET_PUIS=1\n");
|
|
|
|
printf("ID_ATA_FEATURE_SET_PUIS_ENABLED=%d\n", (id.cfs_enable_2 & (1<<5)) ? 1 : 0);
|
|
|
|
}
|
|
|
|
if (id.command_set_2 & (1<<3)) {
|
|
|
|
printf("ID_ATA_FEATURE_SET_APM=1\n");
|
|
|
|
printf("ID_ATA_FEATURE_SET_APM_ENABLED=%d\n", (id.cfs_enable_2 & (1<<3)) ? 1 : 0);
|
|
|
|
if ((id.cfs_enable_2 & (1<<3)))
|
|
|
|
printf("ID_ATA_FEATURE_SET_APM_CURRENT_VALUE=%d\n", id.CurAPMvalues & 0xff);
|
|
|
|
}
|
|
|
|
if (id.command_set_2 & (1<<0))
|
|
|
|
printf("ID_ATA_DOWNLOAD_MICROCODE=1\n");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Word 76 indicates the capabilities of a SATA device. A PATA device shall set
|
|
|
|
* word 76 to 0000h or FFFFh. If word 76 is set to 0000h or FFFFh, then
|
|
|
|
* the device does not claim compliance with the Serial ATA specification and words
|
|
|
|
* 76 through 79 are not valid and shall be ignored.
|
|
|
|
*/
|
2014-12-25 16:44:34 +01:00
|
|
|
|
|
|
|
word = identify.wyde[76];
|
2017-10-04 16:01:32 +02:00
|
|
|
if (!IN_SET(word, 0x0000, 0xffff)) {
|
2012-01-10 01:34:15 +01:00
|
|
|
printf("ID_ATA_SATA=1\n");
|
|
|
|
/*
|
|
|
|
* If bit 2 of word 76 is set to one, then the device supports the Gen2
|
|
|
|
* signaling rate of 3.0 Gb/s (see SATA 2.6).
|
|
|
|
*
|
|
|
|
* If bit 1 of word 76 is set to one, then the device supports the Gen1
|
|
|
|
* signaling rate of 1.5 Gb/s (see SATA 2.6).
|
|
|
|
*/
|
|
|
|
if (word & (1<<2))
|
|
|
|
printf("ID_ATA_SATA_SIGNAL_RATE_GEN2=1\n");
|
|
|
|
if (word & (1<<1))
|
|
|
|
printf("ID_ATA_SATA_SIGNAL_RATE_GEN1=1\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Word 217 indicates the nominal media rotation rate of the device */
|
2014-12-25 16:44:34 +01:00
|
|
|
word = identify.wyde[217];
|
2014-12-25 16:42:23 +01:00
|
|
|
if (word == 0x0001)
|
|
|
|
printf ("ID_ATA_ROTATION_RATE_RPM=0\n"); /* non-rotating e.g. SSD */
|
|
|
|
else if (word >= 0x0401 && word <= 0xfffe)
|
|
|
|
printf ("ID_ATA_ROTATION_RATE_RPM=%d\n", word);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Words 108-111 contain a mandatory World Wide Name (WWN) in the NAA IEEE Registered identifier
|
|
|
|
* format. Word 108 bits (15:12) shall contain 5h, indicating that the naming authority is IEEE.
|
|
|
|
* All other values are reserved.
|
|
|
|
*/
|
2014-12-25 16:44:34 +01:00
|
|
|
word = identify.wyde[108];
|
2015-07-22 06:23:47 +02:00
|
|
|
if ((word & 0xf000) == 0x5000) {
|
|
|
|
uint64_t wwwn;
|
|
|
|
|
|
|
|
wwwn = identify.wyde[108];
|
|
|
|
wwwn <<= 16;
|
|
|
|
wwwn |= identify.wyde[109];
|
|
|
|
wwwn <<= 16;
|
|
|
|
wwwn |= identify.wyde[110];
|
|
|
|
wwwn <<= 16;
|
|
|
|
wwwn |= identify.wyde[111];
|
2015-06-24 01:48:18 +02:00
|
|
|
printf("ID_WWN=0x%1$" PRIx64 "\n"
|
|
|
|
"ID_WWN_WITH_EXTENSION=0x%1$" PRIx64 "\n",
|
2015-07-22 06:23:47 +02:00
|
|
|
wwwn);
|
|
|
|
}
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
/* from Linux's include/linux/ata.h */
|
2017-10-04 16:01:32 +02:00
|
|
|
if (IN_SET(identify.wyde[0], 0x848a, 0x844a) ||
|
2014-12-25 16:44:34 +01:00
|
|
|
(identify.wyde[83] & 0xc004) == 0x4004)
|
2012-01-10 01:34:15 +01:00
|
|
|
printf("ID_ATA_CFA=1\n");
|
|
|
|
} else {
|
|
|
|
if (serial[0] != '\0')
|
|
|
|
printf("%s_%s\n", model, serial);
|
|
|
|
else
|
|
|
|
printf("%s\n", model);
|
|
|
|
}
|
2014-12-25 16:42:23 +01:00
|
|
|
|
|
|
|
return 0;
|
2005-06-25 23:54:28 +02:00
|
|
|
}
|