2 个版本
0.1.1 | 2020 年 9 月 4 日 |
---|---|
0.1.0 | 2020 年 8 月 19 日 |
147 在 操作系统 中排名
每月 12,878 次下载
在 12 个 crate 中使用 (直接使用 4 个)
24KB
114 行
iocuddle
iocuddle
是一个用于构建运行时安全的 ioctl()
接口的库。
现有方法从 Rust 与 ioctl
交互依赖于在调用站点进行类型转换和/或不安全的代码声明。这把安全的负担转移给了 ioctl
接口的使用者,这并不理想。
相比之下,iocuddle
尝试将不安全的代码负担转移到 ioctl
定义。一旦定义了 ioctl
,就可以在安全代码中执行该 ioctl
的所有执行。
接口
iocuddle
旨在处理内核 >=99% 的 ioctl
接口。然而,我们并不旨在处理所有可能的 ioctl
接口。以下将概述不同的 ioctl
接口。
经典接口
经典 ioctl
接口是那些在我们下面将要看到的现代接口之前创建的 ioctl
。它们基本上允许完全使用 ioctl
libc 函数,该函数定义为如下
use std::os::raw::{c_int, c_ulong};
extern "C" { fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int; }
此接口可以接受任何数量和类型的参数,并可以返回任何正整数(-1 保留用于与 errno
结合表示错误)。
此接口的一个主要缺点是它完全忽略了编译器对类型安全性的检查。特定的 request
隐式关联到一或多个类型,通常列在相关的 ioctl
man 页面上。如果程序员类型错误,最终会导致内存损坏。
这种接口的问题被早期认识到。因此,大多数 ioctl
只支持单个参数以减少复杂性。但这并没有解决缺乏编译器强制类型安全性的问题。
iocuddle
目前不支持具有多个参数的 ioctl
。否则,可以通过 Ioctl::classic()
构造函数定义并使用经典的 ioctl
接口,如下所示
use std::os::raw::{c_void, c_int, c_uint};
use iocuddle::*;
let mut file = std::fs::File::open("/dev/tty").unwrap_or_else(|_| std::process::exit(0));
// This is the simplest ioctl call. The request number is provided via the
// Ioctl::classic() constructor. This ioctl reads a C integer from the
// kernel by internally passing a reference to a c_int as the argument to
// the ioctl. This c_int is returned in the Ok status of the ioctl Result.
//
// Notice that since the state of the file descriptor is not modified via
// this ioctl, we define it using the Read parameter.
const TIOCINQ: Ioctl<Read, &c_int> = unsafe { Ioctl::classic(0x541B) };
assert_eq!(TIOCINQ.ioctl(&file).unwrap(), (0 as c_uint, 0 as c_int));
// This ioctl is similar to the previous one. It differs in two important
// respects. First, this raw ioctl takes an input argument rather than an
// output argument. This raw argument is a C integer *NOT* a reference to
// a C integer. Second, since this ioctl modifies the state of the file
// descriptor we use Write instead of Read.
//
// Notice that the return value of the TCSBRK.ioctl() call is the positive
// integer returned from the raw ioctl(), unlike the previous example. It
// is not the input argument type.
const TCSBRK: Ioctl<Write, c_int> = unsafe { Ioctl::classic(0x5409) };
assert_eq!(TCSBRK.ioctl(&mut file, 0).unwrap(), 0 as c_uint);
// `iocuddle` can also support classic ioctls with no argument. These
// always modify the file descriptor state, so the Write parameter is
// used.
const TIOCSBRK: Ioctl<Write, c_void> = unsafe { Ioctl::classic(0x5427) };
const TIOCCBRK: Ioctl<Write, c_void> = unsafe { Ioctl::classic(0x5428) };
assert_eq!(TIOCSBRK.ioctl(&mut file).unwrap(), 0);
assert_eq!(TIOCCBRK.ioctl(&mut file).unwrap(), 0);
现代接口
为了减轻经典接口中的类型安全问题,Linux 内核开发了一套新的规范来开发 ioctl
。我们称这些规范为现代接口。
现代 ioctl
接口始终接受一个结构体或整数的引用,并在失败时返回 -1
,在成功时返回 0
(或偶尔另一个正整数)。ioctl
请求号由四个参数构建
- 一个
group
(在内核宏中混乱地称为type
) - 一个
nr
(数字) - 一个
direction
- (类型的大小)
type
group
参数用作命名空间,以分组相关的 ioctl
。它是一个整数值。
nr
参数是一个整数判别器,用于在 group
内唯一标识 ioctl
。
direction
参数标识数据流动的方向。如果数据从用户空间流向内核,这是 write
方向。如果数据从内核流向用户空间,这是 read
方向。双向流动的数据被标记为 write/read
方向。
type
参数标识应与该 ioctl
一起使用的类型。在内核 C 代码中,此类型仅直接用于使用类型的大小调整 ioctl
请求号。iocuddle
还使用此参数来提供类型安全。
使用 iocuddle
定义现代 ioctl
的样子如下
use iocuddle::*;
// Define the Group of KVM ioctls.
const KVM: Group = Group::new(0xAE);
// Define ioctls within the KVM group.
//
// The nr is passed to the direction-specific constructor.
const KVM_PPC_ALLOCATE_HTAB: Ioctl<WriteRead, &u32> = unsafe { KVM.write_read(0xa7) };
const KVM_X86_GET_MCE_CAP_SUPPORTED: Ioctl<Read, &u64> = unsafe { KVM.read(0x9d) };
const KVM_X86_SETUP_MCE: Ioctl<Write, &u64> = unsafe { KVM.write(0x9c) };
内核文档
有关 ioctl 流程的内核文档,请参阅内核源树中的以下文件:Documentation/userspace-api/ioctl/ioctl-number.rst
许可证:Apache-2.0