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

Commit 8200dba

Browse files
committed
Refs #164 -- added write support to FileOperations
1 parent 46fcd24 commit 8200dba

File tree

4 files changed

+154
-6
lines changed

4 files changed

+154
-6
lines changed

src/file_operations.rs

+41-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use alloc::boxed::Box;
66
use crate::bindings;
77
use crate::c_types;
88
use crate::error::{Error, KernelResult};
9-
use crate::user_ptr::{UserSlicePtr, UserSlicePtrWriter};
9+
use crate::user_ptr::{UserSlicePtr, UserSlicePtrReader, UserSlicePtrWriter};
1010

1111
pub struct File {
1212
ptr: *const bindings::file,
@@ -70,6 +70,33 @@ unsafe extern "C" fn read_callback<T: Read>(
7070
}
7171
}
7272

73+
unsafe extern "C" fn write_callback<T: Write>(
74+
file: *mut bindings::file,
75+
buf: *const c_types::c_char,
76+
len: c_types::c_size_t,
77+
offset: *mut bindings::loff_t,
78+
) -> c_types::c_ssize_t {
79+
let mut data = match UserSlicePtr::new(buf as *mut c_types::c_void, len) {
80+
Ok(ptr) => ptr.reader(),
81+
Err(e) => return e.to_kernel_errno().try_into().unwrap(),
82+
};
83+
let f = &*((*file).private_data as *const T);
84+
// No FMODE_UNSIGNED_OFFSET support, so offset must be in [0, 2^63).
85+
// See discussion in #113
86+
let positive_offset = match (*offset).try_into() {
87+
Ok(v) => v,
88+
Err(_) => return Error::EINVAL.to_kernel_errno().try_into().unwrap(),
89+
};
90+
match f.write(&mut data, positive_offset) {
91+
Ok(()) => {
92+
let read = len - data.len();
93+
(*offset) += bindings::loff_t::try_from(read).unwrap();
94+
read.try_into().unwrap()
95+
}
96+
Err(e) => e.to_kernel_errno().try_into().unwrap(),
97+
}
98+
}
99+
73100
unsafe extern "C" fn release_callback<T: FileOperations>(
74101
_inode: *mut bindings::inode,
75102
file: *mut bindings::file,
@@ -168,6 +195,13 @@ impl<T: Read> FileOperationsVtableBuilder<T> {
168195
}
169196
}
170197

