15e40b591SMatthew Maurer // SPDX-License-Identifier: GPL-2.0 25e40b591SMatthew Maurer // Copyright (C) 2025 Google LLC. 35e40b591SMatthew Maurer 4*839dc1d1SMatthew Maurer use super::{Reader, Writer}; 55e40b591SMatthew Maurer use crate::prelude::*; 65e40b591SMatthew Maurer use crate::seq_file::SeqFile; 75e40b591SMatthew Maurer use crate::seq_print; 8*839dc1d1SMatthew Maurer use crate::uaccess::UserSlice; 95e40b591SMatthew Maurer use core::fmt::{Display, Formatter, Result}; 105e40b591SMatthew Maurer use core::marker::PhantomData; 115e40b591SMatthew Maurer 125e40b591SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)] 135e40b591SMatthew Maurer use core::ops::Deref; 145e40b591SMatthew Maurer 155e40b591SMatthew Maurer /// # Invariant 165e40b591SMatthew Maurer /// 175e40b591SMatthew Maurer /// `FileOps<T>` will always contain an `operations` which is safe to use for a file backed 185e40b591SMatthew Maurer /// off an inode which has a pointer to a `T` in its private data that is safe to convert 195e40b591SMatthew Maurer /// into a reference. 205e40b591SMatthew Maurer pub(super) struct FileOps<T> { 215e40b591SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)] 225e40b591SMatthew Maurer operations: bindings::file_operations, 235e40b591SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)] 245e40b591SMatthew Maurer mode: u16, 255e40b591SMatthew Maurer _phantom: PhantomData<T>, 265e40b591SMatthew Maurer } 275e40b591SMatthew Maurer 285e40b591SMatthew Maurer impl<T> FileOps<T> { 295e40b591SMatthew Maurer /// # Safety 305e40b591SMatthew Maurer /// 315e40b591SMatthew Maurer /// The caller asserts that the provided `operations` is safe to use for a file whose 325e40b591SMatthew Maurer /// inode has a pointer to `T` in its private data that is safe to convert into a reference. 335e40b591SMatthew Maurer const unsafe fn new(operations: bindings::file_operations, mode: u16) -> Self { 345e40b591SMatthew Maurer Self { 355e40b591SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)] 365e40b591SMatthew Maurer operations, 375e40b591SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)] 385e40b591SMatthew Maurer mode, 395e40b591SMatthew Maurer _phantom: PhantomData, 405e40b591SMatthew Maurer } 415e40b591SMatthew Maurer } 425e40b591SMatthew Maurer 435e40b591SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)] 445e40b591SMatthew Maurer pub(crate) const fn mode(&self) -> u16 { 455e40b591SMatthew Maurer self.mode 465e40b591SMatthew Maurer } 475e40b591SMatthew Maurer } 485e40b591SMatthew Maurer 495e40b591SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)] 505e40b591SMatthew Maurer impl<T> Deref for FileOps<T> { 515e40b591SMatthew Maurer type Target = bindings::file_operations; 525e40b591SMatthew Maurer 535e40b591SMatthew Maurer fn deref(&self) -> &Self::Target { 545e40b591SMatthew Maurer &self.operations 555e40b591SMatthew Maurer } 565e40b591SMatthew Maurer } 575e40b591SMatthew Maurer 585e40b591SMatthew Maurer struct WriterAdapter<T>(T); 595e40b591SMatthew Maurer 605e40b591SMatthew Maurer impl<'a, T: Writer> Display for WriterAdapter<&'a T> { 615e40b591SMatthew Maurer fn fmt(&self, f: &mut Formatter<'_>) -> Result { 625e40b591SMatthew Maurer self.0.write(f) 635e40b591SMatthew Maurer } 645e40b591SMatthew Maurer } 655e40b591SMatthew Maurer 665e40b591SMatthew Maurer /// Implements `open` for `file_operations` via `single_open` to fill out a `seq_file`. 675e40b591SMatthew Maurer /// 685e40b591SMatthew Maurer /// # Safety 695e40b591SMatthew Maurer /// 705e40b591SMatthew Maurer /// * `inode`'s private pointer must point to a value of type `T` which will outlive the `inode` 715e40b591SMatthew Maurer /// and will not have any unique references alias it during the call. 725e40b591SMatthew Maurer /// * `file` must point to a live, not-yet-initialized file object. 735e40b591SMatthew Maurer unsafe extern "C" fn writer_open<T: Writer + Sync>( 745e40b591SMatthew Maurer inode: *mut bindings::inode, 755e40b591SMatthew Maurer file: *mut bindings::file, 765e40b591SMatthew Maurer ) -> c_int { 775e40b591SMatthew Maurer // SAFETY: The caller ensures that `inode` is a valid pointer. 785e40b591SMatthew Maurer let data = unsafe { (*inode).i_private }; 795e40b591SMatthew Maurer // SAFETY: 805e40b591SMatthew Maurer // * `file` is acceptable by caller precondition. 815e40b591SMatthew Maurer // * `print_act` will be called on a `seq_file` with private data set to the third argument, 825e40b591SMatthew Maurer // so we meet its safety requirements. 835e40b591SMatthew Maurer // * The `data` pointer passed in the third argument is a valid `T` pointer that outlives 845e40b591SMatthew Maurer // this call by caller preconditions. 855e40b591SMatthew Maurer unsafe { bindings::single_open(file, Some(writer_act::<T>), data) } 865e40b591SMatthew Maurer } 875e40b591SMatthew Maurer 885e40b591SMatthew Maurer /// Prints private data stashed in a seq_file to that seq file. 895e40b591SMatthew Maurer /// 905e40b591SMatthew Maurer /// # Safety 915e40b591SMatthew Maurer /// 925e40b591SMatthew Maurer /// `seq` must point to a live `seq_file` whose private data is a valid pointer to a `T` which may 935e40b591SMatthew Maurer /// not have any unique references alias it during the call. 945e40b591SMatthew Maurer unsafe extern "C" fn writer_act<T: Writer + Sync>( 955e40b591SMatthew Maurer seq: *mut bindings::seq_file, 965e40b591SMatthew Maurer _: *mut c_void, 975e40b591SMatthew Maurer ) -> c_int { 985e40b591SMatthew Maurer // SAFETY: By caller precondition, this pointer is valid pointer to a `T`, and 995e40b591SMatthew Maurer // there are not and will not be any unique references until we are done. 1005e40b591SMatthew Maurer let data = unsafe { &*((*seq).private.cast::<T>()) }; 1015e40b591SMatthew Maurer // SAFETY: By caller precondition, `seq_file` points to a live `seq_file`, so we can lift 1025e40b591SMatthew Maurer // it. 1035e40b591SMatthew Maurer let seq_file = unsafe { SeqFile::from_raw(seq) }; 1045e40b591SMatthew Maurer seq_print!(seq_file, "{}", WriterAdapter(data)); 1055e40b591SMatthew Maurer 0 1065e40b591SMatthew Maurer } 1075e40b591SMatthew Maurer 1085e40b591SMatthew Maurer // Work around lack of generic const items. 1095e40b591SMatthew Maurer pub(crate) trait ReadFile<T> { 1105e40b591SMatthew Maurer const FILE_OPS: FileOps<T>; 1115e40b591SMatthew Maurer } 1125e40b591SMatthew Maurer 1135e40b591SMatthew Maurer impl<T: Writer + Sync> ReadFile<T> for T { 1145e40b591SMatthew Maurer const FILE_OPS: FileOps<T> = { 1155e40b591SMatthew Maurer let operations = bindings::file_operations { 1165e40b591SMatthew Maurer read: Some(bindings::seq_read), 1175e40b591SMatthew Maurer llseek: Some(bindings::seq_lseek), 1185e40b591SMatthew Maurer release: Some(bindings::single_release), 1195e40b591SMatthew Maurer open: Some(writer_open::<Self>), 1205e40b591SMatthew Maurer // SAFETY: `file_operations` supports zeroes in all fields. 1215e40b591SMatthew Maurer ..unsafe { core::mem::zeroed() } 1225e40b591SMatthew Maurer }; 1235e40b591SMatthew Maurer // SAFETY: `operations` is all stock `seq_file` implementations except for `writer_open`. 1245e40b591SMatthew Maurer // `open`'s only requirement beyond what is provided to all open functions is that the 1255e40b591SMatthew Maurer // inode's data pointer must point to a `T` that will outlive it, which matches the 1265e40b591SMatthew Maurer // `FileOps` requirements. 1275e40b591SMatthew Maurer unsafe { FileOps::new(operations, 0o400) } 1285e40b591SMatthew Maurer }; 1295e40b591SMatthew Maurer } 130*839dc1d1SMatthew Maurer 131*839dc1d1SMatthew Maurer fn read<T: Reader + Sync>(data: &T, buf: *const c_char, count: usize) -> isize { 132*839dc1d1SMatthew Maurer let mut reader = UserSlice::new(UserPtr::from_ptr(buf as *mut c_void), count).reader(); 133*839dc1d1SMatthew Maurer 134*839dc1d1SMatthew Maurer if let Err(e) = data.read_from_slice(&mut reader) { 135*839dc1d1SMatthew Maurer return e.to_errno() as isize; 136*839dc1d1SMatthew Maurer } 137*839dc1d1SMatthew Maurer 138*839dc1d1SMatthew Maurer count as isize 139*839dc1d1SMatthew Maurer } 140*839dc1d1SMatthew Maurer 141*839dc1d1SMatthew Maurer /// # Safety 142*839dc1d1SMatthew Maurer /// 143*839dc1d1SMatthew Maurer /// `file` must be a valid pointer to a `file` struct. 144*839dc1d1SMatthew Maurer /// The `private_data` of the file must contain a valid pointer to a `seq_file` whose 145*839dc1d1SMatthew Maurer /// `private` data in turn points to a `T` that implements `Reader`. 146*839dc1d1SMatthew Maurer /// `buf` must be a valid user-space buffer. 147*839dc1d1SMatthew Maurer pub(crate) unsafe extern "C" fn write<T: Reader + Sync>( 148*839dc1d1SMatthew Maurer file: *mut bindings::file, 149*839dc1d1SMatthew Maurer buf: *const c_char, 150*839dc1d1SMatthew Maurer count: usize, 151*839dc1d1SMatthew Maurer _ppos: *mut bindings::loff_t, 152*839dc1d1SMatthew Maurer ) -> isize { 153*839dc1d1SMatthew Maurer // SAFETY: The file was opened with `single_open`, which sets `private_data` to a `seq_file`. 154*839dc1d1SMatthew Maurer let seq = unsafe { &mut *((*file).private_data.cast::<bindings::seq_file>()) }; 155*839dc1d1SMatthew Maurer // SAFETY: By caller precondition, this pointer is live and points to a value of type `T`. 156*839dc1d1SMatthew Maurer let data = unsafe { &*(seq.private as *const T) }; 157*839dc1d1SMatthew Maurer read(data, buf, count) 158*839dc1d1SMatthew Maurer } 159*839dc1d1SMatthew Maurer 160*839dc1d1SMatthew Maurer // A trait to get the file operations for a type. 161*839dc1d1SMatthew Maurer pub(crate) trait ReadWriteFile<T> { 162*839dc1d1SMatthew Maurer const FILE_OPS: FileOps<T>; 163*839dc1d1SMatthew Maurer } 164*839dc1d1SMatthew Maurer 165*839dc1d1SMatthew Maurer impl<T: Writer + Reader + Sync> ReadWriteFile<T> for T { 166*839dc1d1SMatthew Maurer const FILE_OPS: FileOps<T> = { 167*839dc1d1SMatthew Maurer let operations = bindings::file_operations { 168*839dc1d1SMatthew Maurer open: Some(writer_open::<T>), 169*839dc1d1SMatthew Maurer read: Some(bindings::seq_read), 170*839dc1d1SMatthew Maurer write: Some(write::<T>), 171*839dc1d1SMatthew Maurer llseek: Some(bindings::seq_lseek), 172*839dc1d1SMatthew Maurer release: Some(bindings::single_release), 173*839dc1d1SMatthew Maurer // SAFETY: `file_operations` supports zeroes in all fields. 174*839dc1d1SMatthew Maurer ..unsafe { core::mem::zeroed() } 175*839dc1d1SMatthew Maurer }; 176*839dc1d1SMatthew Maurer // SAFETY: `operations` is all stock `seq_file` implementations except for `writer_open` 177*839dc1d1SMatthew Maurer // and `write`. 178*839dc1d1SMatthew Maurer // `writer_open`'s only requirement beyond what is provided to all open functions is that 179*839dc1d1SMatthew Maurer // the inode's data pointer must point to a `T` that will outlive it, which matches the 180*839dc1d1SMatthew Maurer // `FileOps` requirements. 181*839dc1d1SMatthew Maurer // `write` only requires that the file's private data pointer points to `seq_file` 182*839dc1d1SMatthew Maurer // which points to a `T` that will outlive it, which matches what `writer_open` 183*839dc1d1SMatthew Maurer // provides. 184*839dc1d1SMatthew Maurer unsafe { FileOps::new(operations, 0o600) } 185*839dc1d1SMatthew Maurer }; 186*839dc1d1SMatthew Maurer } 187*839dc1d1SMatthew Maurer 188*839dc1d1SMatthew Maurer /// # Safety 189*839dc1d1SMatthew Maurer /// 190*839dc1d1SMatthew Maurer /// `inode` must be a valid pointer to an `inode` struct. 191*839dc1d1SMatthew Maurer /// `file` must be a valid pointer to a `file` struct. 192*839dc1d1SMatthew Maurer unsafe extern "C" fn write_only_open( 193*839dc1d1SMatthew Maurer inode: *mut bindings::inode, 194*839dc1d1SMatthew Maurer file: *mut bindings::file, 195*839dc1d1SMatthew Maurer ) -> c_int { 196*839dc1d1SMatthew Maurer // SAFETY: The caller ensures that `inode` and `file` are valid pointers. 197*839dc1d1SMatthew Maurer unsafe { (*file).private_data = (*inode).i_private }; 198*839dc1d1SMatthew Maurer 0 199*839dc1d1SMatthew Maurer } 200*839dc1d1SMatthew Maurer 201*839dc1d1SMatthew Maurer /// # Safety 202*839dc1d1SMatthew Maurer /// 203*839dc1d1SMatthew Maurer /// * `file` must be a valid pointer to a `file` struct. 204*839dc1d1SMatthew Maurer /// * The `private_data` of the file must contain a valid pointer to a `T` that implements 205*839dc1d1SMatthew Maurer /// `Reader`. 206*839dc1d1SMatthew Maurer /// * `buf` must be a valid user-space buffer. 207*839dc1d1SMatthew Maurer pub(crate) unsafe extern "C" fn write_only_write<T: Reader + Sync>( 208*839dc1d1SMatthew Maurer file: *mut bindings::file, 209*839dc1d1SMatthew Maurer buf: *const c_char, 210*839dc1d1SMatthew Maurer count: usize, 211*839dc1d1SMatthew Maurer _ppos: *mut bindings::loff_t, 212*839dc1d1SMatthew Maurer ) -> isize { 213*839dc1d1SMatthew Maurer // SAFETY: The caller ensures that `file` is a valid pointer and that `private_data` holds a 214*839dc1d1SMatthew Maurer // valid pointer to `T`. 215*839dc1d1SMatthew Maurer let data = unsafe { &*((*file).private_data as *const T) }; 216*839dc1d1SMatthew Maurer read(data, buf, count) 217*839dc1d1SMatthew Maurer } 218*839dc1d1SMatthew Maurer 219*839dc1d1SMatthew Maurer pub(crate) trait WriteFile<T> { 220*839dc1d1SMatthew Maurer const FILE_OPS: FileOps<T>; 221*839dc1d1SMatthew Maurer } 222*839dc1d1SMatthew Maurer 223*839dc1d1SMatthew Maurer impl<T: Reader + Sync> WriteFile<T> for T { 224*839dc1d1SMatthew Maurer const FILE_OPS: FileOps<T> = { 225*839dc1d1SMatthew Maurer let operations = bindings::file_operations { 226*839dc1d1SMatthew Maurer open: Some(write_only_open), 227*839dc1d1SMatthew Maurer write: Some(write_only_write::<T>), 228*839dc1d1SMatthew Maurer llseek: Some(bindings::noop_llseek), 229*839dc1d1SMatthew Maurer // SAFETY: `file_operations` supports zeroes in all fields. 230*839dc1d1SMatthew Maurer ..unsafe { core::mem::zeroed() } 231*839dc1d1SMatthew Maurer }; 232*839dc1d1SMatthew Maurer // SAFETY: 233*839dc1d1SMatthew Maurer // * `write_only_open` populates the file private data with the inode private data 234*839dc1d1SMatthew Maurer // * `write_only_write`'s only requirement is that the private data of the file point to 235*839dc1d1SMatthew Maurer // a `T` and be legal to convert to a shared reference, which `write_only_open` 236*839dc1d1SMatthew Maurer // satisfies. 237*839dc1d1SMatthew Maurer unsafe { FileOps::new(operations, 0o200) } 238*839dc1d1SMatthew Maurer }; 239*839dc1d1SMatthew Maurer } 240