Skip to content
This repository was archived by the owner on Mar 7, 2021. It is now read-only.

Commit baed97f

Browse files
committed
Implement seek on FileOperations
1 parent 71a699a commit baed97f

File tree

5 files changed

+114
-3
lines changed

5 files changed

+114
-3
lines changed

build.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const INCLUDED_FUNCTIONS: &[&str] = &[
2929
const INCLUDED_VARS: &[&str] = &[
3030
"EINVAL",
3131
"ENOMEM",
32+
"ESPIPE",
3233
"EFAULT",
3334
"__this_module",
3435
"FS_REQUIRES_DEV",
@@ -40,6 +41,9 @@ const INCLUDED_VARS: &[&str] = &[
4041
"KERN_INFO",
4142
"VERIFY_WRITE",
4243
"LINUX_VERSION_CODE",
44+
"SEEK_SET",
45+
"SEEK_CUR",
46+
"SEEK_END",
4347
];
4448
const OPAQUE_TYPES: &[&str] = &[
4549
// These need to be opaque because they're both packed and aligned, which rustc

src/chrdev.rs

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,27 @@ impl Drop for Registration {
9696
}
9797
}
9898

99+
pub struct File {
100+
ptr: *const bindings::file,
101+
}
102+
103+
impl File {
104+
unsafe fn from_ptr(ptr: *const bindings::file) -> File {
105+
File { ptr }
106+
}
107+
108+
pub fn pos(&self) -> u64 {
109+
unsafe { (*self.ptr).f_pos as u64 }
110+
}
111+
}
112+
113+
// Matches std::io::SeekFrom in the Rust stdlib
114+
pub enum SeekFrom {
115+
Start(u64),
116+
End(i64),
117+
Current(i64),
118+
}
119+
99120
pub struct FileOperationsVtable(bindings::file_operations);
100121

101122
unsafe extern "C" fn open_callback<T: FileOperations>(
@@ -141,12 +162,34 @@ unsafe extern "C" fn release_callback<T: FileOperations>(
141162
0
142163
}
143164

165+
unsafe extern "C" fn llseek_callback<T: FileOperations>(
166+
file: *mut bindings::file,
167+
offset: bindings::loff_t,
168+
whence: c_types::c_int,
169+
) -> bindings::loff_t {
170+
let off = match whence as u32 {
171+
bindings::SEEK_SET => match offset.try_into() {
172+
Ok(v) => SeekFrom::Start(v),
173+
Err(_) => return Error::EINVAL.to_kernel_errno().into(),
174+
},
175+
bindings::SEEK_CUR => SeekFrom::Current(offset),
176+
bindings::SEEK_END => SeekFrom::End(offset),
177+
_ => return Error::EINVAL.to_kernel_errno().into(),
178+
};
179+
let f = &*((*file).private_data as *const T);
180+
match f.seek(&File::from_ptr(file), off) {
181+
Ok(off) => off as bindings::loff_t,
182+
Err(e) => e.to_kernel_errno().into(),
183+
}
184+
}
185+
144186
impl FileOperationsVtable {
145187
pub const fn new<T: FileOperations>() -> FileOperationsVtable {
146188
FileOperationsVtable(bindings::file_operations {
147189
open: Some(open_callback::<T>),
148190
read: Some(read_callback::<T>),
149191
release: Some(release_callback::<T>),
192+
llseek: Some(llseek_callback::<T>),
150193

151194
check_flags: None,
152195
#[cfg(not(kernel_4_20_0_or_greater))]
@@ -167,7 +210,6 @@ impl FileOperationsVtable {
167210
iterate_shared: None,
168211
#[cfg(kernel_5_1_0_or_greater)]
169212
iopoll: None,
170-
llseek: None,
171213
lock: None,
172214
mmap: None,
173215
#[cfg(kernel_4_15_0_or_greater)]
@@ -194,5 +236,10 @@ pub trait FileOperations: Sync + Sized {
194236
const VTABLE: FileOperationsVtable;
195237

196238
fn open() -> KernelResult<Self>;
197-
fn read(&self, buf: &mut UserSlicePtrWriter) -> KernelResult<()>;
239+
fn read(&self, _buf: &mut UserSlicePtrWriter) -> KernelResult<()> {
240+
Err(Error::EINVAL)
241+
}
242+
fn seek(&self, _file: &File, _offset: SeekFrom) -> KernelResult<u64> {
243+
Err(Error::ESPIPE)
244+
}
198245
}

src/error.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ impl Error {
99
pub const EINVAL: Self = Error(-(bindings::EINVAL as i32));
1010
pub const ENOMEM: Self = Error(-(bindings::ENOMEM as i32));
1111
pub const EFAULT: Self = Error(-(bindings::EFAULT as i32));
12+
pub const ESPIPE: Self = Error(-(bindings::ESPIPE as i32));
1213

1314
pub fn from_kernel_errno(errno: c_types::c_int) -> Error {
1415
Error(errno)

tests/chrdev/src/lib.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,35 @@ impl linux_kernel_module::chrdev::FileOperations for CycleFile {
2424
}
2525
}
2626

27+
struct SeekFile;
28+
29+
impl linux_kernel_module::chrdev::FileOperations for SeekFile {
30+
const VTABLE: linux_kernel_module::chrdev::FileOperationsVtable =
31+
linux_kernel_module::chrdev::FileOperationsVtable::new::<Self>();
32+
33+
fn open() -> linux_kernel_module::KernelResult<Self> {
34+
return Ok(SeekFile);
35+
}
36+
37+
fn seek(
38+
&self,
39+
_file: &linux_kernel_module::chrdev::File,
40+
_offset: linux_kernel_module::chrdev::SeekFrom,
41+
) -> linux_kernel_module::KernelResult<u64> {
42+
return Ok(1234);
43+
}
44+
}
45+
2746
struct ChrdevTestModule {
2847
_chrdev_registration: linux_kernel_module::chrdev::Registration,
2948
}
3049

3150
impl linux_kernel_module::KernelModule for ChrdevTestModule {
3251
fn init() -> linux_kernel_module::KernelResult<Self> {
3352
let chrdev_registration =
34-
linux_kernel_module::chrdev::builder(cstr!("chrdev-tests"), 0..1)?
53+
linux_kernel_module::chrdev::builder(cstr!("chrdev-tests"), 0..2)?
3554
.register_device::<CycleFile>()
55+
.register_device::<SeekFile>()
3656
.build()?;
3757
Ok(ChrdevTestModule {
3858
_chrdev_registration: chrdev_registration,

tests/chrdev/tests/tests.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ fn mknod(path: &PathBuf, major: libc::dev_t, minor: libc::dev_t) -> UnlinkOnDrop
5454
}
5555

5656
const READ_FILE_MINOR: libc::dev_t = 0;
57+
const SEEK_FILE_MINOR: libc::dev_t = 1;
5758

5859
#[test]
5960
fn test_mknod() {
@@ -77,6 +78,22 @@ fn test_read() {
7778
});
7879
}
7980

81+
#[test]
82+
fn test_read_unimplemented() {
83+
with_kernel_module(|| {
84+
let device_number = get_device_major_number();
85+
let p = temporary_file_path();
86+
let _u = mknod(&p, device_number, SEEK_FILE_MINOR);
87+
88+
let mut f = fs::File::open(&p).unwrap();
89+
let mut data = [0; 12];
90+
assert_eq!(
91+
f.read_exact(&mut data).unwrap_err().raw_os_error().unwrap(),
92+
libc::EINVAL
93+
);
94+
})
95+
}
96+
8097
#[test]
8198
fn test_lseek_unimplemented() {
8299
with_kernel_module(|| {
@@ -108,3 +125,25 @@ fn test_lseek_unimplemented() {
108125
);
109126
});
110127
}
128+
129+
#[test]
130+
fn test_lseek() {
131+
with_kernel_module(|| {
132+
let device_number = get_device_major_number();
133+
let p = temporary_file_path();
134+
let _u = mknod(&p, device_number, SEEK_FILE_MINOR);
135+
136+
let mut f = fs::File::open(&p).unwrap();
137+
assert_eq!(f.seek(SeekFrom::Start(12)).unwrap(), 1234);
138+
assert_eq!(f.seek(SeekFrom::End(-12)).unwrap(), 1234);
139+
assert_eq!(f.seek(SeekFrom::Current(12)).unwrap(), 1234);
140+
141+
assert_eq!(
142+
f.seek(SeekFrom::Start(u64::max_value()))
143+
.unwrap_err()
144+
.raw_os_error()
145+
.unwrap(),
146+
libc::EINVAL
147+
);
148+
});
149+
}

0 commit comments

Comments
 (0)