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

Commit 312eb38

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

File tree

4 files changed

+148
-5
lines changed

4 files changed

+148
-5
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

+46
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,6 +58,48 @@ 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
}
@@ -65,6 +110,7 @@ impl linux_kernel_module::KernelModule for ChrdevTestModule {
65110
linux_kernel_module::chrdev::builder(cstr!("chrdev-tests"), 0..2)?
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

+39-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;
@@ -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]).unwrap(), 3);
207+
208+
let mut buf = [0; 1];
209+
f.read_exact(&mut buf).unwrap();
210+
assert_eq!(&buf, b"3");
211+
212+
assert_eq!(f.write(&[1, 2, 3, 4, 5]).unwrap(), 5);
213+
214+
let mut buf = [0; 1];
215+
f.read_exact(&mut buf).unwrap();
216+
assert_eq!(&buf, b"8");
217+
})
218+
}

0 commit comments

Comments
 (0)