Skip to content

Commit 2ef2e88

Browse files
committed
rust: add basic owned C strings
For now they're only constructible from formatted arguments. They will be used in a subsequent patch to allow registration of miscdevs with names that are constructed at runtime. Signed-off-by: Wedson Almeida Filho <[email protected]>
1 parent a4e89cf commit 2ef2e88

File tree

2 files changed

+93
-3
lines changed

2 files changed

+93
-3
lines changed

rust/kernel/prelude.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub use macros::module;
2020
pub use super::build_assert;
2121

2222
pub use super::{
23-
dbg, dev_alert, dev_crit, dev_dbg, dev_emerg, dev_err, dev_info, dev_notice, dev_warn,
23+
dbg, dev_alert, dev_crit, dev_dbg, dev_emerg, dev_err, dev_info, dev_notice, dev_warn, fmt,
2424
pr_alert, pr_crit, pr_debug, pr_emerg, pr_err, pr_info, pr_notice, pr_warn,
2525
};
2626

rust/kernel/str.rs

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
//! String representations.
44
5+
use alloc::vec::Vec;
56
use core::fmt::{self, Write};
67
use core::ops::{self, Deref, Index};
78

8-
use crate::bindings;
9-
use crate::c_types;
9+
use crate::{bindings, c_types, Error};
1010

1111
/// Byte string without UTF-8 validity guarantee.
1212
///
@@ -391,6 +391,16 @@ pub(crate) struct RawFormatter {
391391
}
392392

393393
impl RawFormatter {
394+
/// Creates a new instance of [`RawFormatter`] with an empty buffer.
395+
fn new() -> Self {
396+
// INVARIANT: The buffer is empty, so the region that needs to be writable is empty.
397+
Self {
398+
beg: 0,
399+
pos: 0,
400+
end: 0,
401+
}
402+
}
403+
394404
/// Creates a new instance of [`RawFormatter`] with the given buffer pointers.
395405
///
396406
/// # Safety
@@ -500,3 +510,83 @@ impl fmt::Write for Formatter {
500510
}
501511
}
502512
}
513+
514+
/// An owned string that is guaranteed to have exactly one `NUL` byte, which is at the end.
515+
///
516+
/// Used for interoperability with kernel APIs that take C strings.
517+
///
518+
/// # Invariants
519+
///
520+
/// The string is always `NUL`-terminated and contains no other `NUL` bytes.
521+
///
522+
/// # Examples
523+
///
524+
/// ```
525+
/// # use kernel::prelude::*;
526+
/// use kernel::str::CString;
527+
///
528+
/// let s = CString::try_from_fmt(fmt!("{}{}{}", "abc", 10, 20)).unwrap();
529+
/// assert_eq!(s.as_bytes_with_nul(), "abc1020\0".as_bytes());
530+
///
531+
/// let tmp = "testing";
532+
/// let s = CString::try_from_fmt(fmt!("{tmp}{}", 123)).unwrap();
533+
/// assert_eq!(s.as_bytes_with_nul(), "testing123\0".as_bytes());
534+
///
535+
/// // This fails because it has an embedded `NUL` byte.
536+
/// let s = CString::try_from_fmt(fmt!("a\0b{}", 123));
537+
/// assert_eq!(s.is_ok(), false);
538+
/// ```
539+
pub struct CString {
540+
buf: Vec<u8>,
541+
}
542+
543+
impl CString {
544+
/// Creates an instance of [`CString`] from the given formatted arguments.
545+
pub fn try_from_fmt(args: fmt::Arguments<'_>) -> Result<Self, Error> {
546+
// Calculate the size needed (formatted string plus `NUL` terminator).
547+
let mut f = RawFormatter::new();
548+
f.write_fmt(args)?;
549+
f.write_str("\0")?;
550+
let size = f.bytes_written();
551+
552+
// Allocate a vector with the required number of bytes, and write to it.
553+
let mut buf = Vec::try_with_capacity(size)?;
554+
// SAFETY: The buffer stored in `buf` is at least of size `size` and is valid for writes.
555+
let mut f = unsafe { Formatter::from_buffer(buf.as_mut_ptr(), size) };
556+
f.write_fmt(args)?;
557+
f.write_str("\0")?;
558+
559+
// SAFETY: The number of bytes that can be written to `f` is bounded by `size`, which is
560+
// `buf`'s capacity. The contents of the buffer have been initialised by writes to `f`.
561+
unsafe { buf.set_len(f.bytes_written()) };
562+
563+
// Check that there are no `NUL` bytes before the end.
564+
// SAFETY: The buffer is valid for read because `f.bytes_written()` is bounded by `size`
565+
// (which the minimum buffer size) and is non-zero (we wrote at least the `NUL` terminator)
566+
// so `f.bytes_written() - 1` doesn't underflow.
567+
let ptr = unsafe { bindings::memchr(buf.as_ptr().cast(), 0, (f.bytes_written() - 1) as _) };
568+
if !ptr.is_null() {
569+
return Err(Error::EINVAL);
570+
}
571+
572+
// INVARIANT: We wrote the `NUL` terminator and checked above that no other `NUL` bytes
573+
// exist in the buffer.
574+
Ok(Self { buf })
575+
}
576+
}
577+
578+
impl Deref for CString {
579+
type Target = CStr;
580+
581+
fn deref(&self) -> &Self::Target {
582+
// SAFETY: The type invariants guarantee that the string is `NUL`-terminated and that no
583+
// other `NUL` bytes exist.
584+
unsafe { CStr::from_bytes_with_nul_unchecked(self.buf.as_slice()) }
585+
}
586+
}
587+
588+
/// A convenience alias for [`core::format_args`].
589+
#[macro_export]
590+
macro_rules! fmt {
591+
($($f:tt)*) => ( core::format_args!($($f)*) )
592+
}

0 commit comments

Comments
 (0)