From beb841f8e9ef39f2184f3f77455404eec41baf28 Mon Sep 17 00:00:00 2001 From: Finn Behrens Date: Mon, 5 Oct 2020 10:19:19 +0200 Subject: [PATCH] apply coding style Taken from Documentation/process/coding-style.rst Signed-off-by: Finn Behrens --- drivers/char/rust_example/src/lib.rs | 44 +-- rust/kernel/build.rs | 227 ++++++++------- rust/kernel/src/allocator.rs | 30 +- rust/kernel/src/bindings.rs | 17 +- rust/kernel/src/c_types.rs | 33 ++- rust/kernel/src/chrdev.rs | 162 ++++++----- rust/kernel/src/error.rs | 41 +-- rust/kernel/src/file_operations.rs | 359 ++++++++++++----------- rust/kernel/src/lib.rs | 16 +- rust/kernel/src/prelude.rs | 12 +- rust/kernel/src/printk.rs | 70 +++-- rust/kernel/src/random.rs | 51 ++-- rust/kernel/src/sysctl.rs | 292 ++++++++++--------- rust/kernel/src/types.rs | 59 ++-- rust/kernel/src/user_ptr.rs | 258 ++++++++-------- rust/module/src/lib.rs | 358 ++++++++++++++--------- rust/shlex/src/lib.rs | 420 ++++++++++++++++----------- rustfmt.toml | 4 + 18 files changed, 1387 insertions(+), 1066 deletions(-) create mode 100644 rustfmt.toml diff --git a/drivers/char/rust_example/src/lib.rs b/drivers/char/rust_example/src/lib.rs index e8405f12ce444a..874fe65f373ab2 100644 --- a/drivers/char/rust_example/src/lib.rs +++ b/drivers/char/rust_example/src/lib.rs @@ -5,7 +5,7 @@ use kernel::prelude::*; -module!{ +module! { type: RustExample, name: b"rust_example", author: b"Rust for Linux Contributors", @@ -25,27 +25,31 @@ module!{ }, } -struct RustExample { - message: String, +struct RustExample +{ + message: String, } -impl KernelModule for RustExample { - fn init() -> KernelResult { - println!("Rust Example (init)"); - println!("Am I built-in? {}", !cfg!(MODULE)); - println!("Parameters:"); - println!(" my_bool: {}", my_bool.read()); - println!(" my_i32: {}", my_i32.read()); - Ok(RustExample { - message: "on the heap!".to_owned(), - }) - } +impl KernelModule for RustExample +{ + fn init() -> KernelResult + { + println!("Rust Example (init)"); + println!("Am I built-in? {}", !cfg!(MODULE)); + println!("Parameters:"); + println!(" my_bool: {}", my_bool.read()); + println!(" my_i32: {}", my_i32.read()); + Ok(RustExample { + message: "on the heap!".to_owned(), + }) + } } -impl Drop for RustExample { - fn drop(&mut self) { - println!("My message is {}", self.message); - println!("Rust Example (exit)"); - } +impl Drop for RustExample +{ + fn drop(&mut self) + { + println!("My message is {}", self.message); + println!("Rust Example (exit)"); + } } - diff --git a/rust/kernel/build.rs b/rust/kernel/build.rs index 5a3794fdc99511..e93670bc8af7ce 100644 --- a/rust/kernel/build.rs +++ b/rust/kernel/build.rs @@ -1,135 +1,144 @@ // SPDX-License-Identifier: GPL-2.0 -use std::path::PathBuf; use std::env; +use std::path::PathBuf; -const INCLUDED_TYPES: &[&str] = &["file_system_type", "mode_t", "umode_t", "ctl_table"]; +const INCLUDED_TYPES: &[&str] = + &["file_system_type", "mode_t", "umode_t", "ctl_table"]; const INCLUDED_FUNCTIONS: &[&str] = &[ - "cdev_add", - "cdev_init", - "cdev_del", - "register_filesystem", - "unregister_filesystem", - "krealloc", - "kfree", - "mount_nodev", - "kill_litter_super", - "register_sysctl", - "unregister_sysctl_table", - "access_ok", - "_copy_to_user", - "_copy_from_user", - "alloc_chrdev_region", - "unregister_chrdev_region", - "wait_for_random_bytes", - "get_random_bytes", - "rng_is_initialized", - "printk", - "add_device_randomness", + "cdev_add", + "cdev_init", + "cdev_del", + "register_filesystem", + "unregister_filesystem", + "krealloc", + "kfree", + "mount_nodev", + "kill_litter_super", + "register_sysctl", + "unregister_sysctl_table", + "access_ok", + "_copy_to_user", + "_copy_from_user", + "alloc_chrdev_region", + "unregister_chrdev_region", + "wait_for_random_bytes", + "get_random_bytes", + "rng_is_initialized", + "printk", + "add_device_randomness", ]; const INCLUDED_VARS: &[&str] = &[ - "EINVAL", - "ENOMEM", - "ESPIPE", - "EFAULT", - "EAGAIN", - "__this_module", - "FS_REQUIRES_DEV", - "FS_BINARY_MOUNTDATA", - "FS_HAS_SUBTYPE", - "FS_USERNS_MOUNT", - "FS_RENAME_DOES_D_MOVE", - "BINDINGS_GFP_KERNEL", - "KERN_INFO", - "VERIFY_WRITE", - "LINUX_VERSION_CODE", - "SEEK_SET", - "SEEK_CUR", - "SEEK_END", - "O_NONBLOCK", - "param_ops_bool", - "param_ops_int", + "EINVAL", + "ENOMEM", + "ESPIPE", + "EFAULT", + "EAGAIN", + "__this_module", + "FS_REQUIRES_DEV", + "FS_BINARY_MOUNTDATA", + "FS_HAS_SUBTYPE", + "FS_USERNS_MOUNT", + "FS_RENAME_DOES_D_MOVE", + "BINDINGS_GFP_KERNEL", + "KERN_INFO", + "VERIFY_WRITE", + "LINUX_VERSION_CODE", + "SEEK_SET", + "SEEK_CUR", + "SEEK_END", + "O_NONBLOCK", + "param_ops_bool", + "param_ops_int", ]; const OPAQUE_TYPES: &[&str] = &[ - // These need to be opaque because they're both packed and aligned, which rustc - // doesn't support yet. See https://github.com/rust-lang/rust/issues/59154 - // and https://github.com/rust-lang/rust-bindgen/issues/1538 - "desc_struct", - "xregs_state", + // These need to be opaque because they're both packed and aligned, which rustc + // doesn't support yet. See https://github.com/rust-lang/rust/issues/59154 + // and https://github.com/rust-lang/rust-bindgen/issues/1538 + "desc_struct", + "xregs_state", ]; // Takes the CFLAGS from the kernel Makefile and changes all the include paths to be absolute // instead of relative. -fn prepare_cflags(cflags: &str, kernel_dir: &str) -> Vec { - let cflag_parts = shlex::split(&cflags).unwrap(); - let mut cflag_iter = cflag_parts.iter(); - let mut kernel_args = vec![]; - while let Some(arg) = cflag_iter.next() { - // TODO: bindgen complains - if arg.starts_with("-Wp,-MMD") { - continue; - } +fn prepare_cflags(cflags: &str, kernel_dir: &str) -> Vec +{ + let cflag_parts = shlex::split(&cflags).unwrap(); + let mut cflag_iter = cflag_parts.iter(); + let mut kernel_args = vec![]; + while let Some(arg) = cflag_iter.next() { + // TODO: bindgen complains + if arg.starts_with("-Wp,-MMD") { + continue; + } - if arg.starts_with("-I") && !arg.starts_with("-I/") { - kernel_args.push(format!("-I{}/{}", kernel_dir, &arg[2..])); - } else if arg == "-include" { - kernel_args.push(arg.to_string()); - let include_path = cflag_iter.next().unwrap(); - if include_path.starts_with('/') { - kernel_args.push(include_path.to_string()); - } else { - kernel_args.push(format!("{}/{}", kernel_dir, include_path)); - } - } else { - kernel_args.push(arg.to_string()); + if arg.starts_with("-I") && !arg.starts_with("-I/") { + kernel_args.push(format!( + "-I{}/{}", + kernel_dir, + &arg[2..] + )); + } else if arg == "-include" { + kernel_args.push(arg.to_string()); + let include_path = cflag_iter.next().unwrap(); + if include_path.starts_with('/') { + kernel_args.push(include_path.to_string()); + } else { + kernel_args.push(format!( + "{}/{}", + kernel_dir, include_path + )); + } + } else { + kernel_args.push(arg.to_string()); + } } - } - kernel_args + kernel_args } -fn main() { - println!("cargo:rerun-if-env-changed=CC"); - println!("cargo:rerun-if-env-changed=RUST_BINDGEN_CFLAGS"); +fn main() +{ + println!("cargo:rerun-if-env-changed=CC"); + println!("cargo:rerun-if-env-changed=RUST_BINDGEN_CFLAGS"); - let kernel_dir = "../../"; - let cflags = env::var("RUST_BINDGEN_CFLAGS") - .expect("Must be invoked from kernel makefile"); + let kernel_dir = "../../"; + let cflags = env::var("RUST_BINDGEN_CFLAGS") + .expect("Must be invoked from kernel makefile"); - let kernel_args = prepare_cflags(&cflags, &kernel_dir); + let kernel_args = prepare_cflags(&cflags, &kernel_dir); - let target = env::var("TARGET").unwrap(); + let target = env::var("TARGET").unwrap(); - let mut builder = bindgen::Builder::default() - .use_core() - .ctypes_prefix("c_types") - .derive_default(true) - .size_t_is_usize(true) - .rustfmt_bindings(true); + let mut builder = bindgen::Builder::default() + .use_core() + .ctypes_prefix("c_types") + .derive_default(true) + .size_t_is_usize(true) + .rustfmt_bindings(true); - builder = builder.clang_arg(format!("--target={}", target)); - for arg in kernel_args.iter() { - builder = builder.clang_arg(arg.clone()); - } + builder = builder.clang_arg(format!("--target={}", target)); + for arg in kernel_args.iter() { + builder = builder.clang_arg(arg.clone()); + } - println!("cargo:rerun-if-changed=src/bindings_helper.h"); - builder = builder.header("src/bindings_helper.h"); + println!("cargo:rerun-if-changed=src/bindings_helper.h"); + builder = builder.header("src/bindings_helper.h"); - for t in INCLUDED_TYPES { - builder = builder.whitelist_type(t); - } - for f in INCLUDED_FUNCTIONS { - builder = builder.whitelist_function(f); - } - for v in INCLUDED_VARS { - builder = builder.whitelist_var(v); - } - for t in OPAQUE_TYPES { - builder = builder.opaque_type(t); - } - let bindings = builder.generate().expect("Unable to generate bindings"); + for t in INCLUDED_TYPES { + builder = builder.whitelist_type(t); + } + for f in INCLUDED_FUNCTIONS { + builder = builder.whitelist_function(f); + } + for v in INCLUDED_VARS { + builder = builder.whitelist_var(v); + } + for t in OPAQUE_TYPES { + builder = builder.opaque_type(t); + } + let bindings = builder.generate().expect("Unable to generate bindings"); - let out_path = PathBuf::from("src/bindings_gen.rs"); - bindings - .write_to_file(out_path) - .expect("Couldn't write bindings!"); + let out_path = PathBuf::from("src/bindings_gen.rs"); + bindings.write_to_file(out_path) + .expect("Couldn't write bindings!"); } diff --git a/rust/kernel/src/allocator.rs b/rust/kernel/src/allocator.rs index 27647be92b5134..5338b964deddb3 100644 --- a/rust/kernel/src/allocator.rs +++ b/rust/kernel/src/allocator.rs @@ -8,19 +8,27 @@ use crate::c_types; pub struct KernelAllocator; -unsafe impl GlobalAlloc for KernelAllocator { - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - // krealloc is used instead of kmalloc because kmalloc is an inline function and can't be - // bound to as a result - bindings::krealloc(ptr::null(), layout.size(), bindings::GFP_KERNEL) as *mut u8 - } +unsafe impl GlobalAlloc for KernelAllocator +{ + unsafe fn alloc(&self, layout: Layout) -> *mut u8 + { + // krealloc is used instead of kmalloc because kmalloc is an inline function and can't be + // bound to as a result + bindings::krealloc( + ptr::null(), + layout.size(), + bindings::GFP_KERNEL, + ) as *mut u8 + } - unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { - bindings::kfree(ptr as *const c_types::c_void); - } + unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) + { + bindings::kfree(ptr as *const c_types::c_void); + } } #[alloc_error_handler] -fn oom(_layout: Layout) -> ! { - panic!("Out of memory!"); +fn oom(_layout: Layout) -> ! +{ + panic!("Out of memory!"); } diff --git a/rust/kernel/src/bindings.rs b/rust/kernel/src/bindings.rs index cfb004a6d7861c..14ac7bbd5e8e0c 100644 --- a/rust/kernel/src/bindings.rs +++ b/rust/kernel/src/bindings.rs @@ -1,15 +1,16 @@ // SPDX-License-Identifier: GPL-2.0 #[allow( - clippy::all, - non_camel_case_types, - non_upper_case_globals, - non_snake_case, - improper_ctypes + clippy::all, + non_camel_case_types, + non_upper_case_globals, + non_snake_case, + improper_ctypes )] -mod bindings_raw { - use crate::c_types; - include!("bindings_gen.rs"); +mod bindings_raw +{ + use crate::c_types; + include!("bindings_gen.rs"); } pub use bindings_raw::*; diff --git a/rust/kernel/src/c_types.rs b/rust/kernel/src/c_types.rs index 35776920c99d52..e703d38fd9c003 100644 --- a/rust/kernel/src/c_types.rs +++ b/rust/kernel/src/c_types.rs @@ -3,23 +3,24 @@ #![allow(non_camel_case_types)] #[cfg(target_arch = "x86_64")] -mod c { - use core::ffi; +mod c +{ + use core::ffi; - pub type c_int = i32; - pub type c_char = i8; - pub type c_long = i64; - pub type c_longlong = i64; - pub type c_short = i16; - pub type c_uchar = u8; - pub type c_uint = u32; - pub type c_ulong = u64; - pub type c_ulonglong = u64; - pub type c_ushort = u16; - pub type c_schar = i8; - pub type c_size_t = usize; - pub type c_ssize_t = isize; - pub type c_void = ffi::c_void; + pub type c_int = i32; + pub type c_char = i8; + pub type c_long = i64; + pub type c_longlong = i64; + pub type c_short = i16; + pub type c_uchar = u8; + pub type c_uint = u32; + pub type c_ulong = u64; + pub type c_ulonglong = u64; + pub type c_ushort = u16; + pub type c_schar = i8; + pub type c_size_t = usize; + pub type c_ssize_t = isize; + pub type c_void = ffi::c_void; } pub use c::*; diff --git a/rust/kernel/src/chrdev.rs b/rust/kernel/src/chrdev.rs index d32b9a0d584a56..504721920586bf 100644 --- a/rust/kernel/src/chrdev.rs +++ b/rust/kernel/src/chrdev.rs @@ -14,88 +14,116 @@ use crate::error::{Error, KernelResult}; use crate::file_operations; use crate::types::CStr; -pub fn builder(name: CStr<'static>, minors: Range) -> KernelResult { - Ok(Builder { - name, - minors, - file_ops: vec![], - }) +pub fn builder(name: CStr<'static>, minors: Range) + -> KernelResult +{ + Ok(Builder { + name, + minors, + file_ops: vec![], + }) } -pub struct Builder { - name: CStr<'static>, - minors: Range, - file_ops: Vec<&'static bindings::file_operations>, +pub struct Builder +{ + name: CStr<'static>, + minors: Range, + file_ops: Vec<&'static bindings::file_operations>, } -impl Builder { - pub fn register_device(mut self) -> Builder { - if self.file_ops.len() >= self.minors.len() { - panic!("More devices registered than minor numbers allocated.") +impl Builder +{ + pub fn register_device( + mut self, + ) -> Builder + { + if self.file_ops.len() >= self.minors.len() { + panic!("More devices registered than minor numbers allocated.") + } + self.file_ops.push( + &file_operations::FileOperationsVtable::::VTABLE, + ); + self } - self.file_ops - .push(&file_operations::FileOperationsVtable::::VTABLE); - self - } - pub fn build(self) -> KernelResult { - let mut dev: bindings::dev_t = 0; - let res = unsafe { - bindings::alloc_chrdev_region( - &mut dev, - self.minors.start.into(), - self.minors.len().try_into()?, - self.name.as_ptr() as *const c_types::c_char, - ) - }; - if res != 0 { - return Err(Error::from_kernel_errno(res)); - } + pub fn build(self) -> KernelResult + { + let mut dev: bindings::dev_t = 0; + let res = unsafe { + bindings::alloc_chrdev_region( + &mut dev, + self.minors.start.into(), + self.minors.len().try_into()?, + self.name.as_ptr() as *const c_types::c_char, + ) + }; + if res != 0 { + return Err(Error::from_kernel_errno(res)); + } - // Turn this into a boxed slice immediately because the kernel stores pointers into it, and - // so that data should never be moved. - let mut cdevs = vec![unsafe { mem::zeroed() }; self.file_ops.len()].into_boxed_slice(); - for (i, file_op) in self.file_ops.iter().enumerate() { - unsafe { - bindings::cdev_init(&mut cdevs[i], *file_op); - // TODO: proper `THIS_MODULE` handling - cdevs[i].owner = core::ptr::null_mut(); - let rc = bindings::cdev_add(&mut cdevs[i], dev + i as bindings::dev_t, 1); - if rc != 0 { - // Clean up the ones that were allocated. - for j in 0..=i { - bindings::cdev_del(&mut cdevs[j]); - } - bindings::unregister_chrdev_region(dev, self.minors.len() as _); - return Err(Error::from_kernel_errno(rc)); + // Turn this into a boxed slice immediately because the kernel stores pointers into it, and + // so that data should never be moved. + let mut cdevs = + vec![unsafe { mem::zeroed() }; self.file_ops.len()] + .into_boxed_slice(); + for (i, file_op) in self.file_ops.iter().enumerate() { + unsafe { + bindings::cdev_init(&mut cdevs[i], *file_op); + // TODO: proper `THIS_MODULE` handling + cdevs[i].owner = core::ptr::null_mut(); + let rc = bindings::cdev_add( + &mut cdevs[i], + dev + i as bindings::dev_t, + 1, + ); + if rc != 0 { + // Clean up the ones that were allocated. + for j in 0..=i { + bindings::cdev_del( + &mut cdevs[j], + ); + } + bindings::unregister_chrdev_region( + dev, + self.minors.len() as _, + ); + return Err(Error::from_kernel_errno( + rc, + )); + } + } } - } - } - Ok(Registration { - dev, - count: self.minors.len(), - cdevs, - }) - } + Ok(Registration { + dev, + count: self.minors.len(), + cdevs, + }) + } } -pub struct Registration { - dev: bindings::dev_t, - count: usize, - cdevs: Box<[bindings::cdev]>, +pub struct Registration +{ + dev: bindings::dev_t, + count: usize, + cdevs: Box<[bindings::cdev]>, } // This is safe because Registration doesn't actually expose any methods. unsafe impl Sync for Registration {} -impl Drop for Registration { - fn drop(&mut self) { - unsafe { - for dev in self.cdevs.iter_mut() { - bindings::cdev_del(dev); - } - bindings::unregister_chrdev_region(self.dev, self.count as _); +impl Drop for Registration +{ + fn drop(&mut self) + { + unsafe { + for dev in self.cdevs.iter_mut() { + bindings::cdev_del(dev); + } + bindings::unregister_chrdev_region( + self.dev, + self.count as _, + ); + } } - } } diff --git a/rust/kernel/src/error.rs b/rust/kernel/src/error.rs index 95e322e39a88e7..ad205e1c58e08f 100644 --- a/rust/kernel/src/error.rs +++ b/rust/kernel/src/error.rs @@ -7,26 +7,31 @@ use crate::c_types; pub struct Error(c_types::c_int); -impl Error { - pub const EINVAL: Self = Error(-(bindings::EINVAL as i32)); - pub const ENOMEM: Self = Error(-(bindings::ENOMEM as i32)); - pub const EFAULT: Self = Error(-(bindings::EFAULT as i32)); - pub const ESPIPE: Self = Error(-(bindings::ESPIPE as i32)); - pub const EAGAIN: Self = Error(-(bindings::EAGAIN as i32)); - - pub fn from_kernel_errno(errno: c_types::c_int) -> Error { - Error(errno) - } - - pub fn to_kernel_errno(&self) -> c_types::c_int { - self.0 - } +impl Error +{ + pub const EINVAL: Self = Error(-(bindings::EINVAL as i32)); + pub const ENOMEM: Self = Error(-(bindings::ENOMEM as i32)); + pub const EFAULT: Self = Error(-(bindings::EFAULT as i32)); + pub const ESPIPE: Self = Error(-(bindings::ESPIPE as i32)); + pub const EAGAIN: Self = Error(-(bindings::EAGAIN as i32)); + + pub fn from_kernel_errno(errno: c_types::c_int) -> Error + { + Error(errno) + } + + pub fn to_kernel_errno(&self) -> c_types::c_int + { + self.0 + } } -impl From for Error { - fn from(_: TryFromIntError) -> Error { - Error::EINVAL - } +impl From for Error +{ + fn from(_: TryFromIntError) -> Error + { + Error::EINVAL + } } pub type KernelResult = Result; diff --git a/rust/kernel/src/file_operations.rs b/rust/kernel/src/file_operations.rs index 100fb62281e946..8e5db79531e709 100644 --- a/rust/kernel/src/file_operations.rs +++ b/rust/kernel/src/file_operations.rs @@ -16,204 +16,235 @@ bitflags::bitflags! { } } -pub struct File { - ptr: *const bindings::file, +pub struct File +{ + ptr: *const bindings::file, } -impl File { - unsafe fn from_ptr(ptr: *const bindings::file) -> File { - File { ptr } - } +impl File +{ + unsafe fn from_ptr(ptr: *const bindings::file) -> File + { + File { ptr } + } - pub fn pos(&self) -> u64 { - unsafe { (*self.ptr).f_pos as u64 } - } + pub fn pos(&self) -> u64 + { + unsafe { (*self.ptr).f_pos as u64 } + } - pub fn flags(&self) -> FileFlags { - FileFlags::from_bits_truncate(unsafe { (*self.ptr).f_flags }) - } + pub fn flags(&self) -> FileFlags + { + FileFlags::from_bits_truncate(unsafe { (*self.ptr).f_flags }) + } } // Matches std::io::SeekFrom in the Rust stdlib -pub enum SeekFrom { - Start(u64), - End(i64), - Current(i64), +pub enum SeekFrom +{ + Start(u64), + End(i64), + Current(i64), } unsafe extern "C" fn open_callback( - _inode: *mut bindings::inode, - file: *mut bindings::file, -) -> c_types::c_int { - let f = match T::open() { - Ok(f) => Box::new(f), - Err(e) => return e.to_kernel_errno(), - }; - (*file).private_data = Box::into_raw(f) as *mut c_types::c_void; - 0 + _inode: *mut bindings::inode, + file: *mut bindings::file, +) -> c_types::c_int +{ + let f = match T::open() { + Ok(f) => Box::new(f), + Err(e) => return e.to_kernel_errno(), + }; + (*file).private_data = Box::into_raw(f) as *mut c_types::c_void; + 0 } unsafe extern "C" fn read_callback( - file: *mut bindings::file, - buf: *mut c_types::c_char, - len: c_types::c_size_t, - offset: *mut bindings::loff_t, -) -> c_types::c_ssize_t { - let mut data = match UserSlicePtr::new(buf as *mut c_types::c_void, len) { - Ok(ptr) => ptr.writer(), - Err(e) => return e.to_kernel_errno().try_into().unwrap(), - }; - let f = &*((*file).private_data as *const T); - // No FMODE_UNSIGNED_OFFSET support, so offset must be in [0, 2^63). - // See discussion in #113 - let positive_offset = match (*offset).try_into() { - Ok(v) => v, - Err(_) => return Error::EINVAL.to_kernel_errno().try_into().unwrap(), - }; - let read = T::READ.unwrap(); - match read(f, &File::from_ptr(file), &mut data, positive_offset) { - Ok(()) => { - let written = len - data.len(); - (*offset) += bindings::loff_t::try_from(written).unwrap(); - written.try_into().unwrap() + file: *mut bindings::file, + buf: *mut c_types::c_char, + len: c_types::c_size_t, + offset: *mut bindings::loff_t, +) -> c_types::c_ssize_t +{ + let mut data = match UserSlicePtr::new(buf as *mut c_types::c_void, len) + { + Ok(ptr) => ptr.writer(), + Err(e) => return e.to_kernel_errno().try_into().unwrap(), + }; + let f = &*((*file).private_data as *const T); + // No FMODE_UNSIGNED_OFFSET support, so offset must be in [0, 2^63). + // See discussion in #113 + let positive_offset = match (*offset).try_into() { + Ok(v) => v, + Err(_) => { + return Error::EINVAL + .to_kernel_errno() + .try_into() + .unwrap() + } + }; + let read = T::READ.unwrap(); + match read(f, &File::from_ptr(file), &mut data, positive_offset) { + Ok(()) => { + let written = len - data.len(); + (*offset) += + bindings::loff_t::try_from(written).unwrap(); + written.try_into().unwrap() + } + Err(e) => e.to_kernel_errno().try_into().unwrap(), } - Err(e) => e.to_kernel_errno().try_into().unwrap(), - } } unsafe extern "C" fn write_callback( - file: *mut bindings::file, - buf: *const c_types::c_char, - len: c_types::c_size_t, - offset: *mut bindings::loff_t, -) -> c_types::c_ssize_t { - let mut data = match UserSlicePtr::new(buf as *mut c_types::c_void, len) { - Ok(ptr) => ptr.reader(), - Err(e) => return e.to_kernel_errno().try_into().unwrap(), - }; - let f = &*((*file).private_data as *const T); - // No FMODE_UNSIGNED_OFFSET support, so offset must be in [0, 2^63). - // See discussion in #113 - let positive_offset = match (*offset).try_into() { - Ok(v) => v, - Err(_) => return Error::EINVAL.to_kernel_errno().try_into().unwrap(), - }; - let write = T::WRITE.unwrap(); - match write(f, &mut data, positive_offset) { - Ok(()) => { - let read = len - data.len(); - (*offset) += bindings::loff_t::try_from(read).unwrap(); - read.try_into().unwrap() + file: *mut bindings::file, + buf: *const c_types::c_char, + len: c_types::c_size_t, + offset: *mut bindings::loff_t, +) -> c_types::c_ssize_t +{ + let mut data = match UserSlicePtr::new(buf as *mut c_types::c_void, len) + { + Ok(ptr) => ptr.reader(), + Err(e) => return e.to_kernel_errno().try_into().unwrap(), + }; + let f = &*((*file).private_data as *const T); + // No FMODE_UNSIGNED_OFFSET support, so offset must be in [0, 2^63). + // See discussion in #113 + let positive_offset = match (*offset).try_into() { + Ok(v) => v, + Err(_) => { + return Error::EINVAL + .to_kernel_errno() + .try_into() + .unwrap() + } + }; + let write = T::WRITE.unwrap(); + match write(f, &mut data, positive_offset) { + Ok(()) => { + let read = len - data.len(); + (*offset) += bindings::loff_t::try_from(read).unwrap(); + read.try_into().unwrap() + } + Err(e) => e.to_kernel_errno().try_into().unwrap(), } - Err(e) => e.to_kernel_errno().try_into().unwrap(), - } } unsafe extern "C" fn release_callback( - _inode: *mut bindings::inode, - file: *mut bindings::file, -) -> c_types::c_int { - let ptr = mem::replace(&mut (*file).private_data, ptr::null_mut()); - drop(Box::from_raw(ptr as *mut T)); - 0 + _inode: *mut bindings::inode, + file: *mut bindings::file, +) -> c_types::c_int +{ + let ptr = mem::replace(&mut (*file).private_data, ptr::null_mut()); + drop(Box::from_raw(ptr as *mut T)); + 0 } unsafe extern "C" fn llseek_callback( - file: *mut bindings::file, - offset: bindings::loff_t, - whence: c_types::c_int, -) -> bindings::loff_t { - let off = match whence as u32 { - bindings::SEEK_SET => match offset.try_into() { - Ok(v) => SeekFrom::Start(v), - Err(_) => return Error::EINVAL.to_kernel_errno().into(), - }, - bindings::SEEK_CUR => SeekFrom::Current(offset), - bindings::SEEK_END => SeekFrom::End(offset), - _ => return Error::EINVAL.to_kernel_errno().into(), - }; - let f = &*((*file).private_data as *const T); - let seek = T::SEEK.unwrap(); - match seek(f, &File::from_ptr(file), off) { - Ok(off) => off as bindings::loff_t, - Err(e) => e.to_kernel_errno().into(), - } + file: *mut bindings::file, + offset: bindings::loff_t, + whence: c_types::c_int, +) -> bindings::loff_t +{ + let off = match whence as u32 { + bindings::SEEK_SET => match offset.try_into() { + Ok(v) => SeekFrom::Start(v), + Err(_) => { + return Error::EINVAL.to_kernel_errno().into() + } + }, + bindings::SEEK_CUR => SeekFrom::Current(offset), + bindings::SEEK_END => SeekFrom::End(offset), + _ => return Error::EINVAL.to_kernel_errno().into(), + }; + let f = &*((*file).private_data as *const T); + let seek = T::SEEK.unwrap(); + match seek(f, &File::from_ptr(file), off) { + Ok(off) => off as bindings::loff_t, + Err(e) => e.to_kernel_errno().into(), + } } pub(crate) struct FileOperationsVtable(marker::PhantomData); -impl FileOperationsVtable { - pub(crate) const VTABLE: bindings::file_operations = bindings::file_operations { - open: Some(open_callback::), - release: Some(release_callback::), - read: if let Some(_) = T::READ { - Some(read_callback::) - } else { - None - }, - write: if let Some(_) = T::WRITE { - Some(write_callback::) - } else { - None - }, - llseek: if let Some(_) = T::SEEK { - Some(llseek_callback::) - } else { - None - }, - - check_flags: None, - compat_ioctl: None, - copy_file_range: None, - fallocate: None, - fadvise: None, - fasync: None, - flock: None, - flush: None, - fsync: None, - get_unmapped_area: None, - iterate: None, - iterate_shared: None, - iopoll: None, - lock: None, - mmap: None, - mmap_supported_flags: 0, - owner: ptr::null_mut(), - poll: None, - read_iter: None, - remap_file_range: None, - sendpage: None, - setlease: None, - show_fdinfo: None, - splice_read: None, - splice_write: None, - unlocked_ioctl: None, - write_iter: None, - }; +impl FileOperationsVtable +{ + pub(crate) const VTABLE: bindings::file_operations = + bindings::file_operations { + open: Some(open_callback::), + release: Some(release_callback::), + read: if let Some(_) = T::READ { + Some(read_callback::) + } else { + None + }, + write: if let Some(_) = T::WRITE { + Some(write_callback::) + } else { + None + }, + llseek: if let Some(_) = T::SEEK { + Some(llseek_callback::) + } else { + None + }, + + check_flags: None, + compat_ioctl: None, + copy_file_range: None, + fallocate: None, + fadvise: None, + fasync: None, + flock: None, + flush: None, + fsync: None, + get_unmapped_area: None, + iterate: None, + iterate_shared: None, + iopoll: None, + lock: None, + mmap: None, + mmap_supported_flags: 0, + owner: ptr::null_mut(), + poll: None, + read_iter: None, + remap_file_range: None, + sendpage: None, + setlease: None, + show_fdinfo: None, + splice_read: None, + splice_write: None, + unlocked_ioctl: None, + write_iter: None, + }; } -pub type ReadFn = Option KernelResult<()>>; -pub type WriteFn = Option KernelResult<()>>; +pub type ReadFn = + Option KernelResult<()>>; +pub type WriteFn = + Option KernelResult<()>>; pub type SeekFn = Option KernelResult>; /// `FileOperations` corresponds to the kernel's `struct file_operations`. You /// implement this trait whenever you'd create a `struct file_operations`. /// File descriptors may be used from multiple threads (or processes) /// concurrently, so your type must be `Sync`. -pub trait FileOperations: Sync + Sized { - /// Creates a new instance of this file. Corresponds to the `open` function - /// pointer in `struct file_operations`. - fn open() -> KernelResult; - - /// Reads data from this file to userspace. Corresponds to the `read` - /// function pointer in `struct file_operations`. - const READ: ReadFn = None; - - /// Writes data from userspace o this file. Corresponds to the `write` - /// function pointer in `struct file_operations`. - const WRITE: WriteFn = None; - - /// Changes the position of the file. Corresponds to the `llseek` function - /// pointer in `struct file_operations`. - const SEEK: SeekFn = None; +pub trait FileOperations: Sync + Sized +{ + /// Creates a new instance of this file. Corresponds to the `open` function + /// pointer in `struct file_operations`. + fn open() -> KernelResult; + + /// Reads data from this file to userspace. Corresponds to the `read` + /// function pointer in `struct file_operations`. + const READ: ReadFn = None; + + /// Writes data from userspace o this file. Corresponds to the `write` + /// function pointer in `struct file_operations`. + const WRITE: WriteFn = None; + + /// Changes the position of the file. Corresponds to the `llseek` function + /// pointer in `struct file_operations`. + const SEEK: SeekFn = None; } diff --git a/rust/kernel/src/lib.rs b/rust/kernel/src/lib.rs index a80e6425f1222b..273ce27279d150 100644 --- a/rust/kernel/src/lib.rs +++ b/rust/kernel/src/lib.rs @@ -34,19 +34,21 @@ pub use crate::types::{CStr, Mode}; /// teardown or cleanup operations, your type may implement [`Drop`]. /// /// [`Drop`]: https://doc.rust-lang.org/stable/core/ops/trait.Drop.html -pub trait KernelModule: Sized + Sync { - fn init() -> KernelResult; +pub trait KernelModule: Sized + Sync +{ + fn init() -> KernelResult; } extern "C" { - fn rust_helper_BUG() -> !; + fn rust_helper_BUG() -> !; } #[panic_handler] -fn panic(_info: &PanicInfo) -> ! { - unsafe { - rust_helper_BUG(); - } +fn panic(_info: &PanicInfo) -> ! +{ + unsafe { + rust_helper_BUG(); + } } #[global_allocator] diff --git a/rust/kernel/src/prelude.rs b/rust/kernel/src/prelude.rs index 75a5017488a481..36d9e2104f681a 100644 --- a/rust/kernel/src/prelude.rs +++ b/rust/kernel/src/prelude.rs @@ -2,16 +2,8 @@ //! The `kernel` prelude -pub use alloc::{ - string::String, - borrow::ToOwned, -}; +pub use alloc::{borrow::ToOwned, string::String}; pub use module::module; -pub use super::{ - println, - KernelResult, - KernelModule, -}; - +pub use super::{println, KernelModule, KernelResult}; diff --git a/rust/kernel/src/printk.rs b/rust/kernel/src/printk.rs index 1a27c034258a36..34f184360dc9e3 100644 --- a/rust/kernel/src/printk.rs +++ b/rust/kernel/src/printk.rs @@ -7,47 +7,63 @@ use crate::bindings; use crate::c_types::c_int; #[doc(hidden)] -pub fn printk(s: &[u8]) { - // Don't copy the trailing NUL from `KERN_INFO`. - let mut fmt_str = [0; bindings::KERN_INFO.len() - 1 + b"%.*s\0".len()]; - fmt_str[..bindings::KERN_INFO.len() - 1] - .copy_from_slice(&bindings::KERN_INFO[..bindings::KERN_INFO.len() - 1]); - fmt_str[bindings::KERN_INFO.len() - 1..].copy_from_slice(b"%.*s\0"); +pub fn printk(s: &[u8]) +{ + // Don't copy the trailing NUL from `KERN_INFO`. + let mut fmt_str = [0; bindings::KERN_INFO.len() - 1 + b"%.*s\0".len()]; + fmt_str[..bindings::KERN_INFO.len() - 1].copy_from_slice( + &bindings::KERN_INFO[..bindings::KERN_INFO.len() - 1], + ); + fmt_str[bindings::KERN_INFO.len() - 1..].copy_from_slice(b"%.*s\0"); - // TODO: I believe printk never fails - unsafe { bindings::printk(fmt_str.as_ptr() as _, s.len() as c_int, s.as_ptr()) }; + // TODO: I believe printk never fails + unsafe { + bindings::printk( + fmt_str.as_ptr() as _, + s.len() as c_int, + s.as_ptr(), + ) + }; } // From kernel/print/printk.c const LOG_LINE_MAX: usize = 1024 - 32; #[doc(hidden)] -pub struct LogLineWriter { - data: [u8; LOG_LINE_MAX], - pos: usize, +pub struct LogLineWriter +{ + data: [u8; LOG_LINE_MAX], + pos: usize, } #[allow(clippy::new_without_default)] -impl LogLineWriter { - pub fn new() -> LogLineWriter { - LogLineWriter { - data: [0u8; LOG_LINE_MAX], - pos: 0, +impl LogLineWriter +{ + pub fn new() -> LogLineWriter + { + LogLineWriter { + data: [0u8; LOG_LINE_MAX], + pos: 0, + } } - } - pub fn as_bytes(&self) -> &[u8] { - &self.data[..self.pos] - } + pub fn as_bytes(&self) -> &[u8] + { + &self.data[..self.pos] + } } -impl fmt::Write for LogLineWriter { - fn write_str(&mut self, s: &str) -> fmt::Result { - let copy_len = cmp::min(LOG_LINE_MAX - self.pos, s.as_bytes().len()); - self.data[self.pos..self.pos + copy_len].copy_from_slice(&s.as_bytes()[..copy_len]); - self.pos += copy_len; - Ok(()) - } +impl fmt::Write for LogLineWriter +{ + fn write_str(&mut self, s: &str) -> fmt::Result + { + let copy_len = + cmp::min(LOG_LINE_MAX - self.pos, s.as_bytes().len()); + self.data[self.pos..self.pos + copy_len] + .copy_from_slice(&s.as_bytes()[..copy_len]); + self.pos += copy_len; + Ok(()) + } } /// [`println!`] functions the same as it does in `std`, except instead of diff --git a/rust/kernel/src/random.rs b/rust/kernel/src/random.rs index bd80c835749dac..ae8c6d920a215d 100644 --- a/rust/kernel/src/random.rs +++ b/rust/kernel/src/random.rs @@ -7,37 +7,40 @@ use crate::{bindings, c_types, error}; /// Fills `dest` with random bytes generated from the kernel's CSPRNG. Ensures /// that the CSPRNG has been seeded before generating any random bytes, and /// will block until it's ready. -pub fn getrandom(dest: &mut [u8]) -> error::KernelResult<()> { - let res = unsafe { bindings::wait_for_random_bytes() }; - if res != 0 { - return Err(error::Error::from_kernel_errno(res)); - } +pub fn getrandom(dest: &mut [u8]) -> error::KernelResult<()> +{ + let res = unsafe { bindings::wait_for_random_bytes() }; + if res != 0 { + return Err(error::Error::from_kernel_errno(res)); + } - unsafe { - bindings::get_random_bytes( - dest.as_mut_ptr() as *mut c_types::c_void, - dest.len().try_into()?, - ); - } - Ok(()) + unsafe { + bindings::get_random_bytes( + dest.as_mut_ptr() as *mut c_types::c_void, + dest.len().try_into()?, + ); + } + Ok(()) } /// Fills `dest` with random bytes generated from the kernel's CSPRNG. If the /// CSPRNG is not yet seeded, returns an `Err(EAGAIN)` immediately. -pub fn getrandom_nonblock(dest: &mut [u8]) -> error::KernelResult<()> { - if !unsafe { bindings::rng_is_initialized() } { - return Err(error::Error::EAGAIN); - } - getrandom(dest) +pub fn getrandom_nonblock(dest: &mut [u8]) -> error::KernelResult<()> +{ + if !unsafe { bindings::rng_is_initialized() } { + return Err(error::Error::EAGAIN); + } + getrandom(dest) } /// Contributes the contents of `data` to the kernel's entropy pool. Does _not_ /// credit the kernel entropy counter though. -pub fn add_randomness(data: &[u8]) { - unsafe { - bindings::add_device_randomness( - data.as_ptr() as *const c_types::c_void, - data.len().try_into().unwrap(), - ); - } +pub fn add_randomness(data: &[u8]) +{ + unsafe { + bindings::add_device_randomness( + data.as_ptr() as *const c_types::c_void, + data.len().try_into().unwrap(), + ); + } } diff --git a/rust/kernel/src/sysctl.rs b/rust/kernel/src/sysctl.rs index b94cdbc939cc81..06147f477f7eda 100644 --- a/rust/kernel/src/sysctl.rs +++ b/rust/kernel/src/sysctl.rs @@ -12,69 +12,88 @@ use crate::error; use crate::types; use crate::user_ptr::{UserSlicePtr, UserSlicePtrWriter}; -pub trait SysctlStorage: Sync { - fn store_value(&self, data: &[u8]) -> (usize, error::KernelResult<()>); - fn read_value(&self, data: &mut UserSlicePtrWriter) -> (usize, error::KernelResult<()>); +pub trait SysctlStorage: Sync +{ + fn store_value(&self, data: &[u8]) -> (usize, error::KernelResult<()>); + fn read_value( + &self, + data: &mut UserSlicePtrWriter, + ) -> (usize, error::KernelResult<()>); } -fn trim_whitespace(mut data: &[u8]) -> &[u8] { - while !data.is_empty() && (data[0] == b' ' || data[0] == b'\t' || data[0] == b'\n') { - data = &data[1..]; - } - while !data.is_empty() - && (data[data.len() - 1] == b' ' - || data[data.len() - 1] == b'\t' - || data[data.len() - 1] == b'\n') - { - data = &data[..data.len() - 1]; - } - data +fn trim_whitespace(mut data: &[u8]) -> &[u8] +{ + while !data.is_empty() + && (data[0] == b' ' || data[0] == b'\t' || data[0] == b'\n') + { + data = &data[1..]; + } + while !data.is_empty() + && (data[data.len() - 1] == b' ' + || data[data.len() - 1] == b'\t' + || data[data.len() - 1] == b'\n') + { + data = &data[..data.len() - 1]; + } + data } impl SysctlStorage for &T where - T: SysctlStorage, + T: SysctlStorage, { - fn store_value(&self, data: &[u8]) -> (usize, error::KernelResult<()>) { - (*self).store_value(data) - } + fn store_value(&self, data: &[u8]) -> (usize, error::KernelResult<()>) + { + (*self).store_value(data) + } - fn read_value(&self, data: &mut UserSlicePtrWriter) -> (usize, error::KernelResult<()>) { - (*self).read_value(data) - } + fn read_value( + &self, + data: &mut UserSlicePtrWriter, + ) -> (usize, error::KernelResult<()>) + { + (*self).read_value(data) + } } -impl SysctlStorage for atomic::AtomicBool { - fn store_value(&self, data: &[u8]) -> (usize, error::KernelResult<()>) { - let result = match trim_whitespace(data) { - b"0" => { - self.store(false, atomic::Ordering::Relaxed); - Ok(()) - } - b"1" => { - self.store(true, atomic::Ordering::Relaxed); - Ok(()) - } - _ => Err(error::Error::EINVAL), - }; - (data.len(), result) - } +impl SysctlStorage for atomic::AtomicBool +{ + fn store_value(&self, data: &[u8]) -> (usize, error::KernelResult<()>) + { + let result = match trim_whitespace(data) { + b"0" => { + self.store(false, atomic::Ordering::Relaxed); + Ok(()) + } + b"1" => { + self.store(true, atomic::Ordering::Relaxed); + Ok(()) + } + _ => Err(error::Error::EINVAL), + }; + (data.len(), result) + } - fn read_value(&self, data: &mut UserSlicePtrWriter) -> (usize, error::KernelResult<()>) { - let value = if self.load(atomic::Ordering::Relaxed) { - b"1\n" - } else { - b"0\n" - }; - (value.len(), data.write(value)) - } + fn read_value( + &self, + data: &mut UserSlicePtrWriter, + ) -> (usize, error::KernelResult<()>) + { + let value = if self.load(atomic::Ordering::Relaxed) { + b"1\n" + } else { + b"0\n" + }; + (value.len(), data.write(value)) + } } -pub struct Sysctl { - inner: Box, - // Responsible for keeping the ctl_table alive. - _table: Box<[bindings::ctl_table]>, - header: *mut bindings::ctl_table_header, +pub struct Sysctl +{ + inner: Box, + // Responsible for keeping the ctl_table alive. + _table: Box<[bindings::ctl_table]>, + header: *mut bindings::ctl_table_header, } // This is safe because the only public method we have is get(), which returns @@ -82,94 +101,105 @@ pub struct Sysctl { unsafe impl Sync for Sysctl {} unsafe extern "C" fn proc_handler( - ctl: *mut bindings::ctl_table, - write: c_types::c_int, - buffer: *mut c_types::c_void, - len: *mut usize, - ppos: *mut bindings::loff_t, -) -> c_types::c_int { - // If we're reading from some offset other than the beginning of the file, - // return an empty read to signal EOF. - if *ppos != 0 && write == 0 { - *len = 0; - return 0; - } - - let data = match UserSlicePtr::new(buffer, *len) { - Ok(ptr) => ptr, - Err(e) => return e.to_kernel_errno(), - }; - let storage = &*((*ctl).data as *const T); - let (bytes_processed, result) = if write != 0 { - let data = match data.read_all() { - Ok(r) => r, - Err(e) => return e.to_kernel_errno(), + ctl: *mut bindings::ctl_table, + write: c_types::c_int, + buffer: *mut c_types::c_void, + len: *mut usize, + ppos: *mut bindings::loff_t, +) -> c_types::c_int +{ + // If we're reading from some offset other than the beginning of the file, + // return an empty read to signal EOF. + if *ppos != 0 && write == 0 { + *len = 0; + return 0; + } + + let data = match UserSlicePtr::new(buffer, *len) { + Ok(ptr) => ptr, + Err(e) => return e.to_kernel_errno(), + }; + let storage = &*((*ctl).data as *const T); + let (bytes_processed, result) = if write != 0 { + let data = match data.read_all() { + Ok(r) => r, + Err(e) => return e.to_kernel_errno(), + }; + storage.store_value(&data) + } else { + let mut writer = data.writer(); + storage.read_value(&mut writer) }; - storage.store_value(&data) - } else { - let mut writer = data.writer(); - storage.read_value(&mut writer) - }; - *len = bytes_processed; - *ppos += *len as bindings::loff_t; - match result { - Ok(()) => 0, - Err(e) => e.to_kernel_errno(), - } + *len = bytes_processed; + *ppos += *len as bindings::loff_t; + match result { + Ok(()) => 0, + Err(e) => e.to_kernel_errno(), + } } -impl Sysctl { - pub fn register( - path: types::CStr<'static>, - name: types::CStr<'static>, - storage: T, - mode: types::Mode, - ) -> error::KernelResult> { - if name.contains('/') { - return Err(error::Error::EINVAL); +impl Sysctl +{ + pub fn register( + path: types::CStr<'static>, + name: types::CStr<'static>, + storage: T, + mode: types::Mode, + ) -> error::KernelResult> + { + if name.contains('/') { + return Err(error::Error::EINVAL); + } + + let storage = Box::new(storage); + let mut table = vec![ + bindings::ctl_table { + procname: name.as_ptr() as *const i8, + mode: mode.as_int(), + data: &*storage as *const T + as *mut c_types::c_void, + proc_handler: Some(proc_handler::), + + maxlen: 0, + child: ptr::null_mut(), + poll: ptr::null_mut(), + extra1: ptr::null_mut(), + extra2: ptr::null_mut(), + }, + unsafe { mem::zeroed() }, + ] + .into_boxed_slice(); + + let result = unsafe { + bindings::register_sysctl( + path.as_ptr() as *const i8, + table.as_mut_ptr(), + ) + }; + if result.is_null() { + return Err(error::Error::ENOMEM); + } + + Ok(Sysctl { + inner: storage, + _table: table, + header: result, + }) } - let storage = Box::new(storage); - let mut table = vec![ - bindings::ctl_table { - procname: name.as_ptr() as *const i8, - mode: mode.as_int(), - data: &*storage as *const T as *mut c_types::c_void, - proc_handler: Some(proc_handler::), - - maxlen: 0, - child: ptr::null_mut(), - poll: ptr::null_mut(), - extra1: ptr::null_mut(), - extra2: ptr::null_mut(), - }, - unsafe { mem::zeroed() }, - ] - .into_boxed_slice(); - - let result = - unsafe { bindings::register_sysctl(path.as_ptr() as *const i8, table.as_mut_ptr()) }; - if result.is_null() { - return Err(error::Error::ENOMEM); + pub fn get(&self) -> &T + { + &self.inner } - - Ok(Sysctl { - inner: storage, - _table: table, - header: result, - }) - } - - pub fn get(&self) -> &T { - &self.inner - } } -impl Drop for Sysctl { - fn drop(&mut self) { - unsafe { - bindings::unregister_sysctl_table(self.header); +impl Drop for Sysctl +{ + fn drop(&mut self) + { + unsafe { + bindings::unregister_sysctl_table(self.header); + } + self.header = ptr::null_mut(); } - self.header = ptr::null_mut(); - } } diff --git a/rust/kernel/src/types.rs b/rust/kernel/src/types.rs index e9b4d13d4f8ce4..6b509f6691169f 100644 --- a/rust/kernel/src/types.rs +++ b/rust/kernel/src/types.rs @@ -6,14 +6,17 @@ use crate::bindings; pub struct Mode(bindings::umode_t); -impl Mode { - pub fn from_int(m: u16) -> Mode { - Mode(m) - } - - pub fn as_int(&self) -> u16 { - self.0 - } +impl Mode +{ + pub fn from_int(m: u16) -> Mode + { + Mode(m) + } + + pub fn as_int(&self) -> u16 + { + self.0 + } } /// A string that is guaranteed to have exactly one NUL byte, which is at the @@ -21,23 +24,27 @@ impl Mode { #[repr(transparent)] pub struct CStr<'a>(&'a str); -impl CStr<'_> { - /// Creates a new CStr from a str without performing any additional checks. - /// # Safety - /// - /// `data` _must_ end with a NUL byte, and should only have only a single - /// NUL byte, or the string will be truncated. - pub const unsafe fn new_unchecked(data: &str) -> CStr { - CStr(data) - } +impl CStr<'_> +{ + /// Creates a new CStr from a str without performing any additional checks. + /// # Safety + /// + /// `data` _must_ end with a NUL byte, and should only have only a single + /// NUL byte, or the string will be truncated. + pub const unsafe fn new_unchecked(data: &str) -> CStr + { + CStr(data) + } } -impl Deref for CStr<'_> { - type Target = str; +impl Deref for CStr<'_> +{ + type Target = str; - fn deref(&self) -> &str { - self.0 - } + fn deref(&self) -> &str + { + self.0 + } } /// Creates a new `CStr` from a string literal. The string literal should not contain any NUL @@ -47,8 +54,8 @@ impl Deref for CStr<'_> { /// ``` #[macro_export] macro_rules! cstr { - ($str:expr) => {{ - let s = concat!($str, "\x00"); - unsafe { $crate::CStr::new_unchecked(s) } - }}; + ($str:expr) => {{ + let s = concat!($str, "\x00"); + unsafe { $crate::CStr::new_unchecked(s) } + }}; } diff --git a/rust/kernel/src/user_ptr.rs b/rust/kernel/src/user_ptr.rs index 19ecb711e24a7b..5feec7cc38308c 100644 --- a/rust/kernel/src/user_ptr.rs +++ b/rust/kernel/src/user_ptr.rs @@ -9,7 +9,10 @@ use crate::c_types; use crate::error; extern "C" { - fn rust_helper_access_ok(addr: *const c_types::c_void, len: c_types::c_ulong) -> c_types::c_int; + fn rust_helper_access_ok( + addr: *const c_types::c_void, + len: c_types::c_ulong, + ) -> c_types::c_int; } /// A reference to an area in userspace memory, which can be either @@ -40,137 +43,152 @@ extern "C" { /// current process. pub struct UserSlicePtr(*mut c_types::c_void, usize); -impl UserSlicePtr { - /// Construct a user slice from a raw pointer and a length in bytes. - /// - /// Checks that the provided range is within the legal area for - /// userspace memory, using `access_ok` (e.g., on i386, the range - /// must be within the first 3 gigabytes), but does not check that - /// the actual pages are mapped in the current process with - /// appropriate permissions. Those checks are handled in the read - /// and write methods. - /// - /// This is `unsafe` because if it is called within `set_fs(KERNEL_DS)` context then - /// `access_ok` will not do anything. As a result the only place you can safely use this is - /// with an `__user` pointer that was provided by the kernel. - pub(crate) unsafe fn new( - ptr: *mut c_types::c_void, - length: usize, - ) -> error::KernelResult { - if rust_helper_access_ok(ptr, length as c_types::c_ulong) == 0 { - return Err(error::Error::EFAULT); +impl UserSlicePtr +{ + /// Construct a user slice from a raw pointer and a length in bytes. + /// + /// Checks that the provided range is within the legal area for + /// userspace memory, using `access_ok` (e.g., on i386, the range + /// must be within the first 3 gigabytes), but does not check that + /// the actual pages are mapped in the current process with + /// appropriate permissions. Those checks are handled in the read + /// and write methods. + /// + /// This is `unsafe` because if it is called within `set_fs(KERNEL_DS)` context then + /// `access_ok` will not do anything. As a result the only place you can safely use this is + /// with an `__user` pointer that was provided by the kernel. + pub(crate) unsafe fn new( + ptr: *mut c_types::c_void, + length: usize, + ) -> error::KernelResult + { + if rust_helper_access_ok(ptr, length as c_types::c_ulong) == 0 { + return Err(error::Error::EFAULT); + } + Ok(UserSlicePtr(ptr, length)) + } + + /// Read the entirety of the user slice and return it in a `Vec`. + /// + /// Returns EFAULT if the address does not currently point to + /// mapped, readable memory. + pub fn read_all(self) -> error::KernelResult> + { + self.reader().read_all() + } + + /// Construct a `UserSlicePtrReader` that can incrementally read + /// from the user slice. + pub fn reader(self) -> UserSlicePtrReader + { + UserSlicePtrReader(self.0, self.1) + } + + /// Write the provided slice into the user slice. + /// + /// Returns EFAULT if the address does not currently point to + /// mapped, writable memory (in which case some data from before the + /// fault may be written), or `data` is larger than the user slice + /// (in which case no data is written). + pub fn write_all(self, data: &[u8]) -> error::KernelResult<()> + { + self.writer().write(data) + } + + /// Construct a `UserSlicePtrWrite` that can incrementally write + /// into the user slice. + pub fn writer(self) -> UserSlicePtrWriter + { + UserSlicePtrWriter(self.0, self.1) } - Ok(UserSlicePtr(ptr, length)) - } - - /// Read the entirety of the user slice and return it in a `Vec`. - /// - /// Returns EFAULT if the address does not currently point to - /// mapped, readable memory. - pub fn read_all(self) -> error::KernelResult> { - self.reader().read_all() - } - - /// Construct a `UserSlicePtrReader` that can incrementally read - /// from the user slice. - pub fn reader(self) -> UserSlicePtrReader { - UserSlicePtrReader(self.0, self.1) - } - - /// Write the provided slice into the user slice. - /// - /// Returns EFAULT if the address does not currently point to - /// mapped, writable memory (in which case some data from before the - /// fault may be written), or `data` is larger than the user slice - /// (in which case no data is written). - pub fn write_all(self, data: &[u8]) -> error::KernelResult<()> { - self.writer().write(data) - } - - /// Construct a `UserSlicePtrWrite` that can incrementally write - /// into the user slice. - pub fn writer(self) -> UserSlicePtrWriter { - UserSlicePtrWriter(self.0, self.1) - } } pub struct UserSlicePtrReader(*mut c_types::c_void, usize); -impl UserSlicePtrReader { - /// Returns the number of bytes left to be read from this. Note that even - /// reading less than this number of bytes may return an Error(). - pub fn len(&self) -> usize { - self.1 - } - - /// Returns `true` if `self.len()` is 0. - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Read all data remaining in the user slice and return it in a `Vec`. - /// - /// Returns EFAULT if the address does not currently point to - /// mapped, readable memory. - pub fn read_all(&mut self) -> error::KernelResult> { - let mut data = vec![0; self.1]; - self.read(&mut data)?; - Ok(data) - } - - pub fn read(&mut self, data: &mut [u8]) -> error::KernelResult<()> { - if data.len() > self.1 || data.len() > u32::MAX as usize { - return Err(error::Error::EFAULT); +impl UserSlicePtrReader +{ + /// Returns the number of bytes left to be read from this. Note that even + /// reading less than this number of bytes may return an Error(). + pub fn len(&self) -> usize + { + self.1 } - let res = unsafe { - bindings::_copy_from_user( - data.as_mut_ptr() as *mut c_types::c_void, - self.0, - data.len() as _, - ) - }; - if res != 0 { - return Err(error::Error::EFAULT); + + /// Returns `true` if `self.len()` is 0. + pub fn is_empty(&self) -> bool + { + self.len() == 0 + } + + /// Read all data remaining in the user slice and return it in a `Vec`. + /// + /// Returns EFAULT if the address does not currently point to + /// mapped, readable memory. + pub fn read_all(&mut self) -> error::KernelResult> + { + let mut data = vec![0; self.1]; + self.read(&mut data)?; + Ok(data) + } + + pub fn read(&mut self, data: &mut [u8]) -> error::KernelResult<()> + { + if data.len() > self.1 || data.len() > u32::MAX as usize { + return Err(error::Error::EFAULT); + } + let res = unsafe { + bindings::_copy_from_user( + data.as_mut_ptr() as *mut c_types::c_void, + self.0, + data.len() as _, + ) + }; + if res != 0 { + return Err(error::Error::EFAULT); + } + // Since this is not a pointer to a valid object in our program, + // we cannot use `add`, which has C-style rules for defined + // behavior. + self.0 = self.0.wrapping_add(data.len()); + self.1 -= data.len(); + Ok(()) } - // Since this is not a pointer to a valid object in our program, - // we cannot use `add`, which has C-style rules for defined - // behavior. - self.0 = self.0.wrapping_add(data.len()); - self.1 -= data.len(); - Ok(()) - } } pub struct UserSlicePtrWriter(*mut c_types::c_void, usize); -impl UserSlicePtrWriter { - pub fn len(&self) -> usize { - self.1 - } - - pub fn is_empty(&self) -> bool { - self.len() == 0 - } +impl UserSlicePtrWriter +{ + pub fn len(&self) -> usize + { + self.1 + } - pub fn write(&mut self, data: &[u8]) -> error::KernelResult<()> { - if data.len() > self.1 || data.len() > u32::MAX as usize { - return Err(error::Error::EFAULT); + pub fn is_empty(&self) -> bool + { + self.len() == 0 } - let res = unsafe { - bindings::_copy_to_user( - self.0, - data.as_ptr() as *const c_types::c_void, - data.len() as _, - ) - }; - if res != 0 { - return Err(error::Error::EFAULT); + + pub fn write(&mut self, data: &[u8]) -> error::KernelResult<()> + { + if data.len() > self.1 || data.len() > u32::MAX as usize { + return Err(error::Error::EFAULT); + } + let res = unsafe { + bindings::_copy_to_user( + self.0, + data.as_ptr() as *const c_types::c_void, + data.len() as _, + ) + }; + if res != 0 { + return Err(error::Error::EFAULT); + } + // Since this is not a pointer to a valid object in our program, + // we cannot use `add`, which has C-style rules for defined + // behavior. + self.0 = self.0.wrapping_add(data.len()); + self.1 -= data.len(); + Ok(()) } - // Since this is not a pointer to a valid object in our program, - // we cannot use `add`, which has C-style rules for defined - // behavior. - self.0 = self.0.wrapping_add(data.len()); - self.1 -= data.len(); - Ok(()) - } } diff --git a/rust/module/src/lib.rs b/rust/module/src/lib.rs index 82dac6780b9ccf..11f37a659b36e4 100644 --- a/rust/module/src/lib.rs +++ b/rust/module/src/lib.rs @@ -4,125 +4,191 @@ extern crate proc_macro; -use proc_macro::{TokenStream, TokenTree, Group, Delimiter, token_stream}; - -fn expect_ident(it: &mut token_stream::IntoIter) -> String { - if let TokenTree::Ident(ident) = it.next().unwrap() { - ident.to_string() - } else { - panic!("Expected Ident"); - } +use proc_macro::{token_stream, Delimiter, Group, TokenStream, TokenTree}; + +fn expect_ident(it: &mut token_stream::IntoIter) -> String +{ + if let TokenTree::Ident(ident) = it.next().unwrap() { + ident.to_string() + } else { + panic!("Expected Ident"); + } } -fn expect_punct(it: &mut token_stream::IntoIter) -> char { - if let TokenTree::Punct(punct) = it.next().unwrap() { - punct.as_char() - } else { - panic!("Expected Punct"); - } +fn expect_punct(it: &mut token_stream::IntoIter) -> char +{ + if let TokenTree::Punct(punct) = it.next().unwrap() { + punct.as_char() + } else { + panic!("Expected Punct"); + } } -fn expect_literal(it: &mut token_stream::IntoIter) -> String { - if let TokenTree::Literal(literal) = it.next().unwrap() { - literal.to_string() - } else { - panic!("Expected Literal"); - } +fn expect_literal(it: &mut token_stream::IntoIter) -> String +{ + if let TokenTree::Literal(literal) = it.next().unwrap() { + literal.to_string() + } else { + panic!("Expected Literal"); + } } -fn expect_group(it: &mut token_stream::IntoIter) -> Group { - if let TokenTree::Group(group) = it.next().unwrap() { - group - } else { - panic!("Expected Group"); - } +fn expect_group(it: &mut token_stream::IntoIter) -> Group +{ + if let TokenTree::Group(group) = it.next().unwrap() { + group + } else { + panic!("Expected Group"); + } } -fn expect_end(it: &mut token_stream::IntoIter) { - if let None = it.next() { - } else { - panic!("Expected end"); - } +fn expect_end(it: &mut token_stream::IntoIter) +{ + if let None = it.next() { + } else { + panic!("Expected end"); + } } -fn get_ident(it: &mut token_stream::IntoIter, expected_name: &str) -> String { - assert_eq!(expect_ident(it), expected_name); - assert_eq!(expect_punct(it), ':'); - let ident = expect_ident(it); - assert_eq!(expect_punct(it), ','); - ident +fn get_ident(it: &mut token_stream::IntoIter, expected_name: &str) -> String +{ + assert_eq!(expect_ident(it), expected_name); + assert_eq!(expect_punct(it), ':'); + let ident = expect_ident(it); + assert_eq!(expect_punct(it), ','); + ident } -fn get_literal(it: &mut token_stream::IntoIter, expected_name: &str) -> String { - assert_eq!(expect_ident(it), expected_name); - assert_eq!(expect_punct(it), ':'); - let literal = expect_literal(it); - assert_eq!(expect_punct(it), ','); - literal +fn get_literal(it: &mut token_stream::IntoIter, expected_name: &str) -> String +{ + assert_eq!(expect_ident(it), expected_name); + assert_eq!(expect_punct(it), ':'); + let literal = expect_literal(it); + assert_eq!(expect_punct(it), ','); + literal } -fn get_group(it: &mut token_stream::IntoIter, expected_name: &str) -> Group { - assert_eq!(expect_ident(it), expected_name); - assert_eq!(expect_punct(it), ':'); - let group = expect_group(it); - assert_eq!(expect_punct(it), ','); - group +fn get_group(it: &mut token_stream::IntoIter, expected_name: &str) -> Group +{ + assert_eq!(expect_ident(it), expected_name); + assert_eq!(expect_punct(it), ':'); + let group = expect_group(it); + assert_eq!(expect_punct(it), ','); + group } -fn get_byte_string(it: &mut token_stream::IntoIter, expected_name: &str) -> String { - let byte_string = get_literal(it, expected_name); +fn get_byte_string( + it: &mut token_stream::IntoIter, + expected_name: &str, +) -> String +{ + let byte_string = get_literal(it, expected_name); - assert!(byte_string.starts_with("b\"")); - assert!(byte_string.ends_with("\"")); + assert!(byte_string.starts_with("b\"")); + assert!(byte_string.ends_with("\"")); - byte_string[2..byte_string.len() - 1].to_string() + byte_string[2..byte_string.len() - 1].to_string() } -fn __build_modinfo_string_base(module: &str, field: &str, content: &str, variable: &str, builtin: bool) -> String { - let string = if builtin { - // Built-in modules prefix their modinfo strings by `module.` - format!("{module}.{field}={content}", module=module, field=field, content=content) - } else { - // Loadable modules' modinfo strings go as-is - format!("{field}={content}", field=field, content=content) - }; +fn __build_modinfo_string_base( + module: &str, + field: &str, + content: &str, + variable: &str, + builtin: bool, +) -> String +{ + let string = if builtin { + // Built-in modules prefix their modinfo strings by `module.` + format!( + "{module}.{field}={content}", + module = module, + field = field, + content = content + ) + } else { + // Loadable modules' modinfo strings go as-is + format!("{field}={content}", field = field, content = content) + }; - format!( - " + format!( + " {cfg} #[link_section = \".modinfo\"] #[used] pub static {variable}: [u8; {length}] = *b\"{string}\\0\"; ", - cfg = if builtin { "#[cfg(not(MODULE))]" } else { "#[cfg(MODULE)]" }, - variable = variable, - length = string.len() + 1, - string = string, - ) + cfg = if builtin { + "#[cfg(not(MODULE))]" + } else { + "#[cfg(MODULE)]" + }, + variable = variable, + length = string.len() + 1, + string = string, + ) } -fn __build_modinfo_string_variable(module: &str, field: &str) -> String { - format!("__{module}_{field}", module=module, field=field) +fn __build_modinfo_string_variable(module: &str, field: &str) -> String +{ + format!("__{module}_{field}", module = module, field = field) } -fn build_modinfo_string_only_builtin(module: &str, field: &str, content: &str) -> String { - __build_modinfo_string_base(module, field, content, &__build_modinfo_string_variable(module, field), true) +fn build_modinfo_string_only_builtin( + module: &str, + field: &str, + content: &str, +) -> String +{ + __build_modinfo_string_base( + module, + field, + content, + &__build_modinfo_string_variable(module, field), + true, + ) } -fn build_modinfo_string_only_loadable(module: &str, field: &str, content: &str) -> String { - __build_modinfo_string_base(module, field, content, &__build_modinfo_string_variable(module, field), false) +fn build_modinfo_string_only_loadable( + module: &str, + field: &str, + content: &str, +) -> String +{ + __build_modinfo_string_base( + module, + field, + content, + &__build_modinfo_string_variable(module, field), + false, + ) } -fn build_modinfo_string(module: &str, field: &str, content: &str) -> String { - build_modinfo_string_only_builtin(module, field, content) - + &build_modinfo_string_only_loadable(module, field, content) +fn build_modinfo_string(module: &str, field: &str, content: &str) -> String +{ + build_modinfo_string_only_builtin(module, field, content) + + &build_modinfo_string_only_loadable(module, field, content) } -fn build_modinfo_string_param(module: &str, field: &str, param: &str, content: &str) -> String { - let variable = format!("__{module}_{field}_{param}", module=module, field=field, param=param); - let content = format!("{param}:{content}", param=param, content=content); - __build_modinfo_string_base(module, field, &content, &variable, true) - + &__build_modinfo_string_base(module, field, &content, &variable, false) +fn build_modinfo_string_param( + module: &str, + field: &str, + param: &str, + content: &str, +) -> String +{ + let variable = format!( + "__{module}_{field}_{param}", + module = module, + field = field, + param = param + ); + let content = + format!("{param}:{content}", param = param, content = content); + __build_modinfo_string_base(module, field, &content, &variable, true) + + &__build_modinfo_string_base( + module, field, &content, &variable, false, + ) } /// Declares a kernel module. @@ -152,59 +218,72 @@ fn build_modinfo_string_param(module: &str, field: &str, param: &str, content: & /// } /// ``` #[proc_macro] -pub fn module(ts: TokenStream) -> TokenStream { - let mut it = ts.into_iter(); - - let type_ = get_ident(&mut it, "type"); - let name = get_byte_string(&mut it, "name"); - let author = get_byte_string(&mut it, "author"); - let description = get_byte_string(&mut it, "description"); - let license = get_byte_string(&mut it, "license"); - let params = get_group(&mut it, "params"); - - expect_end(&mut it); - - assert_eq!(params.delimiter(), Delimiter::Brace); - - let mut it = params.stream().into_iter(); - - let mut params_modinfo = String::new(); - - loop { - let param_name = match it.next() { - Some(TokenTree::Ident(ident)) => ident.to_string(), - Some(_) => panic!("Expected Ident or end"), - None => break, - }; - - assert_eq!(expect_punct(&mut it), ':'); - let param_type = expect_ident(&mut it); - let group = expect_group(&mut it); - assert_eq!(expect_punct(&mut it), ','); - - assert_eq!(group.delimiter(), Delimiter::Brace); - - let mut param_it = group.stream().into_iter(); - let param_default = if param_type == "bool" { - get_ident(&mut param_it, "default") - } else { - get_literal(&mut param_it, "default") - }; - let param_permissions = get_literal(&mut param_it, "permissions"); - let param_description = get_byte_string(&mut param_it, "description"); - expect_end(&mut param_it); - - // TODO: more primitive types - // TODO: other kinds: arrays, unsafes, etc. - let param_kernel_type = match param_type.as_ref() { - "bool" => "bool", - "i32" => "int", - t => panic!("Unrecognized type {}", t), - }; - - params_modinfo.push_str(&build_modinfo_string_param(&name, "parmtype", ¶m_name, ¶m_kernel_type)); - params_modinfo.push_str(&build_modinfo_string_param(&name, "parm", ¶m_name, ¶m_description)); - params_modinfo.push_str( +pub fn module(ts: TokenStream) -> TokenStream +{ + let mut it = ts.into_iter(); + + let type_ = get_ident(&mut it, "type"); + let name = get_byte_string(&mut it, "name"); + let author = get_byte_string(&mut it, "author"); + let description = get_byte_string(&mut it, "description"); + let license = get_byte_string(&mut it, "license"); + let params = get_group(&mut it, "params"); + + expect_end(&mut it); + + assert_eq!(params.delimiter(), Delimiter::Brace); + + let mut it = params.stream().into_iter(); + + let mut params_modinfo = String::new(); + + loop { + let param_name = match it.next() { + Some(TokenTree::Ident(ident)) => ident.to_string(), + Some(_) => panic!("Expected Ident or end"), + None => break, + }; + + assert_eq!(expect_punct(&mut it), ':'); + let param_type = expect_ident(&mut it); + let group = expect_group(&mut it); + assert_eq!(expect_punct(&mut it), ','); + + assert_eq!(group.delimiter(), Delimiter::Brace); + + let mut param_it = group.stream().into_iter(); + let param_default = if param_type == "bool" { + get_ident(&mut param_it, "default") + } else { + get_literal(&mut param_it, "default") + }; + let param_permissions = + get_literal(&mut param_it, "permissions"); + let param_description = + get_byte_string(&mut param_it, "description"); + expect_end(&mut param_it); + + // TODO: more primitive types + // TODO: other kinds: arrays, unsafes, etc. + let param_kernel_type = match param_type.as_ref() { + "bool" => "bool", + "i32" => "int", + t => panic!("Unrecognized type {}", t), + }; + + params_modinfo.push_str(&build_modinfo_string_param( + &name, + "parmtype", + ¶m_name, + ¶m_kernel_type, + )); + params_modinfo.push_str(&build_modinfo_string_param( + &name, + "parm", + ¶m_name, + ¶m_description, + )); + params_modinfo.push_str( &format!( " static mut __{name}_{param_name}_value: {param_type} = {param_default}; @@ -261,11 +340,11 @@ pub fn module(ts: TokenStream) -> TokenStream { permissions = param_permissions, ) ); - } + } - let file = std::env::var("RUST_MODFILE").unwrap(); + let file = std::env::var("RUST_MODFILE").unwrap(); - format!( + format!( " static mut __MOD: Option<{type_}> = None; @@ -352,4 +431,3 @@ pub fn module(ts: TokenStream) -> TokenStream { initcall_section = ".initcall6.init" ).parse().unwrap() } - diff --git a/rust/shlex/src/lib.rs b/rust/shlex/src/lib.rs index 0b4a824fe4db33..8c874e98176a7a 100644 --- a/rust/shlex/src/lib.rs +++ b/rust/shlex/src/lib.rs @@ -21,211 +21,295 @@ use std::borrow::Cow; /// An iterator that takes an input string and splits it into the words using the same syntax as /// the POSIX shell. -pub struct Shlex<'a> { - in_iter: std::str::Bytes<'a>, - /// The number of newlines read so far, plus one. - pub line_no: usize, - /// An input string is erroneous if it ends while inside a quotation or right after an - /// unescaped backslash. Since Iterator does not have a mechanism to return an error, if that - /// happens, Shlex just throws out the last token, ends the iteration, and sets 'had_error' to - /// true; best to check it after you're done iterating. - pub had_error: bool, +pub struct Shlex<'a> +{ + in_iter: std::str::Bytes<'a>, + /// The number of newlines read so far, plus one. + pub line_no: usize, + /// An input string is erroneous if it ends while inside a quotation or right after an + /// unescaped backslash. Since Iterator does not have a mechanism to return an error, if that + /// happens, Shlex just throws out the last token, ends the iteration, and sets 'had_error' to + /// true; best to check it after you're done iterating. + pub had_error: bool, } -impl<'a> Shlex<'a> { - pub fn new(in_str: &'a str) -> Self { - Shlex { - in_iter: in_str.bytes(), - line_no: 1, - had_error: false, +impl<'a> Shlex<'a> +{ + pub fn new(in_str: &'a str) -> Self + { + Shlex { + in_iter: in_str.bytes(), + line_no: 1, + had_error: false, + } } - } - - fn parse_word(&mut self, mut ch: u8) -> Option { - let mut result: Vec = Vec::new(); - loop { - match ch as char { - '"' => if let Err(()) = self.parse_double(&mut result) { - self.had_error = true; - return None; - }, - '\'' => if let Err(()) = self.parse_single(&mut result) { - self.had_error = true; - return None; - }, - '\\' => if let Some(ch2) = self.next_char() { - if ch2 != '\n' as u8 { result.push(ch2); } - } else { - self.had_error = true; - return None; - }, - ' ' | '\t' | '\n' => { break; }, - _ => { result.push(ch as u8); }, - } - if let Some(ch2) = self.next_char() { ch = ch2; } else { break; } + + fn parse_word(&mut self, mut ch: u8) -> Option + { + let mut result: Vec = Vec::new(); + loop { + match ch as char { + '"' => { + if let Err(()) = + self.parse_double(&mut result) + { + self.had_error = true; + return None; + } + } + '\'' => { + if let Err(()) = + self.parse_single(&mut result) + { + self.had_error = true; + return None; + } + } + '\\' => { + if let Some(ch2) = self.next_char() { + if ch2 != '\n' as u8 { + result.push(ch2); + } + } else { + self.had_error = true; + return None; + } + } + ' ' | '\t' | '\n' => { + break; + } + _ => { + result.push(ch as u8); + } + } + if let Some(ch2) = self.next_char() { + ch = ch2; + } else { + break; + } + } + unsafe { Some(String::from_utf8_unchecked(result)) } } - unsafe { Some(String::from_utf8_unchecked(result)) } - } - - fn parse_double(&mut self, result: &mut Vec) -> Result<(), ()> { - loop { - if let Some(ch2) = self.next_char() { - match ch2 as char { - '\\' => { - if let Some(ch3) = self.next_char() { - match ch3 as char { - // \$ => $ - '$' | '`' | '"' | '\\' => { result.push(ch3); }, - // \ => nothing - '\n' => {}, - // \x => =x - _ => { result.push('\\' as u8); result.push(ch3); } - } + + fn parse_double(&mut self, result: &mut Vec) -> Result<(), ()> + { + loop { + if let Some(ch2) = self.next_char() { + match ch2 as char { + '\\' => { + if let Some(ch3) = + self.next_char() + { + match ch3 as char { + // \$ => $ + '$' | '`' + | '"' + | '\\' => { + result.push(ch3); + } + // \ => nothing + '\n' => {} + // \x => =x + _ => { + result.push('\\' as u8); + result.push(ch3); + } + } + } else { + return Err(()); + } + } + '"' => { + return Ok(()); + } + _ => { + result.push(ch2); + } + } } else { - return Err(()); + return Err(()); } - }, - '"' => { return Ok(()); }, - _ => { result.push(ch2); }, } - } else { - return Err(()); - } } - } - - fn parse_single(&mut self, result: &mut Vec) -> Result<(), ()> { - loop { - if let Some(ch2) = self.next_char() { - match ch2 as char { - '\\' => { - if let Some(ch3) = self.next_char() { - match ch3 as char { - // for single quotes, only these can be escaped - '\'' | '\\' => { result.push(ch3); }, - _ => { result.push('\\' as u8); result.push(ch3); } - } + + fn parse_single(&mut self, result: &mut Vec) -> Result<(), ()> + { + loop { + if let Some(ch2) = self.next_char() { + match ch2 as char { + '\\' => { + if let Some(ch3) = + self.next_char() + { + match ch3 as char { + // for single quotes, only these can be escaped + '\'' | '\\' => { + result.push(ch3); + } + _ => { + result.push('\\' as u8); + result.push(ch3); + } + } + } else { + return Err(()); + } + } + '\'' => { + return Ok(()); + } + _ => { + result.push(ch2); + } + } } else { - return Err(()); + return Err(()); } - }, - '\'' => { return Ok(()); }, - _ => { result.push(ch2); }, } - } else { - return Err(()); - } } - } - fn next_char(&mut self) -> Option { - let res = self.in_iter.next(); - if res == Some('\n' as u8) { self.line_no += 1; } - res - } + fn next_char(&mut self) -> Option + { + let res = self.in_iter.next(); + if res == Some('\n' as u8) { + self.line_no += 1; + } + res + } } -impl<'a> Iterator for Shlex<'a> { - type Item = String; - fn next(&mut self) -> Option { - if let Some(mut ch) = self.next_char() { - // skip initial whitespace - loop { - match ch as char { - ' ' | '\t' | '\n' => {}, - '#' => { - while let Some(ch2) = self.next_char() { - if ch2 as char == '\n' { break; } +impl<'a> Iterator for Shlex<'a> +{ + type Item = String; + fn next(&mut self) -> Option + { + if let Some(mut ch) = self.next_char() { + // skip initial whitespace + loop { + match ch as char { + ' ' | '\t' | '\n' => {} + '#' => { + while let Some(ch2) = + self.next_char() + { + if ch2 as char == '\n' { + break; + } + } + } + _ => { + break; + } + } + if let Some(ch2) = self.next_char() { + ch = ch2; + } else { + return None; + } } - }, - _ => { break; } + self.parse_word(ch) + } else { + // no initial character + None } - if let Some(ch2) = self.next_char() { ch = ch2; } else { return None; } - } - self.parse_word(ch) - } else { // no initial character - None } - } - } /// Convenience function that consumes the whole string at once. Returns None if the input was /// erroneous. -pub fn split(in_str: &str) -> Option> { - let mut shl = Shlex::new(in_str); - let res = shl.by_ref().collect(); - if shl.had_error { None } else { Some(res) } +pub fn split(in_str: &str) -> Option> +{ + let mut shl = Shlex::new(in_str); + let res = shl.by_ref().collect(); + if shl.had_error { + None + } else { + Some(res) + } } /// Given a single word, return a string suitable to encode it as a shell argument. -pub fn quote(in_str: &str) -> Cow { - if in_str.len() == 0 { - "\"\"".into() - } else if in_str.bytes().any(|c| match c as char { - '|' | '&' | ';' | '<' | '>' | '(' | ')' | '$' | '`' | '\\' | '"' | '\'' | ' ' | '\t' | - '\r' | '\n' | '*' | '?' | '[' | '#' | '~' | '=' | '%' => true, - _ => false - }) { - let mut out: Vec = Vec::new(); - out.push('"' as u8); - for c in in_str.bytes() { - match c as char { - '$' | '`' | '"' | '\\' => out.push('\\' as u8), - _ => () - } - out.push(c); +pub fn quote(in_str: &str) -> Cow +{ + if in_str.len() == 0 { + "\"\"".into() + } else if in_str.bytes().any(|c| match c as char { + '|' | '&' | ';' | '<' | '>' | '(' | ')' | '$' | '`' | '\\' + | '"' | '\'' | ' ' | '\t' | '\r' | '\n' | '*' | '?' | '[' + | '#' | '~' | '=' | '%' => true, + _ => false, + }) { + let mut out: Vec = Vec::new(); + out.push('"' as u8); + for c in in_str.bytes() { + match c as char { + '$' | '`' | '"' | '\\' => out.push('\\' as u8), + _ => (), + } + out.push(c); + } + out.push('"' as u8); + unsafe { String::from_utf8_unchecked(out) }.into() + } else { + in_str.into() } - out.push('"' as u8); - unsafe { String::from_utf8_unchecked(out) }.into() - } else { - in_str.into() - } } #[cfg(test)] -static SPLIT_TEST_ITEMS: &'static [(&'static str, Option<&'static [&'static str]>)] = &[ - ("foo$baz", Some(&["foo$baz"])), - ("foo baz", Some(&["foo", "baz"])), - ("foo\"bar\"baz", Some(&["foobarbaz"])), - ("foo \"bar\"baz", Some(&["foo", "barbaz"])), - (" foo \nbar", Some(&["foo", "bar"])), - ("foo\\\nbar", Some(&["foobar"])), - ("\"foo\\\nbar\"", Some(&["foobar"])), - ("'baz\\$b'", Some(&["baz\\$b"])), - ("'baz\\\''", Some(&["baz\'"])), - ("\\", None), - ("\"\\", None), - ("'\\", None), - ("\"", None), - ("'", None), - ("foo #bar\nbaz", Some(&["foo", "baz"])), - ("foo #bar", Some(&["foo"])), - ("foo#bar", Some(&["foo#bar"])), - ("foo\"#bar", None), +static SPLIT_TEST_ITEMS: &'static [( + &'static str, + Option<&'static [&'static str]>, +)] = &[ + ("foo$baz", Some(&["foo$baz"])), + ("foo baz", Some(&["foo", "baz"])), + ("foo\"bar\"baz", Some(&["foobarbaz"])), + ("foo \"bar\"baz", Some(&["foo", "barbaz"])), + (" foo \nbar", Some(&["foo", "bar"])), + ("foo\\\nbar", Some(&["foobar"])), + ("\"foo\\\nbar\"", Some(&["foobar"])), + ("'baz\\$b'", Some(&["baz\\$b"])), + ("'baz\\\''", Some(&["baz\'"])), + ("\\", None), + ("\"\\", None), + ("'\\", None), + ("\"", None), + ("'", None), + ("foo #bar\nbaz", Some(&["foo", "baz"])), + ("foo #bar", Some(&["foo"])), + ("foo#bar", Some(&["foo#bar"])), + ("foo\"#bar", None), ]; #[test] -fn test_split() { - for &(input, output) in SPLIT_TEST_ITEMS { - assert_eq!(split(input), output.map(|o| o.iter().map(|&x| x.to_owned()).collect())); - } +fn test_split() +{ + for &(input, output) in SPLIT_TEST_ITEMS { + assert_eq!( + split(input), + output.map(|o| o + .iter() + .map(|&x| x.to_owned()) + .collect()) + ); + } } #[test] -fn test_lineno() { - let mut sh = Shlex::new("\nfoo\nbar"); - while let Some(word) = sh.next() { - if word == "bar" { - assert_eq!(sh.line_no, 3); +fn test_lineno() +{ + let mut sh = Shlex::new("\nfoo\nbar"); + while let Some(word) = sh.next() { + if word == "bar" { + assert_eq!(sh.line_no, 3); + } } - } } #[test] -fn test_quote() { - assert_eq!(quote("foobar"), "foobar"); - assert_eq!(quote("foo bar"), "\"foo bar\""); - assert_eq!(quote("\""), "\"\\\"\""); - assert_eq!(quote(""), "\"\""); +fn test_quote() +{ + assert_eq!(quote("foobar"), "foobar"); + assert_eq!(quote("foo bar"), "\"foo bar\""); + assert_eq!(quote("\""), "\"\\\"\""); + assert_eq!(quote(""), "\"\""); } diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 00000000000000..e234c6f32fbf83 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,4 @@ +max_width = 80 +brace_style = "AlwaysNextLine" +tab_spaces = 8 +