Skip to content

Commit 5692ed3

Browse files
committed
ioctl: use bitfield structure
1 parent fa34f51 commit 5692ed3

File tree

5 files changed

+73
-32
lines changed

5 files changed

+73
-32
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ time = { version = "0.3", default-features = false }
134134
volatile = "0.6"
135135
zerocopy = { version = "0.8", default-features = false }
136136
uhyve-interface = "0.1.3"
137+
bitfield-struct = "0.11.0"
137138

138139
[dependencies.smoltcp]
139140
version = "0.12"

src/fd/socket/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ fn socket_handle_ioctl(
1818
) -> io::Result<()> {
1919
const FIONBIO: u32 = 0x8008_667eu32;
2020

21-
if cmd.0 == FIONBIO {
21+
if cmd.into_bits() == FIONBIO {
2222
let value = unsafe { *(argp as *const i32) };
2323
let status_flags = if value != 0 {
2424
fd::StatusFlags::O_NONBLOCK

src/fs/ioctl.rs

Lines changed: 69 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,49 +5,43 @@ use alloc::sync::Arc;
55
use alloc::vec::Vec;
66
use core::fmt::Debug;
77

8+
use bitfield_struct::bitfield;
9+
810
use crate::fd::{AccessPermission, ObjectInterface};
911
use crate::fs::{NodeKind, VfsNode};
1012
use crate::io;
1113

12-
#[derive(Copy, Clone)]
13-
pub struct IoCtlCall(pub u32);
14-
15-
impl Debug for IoCtlCall {
16-
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
17-
f.debug_struct("IoCtlCall")
18-
.field("call_nr", &self.call_nr())
19-
.field("call_type", &self.call_type())
20-
.field("call_size", &self.call_size())
21-
.field("call_dir", &self.call_dir())
22-
.field(".0", &self.0)
23-
.finish()
24-
}
14+
/// Encoding for an IOCTL command, as done in the Linux Kernel.
15+
///
16+
/// See [relevant kernel header](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/asm-generic/ioctl.h?h=v6.15) for reference.
17+
///
18+
/// The goal of this interface is to easily support linux applications that communicate via IOCTL,
19+
/// so linux compatibility is an intended and explicit goal here.
20+
#[bitfield(u32)]
21+
pub struct IoCtlCall {
22+
call_nr: u8,
23+
24+
call_type: u8,
25+
26+
#[bits(2, from = IoCtlDirection::from_bits_truncate, default = IoCtlDirection::empty())]
27+
call_dir: IoCtlDirection,
28+
29+
#[bits(14)]
30+
call_size: u16,
2531
}
2632

2733
bitflags! {
28-
#[derive(Debug, Copy, Clone, Default)]
34+
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
2935
pub struct IoCtlDirection: u8 {
3036
const IOC_WRITE = 1;
3137
const IOC_READ = 2;
3238
}
3339
}
3440

35-
impl IoCtlCall {
36-
pub fn call_nr(&self) -> u8 {
37-
(self.0 & 0xff) as u8
38-
}
39-
40-
pub fn call_type(&self) -> u8 {
41-
((self.0 >> 8) & 0xff) as u8
42-
}
43-
44-
pub fn call_dir(&self) -> IoCtlDirection {
45-
let dir = (self.0 >> 30) & 0x3;
46-
IoCtlDirection::from_bits_truncate(dir as u8)
47-
}
48-
49-
pub fn call_size(&self) -> u16 {
50-
((self.0 >> 16) & 0x3fff) as u16
41+
impl IoCtlDirection {
42+
// Required for IoCtlCall
43+
const fn into_bits(self) -> u8 {
44+
self.bits()
5145
}
5246
}
5347

@@ -94,3 +88,48 @@ pub(crate) fn register_ioctl(path: &str, ioctl_object: Arc<dyn ObjectInterface>)
9488
.traverse_mount(&mut path, Box::new(IoCtlNode(ioctl_object)))
9589
.expect("Failed to mount ioctl: filesystem error");
9690
}
91+
92+
#[cfg(test)]
93+
mod tests {
94+
use crate::fs::ioctl::{IoCtlCall, IoCtlDirection};
95+
96+
#[test_case]
97+
fn ioctl_call_correctly_written() {
98+
let call_nr = 0x12u8;
99+
let call_type = 0x78u8;
100+
let call_dir = IoCtlDirection::IOC_WRITE;
101+
let call_size = 0x423u16;
102+
103+
let ioctl_call_number = (u32::from(call_size) << 18)
104+
| (u32::from(call_dir.bits()) << 16)
105+
| (u32::from(call_type) << 8)
106+
| u32::from(call_nr);
107+
108+
let call = IoCtlCall::new()
109+
.with_call_nr(call_nr)
110+
.with_call_type(call_type)
111+
.with_call_dir(call_dir)
112+
.with_call_size(call_size);
113+
114+
assert_eq!(ioctl_call_number, call.into_bits());
115+
}
116+
#[test_case]
117+
fn ioctl_call_correctly_parsed() {
118+
let call_nr = 0x12u8;
119+
let call_type = 0x78u8;
120+
let call_dir = IoCtlDirection::IOC_WRITE;
121+
let call_size = 0x423u16;
122+
123+
let ioctl_call_number = (u32::from(call_size) << 18)
124+
| (u32::from(call_dir.bits()) << 16)
125+
| (u32::from(call_type) << 8)
126+
| u32::from(call_nr);
127+
128+
let parsed = IoCtlCall::from_bits(ioctl_call_number);
129+
130+
assert_eq!(call_nr, parsed.call_nr());
131+
assert_eq!(call_type, parsed.call_type());
132+
assert_eq!(call_dir, parsed.call_dir());
133+
assert_eq!(call_size, parsed.call_size());
134+
}
135+
}

src/syscalls/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ pub unsafe extern "C" fn sys_ioctl(
483483
obj.map_or_else(
484484
|e| -i32::from(e),
485485
|v| {
486-
(*v).handle_ioctl(IoCtlCall(cmd as u32), argp)
486+
(*v).handle_ioctl(IoCtlCall::from_bits(cmd as u32), argp)
487487
.map_or_else(|e| -i32::from(e), |()| 0)
488488
},
489489
)

0 commit comments

Comments
 (0)