Skip to content

Commit e27e37b

Browse files
committed
rust: CStr overhaul
`CStr` is overhauled to make using it more similar to use a `str`. Signed-off-by: Gary Guo <[email protected]>
1 parent 3abb3f9 commit e27e37b

17 files changed

+271
-98
lines changed

drivers/android/rust_binder.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
use alloc::{boxed::Box, sync::Arc};
1111
use core::pin::Pin;
1212
use kernel::{
13-
cstr,
13+
c_str,
1414
io_buffer::IoBufferWriter,
1515
linked_list::{GetLinks, GetLinksWrapped, Links},
1616
miscdev::Registration,
@@ -111,7 +111,7 @@ impl KernelModule for BinderModule {
111111
let pinned_ctx = Context::new()?;
112112
let ctx = unsafe { Pin::into_inner_unchecked(pinned_ctx) };
113113
let reg = Registration::<Arc<Context>>::new_pinned::<process::Process>(
114-
cstr!("rust_binder"),
114+
c_str!("rust_binder"),
115115
None,
116116
ctx,
117117
)?;

rust/kernel/c_types.rs

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -117,18 +117,3 @@ mod c {
117117
}
118118

119119
pub use c::*;
120-
121-
/// Reads string until null byte is reached and returns slice excluding the
122-
/// terminating null.
123-
///
124-
/// # Safety
125-
///
126-
/// The data from the pointer until the null terminator must be valid for reads
127-
/// and not mutated for all of `'a`. The length of the string must also be less
128-
/// than `isize::MAX`. See the documentation on
129-
/// [`core::slice::from_raw_parts()`] for further details on safety of
130-
/// converting a pointer to a slice.
131-
pub unsafe fn c_string_bytes<'a>(ptr: *const crate::c_types::c_char) -> &'a [u8] {
132-
let length = crate::bindings::strlen(ptr) as usize;
133-
&core::slice::from_raw_parts(ptr as *const u8, length)
134-
}

rust/kernel/chrdev.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::bindings;
1717
use crate::c_types;
1818
use crate::error::{Error, Result};
1919
use crate::file_operations;
20-
use crate::types::CStr;
20+
use crate::str::CStr;
2121

2222
/// Character device.
2323
///
@@ -87,7 +87,7 @@ struct RegistrationInner<const N: usize> {
8787
///
8888
/// May contain up to a fixed number (`N`) of devices. Must be pinned.
8989
pub struct Registration<const N: usize> {
90-
name: CStr<'static>,
90+
name: &'static CStr,
9191
minors_start: u16,
9292
this_module: &'static crate::ThisModule,
9393
inner: Option<RegistrationInner<N>>,
@@ -104,7 +104,7 @@ impl<const N: usize> Registration<{ N }> {
104104
/// are going to pin the registration right away, call
105105
/// [`Self::new_pinned()`] instead.
106106
pub fn new(
107-
name: CStr<'static>,
107+
name: &'static CStr,
108108
minors_start: u16,
109109
this_module: &'static crate::ThisModule,
110110
) -> Self {
@@ -120,7 +120,7 @@ impl<const N: usize> Registration<{ N }> {
120120
///
121121
/// This does *not* register the device: see [`Self::register()`].
122122
pub fn new_pinned(
123-
name: CStr<'static>,
123+
name: &'static CStr,
124124
minors_start: u16,
125125
this_module: &'static crate::ThisModule,
126126
) -> Result<Pin<Box<Self>>> {
@@ -146,7 +146,7 @@ impl<const N: usize> Registration<{ N }> {
146146
&mut dev,
147147
this.minors_start.into(),
148148
N.try_into()?,
149-
this.name.as_ptr() as *const c_types::c_char,
149+
this.name.as_char_ptr(),
150150
)
151151
};
152152
if res != 0 {

rust/kernel/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
const_fn,
2020
const_mut_refs,
2121
const_panic,
22+
const_raw_ptr_deref,
2223
try_reserve
2324
)]
2425
#![deny(clippy::complexity)]
@@ -44,6 +45,7 @@ pub mod file;
4445
pub mod file_operations;
4546
pub mod miscdev;
4647
pub mod pages;
48+
pub mod str;
4749

4850
pub mod linked_list;
4951
mod raw_list;
@@ -66,7 +68,7 @@ mod types;
6668
pub mod user_ptr;
6769

6870
pub use crate::error::{Error, Result};
69-
pub use crate::types::{CStr, Mode};
71+
pub use crate::types::Mode;
7072

7173
/// Page size defined in terms of the `PAGE_SHIFT` macro from C.
7274
///

rust/kernel/miscdev.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
//!
77
//! Reference: <https://www.kernel.org/doc/html/latest/driver-api/misc_devices.html>
88
9+
use crate::bindings;
910
use crate::error::{Error, Result};
1011
use crate::file_operations::{FileOpenAdapter, FileOpener, FileOperationsVtable};
11-
use crate::{bindings, c_types, CStr};
12+
use crate::str::CStr;
1213
use alloc::boxed::Box;
1314
use core::marker::PhantomPinned;
1415
use core::pin::Pin;
@@ -41,7 +42,7 @@ impl<T: Sync> Registration<T> {
4142
///
4243
/// Returns a pinned heap-allocated representation of the registration.
4344
pub fn new_pinned<F: FileOpener<T>>(
44-
name: CStr<'static>,
45+
name: &'static CStr,
4546
minor: Option<i32>,
4647
context: T,
4748
) -> Result<Pin<Box<Self>>> {
@@ -56,7 +57,7 @@ impl<T: Sync> Registration<T> {
5657
/// self-referential. If a minor is not given, the kernel allocates a new one if possible.
5758
pub fn register<F: FileOpener<T>>(
5859
self: Pin<&mut Self>,
59-
name: CStr<'static>,
60+
name: &'static CStr,
6061
minor: Option<i32>,
6162
) -> Result {
6263
// SAFETY: We must ensure that we never move out of `this`.
@@ -68,7 +69,7 @@ impl<T: Sync> Registration<T> {
6869

6970
// SAFETY: The adapter is compatible with `misc_register`.
7071
this.mdev.fops = unsafe { FileOperationsVtable::<Self, F>::build() };
71-
this.mdev.name = name.as_ptr() as *const c_types::c_char;
72+
this.mdev.name = name.as_char_ptr();
7273
this.mdev.minor = minor.unwrap_or(bindings::MISC_DYNAMIC_MINOR as i32);
7374

7475
let ret = unsafe { bindings::misc_register(&mut this.mdev) };

rust/kernel/module_param.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//!
55
//! C header: [`include/linux/moduleparam.h`](../../../include/linux/moduleparam.h)
66
7+
use crate::str::CStr;
78
use core::fmt::Write;
89

910
/// Types that can be used for module parameters.
@@ -70,7 +71,7 @@ pub trait ModuleParam: core::fmt::Display + core::marker::Sized {
7071
let arg = if val.is_null() {
7172
None
7273
} else {
73-
Some(crate::c_types::c_string_bytes(val))
74+
Some(CStr::from_char_ptr(val).as_bytes())
7475
};
7576
match Self::try_from_param_arg(arg) {
7677
Some(new_value) => {

rust/kernel/str.rs

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! String representations.
4+
5+
use core::ops::{self, Deref, Index};
6+
7+
use crate::bindings;
8+
use crate::c_types;
9+
10+
/// Byte string without UTF-8 validity guarantee.
11+
///
12+
/// `BStr` is simply an alias to `[u8]`, but has a more evident semantical meaning.
13+
pub type BStr = [u8];
14+
15+
/// Creates a new [`BStr`] from a string literal.
16+
///
17+
/// `b_str!` converts the supplied string literal to byte string, so non-ASCII
18+
/// characters can be included.
19+
///
20+
/// # Examples
21+
///
22+
/// ```rust,no_run
23+
/// const MY_BSTR: &'static BStr = b_str!("My awesome BStr!");
24+
/// ```
25+
#[macro_export]
26+
macro_rules! b_str {
27+
($str:literal) => {{
28+
const S: &'static str = $str;
29+
const C: &'static $crate::str::BStr = S.as_bytes();
30+
C
31+
}};
32+
}
33+
34+
/// Possible errors when using conversion functions in [`CStr`].
35+
#[derive(Debug, Clone, Copy)]
36+
pub enum CStrConvertError {
37+
/// Supplied bytes contain an interior `NUL`.
38+
InteriorNul,
39+
40+
/// Supplied bytes are not terminated by `NUL`.
41+
NotNulTerminated,
42+
}
43+
44+
impl From<CStrConvertError> for crate::Error {
45+
#[inline]
46+
fn from(_: CStrConvertError) -> crate::Error {
47+
crate::Error::EINVAL
48+
}
49+
}
50+
51+
/// A string that is guaranteed to have exactly one `NUL` byte, which is at the
52+
/// end.
53+
///
54+
/// Used for interoperability with kernel APIs that take C strings.
55+
#[repr(transparent)]
56+
pub struct CStr([u8]);
57+
58+
impl CStr {
59+
/// Returns the length of this string excluding `NUL`.
60+
#[inline]
61+
pub const fn len(&self) -> usize {
62+
self.0.len() - 1
63+
}
64+
65+
/// Returns the length of this string with `NUL`.
66+
#[inline]
67+
pub const fn len_with_nul(&self) -> usize {
68+
self.0.len()
69+
}
70+
71+
/// Returns `true` if the string only includes `NUL`.
72+
#[inline]
73+
pub const fn is_empty(&self) -> bool {
74+
self.len() == 0
75+
}
76+
77+
/// Wraps a raw C string pointer.
78+
///
79+
/// # Safety
80+
///
81+
/// `ptr` must be a valid pointer to a `NUL`-terminated C string, and it must
82+
/// last at least `'a`. When `CStr` is alive, the memory pointed by `ptr`
83+
/// must not be mutated.
84+
#[inline]
85+
pub unsafe fn from_char_ptr<'a>(ptr: *const c_types::c_char) -> &'a Self {
86+
let len = bindings::strlen(ptr) + 1;
87+
Self::from_bytes_with_nul_unchecked(core::slice::from_raw_parts(ptr as _, len as _))
88+
}
89+
90+
/// Creates a [`CStr`] from a `[u8]`.
91+
///
92+
/// The provided slice must be `NUL`-terminated, does not contain any
93+
/// interior `NUL` bytes.
94+
pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, CStrConvertError> {
95+
if bytes.is_empty() {
96+
return Err(CStrConvertError::NotNulTerminated);
97+
}
98+
if bytes[bytes.len() - 1] != 0 {
99+
return Err(CStrConvertError::NotNulTerminated);
100+
}
101+
if bytes[..bytes.len()].contains(&0) {
102+
return Err(CStrConvertError::InteriorNul);
103+
}
104+
// SAFETY: We just checked that all properties hold.
105+
Ok(unsafe { Self::from_bytes_with_nul_unchecked(bytes) })
106+
}
107+
108+
/// Creates a [`CStr`] from a `[u8]` without performing any additional
109+
/// checks.
110+
///
111+
/// # Safety
112+
///
113+
/// `bytes` *must* end with a `NUL` byte, and should only have a single
114+
/// `NUL` byte (or the string will be truncated).
115+
#[inline]
116+
pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr {
117+
// Note: This can be done using pointer deref (which requires
118+
// const_raw_ptr_deref to be const) or transmute (which requires
119+
// const_transmute to be const) or ptr::from_raw_parts (which
120+
// requires ptr_metadata).
121+
// While none of them are current stable, it is very likely that
122+
// one of them will eventually be.
123+
&*(bytes as *const [u8] as *const Self)
124+
}
125+
126+
/// Returns a C pointer to the string.
127+
#[inline]
128+
pub const fn as_char_ptr(&self) -> *const c_types::c_char {
129+
self.0.as_ptr() as _
130+
}
131+
132+
/// Convert the string to a byte slice without the trailing 0 byte.
133+
#[inline]
134+
pub fn as_bytes(&self) -> &[u8] {
135+
&self.0[..self.0.len() - 1]
136+
}
137+
138+
/// Convert the string to a byte slice containing the trailing 0 byte.
139+
#[inline]
140+
pub const fn as_bytes_with_nul(&self) -> &[u8] {
141+
&self.0
142+
}
143+
}
144+
145+
impl AsRef<BStr> for CStr {
146+
#[inline]
147+
fn as_ref(&self) -> &BStr {
148+
self.as_bytes()
149+
}
150+
}
151+
152+
impl Deref for CStr {
153+
type Target = BStr;
154+
155+
#[inline]
156+
fn deref(&self) -> &Self::Target {
157+
self.as_bytes()
158+
}
159+
}
160+
161+
impl Index<ops::RangeFrom<usize>> for CStr {
162+
type Output = CStr;
163+
164+
#[inline]
165+
fn index(&self, index: ops::RangeFrom<usize>) -> &Self::Output {
166+
assert!(
167+
index.start <= self.len(),
168+
"range start index {} out of range for slice of length {}",
169+
index.start,
170+
self.len()
171+
);
172+
// SAFETY: We just checked the length.
173+
unsafe { Self::from_bytes_with_nul_unchecked(&self.0[index.start..]) }
174+
}
175+
}
176+
177+
impl Index<ops::RangeFull> for CStr {
178+
type Output = CStr;
179+
180+
#[inline]
181+
fn index(&self, _index: ops::RangeFull) -> &Self::Output {
182+
self
183+
}
184+
}
185+
186+
#[doc(hidden)]
187+
pub trait CStrIndex {}
188+
189+
impl CStrIndex for usize {}
190+
impl CStrIndex for ops::Range<usize> {}
191+
impl CStrIndex for ops::RangeInclusive<usize> {}
192+
impl CStrIndex for ops::RangeToInclusive<usize> {}
193+
194+
impl<Idx> Index<Idx> for CStr
195+
where
196+
Idx: CStrIndex,
197+
BStr: Index<Idx>,
198+
{
199+
type Output = <BStr as Index<Idx>>::Output;
200+
201+
#[inline]
202+
fn index(&self, index: Idx) -> &Self::Output {
203+
&self.0[index]
204+
}
205+
}
206+
207+
/// Creates a new [`CStr`] from a string literal.
208+
///
209+
/// The string literal should not contain any `NUL` bytes.
210+
///
211+
/// # Examples
212+
///
213+
/// ```rust,no_run
214+
/// const MY_CSTR: &'static CStr = c_str!("My awesome CStr!");
215+
/// ```
216+
#[macro_export]
217+
macro_rules! c_str {
218+
($str:literal) => {{
219+
// FIXME: Check that `$str` does not contain interior `NUL`.
220+
const S: &str = concat!($str, "\0");
221+
const C: &$crate::str::CStr =
222+
{ unsafe { $crate::str::CStr::from_bytes_with_nul_unchecked(S.as_bytes()) } };
223+
C
224+
}};
225+
}

0 commit comments

Comments
 (0)