You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 06:54:50