Skip to content

Commit 8bbb1a9

Browse files
authored
Fix Linux custom ioctl opcode computation (#861)
* Fix Linux ioctl opcode computation, add test * Document the corresponding C macros for ioctl opcode computation * Rename shift_and_mask -> mask_and_shift in ioctl opcode computation
1 parent 2bdf3ed commit 8bbb1a9

3 files changed

Lines changed: 58 additions & 6 deletions

File tree

src/ioctl/linux.rs

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ pub(super) const fn compose_opcode(
1010
num: RawOpcode,
1111
size: RawOpcode,
1212
) -> RawOpcode {
13-
macro_rules! shift_and_mask {
13+
macro_rules! mask_and_shift {
1414
($val:expr, $shift:expr, $mask:expr) => {{
15-
($val << $shift) & $mask
15+
($val & $mask) << $shift
1616
}};
1717
}
1818

@@ -23,10 +23,10 @@ pub(super) const fn compose_opcode(
2323
Direction::ReadWrite => READ | WRITE,
2424
};
2525

26-
shift_and_mask!(group, GROUP_SHIFT, GROUP_MASK)
27-
| shift_and_mask!(num, NUM_SHIFT, NUM_MASK)
28-
| shift_and_mask!(size, SIZE_SHIFT, SIZE_MASK)
29-
| shift_and_mask!(dir, DIR_SHIFT, DIR_MASK)
26+
mask_and_shift!(group, GROUP_SHIFT, GROUP_MASK)
27+
| mask_and_shift!(num, NUM_SHIFT, NUM_MASK)
28+
| mask_and_shift!(size, SIZE_SHIFT, SIZE_MASK)
29+
| mask_and_shift!(dir, DIR_SHIFT, DIR_MASK)
3030
}
3131

3232
const NUM_BITS: RawOpcode = 8;
@@ -81,3 +81,37 @@ mod consts {
8181
pub(super) const SIZE_BITS: RawOpcode = 13;
8282
pub(super) const DIR_BITS: RawOpcode = 3;
8383
}
84+
85+
#[cfg(not(any(
86+
// These have no ioctl opcodes defined in linux_raw_sys
87+
// so can't use that as a known-good value for this test.
88+
target_arch = "sparc",
89+
target_arch = "sparc64"
90+
)))]
91+
#[test]
92+
fn check_known_opcodes() {
93+
use crate::backend::c::{c_long, c_uint};
94+
use core::mem::size_of;
95+
96+
// _IOR('U', 15, unsigned int)
97+
assert_eq!(
98+
compose_opcode(
99+
Direction::Read,
100+
b'U' as RawOpcode,
101+
15,
102+
size_of::<c_uint>() as RawOpcode
103+
),
104+
linux_raw_sys::ioctl::USBDEVFS_CLAIMINTERFACE as RawOpcode
105+
);
106+
107+
// _IOW('v', 2, long)
108+
assert_eq!(
109+
compose_opcode(
110+
Direction::Write,
111+
b'v' as RawOpcode,
112+
2,
113+
size_of::<c_long>() as RawOpcode
114+
),
115+
linux_raw_sys::ioctl::FS_IOC_SETVERSION as RawOpcode
116+
);
117+
}

src/ioctl/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ impl Opcode {
205205
}
206206

207207
/// Create a new opcode from a direction, group, number and size.
208+
///
209+
/// This corresponds to the C macro `_IOC(direction, group, number, size)`
208210
#[cfg(any(linux_kernel, bsd))]
209211
#[inline]
210212
pub const fn from_components(
@@ -227,6 +229,8 @@ impl Opcode {
227229

228230
/// Create a new non-mutating opcode from a group, a number and the type of
229231
/// data.
232+
///
233+
/// This corresponds to the C macro `_IO(group, number)` when `T` is zero sized.
230234
#[cfg(any(linux_kernel, bsd))]
231235
#[inline]
232236
pub const fn none<T>(group: u8, number: u8) -> Self {
@@ -235,6 +239,8 @@ impl Opcode {
235239

236240
/// Create a new reading opcode from a group, a number and the type of
237241
/// data.
242+
///
243+
/// This corresponds to the C macro `_IOR(group, number, T)`.
238244
#[cfg(any(linux_kernel, bsd))]
239245
#[inline]
240246
pub const fn read<T>(group: u8, number: u8) -> Self {
@@ -243,6 +249,8 @@ impl Opcode {
243249

244250
/// Create a new writing opcode from a group, a number and the type of
245251
/// data.
252+
///
253+
/// This corresponds to the C macro `_IOW(group, number, T)`.
246254
#[cfg(any(linux_kernel, bsd))]
247255
#[inline]
248256
pub const fn write<T>(group: u8, number: u8) -> Self {
@@ -251,6 +259,8 @@ impl Opcode {
251259

252260
/// Create a new reading and writing opcode from a group, a number and the
253261
/// type of data.
262+
///
263+
/// This corresponds to the C macro `_IOWR(group, number, T)`.
254264
#[cfg(any(linux_kernel, bsd))]
255265
#[inline]
256266
pub const fn read_write<T>(group: u8, number: u8) -> Self {

src/ioctl/patterns.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ impl<const OPCODE: RawOpcode> CompileTimeOpcode for BadOpcode<OPCODE> {
166166
}
167167

168168
/// Provides a read code at compile time.
169+
///
170+
/// This corresponds to the C macro `_IOR(GROUP, NUM, Data)`.
169171
#[cfg(any(linux_kernel, bsd))]
170172
pub struct ReadOpcode<const GROUP: u8, const NUM: u8, Data>(Data);
171173

@@ -175,6 +177,8 @@ impl<const GROUP: u8, const NUM: u8, Data> CompileTimeOpcode for ReadOpcode<GROU
175177
}
176178

177179
/// Provides a write code at compile time.
180+
///
181+
/// This corresponds to the C macro `_IOW(GROUP, NUM, Data)`.
178182
#[cfg(any(linux_kernel, bsd))]
179183
pub struct WriteOpcode<const GROUP: u8, const NUM: u8, Data>(Data);
180184

@@ -184,6 +188,8 @@ impl<const GROUP: u8, const NUM: u8, Data> CompileTimeOpcode for WriteOpcode<GRO
184188
}
185189

186190
/// Provides a read/write code at compile time.
191+
///
192+
/// This corresponds to the C macro `_IOWR(GROUP, NUM, Data)`.
187193
#[cfg(any(linux_kernel, bsd))]
188194
pub struct ReadWriteOpcode<const GROUP: u8, const NUM: u8, Data>(Data);
189195

@@ -193,6 +199,8 @@ impl<const GROUP: u8, const NUM: u8, Data> CompileTimeOpcode for ReadWriteOpcode
193199
}
194200

195201
/// Provides a `None` code at compile time.
202+
///
203+
/// This corresponds to the C macro `_IO(GROUP, NUM)` when `Data` is zero sized.
196204
#[cfg(any(linux_kernel, bsd))]
197205
pub struct NoneOpcode<const GROUP: u8, const NUM: u8, Data>(Data);
198206

0 commit comments

Comments
 (0)