From d5d060d624e34c6ce1748a157cf2391e49af2a54 Mon Sep 17 00:00:00 2001 From: Andreas Hindborg Date: Tue, 2 Sep 2025 11:54:55 +0200 Subject: rust: str: normalize imports in `str.rs` Clean up imports in `str.rs`. This makes future code manipulation more manageable. Reviewed-by: Alice Ryhl Reviewed-by: Daniel Almeida Signed-off-by: Andreas Hindborg Link: https://lore.kernel.org/r/20250902-rnull-up-v6-16-v7-1-b5212cc89b98@kernel.org Signed-off-by: Jens Axboe --- rust/kernel/str.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'rust/kernel/str.rs') diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs index 6c892550c0ba..082790b7a621 100644 --- a/rust/kernel/str.rs +++ b/rust/kernel/str.rs @@ -2,12 +2,13 @@ //! String representations. -use crate::alloc::{flags::*, AllocError, KVec}; -use crate::fmt::{self, Write}; +use crate::{ + alloc::{flags::*, AllocError, KVec}, + fmt::{self, Write}, + prelude::*, +}; use core::ops::{self, Deref, DerefMut, Index}; -use crate::prelude::*; - /// Byte string without UTF-8 validity guarantee. #[repr(transparent)] pub struct BStr([u8]); -- cgit From 87482d6d9104d087935592ebbf52b70f8a777c47 Mon Sep 17 00:00:00 2001 From: Andreas Hindborg Date: Tue, 2 Sep 2025 11:54:56 +0200 Subject: rust: str: allow `str::Formatter` to format into `&mut [u8]`. Improve `Formatter` so that it can write to an array or slice buffer. Reviewed-by: Daniel Almeida Reviewed-by: Alice Ryhl Signed-off-by: Andreas Hindborg Link: https://lore.kernel.org/r/20250902-rnull-up-v6-16-v7-2-b5212cc89b98@kernel.org Signed-off-by: Jens Axboe --- rust/kernel/str.rs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'rust/kernel/str.rs') diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs index 082790b7a621..76632da357a6 100644 --- a/rust/kernel/str.rs +++ b/rust/kernel/str.rs @@ -7,7 +7,10 @@ use crate::{ fmt::{self, Write}, prelude::*, }; -use core::ops::{self, Deref, DerefMut, Index}; +use core::{ + marker::PhantomData, + ops::{self, Deref, DerefMut, Index}, +}; /// Byte string without UTF-8 validity guarantee. #[repr(transparent)] @@ -825,9 +828,9 @@ impl fmt::Write for RawFormatter { /// Allows formatting of [`fmt::Arguments`] into a raw buffer. /// /// Fails if callers attempt to write more than will fit in the buffer. -pub(crate) struct Formatter(RawFormatter); +pub(crate) struct Formatter<'a>(RawFormatter, PhantomData<&'a mut ()>); -impl Formatter { +impl Formatter<'_> { /// Creates a new instance of [`Formatter`] with the given buffer. /// /// # Safety @@ -836,11 +839,19 @@ impl Formatter { /// for the lifetime of the returned [`Formatter`]. pub(crate) unsafe fn from_buffer(buf: *mut u8, len: usize) -> Self { // SAFETY: The safety requirements of this function satisfy those of the callee. - Self(unsafe { RawFormatter::from_buffer(buf, len) }) + Self(unsafe { RawFormatter::from_buffer(buf, len) }, PhantomData) + } + + /// Create a new [`Self`] instance. + #[expect(dead_code)] + pub(crate) fn new(buffer: &mut [u8]) -> Self { + // SAFETY: `buffer` is valid for writes for the entire length for + // the lifetime of `Self`. + unsafe { Formatter::from_buffer(buffer.as_mut_ptr(), buffer.len()) } } } -impl Deref for Formatter { +impl Deref for Formatter<'_> { type Target = RawFormatter; fn deref(&self) -> &Self::Target { @@ -848,7 +859,7 @@ impl Deref for Formatter { } } -impl fmt::Write for Formatter { +impl fmt::Write for Formatter<'_> { fn write_str(&mut self, s: &str) -> fmt::Result { self.0.write_str(s)?; -- cgit From 8c5ac71cf19bc8a2ad5bc905d1fd3191d887d469 Mon Sep 17 00:00:00 2001 From: Andreas Hindborg Date: Tue, 2 Sep 2025 11:54:57 +0200 Subject: rust: str: expose `str::{Formatter, RawFormatter}` publicly. rnull is going to make use of `str::Formatter` and `str::RawFormatter`, so expose them with public visibility. Reviewed-by: Alice Ryhl Reviewed-by: Daniel Almeida Signed-off-by: Andreas Hindborg Link: https://lore.kernel.org/r/20250902-rnull-up-v6-16-v7-3-b5212cc89b98@kernel.org Signed-off-by: Jens Axboe --- rust/kernel/str.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'rust/kernel/str.rs') diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs index 76632da357a6..46cdc85dad63 100644 --- a/rust/kernel/str.rs +++ b/rust/kernel/str.rs @@ -736,7 +736,7 @@ mod tests { /// /// The memory region between `pos` (inclusive) and `end` (exclusive) is valid for writes if `pos` /// is less than `end`. -pub(crate) struct RawFormatter { +pub struct RawFormatter { // Use `usize` to use `saturating_*` functions. beg: usize, pos: usize, @@ -794,7 +794,7 @@ impl RawFormatter { } /// Returns the number of bytes written to the formatter. - pub(crate) fn bytes_written(&self) -> usize { + pub fn bytes_written(&self) -> usize { self.pos - self.beg } } @@ -828,7 +828,7 @@ impl fmt::Write for RawFormatter { /// Allows formatting of [`fmt::Arguments`] into a raw buffer. /// /// Fails if callers attempt to write more than will fit in the buffer. -pub(crate) struct Formatter<'a>(RawFormatter, PhantomData<&'a mut ()>); +pub struct Formatter<'a>(RawFormatter, PhantomData<&'a mut ()>); impl Formatter<'_> { /// Creates a new instance of [`Formatter`] with the given buffer. @@ -843,8 +843,7 @@ impl Formatter<'_> { } /// Create a new [`Self`] instance. - #[expect(dead_code)] - pub(crate) fn new(buffer: &mut [u8]) -> Self { + pub fn new(buffer: &mut [u8]) -> Self { // SAFETY: `buffer` is valid for writes for the entire length for // the lifetime of `Self`. unsafe { Formatter::from_buffer(buffer.as_mut_ptr(), buffer.len()) } -- cgit From cdde7a1951ff0600adc45718ba251559e4d3fd7c Mon Sep 17 00:00:00 2001 From: Andreas Hindborg Date: Tue, 2 Sep 2025 11:54:58 +0200 Subject: rust: str: introduce `NullTerminatedFormatter` Add `NullTerminatedFormatter`, a formatter that writes a null terminated string to an array or slice buffer. Because this type needs to manage the trailing null marker, the existing formatters cannot be used to implement this type. Reviewed-by: Alice Ryhl Reviewed-by: Daniel Almeida Signed-off-by: Andreas Hindborg Link: https://lore.kernel.org/r/20250902-rnull-up-v6-16-v7-4-b5212cc89b98@kernel.org Signed-off-by: Jens Axboe --- rust/kernel/str.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) (limited to 'rust/kernel/str.rs') diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs index 46cdc85dad63..d8326f7bc9c1 100644 --- a/rust/kernel/str.rs +++ b/rust/kernel/str.rs @@ -871,6 +871,55 @@ impl fmt::Write for Formatter<'_> { } } +/// A mutable reference to a byte buffer where a string can be written into. +/// +/// The buffer will be automatically null terminated after the last written character. +/// +/// # Invariants +/// +/// * The first byte of `buffer` is always zero. +/// * The length of `buffer` is at least 1. +pub(crate) struct NullTerminatedFormatter<'a> { + buffer: &'a mut [u8], +} + +impl<'a> NullTerminatedFormatter<'a> { + /// Create a new [`Self`] instance. + #[expect(dead_code)] + pub(crate) fn new(buffer: &'a mut [u8]) -> Option> { + *(buffer.first_mut()?) = 0; + + // INVARIANT: + // - We wrote zero to the first byte above. + // - If buffer was not at least length 1, `buffer.first_mut()` would return None. + Some(Self { buffer }) + } +} + +impl Write for NullTerminatedFormatter<'_> { + fn write_str(&mut self, s: &str) -> fmt::Result { + let bytes = s.as_bytes(); + let len = bytes.len(); + + // We want space for a zero. By type invariant, buffer length is always at least 1, so no + // underflow. + if len > self.buffer.len() - 1 { + return Err(fmt::Error); + } + + let buffer = core::mem::take(&mut self.buffer); + // We break the zero start invariant for a short while. + buffer[..len].copy_from_slice(bytes); + // INVARIANT: We checked above that buffer will have size at least 1 after this assignment. + self.buffer = &mut buffer[len..]; + + // INVARIANT: We write zero to the first byte of the buffer. + self.buffer[0] = 0; + + Ok(()) + } +} + /// An owned string that is guaranteed to have exactly one `NUL` byte, which is at the end. /// /// Used for interoperability with kernel APIs that take C strings. -- cgit From b1dae0be89278348e2c99ddca820d91292856b10 Mon Sep 17 00:00:00 2001 From: Andreas Hindborg Date: Tue, 2 Sep 2025 11:54:59 +0200 Subject: rust: str: introduce `kstrtobool` function Add a Rust wrapper for the kernel's `kstrtobool` function that converts common user inputs into boolean values. Reviewed-by: Daniel Almeida Signed-off-by: Andreas Hindborg Reviewed-by: Alice Ryhl Link: https://lore.kernel.org/r/20250902-rnull-up-v6-16-v7-5-b5212cc89b98@kernel.org Signed-off-by: Jens Axboe --- rust/kernel/str.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) (limited to 'rust/kernel/str.rs') diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs index d8326f7bc9c1..d2f9ebc94b75 100644 --- a/rust/kernel/str.rs +++ b/rust/kernel/str.rs @@ -4,6 +4,7 @@ use crate::{ alloc::{flags::*, AllocError, KVec}, + error::{to_result, Result}, fmt::{self, Write}, prelude::*, }; @@ -920,6 +921,84 @@ impl Write for NullTerminatedFormatter<'_> { } } +/// # Safety +/// +/// - `string` must point to a null terminated string that is valid for read. +unsafe fn kstrtobool_raw(string: *const u8) -> Result { + let mut result: bool = false; + + // SAFETY: + // - By function safety requirement, `string` is a valid null-terminated string. + // - `result` is a valid `bool` that we own. + to_result(unsafe { bindings::kstrtobool(string, &mut result) })?; + Ok(result) +} + +/// Convert common user inputs into boolean values using the kernel's `kstrtobool` function. +/// +/// This routine returns `Ok(bool)` if the first character is one of 'YyTt1NnFf0', or +/// \[oO\]\[NnFf\] for "on" and "off". Otherwise it will return `Err(EINVAL)`. +/// +/// # Examples +/// +/// ``` +/// # use kernel::{c_str, str::kstrtobool}; +/// +/// // Lowercase +/// assert_eq!(kstrtobool(c_str!("true")), Ok(true)); +/// assert_eq!(kstrtobool(c_str!("tr")), Ok(true)); +/// assert_eq!(kstrtobool(c_str!("t")), Ok(true)); +/// assert_eq!(kstrtobool(c_str!("twrong")), Ok(true)); +/// assert_eq!(kstrtobool(c_str!("false")), Ok(false)); +/// assert_eq!(kstrtobool(c_str!("f")), Ok(false)); +/// assert_eq!(kstrtobool(c_str!("yes")), Ok(true)); +/// assert_eq!(kstrtobool(c_str!("no")), Ok(false)); +/// assert_eq!(kstrtobool(c_str!("on")), Ok(true)); +/// assert_eq!(kstrtobool(c_str!("off")), Ok(false)); +/// +/// // Camel case +/// assert_eq!(kstrtobool(c_str!("True")), Ok(true)); +/// assert_eq!(kstrtobool(c_str!("False")), Ok(false)); +/// assert_eq!(kstrtobool(c_str!("Yes")), Ok(true)); +/// assert_eq!(kstrtobool(c_str!("No")), Ok(false)); +/// assert_eq!(kstrtobool(c_str!("On")), Ok(true)); +/// assert_eq!(kstrtobool(c_str!("Off")), Ok(false)); +/// +/// // All caps +/// assert_eq!(kstrtobool(c_str!("TRUE")), Ok(true)); +/// assert_eq!(kstrtobool(c_str!("FALSE")), Ok(false)); +/// assert_eq!(kstrtobool(c_str!("YES")), Ok(true)); +/// assert_eq!(kstrtobool(c_str!("NO")), Ok(false)); +/// assert_eq!(kstrtobool(c_str!("ON")), Ok(true)); +/// assert_eq!(kstrtobool(c_str!("OFF")), Ok(false)); +/// +/// // Numeric +/// assert_eq!(kstrtobool(c_str!("1")), Ok(true)); +/// assert_eq!(kstrtobool(c_str!("0")), Ok(false)); +/// +/// // Invalid input +/// assert_eq!(kstrtobool(c_str!("invalid")), Err(EINVAL)); +/// assert_eq!(kstrtobool(c_str!("2")), Err(EINVAL)); +/// ``` +pub fn kstrtobool(string: &CStr) -> Result { + // SAFETY: + // - The pointer returned by `CStr::as_char_ptr` is guaranteed to be + // null terminated. + // - `string` is live and thus the string is valid for read. + unsafe { kstrtobool_raw(string.as_char_ptr()) } +} + +/// Convert `&[u8]` to `bool` by deferring to [`kernel::str::kstrtobool`]. +/// +/// Only considers at most the first two bytes of `bytes`. +pub fn kstrtobool_bytes(bytes: &[u8]) -> Result { + // `ktostrbool` only considers the first two bytes of the input. + let stack_string = [*bytes.first().unwrap_or(&0), *bytes.get(1).unwrap_or(&0), 0]; + // SAFETY: `stack_string` is null terminated and it is live on the stack so + // it is valid for read. + unsafe { kstrtobool_raw(stack_string.as_ptr()) } +} + /// An owned string that is guaranteed to have exactly one `NUL` byte, which is at the end. /// /// Used for interoperability with kernel APIs that take C strings. -- cgit From c3a54220b54a1bda0662f0e7ab90ffabf5036d50 Mon Sep 17 00:00:00 2001 From: Andreas Hindborg Date: Tue, 2 Sep 2025 11:55:02 +0200 Subject: rust: block: use `NullTerminatedFormatter` Use the new `NullTerminatedFormatter` to write the name of a `GenDisk` to the name buffer. This new formatter automatically adds a trailing null marker after the written characters, so we don't need to append that at the call site any longer. Reviewed-by: Alice Ryhl Reviewed-by: Daniel Almeida Signed-off-by: Andreas Hindborg Link: https://lore.kernel.org/r/20250902-rnull-up-v6-16-v7-8-b5212cc89b98@kernel.org Signed-off-by: Jens Axboe --- rust/kernel/str.rs | 1 - 1 file changed, 1 deletion(-) (limited to 'rust/kernel/str.rs') diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs index d2f9ebc94b75..5c74e5f77601 100644 --- a/rust/kernel/str.rs +++ b/rust/kernel/str.rs @@ -886,7 +886,6 @@ pub(crate) struct NullTerminatedFormatter<'a> { impl<'a> NullTerminatedFormatter<'a> { /// Create a new [`Self`] instance. - #[expect(dead_code)] pub(crate) fn new(buffer: &'a mut [u8]) -> Option> { *(buffer.first_mut()?) = 0; -- cgit