Python调用FS_IOC_ADD_ENCRYPTION_KEY ioctl遇无效参数问题求解
问题:Python调用
FS_IOC_ADD_ENCRYPTION_KEY ioctl时出现无效参数错误 我正在编写Python脚本,需要使用FS_IOC_ADD_ENCRYPTION_KEY ioctl接口。该ioctl要求传入指向fscrypt_add_key_arg结构体的指针,其Linux内核头文件定义如下:
struct fscrypt_add_key_arg { struct fscrypt_key_specifier key_spec; __u32 raw_size; __u32 key_id; __u32 __reserved[8]; __u8 raw[]; }; #define FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR 1 #define FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER 2 #define FSCRYPT_KEY_DESCRIPTOR_SIZE 8 #define FSCRYPT_KEY_IDENTIFIER_SIZE 16 struct fscrypt_key_specifier { __u32 type; /* one of FSCRYPT_KEY_SPEC_TYPE_* */ __u32 __reserved; union { __u8 __reserved[32]; /* reserve some extra space */ __u8 descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; __u8 identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]; } u; };
我的Python代码如下:
import fcntl import struct import os FS_IOC_ADD_ENCRYPTION_KEY = 0xc0506617 policy_data.key_descriptor: str = get_key_descriptor() policy_key: bytes = get_policy_key(policy_data) fscrypt_key_specifier = struct.pack( 'II16s', 0x2, 0, bytes.fromhex(policy_data.key_descriptor) ) fscrypt_add_key_arg = struct.pack( f'{len(fscrypt_key_specifier)}sII8I{len(policy_key)}s', fscrypt_key_specifier, len(policy_key), 0, 0, 0, 0, 0, 0, 0, 0, 0, policy_key ) fd = os.open('/mnt/external', os.O_RDONLY) res = fcntl.ioctl(fd, FS_IOC_ADD_ENCRYPTION_KEY, fscrypt_add_key_arg) print(res)
执行代码时出现以下OSError错误:
Traceback (most recent call last): File "/home/foo/fscryptdump/./main.py", line 101, in <module> res = fcntl.ioctl(fd, FS_IOC_ADD_ENCRYPTION_KEY, fscrypt_add_key_arg) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ OSError: [Errno 22] Invalid argument
我已核对该ioctl的文档,确认传入值正确,但怀疑是结构体打包方式存在问题,该如何解决?
解决方法
问题核心是C结构体的内存对齐和Python struct打包格式不匹配,以及对union和可变长度数组的处理有误。以下是具体修正步骤:
1. 正确处理fscrypt_key_specifier的内存布局
C语言中union的大小由其最大成员决定,这里union的最大成员是__reserved[32],因此整个fscrypt_key_specifier的总大小为:
- 4字节(
__u32 type) + 4字节(__u32 __reserved) + 32字节(union) = 40字节。
你之前用II16s打包仅处理了16字节的identifier,但union实际占32字节,剩余16字节未填充,导致结构体大小错误、内存布局错位。正确的打包格式应为II32s,且使用identifier(类型0x2)时,需将16字节的identifier放在union前16位,剩余16字节补0。
2. 修正fscrypt_add_key_arg的打包方式
不要用{len(fscrypt_key_specifier)}s嵌套打包,直接按结构体字段顺序打包,并指定原生字节对齐(Linux内核默认4字节对齐,Python中用@格式符):
- 40字节的
fscrypt_key_specifier - 4字节的
__u32 raw_size - 4字节的
__u32 key_id - 32字节的
__u32 __reserved[8] - 可变长度的
raw[](密钥字节数据)
3. 修正后的完整代码
import fcntl import struct import os FS_IOC_ADD_ENCRYPTION_KEY = 0xc0506617 # 假设get_key_descriptor和get_policy_key为已实现的函数 policy_key_descriptor = get_key_descriptor() policy_key = get_policy_key(policy_data) # 构造fscrypt_key_specifier:类型为IDENTIFIER(0x2),union填充identifier+16字节0 key_spec_type = 0x2 identifier_bytes = bytes.fromhex(policy_key_descriptor) # union部分:identifier占16字节,剩余16字节补0 union_data = identifier_bytes.ljust(32, b'\x00') # 打包fscrypt_key_specifier:原生对齐 + type + reserved + union key_spec_packed = struct.pack('@II32s', key_spec_type, 0, union_data) # 构造并打包fscrypt_add_key_arg raw_size = len(policy_key) key_id = 0 reserved_fields = [0] * 8 # 格式符:原生对齐 + 40字节key_spec + raw_size + key_id + 8个reserved + 密钥数据 add_key_format = f'@40sII8I{raw_size}s' add_key_arg = struct.pack( add_key_format, key_spec_packed, raw_size, key_id, *reserved_fields, policy_key ) # 打开加密目录挂载点(确保/mnt/external是支持fscrypt的文件系统) fd = os.open('/mnt/external', os.O_RDONLY) try: res = fcntl.ioctl(fd, FS_IOC_ADD_ENCRYPTION_KEY, add_key_arg) print(f"ioctl执行成功,返回值:{res}") finally: os.close(fd)
4. 额外检查点
- 确认
policy_key_descriptor是16字节的十六进制字符串,转换后的字节长度必须为16(匹配FSCRYPT_KEY_IDENTIFIER_SIZE)。 - 确保
/mnt/external是ext4、f2fs等支持fscrypt的文件系统挂载点,且已启用加密策略。 - 核对
FS_IOC_ADD_ENCRYPTION_KEY的魔术值:x86_64架构下为0xc0506617,其他架构需通过grep FS_IOC_ADD_ENCRYPTION_KEY /usr/include/linux/fscrypt.h确认。
内容的提问来源于stack exchange,提问作者Ricky Sixx
相关产品推荐
相关产品推荐

