当尝试为我的一个驱动器提取特定于供应商的功能时 (cmd = 0x89),我收到溢出错误:
OverflowError: signed integer is greater than maximum
。调用 ioctl 后立即抛出该错误。回溯也不是很有信息:
Traceback (most recent call last):
File "lockbx/atacmd.py", line 148, in <module>
GetDriveIdSgIo("/dev/sda")
File "lockbx/atacmd.py", line 134, in GetDriveIdSgIo
ret = fcntl.ioctl(fd.fileno(), SG_IO, ctypes.addressof(sgio))
OverflowError: signed integer is greater than maximum
ATA 命令传递参考:http://www.t10.org/ftp/t10/document.04/04-262r8.pdf
class AtaCmd(ctypes.Structure):
_fields_ = [
('opcode', ctypes.c_ubyte),
('protocol', ctypes.c_ubyte),
('flags', ctypes.c_ubyte),
('features', ctypes.c_ubyte),
('sector_count', ctypes.c_ubyte),
('lba_low', ctypes.c_ubyte),
('lba_mid', ctypes.c_ubyte),
('lba_high', ctypes.c_ubyte),
('device', ctypes.c_ubyte),
('command', ctypes.c_ubyte),
('reserved', ctypes.c_ubyte),
('control', ctypes.c_ubyte)]
class SgioHdr(ctypes.Structure):
"""<scsi/sg.h> sg_io_hdr_t."""
_fields_ = [
('interface_id', ctypes.c_int),
('dxfer_direction', ctypes.c_int),
('cmd_len', ctypes.c_ubyte),
('mx_sb_len', ctypes.c_ubyte),
('iovec_count', ctypes.c_ushort),
('dxfer_len', ctypes.c_uint),
('dxferp', ctypes.c_void_p),
('cmdp', ctypes.c_void_p),
('sbp', ctypes.c_void_p),
('timeout', ctypes.c_uint),
('flags', ctypes.c_uint),
('pack_id', ctypes.c_int),
('usr_ptr', ctypes.c_void_p),
('status', ctypes.c_ubyte),
('masked_status', ctypes.c_ubyte),
('msg_status', ctypes.c_ubyte),
('sb_len_wr', ctypes.c_ubyte),
('host_status', ctypes.c_ushort),
('driver_status', ctypes.c_ushort),
('resid', ctypes.c_int),
('duration', ctypes.c_uint),
('info', ctypes.c_uint)]
def GetDriveIdSgIo(dev):
ata_cmd = AtaCmd(opcode=0xA1, # ATA PASS-THROUGH (12)
protocol=3 << 1, # PIO Data-In
flags=0x06,
features=0xF5,
sector_count=1,
lba_low=0, lba_mid=0, lba_high=0,
device=0,
command=0x89, # IDENTIFY
reserved=0,
control=0)
SG_DXFER_FROM_DEV = -3
sense = ctypes.c_buffer(64)
identify = ctypes.c_buffer(512)
sgio = SgioHdr(
interface_id=ord('S'),
dxfer_direction=SG_DXFER_FROM_DEV,
cmd_len=ctypes.sizeof(ata_cmd),
mx_sb_len=ctypes.sizeof(sense),
iovec_count=0,
dxfer_len=ctypes.sizeof(identify),
dxferp=0,
cmdp=ctypes.addressof(ata_cmd),
sbp=ctypes.addressof(sense),
timeout=3000,
flags=0,
pack_id=0,
usr_ptr=None,
# Set values
status=0, masked_status=0, msg_status=0, sb_len_wr=0, host_status=0,
driver_status=0, resid=0, duration=0, info=0
)
SG_IO = 0x2285 # <scsi/sg.h>
with open(dev, 'r') as fd:
ret = fcntl.ioctl(fd.fileno(), SG_IO, ctypes.addressof(sgio)) # fails here
# some other stuff returns endian swapped buffer info but not relevant
GetDriveIdSgIo("/dev/sda")
我哪里出错了?
试试这个:
ret = fcntl.ioctl(fd.fileno(), 0x2285, ctypes.addressof(sgio))
也许 python 将 SG_IO 存储为 int64,但 ioctl 只接受 32 位整数作为 arg。或者明确地将其转换为 32 位:
c_uint(0x2285)
如果这不起作用,可能是 arg 参数的问题。试试这个:
ret = fcntl.ioctl(fd.fileno(), 0x2285, sgio)
如果这不起作用,请尝试使用
struct.pack
而不是 ctypes
作为 sgio 参数,并使用 struct.unpack
来读取 ret。
您收到的是 32 位还是 64 位? 如果我没记错的话,默认情况下它是 ctypes 中的 32 位值。
晚了 3 年,但我自己也遇到过这个问题,解决方案就是使用 libc ioctl。
# We use libc instead of the builtin ioctl as the builtin can have
# issues with 64-bit pointers.
result = get_libc().ioctl(
self.fd, IOCTL_SG_IO, ctypes.byref(sg_io_header)
)
get_libc() 的位置是:
import ctypes
from functools import cache
@cache
def get_libc():
# Opens the libc.so, which can be quite a slow process, and
# saves it for future use.
return ctypes.CDLL("libc.so.6", use_errno=True)
这就是我的 SMARTie 库中所做的事情。