mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2024-12-28 16:52:18 +00:00
Merge branch 'rust-next' of https://github.com/Rust-for-Linux/linux.git
# Conflicts: # rust/kernel/miscdevice.rs # rust/kernel/security.rs
This commit is contained in:
commit
9b9b567376
@ -931,7 +931,7 @@ fn draw_all(&mut self, data: impl Iterator<Item = u8>) {
|
|||||||
/// They must remain valid for the duration of the function call.
|
/// They must remain valid for the duration of the function call.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn drm_panic_qr_generate(
|
pub unsafe extern "C" fn drm_panic_qr_generate(
|
||||||
url: *const i8,
|
url: *const kernel::ffi::c_char,
|
||||||
data: *mut u8,
|
data: *mut u8,
|
||||||
data_len: usize,
|
data_len: usize,
|
||||||
data_size: usize,
|
data_size: usize,
|
||||||
|
37
rust/ffi.rs
37
rust/ffi.rs
@ -10,4 +10,39 @@
|
|||||||
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
pub use core::ffi::*;
|
macro_rules! alias {
|
||||||
|
($($name:ident = $ty:ty;)*) => {$(
|
||||||
|
#[allow(non_camel_case_types, missing_docs)]
|
||||||
|
pub type $name = $ty;
|
||||||
|
|
||||||
|
// Check size compatibility with `core`.
|
||||||
|
const _: () = assert!(
|
||||||
|
core::mem::size_of::<$name>() == core::mem::size_of::<core::ffi::$name>()
|
||||||
|
);
|
||||||
|
)*}
|
||||||
|
}
|
||||||
|
|
||||||
|
alias! {
|
||||||
|
// `core::ffi::c_char` is either `i8` or `u8` depending on architecture. In the kernel, we use
|
||||||
|
// `-funsigned-char` so it's always mapped to `u8`.
|
||||||
|
c_char = u8;
|
||||||
|
|
||||||
|
c_schar = i8;
|
||||||
|
c_uchar = u8;
|
||||||
|
|
||||||
|
c_short = i16;
|
||||||
|
c_ushort = u16;
|
||||||
|
|
||||||
|
c_int = i32;
|
||||||
|
c_uint = u32;
|
||||||
|
|
||||||
|
// In the kernel, `intptr_t` is defined to be `long` in all platforms, so we can map the type to
|
||||||
|
// `isize`.
|
||||||
|
c_long = isize;
|
||||||
|
c_ulong = usize;
|
||||||
|
|
||||||
|
c_longlong = i64;
|
||||||
|
c_ulonglong = u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use core::ffi::c_void;
|
||||||
|
@ -427,13 +427,23 @@ fn deref_mut(&mut self) -> &mut T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T, A> fmt::Display for Box<T, A>
|
||||||
|
where
|
||||||
|
T: ?Sized + fmt::Display,
|
||||||
|
A: Allocator,
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
<T as fmt::Display>::fmt(&**self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T, A> fmt::Debug for Box<T, A>
|
impl<T, A> fmt::Debug for Box<T, A>
|
||||||
where
|
where
|
||||||
T: ?Sized + fmt::Debug,
|
T: ?Sized + fmt::Debug,
|
||||||
A: Allocator,
|
A: Allocator,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fmt::Debug::fmt(&**self, f)
|
<T as fmt::Debug>::fmt(&**self, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,10 +173,10 @@ unsafe fn printk(&self, klevel: &[u8], msg: fmt::Arguments<'_>) {
|
|||||||
#[cfg(CONFIG_PRINTK)]
|
#[cfg(CONFIG_PRINTK)]
|
||||||
unsafe {
|
unsafe {
|
||||||
bindings::_dev_printk(
|
bindings::_dev_printk(
|
||||||
klevel as *const _ as *const core::ffi::c_char,
|
klevel as *const _ as *const crate::ffi::c_char,
|
||||||
self.as_raw(),
|
self.as_raw(),
|
||||||
c_str!("%pA").as_char_ptr(),
|
c_str!("%pA").as_char_ptr(),
|
||||||
&msg as *const _ as *const core::ffi::c_void,
|
&msg as *const _ as *const crate::ffi::c_void,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -101,19 +101,16 @@ impl Error {
|
|||||||
/// It is a bug to pass an out-of-range `errno`. `EINVAL` would
|
/// It is a bug to pass an out-of-range `errno`. `EINVAL` would
|
||||||
/// be returned in such a case.
|
/// be returned in such a case.
|
||||||
pub fn from_errno(errno: crate::ffi::c_int) -> Error {
|
pub fn from_errno(errno: crate::ffi::c_int) -> Error {
|
||||||
if errno < -(bindings::MAX_ERRNO as i32) || errno >= 0 {
|
if let Some(error) = Self::try_from_errno(errno) {
|
||||||
|
error
|
||||||
|
} else {
|
||||||
// TODO: Make it a `WARN_ONCE` once available.
|
// TODO: Make it a `WARN_ONCE` once available.
|
||||||
crate::pr_warn!(
|
crate::pr_warn!(
|
||||||
"attempted to create `Error` with out of range `errno`: {}",
|
"attempted to create `Error` with out of range `errno`: {}",
|
||||||
errno
|
errno
|
||||||
);
|
);
|
||||||
return code::EINVAL;
|
code::EINVAL
|
||||||
}
|
}
|
||||||
|
|
||||||
// INVARIANT: The check above ensures the type invariant
|
|
||||||
// will hold.
|
|
||||||
// SAFETY: `errno` is checked above to be in a valid range.
|
|
||||||
unsafe { Error::from_errno_unchecked(errno) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an [`Error`] from a kernel error code.
|
/// Creates an [`Error`] from a kernel error code.
|
||||||
@ -153,11 +150,8 @@ pub(crate) fn to_blk_status(self) -> bindings::blk_status_t {
|
|||||||
|
|
||||||
/// Returns the error encoded as a pointer.
|
/// Returns the error encoded as a pointer.
|
||||||
pub fn to_ptr<T>(self) -> *mut T {
|
pub fn to_ptr<T>(self) -> *mut T {
|
||||||
#[cfg_attr(target_pointer_width = "32", allow(clippy::useless_conversion))]
|
|
||||||
// SAFETY: `self.0` is a valid error due to its invariant.
|
// SAFETY: `self.0` is a valid error due to its invariant.
|
||||||
unsafe {
|
unsafe { bindings::ERR_PTR(self.0.get() as _) as *mut _ }
|
||||||
bindings::ERR_PTR(self.0.get().into()) as *mut _
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a string representing the error, if one exists.
|
/// Returns a string representing the error, if one exists.
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
/// One of the following: `bindings::request_firmware`, `bindings::firmware_request_nowarn`,
|
/// One of the following: `bindings::request_firmware`, `bindings::firmware_request_nowarn`,
|
||||||
/// `bindings::firmware_request_platform`, `bindings::request_firmware_direct`.
|
/// `bindings::firmware_request_platform`, `bindings::request_firmware_direct`.
|
||||||
struct FwFunc(
|
struct FwFunc(
|
||||||
unsafe extern "C" fn(*mut *const bindings::firmware, *const i8, *mut bindings::device) -> i32,
|
unsafe extern "C" fn(*mut *const bindings::firmware, *const u8, *mut bindings::device) -> i32,
|
||||||
);
|
);
|
||||||
|
|
||||||
impl FwFunc {
|
impl FwFunc {
|
||||||
|
@ -12,18 +12,14 @@
|
|||||||
bindings,
|
bindings,
|
||||||
device::Device,
|
device::Device,
|
||||||
error::{to_result, Error, Result, VTABLE_DEFAULT_ERROR},
|
error::{to_result, Error, Result, VTABLE_DEFAULT_ERROR},
|
||||||
|
ffi::{c_int, c_long, c_uint, c_ulong},
|
||||||
fs::File,
|
fs::File,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
seq_file::SeqFile,
|
seq_file::SeqFile,
|
||||||
str::CStr,
|
str::CStr,
|
||||||
types::{ForeignOwnable, Opaque},
|
types::{ForeignOwnable, Opaque},
|
||||||
};
|
};
|
||||||
use core::{
|
use core::{marker::PhantomData, mem::MaybeUninit, pin::Pin};
|
||||||
ffi::{c_int, c_long, c_uint, c_ulong},
|
|
||||||
marker::PhantomData,
|
|
||||||
mem::MaybeUninit,
|
|
||||||
pin::Pin,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Options for creating a misc device.
|
/// Options for creating a misc device.
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
@ -279,7 +275,7 @@ impl<T: MiscDevice> VtableHelper<T> {
|
|||||||
// * There is no active fdget_pos region on the file on this thread.
|
// * There is no active fdget_pos region on the file on this thread.
|
||||||
let file = unsafe { File::from_raw_file(file) };
|
let file = unsafe { File::from_raw_file(file) };
|
||||||
|
|
||||||
match T::ioctl(device, file, cmd, arg as usize) {
|
match T::ioctl(device, file, cmd, arg) {
|
||||||
Ok(ret) => ret as c_long,
|
Ok(ret) => ret as c_long,
|
||||||
Err(err) => err.to_errno() as c_long,
|
Err(err) => err.to_errno() as c_long,
|
||||||
}
|
}
|
||||||
@ -304,7 +300,7 @@ impl<T: MiscDevice> VtableHelper<T> {
|
|||||||
// * There is no active fdget_pos region on the file on this thread.
|
// * There is no active fdget_pos region on the file on this thread.
|
||||||
let file = unsafe { File::from_raw_file(file) };
|
let file = unsafe { File::from_raw_file(file) };
|
||||||
|
|
||||||
match T::compat_ioctl(device, file, cmd, arg as usize) {
|
match T::compat_ioctl(device, file, cmd, arg) {
|
||||||
Ok(ret) => ret as c_long,
|
Ok(ret) => ret as c_long,
|
||||||
Err(err) => err.to_errno() as c_long,
|
Err(err) => err.to_errno() as c_long,
|
||||||
}
|
}
|
||||||
|
@ -107,7 +107,7 @@ pub unsafe fn call_printk(
|
|||||||
// SAFETY: TODO.
|
// SAFETY: TODO.
|
||||||
unsafe {
|
unsafe {
|
||||||
bindings::_printk(
|
bindings::_printk(
|
||||||
format_string.as_ptr() as _,
|
format_string.as_ptr(),
|
||||||
module_name.as_ptr(),
|
module_name.as_ptr(),
|
||||||
&args as *const _ as *const c_void,
|
&args as *const _ as *const c_void,
|
||||||
);
|
);
|
||||||
@ -128,7 +128,7 @@ pub fn call_printk_cont(args: fmt::Arguments<'_>) {
|
|||||||
#[cfg(CONFIG_PRINTK)]
|
#[cfg(CONFIG_PRINTK)]
|
||||||
unsafe {
|
unsafe {
|
||||||
bindings::_printk(
|
bindings::_printk(
|
||||||
format_strings::CONT.as_ptr() as _,
|
format_strings::CONT.as_ptr(),
|
||||||
&args as *const _ as *const c_void,
|
&args as *const _ as *const c_void,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ pub fn call_printf(&self, args: core::fmt::Arguments<'_>) {
|
|||||||
bindings::seq_printf(
|
bindings::seq_printf(
|
||||||
self.inner.get(),
|
self.inner.get(),
|
||||||
c_str!("%pA").as_char_ptr(),
|
c_str!("%pA").as_char_ptr(),
|
||||||
&args as *const _ as *const core::ffi::c_void,
|
&args as *const _ as *const crate::ffi::c_void,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -189,7 +189,7 @@ pub unsafe fn from_char_ptr<'a>(ptr: *const crate::ffi::c_char) -> &'a Self {
|
|||||||
// to a `NUL`-terminated C string.
|
// to a `NUL`-terminated C string.
|
||||||
let len = unsafe { bindings::strlen(ptr) } + 1;
|
let len = unsafe { bindings::strlen(ptr) } + 1;
|
||||||
// SAFETY: Lifetime guaranteed by the safety precondition.
|
// SAFETY: Lifetime guaranteed by the safety precondition.
|
||||||
let bytes = unsafe { core::slice::from_raw_parts(ptr as _, len as _) };
|
let bytes = unsafe { core::slice::from_raw_parts(ptr as _, len) };
|
||||||
// SAFETY: As `len` is returned by `strlen`, `bytes` does not contain interior `NUL`.
|
// SAFETY: As `len` is returned by `strlen`, `bytes` does not contain interior `NUL`.
|
||||||
// As we have added 1 to `len`, the last byte is known to be `NUL`.
|
// As we have added 1 to `len`, the last byte is known to be `NUL`.
|
||||||
unsafe { Self::from_bytes_with_nul_unchecked(bytes) }
|
unsafe { Self::from_bytes_with_nul_unchecked(bytes) }
|
||||||
@ -248,7 +248,7 @@ pub unsafe fn from_bytes_with_nul_unchecked_mut(bytes: &mut [u8]) -> &mut CStr {
|
|||||||
/// Returns a C pointer to the string.
|
/// Returns a C pointer to the string.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn as_char_ptr(&self) -> *const crate::ffi::c_char {
|
pub const fn as_char_ptr(&self) -> *const crate::ffi::c_char {
|
||||||
self.0.as_ptr() as _
|
self.0.as_ptr()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the string to a byte slice without the trailing `NUL` byte.
|
/// Convert the string to a byte slice without the trailing `NUL` byte.
|
||||||
@ -838,7 +838,7 @@ pub fn try_from_fmt(args: fmt::Arguments<'_>) -> Result<Self, Error> {
|
|||||||
// SAFETY: The buffer is valid for read because `f.bytes_written()` is bounded by `size`
|
// SAFETY: The buffer is valid for read because `f.bytes_written()` is bounded by `size`
|
||||||
// (which the minimum buffer size) and is non-zero (we wrote at least the `NUL` terminator)
|
// (which the minimum buffer size) and is non-zero (we wrote at least the `NUL` terminator)
|
||||||
// so `f.bytes_written() - 1` doesn't underflow.
|
// so `f.bytes_written() - 1` doesn't underflow.
|
||||||
let ptr = unsafe { bindings::memchr(buf.as_ptr().cast(), 0, (f.bytes_written() - 1) as _) };
|
let ptr = unsafe { bindings::memchr(buf.as_ptr().cast(), 0, f.bytes_written() - 1) };
|
||||||
if !ptr.is_null() {
|
if !ptr.is_null() {
|
||||||
return Err(EINVAL);
|
return Err(EINVAL);
|
||||||
}
|
}
|
||||||
|
@ -127,6 +127,14 @@
|
|||||||
/// ```
|
/// ```
|
||||||
pub struct Arc<T: ?Sized> {
|
pub struct Arc<T: ?Sized> {
|
||||||
ptr: NonNull<ArcInner<T>>,
|
ptr: NonNull<ArcInner<T>>,
|
||||||
|
// NB: this informs dropck that objects of type `ArcInner<T>` may be used in `<Arc<T> as
|
||||||
|
// Drop>::drop`. Note that dropck already assumes that objects of type `T` may be used in
|
||||||
|
// `<Arc<T> as Drop>::drop` and the distinction between `T` and `ArcInner<T>` is not presently
|
||||||
|
// meaningful with respect to dropck - but this may change in the future so this is left here
|
||||||
|
// out of an abundance of caution.
|
||||||
|
//
|
||||||
|
// See https://doc.rust-lang.org/nomicon/phantom-data.html#generic-parameters-and-drop-checking
|
||||||
|
// for more detail on the semantics of dropck in the presence of `PhantomData`.
|
||||||
_p: PhantomData<ArcInner<T>>,
|
_p: PhantomData<ArcInner<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
alloc::Flags,
|
alloc::Flags,
|
||||||
bindings,
|
bindings,
|
||||||
error::Result,
|
error::Result,
|
||||||
ffi::{c_ulong, c_void},
|
ffi::c_void,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
transmute::{AsBytes, FromBytes},
|
transmute::{AsBytes, FromBytes},
|
||||||
};
|
};
|
||||||
@ -224,13 +224,9 @@ pub fn read_raw(&mut self, out: &mut [MaybeUninit<u8>]) -> Result {
|
|||||||
if len > self.length {
|
if len > self.length {
|
||||||
return Err(EFAULT);
|
return Err(EFAULT);
|
||||||
}
|
}
|
||||||
let Ok(len_ulong) = c_ulong::try_from(len) else {
|
// SAFETY: `out_ptr` points into a mutable slice of length `len`, so we may write
|
||||||
return Err(EFAULT);
|
|
||||||
};
|
|
||||||
// SAFETY: `out_ptr` points into a mutable slice of length `len_ulong`, so we may write
|
|
||||||
// that many bytes to it.
|
// that many bytes to it.
|
||||||
let res =
|
let res = unsafe { bindings::copy_from_user(out_ptr, self.ptr as *const c_void, len) };
|
||||||
unsafe { bindings::copy_from_user(out_ptr, self.ptr as *const c_void, len_ulong) };
|
|
||||||
if res != 0 {
|
if res != 0 {
|
||||||
return Err(EFAULT);
|
return Err(EFAULT);
|
||||||
}
|
}
|
||||||
@ -259,9 +255,6 @@ pub fn read<T: FromBytes>(&mut self) -> Result<T> {
|
|||||||
if len > self.length {
|
if len > self.length {
|
||||||
return Err(EFAULT);
|
return Err(EFAULT);
|
||||||
}
|
}
|
||||||
let Ok(len_ulong) = c_ulong::try_from(len) else {
|
|
||||||
return Err(EFAULT);
|
|
||||||
};
|
|
||||||
let mut out: MaybeUninit<T> = MaybeUninit::uninit();
|
let mut out: MaybeUninit<T> = MaybeUninit::uninit();
|
||||||
// SAFETY: The local variable `out` is valid for writing `size_of::<T>()` bytes.
|
// SAFETY: The local variable `out` is valid for writing `size_of::<T>()` bytes.
|
||||||
//
|
//
|
||||||
@ -272,7 +265,7 @@ pub fn read<T: FromBytes>(&mut self) -> Result<T> {
|
|||||||
bindings::_copy_from_user(
|
bindings::_copy_from_user(
|
||||||
out.as_mut_ptr().cast::<c_void>(),
|
out.as_mut_ptr().cast::<c_void>(),
|
||||||
self.ptr as *const c_void,
|
self.ptr as *const c_void,
|
||||||
len_ulong,
|
len,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if res != 0 {
|
if res != 0 {
|
||||||
@ -335,12 +328,9 @@ pub fn write_slice(&mut self, data: &[u8]) -> Result {
|
|||||||
if len > self.length {
|
if len > self.length {
|
||||||
return Err(EFAULT);
|
return Err(EFAULT);
|
||||||
}
|
}
|
||||||
let Ok(len_ulong) = c_ulong::try_from(len) else {
|
// SAFETY: `data_ptr` points into an immutable slice of length `len`, so we may read
|
||||||
return Err(EFAULT);
|
|
||||||
};
|
|
||||||
// SAFETY: `data_ptr` points into an immutable slice of length `len_ulong`, so we may read
|
|
||||||
// that many bytes from it.
|
// that many bytes from it.
|
||||||
let res = unsafe { bindings::copy_to_user(self.ptr as *mut c_void, data_ptr, len_ulong) };
|
let res = unsafe { bindings::copy_to_user(self.ptr as *mut c_void, data_ptr, len) };
|
||||||
if res != 0 {
|
if res != 0 {
|
||||||
return Err(EFAULT);
|
return Err(EFAULT);
|
||||||
}
|
}
|
||||||
@ -359,9 +349,6 @@ pub fn write<T: AsBytes>(&mut self, value: &T) -> Result {
|
|||||||
if len > self.length {
|
if len > self.length {
|
||||||
return Err(EFAULT);
|
return Err(EFAULT);
|
||||||
}
|
}
|
||||||
let Ok(len_ulong) = c_ulong::try_from(len) else {
|
|
||||||
return Err(EFAULT);
|
|
||||||
};
|
|
||||||
// SAFETY: The reference points to a value of type `T`, so it is valid for reading
|
// SAFETY: The reference points to a value of type `T`, so it is valid for reading
|
||||||
// `size_of::<T>()` bytes.
|
// `size_of::<T>()` bytes.
|
||||||
//
|
//
|
||||||
@ -372,7 +359,7 @@ pub fn write<T: AsBytes>(&mut self, value: &T) -> Result {
|
|||||||
bindings::_copy_to_user(
|
bindings::_copy_to_user(
|
||||||
self.ptr as *mut c_void,
|
self.ptr as *mut c_void,
|
||||||
(value as *const T).cast::<c_void>(),
|
(value as *const T).cast::<c_void>(),
|
||||||
len_ulong,
|
len,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if res != 0 {
|
if res != 0 {
|
||||||
|
@ -69,6 +69,7 @@
|
|||||||
//! fn print_later(val: Arc<MyStruct>) {
|
//! fn print_later(val: Arc<MyStruct>) {
|
||||||
//! let _ = workqueue::system().enqueue(val);
|
//! let _ = workqueue::system().enqueue(val);
|
||||||
//! }
|
//! }
|
||||||
|
//! # print_later(MyStruct::new(42).unwrap());
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! The following example shows how multiple `work_struct` fields can be used:
|
//! The following example shows how multiple `work_struct` fields can be used:
|
||||||
@ -126,6 +127,8 @@
|
|||||||
//! fn print_2_later(val: Arc<MyStruct>) {
|
//! fn print_2_later(val: Arc<MyStruct>) {
|
||||||
//! let _ = workqueue::system().enqueue::<Arc<MyStruct>, 2>(val);
|
//! let _ = workqueue::system().enqueue::<Arc<MyStruct>, 2>(val);
|
||||||
//! }
|
//! }
|
||||||
|
//! # print_1_later(MyStruct::new(24, 25).unwrap());
|
||||||
|
//! # print_2_later(MyStruct::new(41, 42).unwrap());
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! C header: [`include/linux/workqueue.h`](srctree/include/linux/workqueue.h)
|
//! C header: [`include/linux/workqueue.h`](srctree/include/linux/workqueue.h)
|
||||||
|
@ -83,7 +83,7 @@ fn drop(&mut self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod trace {
|
mod trace {
|
||||||
use core::ffi::c_int;
|
use kernel::ffi::c_int;
|
||||||
|
|
||||||
kernel::declare_trace! {
|
kernel::declare_trace! {
|
||||||
/// # Safety
|
/// # Safety
|
||||||
|
Loading…
Reference in New Issue
Block a user