Skip to content
This repository was archived by the owner on Mar 7, 2021. It is now read-only.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 6e510b0

Browse files
committedSep 29, 2019
Refs #164 -- added write suppot to FileOperations
1 parent 8ece413 commit 6e510b0

File tree

4 files changed

+134
-6
lines changed

4 files changed

+134
-6
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

+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

+39
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;
@@ -48,6 +51,41 @@ impl linux_kernel_module::chrdev::FileOperations for SeekFile {
4851
}
4952
}
5053

54+
struct WriteFile {
55+
written: AtomicUsize,
56+
}
57+
58+
impl linux_kernel_module::chrdev::FileOperations for WriteFile {
59+
const VTABLE: linux_kernel_module::chrdev::FileOperationsVtable =
60+
linux_kernel_module::chrdev::FileOperationsVtable::new::<Self>();
61+
62+
fn open() -> linux_kernel_module::KernelResult<Self> {
63+
return Ok(WriteFile {
64+
written: AtomicUsize::new(0),
65+
});
66+
}
67+
68+
fn write(
69+
&self,
70+
buf: &mut linux_kernel_module::user_ptr::UserSlicePtrReader,
71+
_offset: u64,
72+
) -> linux_kernel_module::KernelResult<()> {
73+
let data = buf.read_all()?;
74+
self.written.fetch_add(data.len(), Ordering::SeqCst);
75+
return Ok(());
76+
}
77+
78+
fn read(
79+
&self,
80+
buf: &mut linux_kernel_module::user_ptr::UserSlicePtrWriter,
81+
_offset: u64,
82+
) -> linux_kernel_module::KernelResult<()> {
83+
let val = self.written.load(Ordering::SeqCst);
84+
buf.write(val.to_string().as_bytes())?;
85+
return Ok(());
86+
}
87+
}
88+
5189
struct ChrdevTestModule {
5290
_chrdev_registration: linux_kernel_module::chrdev::Registration,
5391
}
@@ -58,6 +96,7 @@ impl linux_kernel_module::KernelModule for ChrdevTestModule {
5896
linux_kernel_module::chrdev::builder(cstr!("chrdev-tests"), 0..2)?
5997
.register_device::<CycleFile>()
6098
.register_device::<SeekFile>()
99+
.register_device::<WriteFile>()
61100
.build()?;
62101
Ok(ChrdevTestModule {
63102
_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 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]).unwrap(), 5);
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)
This repository has been archived.