198+
impl<T: Write> FileOperationsVtableBuilder<T> {
199+
pub const fn write(mut self) -> FileOperationsVtableBuilder<T> {
200+
self.0.write = Some(write_callback::<T>);
201+
self
202+
}
203+
}
204+
171205
impl<T: Seek> FileOperationsVtableBuilder<T> {
172206
pub const fn seek(mut self) -> FileOperationsVtableBuilder<T> {
173207
self.0.llseek = Some(llseek_callback::<T>);
@@ -199,6 +233,12 @@ pub trait Read {
199233
fn read(&self, buf: &mut UserSlicePtrWriter, offset: u64) -> KernelResult<()>;
200234
}
201235

236+
pub trait Write {
237+
/// Writes data from userspace o this file. Corresponds to the `write`
238+
/// function pointer in `struct file_operations`.
239+
fn write(&self, buf: &mut UserSlicePtrReader, offset: u64) -> KernelResult<()>;
240+
}
241+
202242
pub trait Seek {
203243
/// Changes the position of the file. Corresponds to the `llseek` function
204244
/// pointer in `struct file_operations`.

src/user_ptr.rs

+22-3
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,7 @@ impl UserSlicePtr {
6666
/// Returns EFAULT if the address does not currently point to
6767
/// mapped, readable memory.
6868
pub fn read_all(self) -> error::KernelResult<Vec<u8>> {
69-
let mut data = vec![0; self.1];
70-
self.reader().read(&mut data)?;
71-
Ok(data)
69+
self.reader().read_all()
7270
}
7371

7472
/// Construct a `UserSlicePtrReader` that can incrementally read
@@ -97,6 +95,27 @@ impl UserSlicePtr {
9795
pub struct UserSlicePtrReader(*mut c_types::c_void, usize);
9896

9997
impl UserSlicePtrReader {
98+
/// Returns the number of bytes left to be read from this. Note that even
99+
/// reading less than this number of bytes may return an Error().
100+
pub fn len(&self) -> usize {
101+
self.1
102+
}
103+
104+
/// Returns `true` if `self.len()` is 0.
105+
pub fn is_empty(&self) -> bool {
106+
self.len() == 0
107+
}
108+
109+
/// Read all data remaining in the user slice and return it in a `Vec`.
110+
///
111+
/// Returns EFAULT if the address does not currently point to
112+
/// mapped, readable memory.
113+
pub fn read_all(&mut self) -> error::KernelResult<Vec<u8>> {
114+
let mut data = vec![0; self.1];
115+
self.read(&mut data)?;
116+
Ok(data)
117+
}
118+
100119
pub fn read(&mut self, data: &mut [u8]) -> error::KernelResult<()> {
101120
if data.len() > self.1 || data.len() > u32::MAX as usize {
102121
return Err(error::Error::EFAULT);

tests/chrdev/src/lib.rs

+47-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#![no_std]
22

3+
use alloc::string::ToString;
4+
use core::sync::atomic::{AtomicUsize, Ordering};
5+
36
use linux_kernel_module::{self, cstr};
47

58
struct CycleFile;
@@ -55,16 +58,59 @@ impl linux_kernel_module::file_operations::Seek for SeekFile {
5558
}
5659
}
5760

61+
struct WriteFile {
62+
written: AtomicUsize,
63+
}
64+
65+
impl linux_kernel_module::file_operations::FileOperations for WriteFile {
66+
const VTABLE: linux_kernel_module::file_operations::FileOperationsVtable =
67+
linux_kernel_module::file_operations::FileOperationsVtable::builder::<Self>()
68+
.read()
69+
.write()
70+
.build();
71+
72+
fn open() -> linux_kernel_module::KernelResult<Self> {
73+
return Ok(WriteFile {
74+
written: AtomicUsize::new(0),
75+
});
76+
}
77+
}
78+
79+
impl linux_kernel_module::file_operations::Read for WriteFile {
80+
fn read(
81+
&self,
82+
buf: &mut linux_kernel_module::user_ptr::UserSlicePtrWriter,
83+
_offset: u64,
84+
) -> linux_kernel_module::KernelResult<()> {
85+
let val = self.written.load(Ordering::SeqCst).to_string();
86+
buf.write(val.as_bytes())?;
87+
return Ok(());
88+
}
89+
}
90+
91+
impl linux_kernel_module::file_operations::Write for WriteFile {
92+
fn write(
93+
&self,
94+
buf: &mut linux_kernel_module::user_ptr::UserSlicePtrReader,
95+
_offset: u64,
96+
) -> linux_kernel_module::KernelResult<()> {
97+
let data = buf.read_all()?;
98+
self.written.fetch_add(data.len(), Ordering::SeqCst);
99+
return Ok(());
100+
}
101+
}
102+
58103
struct ChrdevTestModule {
59104
_chrdev_registration: linux_kernel_module::chrdev::Registration,
60105
}
61106

62107
impl linux_kernel_module::KernelModule for ChrdevTestModule {
63108
fn init() -> linux_kernel_module::KernelResult<Self> {
64109
let chrdev_registration =
65-
linux_kernel_module::chrdev::builder(cstr!("chrdev-tests"), 0..2)?
110+
linux_kernel_module::chrdev::builder(cstr!("chrdev-tests"), 0..3)?
66111
.register_device::<CycleFile>()
67112
.register_device::<SeekFile>()
113+
.register_device::<WriteFile>()
68114
.build()?;
69115
Ok(ChrdevTestModule {
70116
_chrdev_registration: chrdev_registration,

tests/chrdev/tests/tests.rs

+44-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::fs;
2-
use std::io::{Read, Seek, SeekFrom};
2+
use std::io::{Read, Seek, SeekFrom, Write};
33
use std::os::unix::prelude::FileExt;
44
use std::path::PathBuf;
55
use std::process::Command;
@@ -45,6 +45,7 @@ impl Drop for UnlinkOnDrop<'_> {
4545
fn mknod(path: &PathBuf, major: libc::dev_t, minor: libc::dev_t) -> UnlinkOnDrop {
4646
Command::new("sudo")
4747
.arg("mknod")
48+
.arg("--mode=a=rw")
4849
.arg(path.to_str().unwrap())
4950
.arg("c")
5051
.arg(major.to_string())
@@ -56,6 +57,7 @@ fn mknod(path: &PathBuf, major: libc::dev_t, minor: libc::dev_t) -> UnlinkOnDrop
5657

5758
const READ_FILE_MINOR: libc::dev_t = 0;
5859
const SEEK_FILE_MINOR: libc::dev_t = 1;
60+
const WRITE_FILE_MINOR: libc::dev_t = 2;
5961

6062
#[test]
6163
fn test_mknod() {
@@ -178,3 +180,44 @@ fn test_lseek() {
178180
);
179181
});
180182
}
183+
184+
#[test]
185+
fn test_write_unimplemented() {
186+
with_kernel_module(|| {
187+
let device_number = get_device_major_number();
188+
let p = temporary_file_path();
189+
let _u = mknod(&p, device_number, READ_FILE_MINOR);
190+
191+
let mut f = fs::OpenOptions::new().write(true).open(&p).unwrap();
192+
assert_eq!(
193+
f.write(&[1, 2, 3]).unwrap_err().raw_os_error().unwrap(),
194+
libc::EBADF
195+
);
196+
})
197+
}
198+
199+
#[test]
200+
fn test_write() {
201+
with_kernel_module(|| {
202+
let device_number = get_device_major_number();
203+
let p = temporary_file_path();
204+
let _u = mknod(&p, device_number, WRITE_FILE_MINOR);
205+
206+
let mut f = fs::OpenOptions::new()
207+
.read(true)
208+
.write(true)
209+
.open(&p)
210+
.unwrap();
211+
assert_eq!(f.write(&[1, 2, 3]).unwrap(), 3);
212+
213+
let mut buf = [0; 1];
214+
f.read_exact(&mut buf).unwrap();
215+
assert_eq!(&buf, b"3");
216+
217+
assert_eq!(f.write(&[1, 2, 3, 4, 5]).unwrap(), 5);
218+
219+
let mut buf = [0; 1];
220+
f.read_exact(&mut buf).unwrap();
221+
assert_eq!(&buf, b"8");
222+
})
223+
}

0 commit comments

Comments
 (0)