1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
|
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2025 Google LLC.
use super::{BinaryReader, BinaryWriter, Reader, Writer};
use crate::debugfs::callback_adapters::Adapter;
use crate::fmt;
use crate::fs::file;
use crate::prelude::*;
use crate::seq_file::SeqFile;
use crate::seq_print;
use crate::uaccess::UserSlice;
use core::marker::PhantomData;
#[cfg(CONFIG_DEBUG_FS)]
use core::ops::Deref;
/// # Invariant
///
/// `FileOps<T>` will always contain an `operations` which is safe to use for a file backed
/// off an inode which has a pointer to a `T` in its private data that is safe to convert
/// into a reference.
pub(super) struct FileOps<T> {
#[cfg(CONFIG_DEBUG_FS)]
operations: bindings::file_operations,
#[cfg(CONFIG_DEBUG_FS)]
mode: u16,
_phantom: PhantomData<T>,
}
impl<T> FileOps<T> {
/// # Safety
///
/// The caller asserts that the provided `operations` is safe to use for a file whose
/// inode has a pointer to `T` in its private data that is safe to convert into a reference.
const unsafe fn new(operations: bindings::file_operations, mode: u16) -> Self {
Self {
#[cfg(CONFIG_DEBUG_FS)]
operations,
#[cfg(CONFIG_DEBUG_FS)]
mode,
_phantom: PhantomData,
}
}
#[cfg(CONFIG_DEBUG_FS)]
pub(crate) const fn mode(&self) -> u16 {
self.mode
}
}
impl<T: Adapter> FileOps<T> {
pub(super) const fn adapt(&self) -> &FileOps<T::Inner> {
// SAFETY: `Adapter` asserts that `T` can be legally cast to `T::Inner`.
unsafe { core::mem::transmute(self) }
}
}
#[cfg(CONFIG_DEBUG_FS)]
impl<T> Deref for FileOps<T> {
type Target = bindings::file_operations;
fn deref(&self) -> &Self::Target {
&self.operations
}
}
struct WriterAdapter<T>(T);
impl<'a, T: Writer> fmt::Display for WriterAdapter<&'a T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.write(f)
}
}
/// Implements `open` for `file_operations` via `single_open` to fill out a `seq_file`.
///
/// # Safety
///
/// * `inode`'s private pointer must point to a value of type `T` which will outlive the `inode`
/// and will not have any unique references alias it during the call.
/// * `file` must point to a live, not-yet-initialized file object.
unsafe extern "C" fn writer_open<T: Writer + Sync>(
inode: *mut bindings::inode,
file: *mut bindings::file,
) -> c_int {
// SAFETY: The caller ensures that `inode` is a valid pointer.
let data = unsafe { (*inode).i_private };
// SAFETY:
// * `file` is acceptable by caller precondition.
// * `print_act` will be called on a `seq_file` with private data set to the third argument,
// so we meet its safety requirements.
// * The `data` pointer passed in the third argument is a valid `T` pointer that outlives
// this call by caller preconditions.
unsafe { bindings::single_open(file, Some(writer_act::<T>), data) }
}
/// Prints private data stashed in a seq_file to that seq file.
///
/// # Safety
///
/// `seq` must point to a live `seq_file` whose private data is a valid pointer to a `T` which may
/// not have any unique references alias it during the call.
unsafe extern "C" fn writer_act<T: Writer + Sync>(
seq: *mut bindings::seq_file,
_: *mut c_void,
) -> c_int {
// SAFETY: By caller precondition, this pointer is valid pointer to a `T`, and
// there are not and will not be any unique references until we are done.
let data = unsafe { &*((*seq).private.cast::<T>()) };
// SAFETY: By caller precondition, `seq_file` points to a live `seq_file`, so we can lift
// it.
let seq_file = unsafe { SeqFile::from_raw(seq) };
seq_print!(seq_file, "{}", WriterAdapter(data));
0
}
// Work around lack of generic const items.
pub(crate) trait ReadFile<T> {
const FILE_OPS: FileOps<T>;
}
impl<T: Writer + Sync> ReadFile<T> for T {
const FILE_OPS: FileOps<T> = {
let operations = bindings::file_operations {
read: Some(bindings::seq_read),
llseek: Some(bindings::seq_lseek),
release: Some(bindings::single_release),
open: Some(writer_open::<Self>),
// SAFETY: `file_operations` supports zeroes in all fields.
..unsafe { core::mem::zeroed() }
};
// SAFETY: `operations` is all stock `seq_file` implementations except for `writer_open`.
// `open`'s only requirement beyond what is provided to all open functions is that the
// inode's data pointer must point to a `T` that will outlive it, which matches the
// `FileOps` requirements.
unsafe { FileOps::new(operations, 0o400) }
};
}
fn read<T: Reader + Sync>(data: &T, buf: *const c_char, count: usize) -> isize {
let mut reader = UserSlice::new(UserPtr::from_ptr(buf as *mut c_void), count).reader();
if let Err(e) = data.read_from_slice(&mut reader) {
return e.to_errno() as isize;
}
count as isize
}
/// # Safety
///
/// `file` must be a valid pointer to a `file` struct.
/// The `private_data` of the file must contain a valid pointer to a `seq_file` whose
/// `private` data in turn points to a `T` that implements `Reader`.
/// `buf` must be a valid user-space buffer.
pub(crate) unsafe extern "C" fn write<T: Reader + Sync>(
file: *mut bindings::file,
buf: *const c_char,
count: usize,
_ppos: *mut bindings::loff_t,
) -> isize {
// SAFETY: The file was opened with `single_open`, which sets `private_data` to a `seq_file`.
let seq = unsafe { &mut *((*file).private_data.cast::<bindings::seq_file>()) };
// SAFETY: By caller precondition, this pointer is live and points to a value of type `T`.
let data = unsafe { &*(seq.private as *const T) };
read(data, buf, count)
}
// A trait to get the file operations for a type.
pub(crate) trait ReadWriteFile<T> {
const FILE_OPS: FileOps<T>;
}
impl<T: Writer + Reader + Sync> ReadWriteFile<T> for T {
const FILE_OPS: FileOps<T> = {
let operations = bindings::file_operations {
open: Some(writer_open::<T>),
read: Some(bindings::seq_read),
write: Some(write::<T>),
llseek: Some(bindings::seq_lseek),
release: Some(bindings::single_release),
// SAFETY: `file_operations` supports zeroes in all fields.
..unsafe { core::mem::zeroed() }
};
// SAFETY: `operations` is all stock `seq_file` implementations except for `writer_open`
// and `write`.
// `writer_open`'s only requirement beyond what is provided to all open functions is that
// the inode's data pointer must point to a `T` that will outlive it, which matches the
// `FileOps` requirements.
// `write` only requires that the file's private data pointer points to `seq_file`
// which points to a `T` that will outlive it, which matches what `writer_open`
// provides.
unsafe { FileOps::new(operations, 0o600) }
};
}
/// # Safety
///
/// `inode` must be a valid pointer to an `inode` struct.
/// `file` must be a valid pointer to a `file` struct.
unsafe extern "C" fn write_only_open(
inode: *mut bindings::inode,
file: *mut bindings::file,
) -> c_int {
// SAFETY: The caller ensures that `inode` and `file` are valid pointers.
unsafe { (*file).private_data = (*inode).i_private };
0
}
/// # Safety
///
/// * `file` must be a valid pointer to a `file` struct.
/// * The `private_data` of the file must contain a valid pointer to a `T` that implements
/// `Reader`.
/// * `buf` must be a valid user-space buffer.
pub(crate) unsafe extern "C" fn write_only_write<T: Reader + Sync>(
file: *mut bindings::file,
buf: *const c_char,
count: usize,
_ppos: *mut bindings::loff_t,
) -> isize {
// SAFETY: The caller ensures that `file` is a valid pointer and that `private_data` holds a
// valid pointer to `T`.
let data = unsafe { &*((*file).private_data as *const T) };
read(data, buf, count)
}
pub(crate) trait WriteFile<T> {
const FILE_OPS: FileOps<T>;
}
impl<T: Reader + Sync> WriteFile<T> for T {
const FILE_OPS: FileOps<T> = {
let operations = bindings::file_operations {
open: Some(write_only_open),
write: Some(write_only_write::<T>),
llseek: Some(bindings::noop_llseek),
// SAFETY: `file_operations` supports zeroes in all fields.
..unsafe { core::mem::zeroed() }
};
// SAFETY:
// * `write_only_open` populates the file private data with the inode private data
// * `write_only_write`'s only requirement is that the private data of the file point to
// a `T` and be legal to convert to a shared reference, which `write_only_open`
// satisfies.
unsafe { FileOps::new(operations, 0o200) }
};
}
extern "C" fn blob_read<T: BinaryWriter>(
file: *mut bindings::file,
buf: *mut c_char,
count: usize,
ppos: *mut bindings::loff_t,
) -> isize {
// SAFETY:
// - `file` is a valid pointer to a `struct file`.
// - The type invariant of `FileOps` guarantees that `private_data` points to a valid `T`.
let this = unsafe { &*((*file).private_data.cast::<T>()) };
// SAFETY:
// - `ppos` is a valid `file::Offset` pointer.
// - We have exclusive access to `ppos`.
let pos: &mut file::Offset = unsafe { &mut *ppos };
let mut writer = UserSlice::new(UserPtr::from_ptr(buf.cast()), count).writer();
let ret = || -> Result<isize> {
let written = this.write_to_slice(&mut writer, pos)?;
Ok(written.try_into()?)
}();
match ret {
Ok(n) => n,
Err(e) => e.to_errno() as isize,
}
}
/// Representation of [`FileOps`] for read only binary files.
pub(crate) trait BinaryReadFile<T> {
const FILE_OPS: FileOps<T>;
}
impl<T: BinaryWriter + Sync> BinaryReadFile<T> for T {
const FILE_OPS: FileOps<T> = {
let operations = bindings::file_operations {
read: Some(blob_read::<T>),
llseek: Some(bindings::default_llseek),
open: Some(bindings::simple_open),
// SAFETY: `file_operations` supports zeroes in all fields.
..unsafe { core::mem::zeroed() }
};
// SAFETY:
// - The private data of `struct inode` does always contain a pointer to a valid `T`.
// - `simple_open()` stores the `struct inode`'s private data in the private data of the
// corresponding `struct file`.
// - `blob_read()` re-creates a reference to `T` from the `struct file`'s private data.
// - `default_llseek()` does not access the `struct file`'s private data.
unsafe { FileOps::new(operations, 0o400) }
};
}
extern "C" fn blob_write<T: BinaryReader>(
file: *mut bindings::file,
buf: *const c_char,
count: usize,
ppos: *mut bindings::loff_t,
) -> isize {
// SAFETY:
// - `file` is a valid pointer to a `struct file`.
// - The type invariant of `FileOps` guarantees that `private_data` points to a valid `T`.
let this = unsafe { &*((*file).private_data.cast::<T>()) };
// SAFETY:
// - `ppos` is a valid `file::Offset` pointer.
// - We have exclusive access to `ppos`.
let pos: &mut file::Offset = unsafe { &mut *ppos };
let mut reader = UserSlice::new(UserPtr::from_ptr(buf.cast_mut().cast()), count).reader();
let ret = || -> Result<isize> {
let read = this.read_from_slice(&mut reader, pos)?;
Ok(read.try_into()?)
}();
match ret {
Ok(n) => n,
Err(e) => e.to_errno() as isize,
}
}
/// Representation of [`FileOps`] for write only binary files.
pub(crate) trait BinaryWriteFile<T> {
const FILE_OPS: FileOps<T>;
}
impl<T: BinaryReader + Sync> BinaryWriteFile<T> for T {
const FILE_OPS: FileOps<T> = {
let operations = bindings::file_operations {
write: Some(blob_write::<T>),
llseek: Some(bindings::default_llseek),
open: Some(bindings::simple_open),
// SAFETY: `file_operations` supports zeroes in all fields.
..unsafe { core::mem::zeroed() }
};
// SAFETY:
// - The private data of `struct inode` does always contain a pointer to a valid `T`.
// - `simple_open()` stores the `struct inode`'s private data in the private data of the
// corresponding `struct file`.
// - `blob_write()` re-creates a reference to `T` from the `struct file`'s private data.
// - `default_llseek()` does not access the `struct file`'s private data.
unsafe { FileOps::new(operations, 0o200) }
};
}
/// Representation of [`FileOps`] for read/write binary files.
pub(crate) trait BinaryReadWriteFile<T> {
const FILE_OPS: FileOps<T>;
}
impl<T: BinaryWriter + BinaryReader + Sync> BinaryReadWriteFile<T> for T {
const FILE_OPS: FileOps<T> = {
let operations = bindings::file_operations {
read: Some(blob_read::<T>),
write: Some(blob_write::<T>),
llseek: Some(bindings::default_llseek),
open: Some(bindings::simple_open),
// SAFETY: `file_operations` supports zeroes in all fields.
..unsafe { core::mem::zeroed() }
};
// SAFETY:
// - The private data of `struct inode` does always contain a pointer to a valid `T`.
// - `simple_open()` stores the `struct inode`'s private data in the private data of the
// corresponding `struct file`.
// - `blob_read()` re-creates a reference to `T` from the `struct file`'s private data.
// - `blob_write()` re-creates a reference to `T` from the `struct file`'s private data.
// - `default_llseek()` does not access the `struct file`'s private data.
unsafe { FileOps::new(operations, 0o600) }
};
}
|