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

Commit 1e2a028

Browse files
committed
Refs #164 -- added write suppot to FileOperations
1 parent 8ece413 commit 1e2a028

File tree

4 files changed

+127
-5
lines changed

4 files changed

+127
-5
lines changed

src/chrdev.rs

+34-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::bindings;
1010
use crate::c_types;
1111
use crate::error::{Error, KernelResult};
1212
use crate::types::CStr;
13-
use crate::user_ptr::{UserSlicePtr, UserSlicePtrWriter};
13+
use crate::user_ptr::{UserSlicePtr, UserSlicePtrReader, UserSlicePtrWriter};
1414

1515
pub fn builder(name: &'static CStr, minors: Range<u16>) -> KernelResult<Builder> {
1616
Ok(Builder {
@@ -158,6 +158,32 @@ unsafe extern "C" fn read_callback<T: FileOperations>(
158158
}
159159
}
160160

161+
unsafe extern "C" fn write_callback<T: FileOperations>(
162+
file: *mut bindings::file,
163+
buf: *const c_types::c_char,
164+
len: c_types::c_size_t,
165+
offset: *mut bindings::loff_t,
166+
) -> c_types::c_ssize_t {
167+
let mut data = match UserSlicePtr::new(buf as *mut c_types::c_void, len) {
168+
Ok(ptr) => ptr.reader(),
169+
Err(e) => return e.to_kernel_errno().try_into().unwrap(),
170+
};
171+
let f = &*((*file).private_data as *const T);
172+
// No FMODE_UNSIGNED_OFFSET support, so offset must be in [0, 2^63).
173+
// See discussion in #113
174+
let positive_offset = match (*offset).try_into() {
175+
Ok(v) => v,
176+
Err(_) => return Error::EINVAL.to_kernel_errno().try_into().unwrap(),
177+
};
178+
match f.write(&mut data, positive_offset) {
179+
Ok(()) => {
180+
let read = len - data.len();
181+
read.try_into().unwrap()
182+
}
183+
Err(e) => e.to_kernel_errno().try_into().unwrap(),
184+
}
185+
}
186+
161187
unsafe extern "C" fn release_callback<T: FileOperations>(
162188
_inode: *mut bindings::inode,
163189
file: *mut bindings::file,
@@ -193,6 +219,7 @@ impl FileOperationsVtable {
193219
FileOperationsVtable(bindings::file_operations {
194220
open: Some(open_callback::<T>),
195221
read: Some(read_callback::<T>),
222+
write: Some(write_callback::<T>),
196223
release: Some(release_callback::<T>),
197224
llseek: Some(llseek_callback::<T>),
198225

@@ -232,7 +259,6 @@ impl FileOperationsVtable {
232259
splice_read: None,
233260
splice_write: None,
234261
unlocked_ioctl: None,
235-
write: None,
236262
write_iter: None,
237263
})
238264
}
@@ -260,6 +286,12 @@ pub trait FileOperations: Sync + Sized {
260286
Err(Error::EINVAL)
261287
}
262288

289+
/// Writes data from userspace o this file. Corresponds to the `write`
290+
/// function pointer in `struct file_operations`.
291+
fn write(&self, _buf: &mut UserSlicePtrReader, _offset: u64) -> KernelResult<()> {
292+
Err(Error::EINVAL)
293+
}
294+
263295
/// Changes the position of the file. Corresponds to the `llseek` function
264296
/// pointer in `struct file_operations`.
265297
fn seek(&self, _file: &File, _offset: SeekFrom) -> KernelResult<u64> {

src/user_ptr.rs

+17-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,22 @@ 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+
/// Read all data remaining in the user slice and return it in a `Vec`.
105+
///
106+
/// Returns EFAULT if the address does not currently point to
107+
/// mapped, readable memory.
108+
pub fn read_all(&mut self) -> error::KernelResult<Vec<u8>> {
109+
let mut data = vec![0; self.1];
110+
self.read(&mut data)?;
111+
Ok(data)
112+
}
113+
100114
pub fn read(&mut self, data: &mut [u8]) -> error::KernelResult<()> {
101115
if data.len() > self.1 || data.len() > u32::MAX as usize {
102116
return Err(error::Error::EFAULT);

tests/chrdev/src/lib.rs

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

3+
use core::sync::atomic::{AtomicUsize, Ordering};
4+
35
use linux_kernel_module::{self, cstr};
46

57
struct CycleFile;
@@ -48,6 +50,41 @@ impl linux_kernel_module::chrdev::FileOperations for SeekFile {
4850
}
4951
}
5052

53+
struct WriteFile {
54+
written: AtomicUsize,
55+
}
56+
57+
impl linux_kernel_module::chrdev::FileOperations for WriteFile {
58+
const VTABLE: linux_kernel_module::chrdev::FileOperationsVtable =
59+
linux_kernel_module::chrdev::FileOperationsVtable::new::<Self>();
60+
61+
fn open() -> linux_kernel_module::KernelResult<Self> {
62+
return Ok(WriteFile {
63+
written: AtomicUsize::new(0),
64+
});
65+
}
66+
67+
fn write(
68+
&self,
69+
buf: &mut linux_kernel_module::user_ptr::UserSlicePtrReader,
70+
offset: u64,
71+
) -> linux_kernel_module::KernelResult<()> {
72+
let data = buf.read_all()?;
73+
self.written.fetch_add(data.len(), Ordering::SeqCst);
74+
return Ok(());
75+
}
76+
77+
fn read(
78+
&self,
79+
buf: &mut linux_kernel_module::user_ptr::UserSlicePtrWriter,
80+
offset: u64,
81+
) -> linux_kernel_module::KernelResult<()> {
82+
let val = self.written.load(Ordering::SeqCst);
83+
buf.write(val.to_string().as_bytes())?;
84+
return Ok(());
85+
}
86+
}
87+
5188
struct ChrdevTestModule {
5289
_chrdev_registration: linux_kernel_module::chrdev::Registration,
5390
}
@@ -58,6 +95,7 @@ impl linux_kernel_module::KernelModule for ChrdevTestModule {
5895
linux_kernel_module::chrdev::builder(cstr!("chrdev-tests"), 0..2)?
5996
.register_device::<CycleFile>()
6097
.register_device::<SeekFile>()
98+
.register_device::<WriteFile>()
6199
.build()?;
62100
Ok(ChrdevTestModule {
63101
_chrdev_registration: chrdev_registration,

tests/chrdev/tests/tests.rs

+38
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ fn mknod(path: &PathBuf, major: libc::dev_t, minor: libc::dev_t) -> UnlinkOnDrop
5656

5757
const READ_FILE_MINOR: libc::dev_t = 0;
5858
const SEEK_FILE_MINOR: libc::dev_t = 1;
59+
const WRITE_FILE_MINOR: libc::dev_t = 2;
5960

6061
#[test]
6162
fn test_mknod() {
@@ -178,3 +179,40 @@ fn test_lseek() {
178179
);
179180
});
180181
}
182+
183+
#[test]
184+
fn test_write_unimplemented() {
185+
with_kernel_module(|| {
186+
let device_number = get_device_major_number();
187+
let p = temporary_file_path();
188+
let _u = mknod(&p, device_number, READ_FILE_MINOR);
189+
190+
let mut f = fs::File::open(&p).unwrap();
191+
assert_eq!(
192+
f.write(&[1, 2, 3]).unwrap_err().raw_os_error().unwrap(),
193+
libc::EINVAL
194+
);
195+
})
196+
}
197+
198+
#[test]
199+
fn test_write() {
200+
with_kernel_module(|| {
201+
let device_number = get_device_major_number();
202+
let p = temporary_file_path();
203+
let _u = mknod(&p, device_number, WRITE_FILE_MINOR);
204+
205+
let mut f = fs::File::open(&p).unwrap();
206+
assert_eq(f.write(&[1, 2, 3]), Ok(3));
207+
208+
let buf = vec![];
209+
f.read_to_end(&mut buf).unwrap();
210+
assert_eq(buf, b"3");
211+
212+
assert_eq(f.write(&[1, 2, 3, 4, 5]), Ok(3));
213+
214+
let buf = vec![];
215+
f.read_to_end(&mut buf).unwrap();
216+
assert_eq(buf, b"8");
217+
})
218+
}

0 commit comments

Comments
 (0)