Skip to content
This repository was archived by the owner on Mar 7, 2021. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 34 additions & 4 deletions src/file_operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,23 +138,46 @@ unsafe extern "C" fn llseek_callback<T: FileOperations>(
}
}

unsafe extern "C" fn fsync_callback<T: FileOperations>(
file: *mut bindings::file,
start: bindings::loff_t,
end: bindings::loff_t,
datasync: c_types::c_int,
) -> c_types::c_int {
let start = match start.try_into() {
Ok(v) => v,
Err(_) => return Error::EINVAL.to_kernel_errno(),
};
let end = match end.try_into() {
Ok(v) => v,
Err(_) => return Error::EINVAL.to_kernel_errno(),
};
let datasync = datasync != 0;
let fsync = T::FSYNC.unwrap();
let f = &*((*file).private_data as *const T);
match fsync(f, &File::from_ptr(file), start, end, datasync) {
Ok(result) => result as c_types::c_int,
Err(e) => e.to_kernel_errno(),
}
}

pub(crate) struct FileOperationsVtable<T>(marker::PhantomData<T>);

impl<T: FileOperations> FileOperationsVtable<T> {
pub(crate) const VTABLE: bindings::file_operations = bindings::file_operations {
open: Some(open_callback::<T>),
release: Some(release_callback::<T>),
read: if let Some(_) = T::READ {
read: if T::READ.is_some() {
Some(read_callback::<T>)
} else {
None
},
write: if let Some(_) = T::WRITE {
write: if T::WRITE.is_some() {
Some(write_callback::<T>)
} else {
None
},
llseek: if let Some(_) = T::SEEK {
llseek: if T::SEEK.is_some() {
Some(llseek_callback::<T>)
} else {
None
Expand All @@ -176,7 +199,11 @@ impl<T: FileOperations> FileOperationsVtable<T> {
fasync: None,
flock: None,
flush: None,
fsync: None,
fsync: if T::FSYNC.is_some() {
Some(fsync_callback::<T>)
} else {
None
},
get_unmapped_area: None,
iterate: None,
#[cfg(kernel_4_7_0_or_greater)]
Expand Down Expand Up @@ -207,6 +234,7 @@ impl<T: FileOperations> FileOperationsVtable<T> {
pub type ReadFn<T> = Option<fn(&T, &File, &mut UserSlicePtrWriter, u64) -> KernelResult<()>>;
pub type WriteFn<T> = Option<fn(&T, &mut UserSlicePtrReader, u64) -> KernelResult<()>>;
pub type SeekFn<T> = Option<fn(&T, &File, SeekFrom) -> KernelResult<u64>>;
pub type FSync<T> = Option<fn(&T, &File, u64, u64, bool) -> KernelResult<u32>>;

/// `FileOperations` corresponds to the kernel's `struct file_operations`. You
/// implement this trait whenever you'd create a `struct file_operations`.
Expand All @@ -228,4 +256,6 @@ pub trait FileOperations: Sync + Sized {
/// Changes the position of the file. Corresponds to the `llseek` function
/// pointer in `struct file_operations`.
const SEEK: SeekFn<Self> = None;

const FSYNC: FSync<Self> = None;
}
58 changes: 56 additions & 2 deletions tests/chrdev/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
extern crate alloc;

use alloc::string::ToString;
use core::sync::atomic::{AtomicUsize, Ordering};
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};

use linux_kernel_module::{self, cstr};

Expand Down Expand Up @@ -83,17 +83,71 @@ impl linux_kernel_module::file_operations::FileOperations for WriteFile {
);
}

struct FSyncFile {
data_synced: AtomicBool,
meta_synced: AtomicBool,
}

impl linux_kernel_module::file_operations::FileOperations for FSyncFile {
fn open() -> linux_kernel_module::KernelResult<Self> {
Ok(FSyncFile {
data_synced: AtomicBool::new(true),
meta_synced: AtomicBool::new(true),
})
}

const READ: linux_kernel_module::file_operations::ReadFn<Self> = Some(
|this: &Self,
_file: &linux_kernel_module::file_operations::File,
buf: &mut linux_kernel_module::user_ptr::UserSlicePtrWriter,
_offset: u64|
-> linux_kernel_module::KernelResult<()> {
let data = (this.data_synced.load(Ordering::SeqCst) as i32).to_string();
let meta = (this.meta_synced.load(Ordering::SeqCst) as i32).to_string();
buf.write((data + &meta).as_bytes())?;
Ok(())
},
);

const WRITE: linux_kernel_module::file_operations::WriteFn<Self> = Some(
|this: &Self,
_buf: &mut linux_kernel_module::user_ptr::UserSlicePtrReader,
_offset: u64|
-> linux_kernel_module::KernelResult<()> {
this.data_synced.store(false, Ordering::SeqCst);
this.meta_synced.store(false, Ordering::SeqCst);
Ok(())
},
);

const FSYNC: linux_kernel_module::file_operations::FSync<Self> = Some(
|this: &Self,
_file: &linux_kernel_module::file_operations::File,
_start: u64,
_end: u64,
datasync: bool|
-> linux_kernel_module::KernelResult<u32> {
this.data_synced.store(true, Ordering::SeqCst);
if !datasync {
this.meta_synced.store(true, Ordering::SeqCst);
}
Ok(0)
},
);
}

struct ChrdevTestModule {
_chrdev_registration: linux_kernel_module::chrdev::Registration,
}

impl linux_kernel_module::KernelModule for ChrdevTestModule {
fn init() -> linux_kernel_module::KernelResult<Self> {
let chrdev_registration =
linux_kernel_module::chrdev::builder(cstr!("chrdev-tests"), 0..3)?
linux_kernel_module::chrdev::builder(cstr!("chrdev-tests"), 0..4)?
.register_device::<CycleFile>()
.register_device::<SeekFile>()
.register_device::<WriteFile>()
.register_device::<FSyncFile>()
.build()?;
Ok(ChrdevTestModule {
_chrdev_registration: chrdev_registration,
Expand Down
53 changes: 53 additions & 0 deletions tests/chrdev/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const DEVICE_NAME: &'static str = "chrdev-tests";
const READ_FILE_MINOR: libc::dev_t = 0;
const SEEK_FILE_MINOR: libc::dev_t = 1;
const WRITE_FILE_MINOR: libc::dev_t = 2;
const SYNC_FILE_MINOR: libc::dev_t = 3;

#[test]
fn test_mknod() {
Expand Down Expand Up @@ -171,3 +172,55 @@ fn test_write() {
assert_eq!(&buf, b"8");
})
}

#[test]
fn test_fsync_unimplemented() {
with_kernel_module(|| {
let device_number = get_device_major_number(DEVICE_NAME);
let p = temporary_file_path();
let _u = mknod(&p, device_number, READ_FILE_MINOR);

let f = fs::OpenOptions::new().write(true).open(&p).unwrap();
assert_eq!(
f.sync_all().unwrap_err().raw_os_error().unwrap(),
libc::EINVAL
);
})
}

#[test]
fn test_fsync() {
with_kernel_module(|| {
let device_number = get_device_major_number(DEVICE_NAME);
let p = temporary_file_path();
let _u = mknod(&p, device_number, SYNC_FILE_MINOR);

let mut f = fs::OpenOptions::new()
.read(true)
.write(true)
.open(&p)
.unwrap();

let mut buf = [0; 2];
f.read_exact(&mut buf).unwrap();
assert_eq!(&buf, b"11");

f.write(&[1, 2]).unwrap();

let mut buf = [0; 2];
f.read_exact(&mut buf).unwrap();
assert_eq!(&buf, b"00");

f.sync_data().unwrap();

let mut buf = [0; 2];
f.read_exact(&mut buf).unwrap();
assert_eq!(&buf, b"10");

f.sync_all().unwrap();

let mut buf = [0; 2];
f.read_exact(&mut buf).unwrap();
assert_eq!(&buf, b"11");
})
}